Merge "msm: camera: Write a few registers before camera probe"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..4341e3a
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,27 @@
+cc_binary_host {
+ name: "unifdef",
+ srcs: ["scripts/unifdef.c"],
+ sanitize: {
+ never: true,
+ }
+}
+
+gensrcs {
+ name: "qseecom-kernel-includes",
+
+ // move to out/ as root for header generation because of scripts/unifdef
+ // storage - at the expense of extra ../ references
+ cmd: "pushd out && mkdir -p scripts && rm -f scripts/unifdef && ln -s ../../$(location unifdef) scripts/unifdef && ../$(location scripts/headers_install.sh) `dirname ../$(out)` ../ $(in) && popd",
+
+ tools: ["unifdef"],
+ tool_files: ["scripts/headers_install.sh"],
+ export_include_dirs: ["include/uapi"],
+ srcs: ["include/uapi/linux/qseecom.h"],
+ output_extension: "h",
+}
+
+cc_library_headers {
+ name: "qseecom-kernel-headers",
+ generated_headers: ["qseecom-kernel-includes"],
+ export_generated_headers: ["qseecom-kernel-includes"],
+}
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index fee35c0..0406076e4 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -32,7 +32,7 @@
Description of the physical chip / device for device X.
Typically a part number.
-What: /sys/bus/iio/devices/iio:deviceX/timestamp_clock
+What: /sys/bus/iio/devices/iio:deviceX/current_timestamp_clock
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
Description:
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index dfd56ec..6d75a9c 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -355,6 +355,7 @@
/sys/devices/system/cpu/vulnerabilities/meltdown
/sys/devices/system/cpu/vulnerabilities/spectre_v1
/sys/devices/system/cpu/vulnerabilities/spectre_v2
+ /sys/devices/system/cpu/vulnerabilities/spec_store_bypass
Date: January 2018
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
Description: Information about CPU vulnerabilities
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs
index db7aab1..b8d0a30 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -192,3 +192,14 @@
Contact: "Sheng Yong" <shengyong1@huawei.com>
Description:
Controls readahead inode block in readdir.
+
+What: /sys/fs/f2fs/<disk>/extension_list
+Date: Feburary 2018
+Contact: "Chao Yu" <yuchao0@huawei.com>
+Description:
+ Used to control configure extension list:
+ - Query: cat /sys/fs/f2fs/<disk>/extension_list
+ - Add: echo '[h/c]extension' > /sys/fs/f2fs/<disk>/extension_list
+ - Del: echo '[h/c]!extension' > /sys/fs/f2fs/<disk>/extension_list
+ - [h] means add/del hot file extension
+ - [c] means add/del cold file extension
diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt
index 89fd8f9..b3d2e4a 100644
--- a/Documentation/device-mapper/verity.txt
+++ b/Documentation/device-mapper/verity.txt
@@ -109,6 +109,17 @@
This is the offset, in <data_block_size> blocks, from the start of the
FEC device to the beginning of the encoding data.
+check_at_most_once
+ Verify data blocks only the first time they are read from the data device,
+ rather than every time. This reduces the overhead of dm-verity so that it
+ can be used on systems that are memory and/or CPU constrained. However, it
+ provides a reduced level of security because only offline tampering of the
+ data device's content will be detected, not online tampering.
+
+ Hash blocks are still verified each time they are read from the hash device,
+ since verification of hash blocks is less performance critical than data
+ blocks, and a hash block will not be verified any more after all the data
+ blocks it covers have been verified anyway.
Theory of operation
===================
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt
index bda03f0..ee9b465 100644
--- a/Documentation/devicetree/bindings/arm/coresight.txt
+++ b/Documentation/devicetree/bindings/arm/coresight.txt
@@ -1,222 +1,12 @@
* CoreSight Components:
CoreSight components are compliant with the ARM CoreSight architecture
-specification and can be connected in various topologies to suite a particular
-SoCs tracing needs. These trace components can generally be classified as sinks,
-links and sources. Trace data produced by one or more sources flows through the
-intermediate links connecting the source to the currently selected sink. Each
-CoreSight component device should use these properties to describe its hardware
-characteristcs.
-
-Required properties:
-
-- compatible : name of the component used for driver matching, should be one of
- the following:
- "arm,coresight-tmc" for coresight tmc-etr or tmc-etf device,
- "arm,coresight-tpiu" for coresight tpiu device,
- "qcom,coresight-replicator" for coresight replicator device,
- "arm,coresight-funnel" for coresight funnel devices,
- "qcom,coresight-tpda" for coresight tpda device,
- "qcom,coresight-tpdm" for coresight tpdm device,
- "qcom,coresight-dbgui" for coresight dbgui device
- "arm,coresight-stm" for coresight stm trace device,
- "arm,coresight-etm" for coresight etm trace devices,
- "arm,coresight-etmv4" for coresight etmv4 trace devices,
- "qcom,coresight-csr" for coresight csr device,
- "arm,coresight-cti" for coresight cti devices,
- "qcom,coresight-hwevent" for coresight hardware event devices
- "arm,coresight-fuse" for coresight fuse v1 device,
- "arm,coresight-fuse-v2" for coresight fuse v2 device,
- "arm,coresight-fuse-v3" for coresight fuse v3 device,
- "qcom,coresight-remote-etm" for coresight remote processor etm trace device,
- "qcom,coresight-qpdi" for coresight qpdi device
-- reg : physical base address and length of the register set(s) of the component.
- Not required for the following compatible string:
- - "qcom,coresight-remote-etm"
-- reg-names : names corresponding to each reg property value.
- Not required for the following compatible string:
- - "qcom,coresight-remote-etm"
- The reg-names that need to be used with corresponding compatible string
- for a coresight device are:
- - for coresight tmc-etr or tmc-etf device:
- compatible : should be "arm,coresight-tmc"
- reg-names : should be:
- "tmc-base" - physical base address of tmc configuration
- registers
- "bam-base" - physical base address of tmc-etr bam registers
- - for coresight tpiu device:
- compatible : should be "arm,coresight-tpiu"
- reg-names : should be:
- "tpiu-base" - physical base address of tpiu registers
- - for coresight replicator device
- compatible : should be "qcom,coresight-replicator"
- reg-names : should be:
- "replicator-base" - physical base address of replicator
- registers
- - for coresight funnel devices
- compatible : should be "arm,coresight-funnel"
- reg-names : should be:
- "funnel-base" - physical base address of funnel registers
- - for coresight tpda trace device
- compatible : should be "qcom,coresight-tpda"
- reg-names : should be:
- "tpda-base" - physical base address of tpda registers
- - for coresight tpdm trace device
- compatible : should be "qcom,coresight-tpdm"
- reg-names : should be:
- "tpdm-base" - physical base address of tpdm registers
- - for coresight dbgui device:
- compatible : should be "qcom,coresight-dbgui"
- reg-names : should be:
- "dbgui-base" - physical base address of dbgui registers
- - for coresight stm trace device
- compatible : should be "arm,coresight-stm"
- reg-names : should be:
- "stm-base" - physical base address of stm configuration
- registers
- "stm-data-base" - physical base address of stm data registers
- - for coresight etm trace devices
- compatible : should be "arm,coresight-etm"
- reg-names : should be:
- "etm-base" - physical base address of etm registers
- - for coresight etmv4 trace devices
- compatible : should be "arm,coresight-etmv4"
- reg-names : should be:
- "etm-base" - physical base address of etmv4 registers
- - for coresight csr device:
- compatible : should be "qcom,coresight-csr"
- reg-names : should be:
- "csr-base" - physical base address of csr registers
- - for coresight cti devices:
- compatible : should be "arm,coresight-cti"
- reg-names : should be:
- "cti<num>-base" - physical base address of cti registers
- - for coresight hardware event devices:
- compatible : should be "qcom,coresight-hwevent"
- reg-names : should be:
- "<ss-mux>" - physical base address of hardware event mux
- control registers where <ss-mux> is subsystem mux it
- represents
- - for coresight fuse device:
- compatible : should be "arm,coresight-fuse"
- reg-names : should be:
- "fuse-base" - physical base address of fuse registers
- "nidnt-fuse-base" - physical base address of nidnt fuse registers
- "qpdi-fuse-base" - physical base address of qpdi fuse registers
- - for coresight qpdi device:
- compatible : should be "qcom,coresight-qpdi"
- reg-names : should be:
- "qpdi-base" - physical base address of qpdi registers
-- coresight-id : unique integer identifier for the component
-- coresight-name : unique descriptive name of the component
-- coresight-nr-inports : number of input ports on the component
-
-Optional properties:
-
-- coresight-outports : list of output port numbers of this component
-- coresight-child-list : list of phandles pointing to the children of this
- component
-- coresight-child-ports : list of input port numbers of the children
-- coresight-default-sink : represents the default compile time CoreSight sink
-- coresight-ctis : list of ctis that this component interacts with
-- qcom,cti-save : boolean, indicating cti context needs to be saved and restored
-- qcom,cti-hwclk : boolean, indicating support of hardware clock to access cti
- registers to be saved and restored
-- qcom,cti-gpio-trigin : cti trigger input driven by gpio
-- qcom,cti-gpio-trigout : cti trigger output sent to gpio
-- qcom,pc-save : program counter save implemented
-- qcom,blk-size : block size for tmc-etr to usb transfers
-- qcom,memory-size : size of coherent memory to be allocated for tmc-etr buffer
-- qcom,round-robin : indicates if per core etms are allowed round-robin access
- by the funnel
-- qcom,write-64bit : only 64bit data writes supported by stm
-- qcom,data-barrier : barrier required for every stm data write to channel space
-- <supply-name>-supply: phandle to the regulator device tree node. The required
- <supply-name> is "vdd" for SD card and "vdd-io" for SD
- I/O supply. Used for tpiu component
-- qcom,<supply>-voltage-level : specifies voltage level for vdd supply. Should
- be specified in pairs (min, max) with units
- being uV. Here <supply> can be "vdd" for SD card
- vdd supply or "vdd-io" for SD I/O vdd supply.
-- qcom,<supply>-current-level : specifies current load levels for vdd supply.
- Should be specified in pairs (lpm, hpm) with
- units being uA. Here <supply> can be "vdd" for
- SD card vdd supply or "vdd-io" for SD I/O vdd
- supply.
-- qcom,hwevent-clks : list of clocks required by hardware event driver
-- qcom,hwevent-regs : list of regulators required by hardware event driver
-- qcom,byte-cntr-absent : specifies if the byte counter feature is absent on
- the device. Only relevant in case of tmc-etr device.
-- interrupts : <a b c> where a is 0 or 1 depending on if the interrupt is
- spi/ppi, b is the interrupt number and c is the mask,
-- interrupt-names : a list of strings that map in order to the list of
- interrupts specified in the 'interrupts' property.
-- qcom,sg-enable : indicates whether scatter gather feature is supported for TMC
- ETR configuration.
-- qcom,force-reg-dump : boolean, indicate whether TMC register need to be dumped.
- Used for TMC component
-- qcom,nidntsw : boolean, indicating NIDnT software debug or trace support
- present. Used for tpiu component
-- qcom,nidnthw : boolean, indicating NIDnT hardware sensing support present.
- Used for tpiu component
- qcom,nidntsw and qcom,nidnthw are mutually exclusive properties, either of
- these may specified for tpiu component
-- qcom,nidnt-swduart : boolean, indicating NIDnT swd uart support present. Used
- for tpiu component
-- qcom,nidnt-swdtrc : boolean, indicating NIDnT swd trace support present. Used
- for tpiu component
-- qcom,nidnt-jtag : boolean, indicating NIDnT jtag debug support present. Used
- for tpiu component
-- qcom,nidnt-spmi : boolean, indicating NIDnT spmi debug support present. Used
- for tpiu component
-- nidnt-gpio : specifies gpio for NIDnT hardware detection
-- nidnt-gpio-polarity : specifies gpio polarity for NIDnT hardware detection
-- pinctrl-names : names corresponding to the numbered pinctrl. The allowed
- names are subset of the following: cti-trigin-pctrl,
- cti-trigout-pctrl. Used for cti component
-- pinctrl-<n>: list of pinctrl phandles for the different pinctrl states. Refer
- to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt".
-- qcom,funnel-save-restore : boolean, indicating funnel port needs to be disabled
- for the ETM whose CPU is being powered down. The port
- state is restored when CPU is powered up. Used for
- funnel component.
-- qcom,tmc-flush-powerdown : boolean, indicating trace data needs to be flushed before
- powering down CPU. Used for TMC component.
-- qcom,bc-elem-size : specifies the BC element size supported by each monitor
- connected to the aggregator on each port. Should be specified
- in pairs (port, bc element size).
-- qcom,tc-elem-size : specifies the TC element size supported by each monitor
- connected to the aggregator on each port. Should be specified
- in pairs (port, tc element size).
-- qcom,dsb-elem-size : specifies the DSB element size supported by each monitor
- connected to the aggregator on each port. Should be specified
- in pairs (port, dsb element size).
-- qcom,cmb-elem-size : specifies the CMB element size supported by each monitor
- connected to the aggregator on each port. Should be specified
- in pairs (port, cmb element size).
-- qcom,clk-enable: specifies whether additional clock bit needs to be set for
- M4M TPDM.
-- qcom,tpda-atid : specifies the ATID for TPDA.
-- qcom,inst-id : QMI instance id for remote ETMs.
-- qcom,noovrflw-enable : boolean, indicating whether no overflow bit needs to be
- set in ETM stall control register.
-- coresight-cti-cpu : cpu phandle for cpu cti, required when qcom,cti-save is true
-- coresight-etm-cpu : specifies phandle for the cpu associated with the ETM device
-- qcom,dbgui-addr-offset : indicates the offset of dbgui address registers
-- qcom,dbgui-data-offset : indicates the offset of dbgui data registers
-- qcom,dbgui-size : indicates the size of dbgui address and data registers
-- qcom,pmic-carddetect-gpio : indicates the hotplug capabilities of the qpdi driver
-- qcom,cpuss-debug-cgc: debug clock gating phandle for etm
- reg : the clock gating register for each cluster
- cluster : indicate the cluster number
-
-coresight-outports, coresight-child-list and coresight-child-ports lists will
-be of the same length and will have a one to one correspondence among the
-elements at the same list index.
-
-coresight-default-sink must be specified for one of the sink devices that is
-intended to be made the default sink. Other sink devices must not have this
-specified. Not specifying this property on any of the sinks is invalid.
+specification and can be connected in various topologies to suit a particular
+SoCs tracing needs. These trace components can generally be classified as
+sinks, links and sources. Trace data produced by one or more sources flows
+through the intermediate links connecting the source to the currently selected
+sink. Each CoreSight component device should use these properties to describe
+its hardware characteristcs.
* Required properties for all components *except* non-configurable replicators:
@@ -320,6 +110,9 @@
* cpu: the cpu phandle this ETM/PTM is affined to. When omitted the
source is considered to belong to CPU0.
+ * qcom,tupwr-disable: For ETM, don't keep trace unit powered across power
+ collapse.
+
* Optional property for TMC:
* arm,buffer-size: size of contiguous buffer space for TMC ETR
diff --git a/Documentation/devicetree/bindings/arm/msm/heap-sharing.txt b/Documentation/devicetree/bindings/arm/msm/heap-sharing.txt
index e63d09b..de2a963 100644
--- a/Documentation/devicetree/bindings/arm/msm/heap-sharing.txt
+++ b/Documentation/devicetree/bindings/arm/msm/heap-sharing.txt
@@ -29,6 +29,10 @@
- qcom,allocate-boot-time: Indicates whether clients needs boot time memory allocation.
+- qcom,allocate-on-request: Indicates memory allocation happens only when client requests.
+
+/* "qcom,allocate-boot-time" and "qcom,allocate-on-request" are mutually exclusive properties. */
+
Example:
qcom,memshare {
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 9c2d647..1a9c8bd 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -95,6 +95,9 @@
- QCS605
compatible = "qcom,qcs605"
+- SXR1120
+ compatible = "qcom,sxr1120"
+
- SDA670
compatible = "qcom,sda670"
@@ -324,8 +327,11 @@
compatible = "qcom,sdm670-qrd"
compatible = "qcom,qcs605-cdp"
compatible = "qcom,qcs605-mtp"
+compatible = "qcom,sxr1120-mtp"
+compatible = "qcom,sxr1120-cdp"
compatible = "qcom,sda670-cdp"
compatible = "qcom,sda670-mtp"
+compatible = "qcom,sda670-hdk"
compatible = "qcom,msm8952-rumi"
compatible = "qcom,msm8952-sim"
compatible = "qcom,msm8952-qrd"
diff --git a/Documentation/devicetree/bindings/arm/msm/pm.txt b/Documentation/devicetree/bindings/arm/msm/pm.txt
new file mode 100644
index 0000000..b66d4a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm.txt
@@ -0,0 +1,49 @@
+* MSM PM
+
+PM is the low power management device for MSM (Snapdragon class) chipsets.
+This device sets up different components to do low power modes and registers with
+the kernel to be notified of idle and suspend states and when called, follows
+through the set of instructions in putting the application cores to the lowest
+power mode possible.
+The PC debug counter reserves 16 registers in the IMEM memory space which maintains
+a count on the state of power collapse on each core. This count will be useful to
+debug the power collapse state on each core.
+
+The required properties for PM are:
+
+- compatible: "qcom,pm"
+
+The optional properties are:
+
+- reg: physical IMEM address reserved for PC counters and the size
+- qcom,use-sync-timer: Indicates whether the target uses the synchronized QTimer.
+- qcom,synced-clocks: Indicates that all cpus running off a single clock source and to
+ instantiate the necessary clock source.
+- qcom,pc-resets-timer: Indicates that the timer gets reset during power collapse.
+- qcom,tz-flushes-cache: Indicates that TZ flushes all of the cache during
+power collapse. MSM PM can decide to not perform cache flush operations to
+reduce latency associated with L2 PC.
+- qcom,saw-turns-off-pll: Indicates that the CPU's PLL can be managed from SAW
+ hardware. On such targets software management of PLL is not required. If
+ this property is specified then qcom,synced-clocks would be ignored.
+- qcom,no-pll-switch-for-retention: Boolean property, to indicate that the cpu
+ clock can be sourced even from the HFPLL even when the cpu is in
+ retention, and need not be switched to an always on pll. If this flag
+ is set then the cpu clock is not ramped down when entering retention or
+ ramped up on exiting retention.
+
+Example 1:
+
+qcom,pm@fe800664 {
+ compatible = "qcom,pm";
+ reg = <0xfe800664 0x40>;
+ qcom,use-sync-timer;
+ };
+
+Example 2:
+
+qcom,pm@fe800664 {
+ compatible = "qcom,pm";
+ reg = <0xfe800664 0x40>;
+ qcom,saw-turns-off-pll;
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt b/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt
new file mode 100644
index 0000000..4f7111f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt
@@ -0,0 +1,35 @@
+* MSM PM SNOC client
+
+MSM PM SNOC client device is used to setup a bus request for 100 Mhz for the
+SNOC bus when the Apps cores are active. This bus request helps mitigate the
+exit latency from power collapse in cases where there aren't any active bus
+requests for SNOC.
+
+This device is dependent on the pm-8x60 device, which configures the low power
+mode of respective cores.
+
+The required properties of this device are:
+
+- compatible: qcom,pm-snoc-client
+- qcom,msm-bus,name: String representing the client-name
+- qcom,msm-bus,num-cases: Total number of usecases
+- qcom,msm-bus,active-only: Boolean context flag for requests in active or
+ dual (active & sleep) contex
+- qcom,msm-bus,num-paths: Total number of master-slave pairs
+- qcom,msm-bus,vectors-KBps: Arrays of unsigned integers representing:
+ master-id, slave-id, arbitrated bandwidth
+ in KBps, instantaneous bandwidth in KBps
+
+
+Example:
+ qcom,pm-snoc-client {
+ compatible = "qcom,pm-snoc-client";
+ qcom,msm-bus,name = "ocimem_snoc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors =
+ <22 512 0 0>,
+ <22 512 320000 3200000>;
+ };
+
diff --git a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt
index 2b7b3fa..606da38 100644
--- a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt
+++ b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt
@@ -1,11 +1,14 @@
-* Amlogic Meson8b Clock and Reset Unit
+* Amlogic Meson8, Meson8b and Meson8m2 Clock and Reset Unit
-The Amlogic Meson8b clock controller generates and supplies clock to various
-controllers within the SoC.
+The Amlogic Meson8 / Meson8b / Meson8m2 clock controller generates and
+supplies clock to various controllers within the SoC.
Required Properties:
-- compatible: should be "amlogic,meson8b-clkc"
+- compatible: must be one of:
+ - "amlogic,meson8-clkc" for Meson8 (S802) SoCs
+ - "amlogic,meson8b-clkc" for Meson8 (S805) SoCs
+ - "amlogic,meson8m2-clkc" for Meson8m2 (S812) SoCs
- reg: it must be composed by two tuples:
0) physical base address of the xtal register and length of memory
mapped region.
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 0589165..15f1e93 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -103,6 +103,8 @@
- qcom,sde-dsc-size: A u32 value indicates the address range for each dsc.
- qcom,sde-cdm-size: A u32 value indicates the address range for each cdm.
- qcom,sde-pp-size: A u32 value indicates the address range for each pingpong.
+- qcom,sde-te-source: Array of GPIO sources indicating which pingpong TE is
+ sourced to which panel TE gpio.
- qcom,sde-wb-size: A u32 value indicates the address range for each writeback.
- qcom,sde-len: A u32 entry for SDE address range.
- qcom,sde-intf-max-prefetch-lines: Array of u32 values for max prefetch lines on
@@ -502,6 +504,7 @@
qcom,sde-pp-off = <0x00071000 0x00071800
0x00072000 0x00072800>;
qcom,sde-pp-slave = <0x0 0x0 0x0 0x0>;
+ qcom,sde-te-source = <0x0 0x1 0x0 0x0>;
qcom,sde-cdm-off = <0x0007a200>;
qcom,sde-dsc-off = <0x00081000 0x00081400>;
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
diff --git a/Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt b/Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt
similarity index 84%
rename from Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt
rename to Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt
index 7175dc3..ed34253 100644
--- a/Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt
+++ b/Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt
@@ -2,7 +2,7 @@
========================
Required properties:
-- compatible: "toppoly,td028ttec1"
+- compatible: "tpo,td028ttec1"
Optional properties:
- label: a symbolic name for the panel
@@ -14,7 +14,7 @@
-------
lcd-panel: td028ttec1@0 {
- compatible = "toppoly,td028ttec1";
+ compatible = "tpo,td028ttec1";
reg = <0>;
spi-max-frequency = <100000>;
spi-cpol;
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index 4f7ae75..bda9d6f 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -47,10 +47,13 @@
Documentation/devicetree/bindings/media/video-interfaces.txt. The
first port should be the input endpoint, the second one the output
- The output should have two endpoints. The first is the block
- connected to the TCON channel 0 (usually a panel or a bridge), the
- second the block connected to the TCON channel 1 (usually the TV
- encoder)
+ The output may have multiple endpoints. The TCON has two channels,
+ usually with the first channel being used for the panels interfaces
+ (RGB, LVDS, etc.), and the second being used for the outputs that
+ require another controller (TV Encoder, HDMI, etc.). The endpoints
+ will take an extra property, allwinner,tcon-channel, to specify the
+ channel the endpoint is associated to. If that property is not
+ present, the endpoint number will be used as the channel number.
On SoCs other than the A33, there is one more clock required:
- 'tcon-ch1': The clock driving the TCON channel 1
diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
index 806c458..c17970c 100644
--- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
@@ -519,6 +519,11 @@
to identify the default topology for the
display. The first set is indexed by the
value 0.
+- qcom,mdss-dsi-dma-schedule-line: An integer value indicates the line number after vertical active
+ region, at which command DMA needs to be triggered.
+- qcom,mdss-dsi-panel-cmds-only-by-right: Boolean used to mention whether the panel support DSI1 or
+ DSI0 to send commands. If this was set, that mean the panel only support
+ DSI1 to send commands, otherwise DSI0 will send comands.
Required properties for sub-nodes: None
Optional properties:
@@ -771,5 +776,6 @@
qcom,display-topology = <1 1 1>,
<2 2 1>;
qcom,default-topology-index = <0>;
+ qcom,mdss-dsi-dma-schedule-line = <5>;
};
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index b8cb903..73a6dbc 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -195,6 +195,10 @@
"bl_ctrl_wled" = Backlight controlled by WLED.
"bl_ctrl_dcs" = Backlight controlled by DCS commands.
other: Unknown backlight control. (default)
+- qcom,mdss-dsi-bl-dcs-command-state: A string that specifies the ctrl state for sending brightness
+ controlling commands, this is only available when backlight is controlled by DCS commands.
+ "dsi_lp_mode" = DSI low power mode (default).
+ "dsi_hs_mode" = DSI high speed mode.
- qcom,mdss-dsi-bl-pwm-pmi: Boolean to indicate that PWM control is through second pmic chip.
- qcom,mdss-dsi-bl-pmic-bank-select: LPG channel for backlight.
Required if blpmiccontroltype is PWM
@@ -452,6 +456,11 @@
- qcom,cmd-to-video-mode-switch-commands: List of commands that need to be sent
to panel in order to switch from command mode to video mode dynamically.
Refer to "qcom,mdss-dsi-on-command" section for adding commands.
+- qcom,mode-switch-commands-state: String that specifies the ctrl state for sending commands to switch
+ the panel mode, it applies for both switches, from command to video and
+ from video to command.
+ "dsi_lp_mode" = DSI low power mode (default)
+ "dsi_hs_mode" = DSI high speed mode
- qcom,send-pps-before-switch: Boolean propety to indicate when PPS commands should be sent,
either before or after switch commands during dynamic resolution
switch in DSC panels. If the property is not present, the default
@@ -687,6 +696,7 @@
qcom,video-to-cmd-mode-switch-commands = [15 01 00 00 00 00 02 C2 0B
15 01 00 00 00 00 02 C2 08];
qcom,cmd-to-video-mode-switch-commands = [15 01 00 00 00 00 02 C2 03];
+ qcom,mode-switch-commands-state = "dsi_hs_mode";
qcom,send-pps-before-switch;
qcom,panel-ack-disabled;
qcom,mdss-dsi-horizontal-line-idle = <0 40 256>,
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt
index 8b593a9..2d689d2 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt
@@ -110,6 +110,10 @@
both modes.
- qcom,platform-intf-mux-gpio: Select dsi/external(hdmi) interface through gpio when it supports
either dsi or external interface.
+- qcom,platform-bklight-en-gpio-invert: Invert the gpio used to enable display back-light
+- qcom,panel-mode-gpio: Specifies the GPIO to select video/command/single-port/dual-port
+ mode of panel through gpio when it supports these modes.
+- qcom,ext-vdd-gpio: Specifies the GPIO to enable external VDD supply.
- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node
Refer to pinctrl-bindings.txt
- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin
@@ -253,6 +257,8 @@
qcom,platform-bklight-en-gpio = <&msmgpio 86 0>;
qcom,platform-mode-gpio = <&msmgpio 7 0>;
qcom,platform-intf-mux-gpio = <&tlmm 115 0>;
+ qcom,platform-bklight-en-gpio-invert;
+ qcom,panel-mode-gpio = <&msmgpio 107 0>;
qcom,dsi-irq-line;
qcom,lane-map = "lane_map_3012";
qcom,display-id = "primary";
diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt
index 6b9238c..5c0bb2a 100644
--- a/Documentation/devicetree/bindings/fb/mdss-pll.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt
@@ -14,7 +14,7 @@
"qcom,mdss_hdmi_pll_8996_v2", "qcom,mdss_dsi_pll_8996_v2",
"qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_dsi_pll_8952",
"qcom,mdss_dsi_pll_8937", "qcom,mdss_hdmi_pll_8996_v3_1p8",
- "qcom,mdss_dsi_pll_8953"
+ "qcom,mdss_dsi_pll_8953", "qcom,mdss_dsi_pll_sdm439"
- cell-index: Specifies the controller used
- reg: offset and length of the register set for the device.
- reg-names : names to refer to register sets related to this device
diff --git a/Documentation/devicetree/bindings/fb/mdss-spi-client.txt b/Documentation/devicetree/bindings/fb/mdss-spi-client.txt
new file mode 100644
index 0000000..0d5fde8
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-spi-client.txt
@@ -0,0 +1,27 @@
+Qualcomm Technologies, Inc. mdss-spi-client
+
+mdss-spi-client is for SPI display to send the FB data to SPI master.
+
+Required properties:
+- compatible : should be "qcom,mdss-spi-client"
+- spi-max-frequency : Maximum SPI clocking speed of device in Hz
+
+Optional properties:
+- label: A string used to describe the controller used.
+- spi-cpol : Boolean property indicating device requires inverse
+ clock polarity (CPOL) mode
+- spi-cpha : Empty property indicating device requires shifted
+ clock phase (CPHA) mode
+- spi-cs-high : Empty property indicating device requires
+ chip select active high
+
+Example:
+spi@78b9000 { /* BLSP1 QUP5 */
+ qcom,mdss_spi_client {
+ reg = <0>;
+ compatible = "qcom,mdss-spi-client";
+ label = "MDSS SPI QUP5 CLIENT";
+ spi-max-frequency = <50000000>;
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt
new file mode 100644
index 0000000..d46068f
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt
@@ -0,0 +1,203 @@
+Qualcomm Technologies, Inc. mdss-spi-panel
+
+mdss-spi-panel is a SPI panel device which supports panels that
+are compatible with display serial interface specification.
+
+Required properties:
+- qcom,mdss-spi-panel-controller: Specifies the phandle for the SPI controller that
+ this panel will be mapped to.
+- qcom,mdss-spi-panel-width: Specifies panel width in pixels.
+- qcom,mdss-spi-panel-height: Specifies panel height in pixels.
+- qcom,mdss-spi-bpp: Specifies the panel bits per pixels.
+ 3 = for rgb111
+ 8 = for rgb332
+ 12 = for rgb444
+ 16 = for rgb565
+ 18 = for rgb666
+ 24 = for rgb888
+- qcom,mdss-spi-panel-destination: A string that specifies the destination display for the panel.
+ "display_1" = DISPLAY_1
+ "display_2" = DISPLAY_2
+- qcom,mdss-spi-on-command: A byte stream formed by multiple packets
+ byte 0: wait number of specified ms after command
+ transmitted
+ byte 1: 8 bits length in network byte order
+ byte 3 and beyond: number byte of payload
+- qcom,mdss-spi-off-command: A byte stream formed by multiple packets
+ byte 0: wait number of specified ms after command
+ transmitted
+ byte 1: 8 bits length in network byte order
+ byte 3 and beyond: number byte of payload
+Optional properties:
+- qcom,mdss-spi-panel-name: A string used as a descriptive name of the panel
+- qcom,cont-splash-enabled: Boolean used to enable continuous splash mode.
+ If this property is specified, it is required to
+ to specify the memory reserved for the splash
+ screen using the qcom,memblock-reserve binding
+ for the framebuffer device attached to the panel.
+- qcom,mdss-spi-h-back-porch: Horizontal back porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-h-front-porch: Horizontal front porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-h-pulse-width: Horizontal pulse width.
+ 2 = default value.
+- qcom,mdss-spi-h-sync-skew: Horizontal sync skew value.
+ 0 = default value.
+- qcom,mdss-spi-v-back-porch: Vertical back porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-v-front-porch: Vertical front porch value in pixels.
+ 6 = default value.
+- qcom,mdss-spi-v-pulse-width: Vertical pulse width.
+ 2 = default value.
+- qcom,mdss-spi-bl-pmic-control-type: A string that specifies the implementation of backlight
+ control for this panel.
+ "bl_ctrl_pwm" = Backlight controlled by PWM gpio.
+ "bl_ctrl_wled" = Backlight controlled by WLED.
+ other: Unknown backlight control. (default)
+- qcom,mdss-spi-bl-min-level: Specifies the min backlight level supported by the panel.
+ 0 = default value.
+- qcom,mdss-spi-bl-max-level: Specifies the max backlight level supported by the panel.
+ 255 = default value.
+- qcom,mdss-spi-panel-framerate: Specifies the frame rate for the panel.
+- qcom,esd-check-enabled: Boolean used to enable ESD recovery feature.
+- qcom,mdss-spi-panel-status-check-mode:Specifies the panel status check method for ESD recovery.
+ "send_init_command" = send init code to recover panel status.
+ "reg_read" = Read register value to check the panel status.
+- qcom,mdss-spi-panel-status-reg:Unsigned 8bits integer value to specifies the value
+ of panel status register address.
+- qcom,mdss-spi-panel-status-read-length:Unsigned 8bits integer value that specifies
+ the expected read-back length of the panel register.
+- qcom,mdss-spi-panel-status-value:An unsigned 8bits integer araray that specifies the
+ values of the panel status register which is used to
+ check the panel status.
+ The size of this array is specified by
+ qcom,mdss-dsi-panel-status-read-length.
+
+Example:
+&mdss_mdp {
+ spi_gc9305_qvga_cmd: qcom,mdss_spi_gc9305_qvga_cmd {
+ qcom,mdss-spi-panel-name = "gc9305 qvga command mode spi panel";
+ qcom,mdss-spi-panel-destination = "display_1";
+ qcom,mdss-spi-panel-controller = <&mdss_spi>;
+ qcom,mdss-spi-panel-framerate = <30>;
+ qcom,mdss-spi-panel-width = <240>;
+ qcom,mdss-spi-panel-height = <320>;
+ qcom,mdss-spi-h-front-porch = <79>;
+ qcom,mdss-spi-h-back-porch = <59>;
+ qcom,mdss-spi-h-pulse-width = <60>;
+ qcom,mdss-spi-v-back-porch = <10>;
+ qcom,mdss-spi-v-front-porch = <7>;
+ qcom,mdss-spi-v-pulse-width = <2>;
+ qcom,mdss-spi-h-left-border = <0>;
+ qcom,mdss-spi-h-right-border = <0>;
+ qcom,mdss-spi-v-top-border = <0>;
+ qcom,mdss-spi-v-bottom-border = <0>;
+ qcom,mdss-spi-bpp = <16>;
+ qcom,mdss-spi-on-command = [00 01 FE
+ 00 01 EF
+ 00 02 36 48
+ 00 02 3A 05
+ 00 02 35 00
+ 00 03 A4 44 44
+ 00 03 A5 42 42
+ 00 03 AA 88 88
+ 00 03 E8 12 40
+ 00 03 E3 01 10
+ 00 02 FF 61
+ 00 02 AC 00
+ 00 03 A6 2A 2A
+ 00 03 A7 2B 2B
+ 00 03 A8 18 18
+ 00 03 A9 2A 2A
+ 00 02 AD 33
+ 00 02 AF 55
+ 00 02 AE 2B
+ 00 05 2A 00 00 00 EF
+ 00 05 2B 00 00 01 3F
+ 00 01 2C
+ 00 07 F0 02 02 00 08 0C 10
+ 00 07 F1 01 00 00 14 1D 0E
+ 00 07 F2 10 09 37 04 04 48
+ 00 07 F3 10 0B 3F 05 05 4E
+ 00 07 F4 0D 19 17 1D 1E 0F
+ 00 07 F5 06 12 13 1A 1B 0F
+ 78 01 11
+ 00 01 29
+ 00 01 2C];
+ qcom,mdss-spi-off-command = [20 01 28
+ 20 01 10];
+ qcom,mdss-spi-bl-min-level = <1>;
+ qcom,mdss-spi-bl-max-level = <4095>;
+ qcom,esd-check-enabled;
+ qcom,mdss-spi-panel-status-check-mode = "reg_read";
+ qcom,mdss-spi-panel-status-reg = /bits/ 8 <0x09>;
+ qcom,mdss-spi-panel-status-read-length = <4>;
+ qcom,mdss-spi-panel-status-value = /bits/ 8 <0x52 0x29 0x83 0x00>;
+ };
+};
+
+mdss-spi-display is a spi interface display which support send frame
+data and command to panel, compatible with SPI interface specification.
+
+Required properties:
+- compatible: This property applies to SPI panels only.
+ compatible = "qcom,mdss-spi-display".
+- vdd-supply: Phandle for vdd regulator device node.
+- vddio-supply: Phandle for vdd-io regulator device node.
+- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the interface is mapped.
+- qcom,mdss-mdp: pHandle that specifies the mdss-mdp device.
+- qcom,panel-supply-entries: A node that lists the elements of the supply used to
+ power the DSI panel. There can be more than one instance
+ of this binding, in which case the entry would be appended
+ with the supply entry index. For a detailed description
+ fields in the supply entry, refer to the qcom,ctrl-supply-entries
+ binding above.
+- qcom,platform-spi-dc-gpio: Pull down this gpio indicate current package is command,
+ Pull up this gpio indicate current package is parameter or pixels.
+
+Optional properties:
+- label:A string used to describe the controller used.
+ -- qcom,supply-name: name of the supply (vdd/vdda/vddio)
+ -- qcom,supply-min-voltage: minimum voltage level (uV)
+ -- qcom,supply-max-voltage: maximum voltage level (uV)
+ -- qcom,supply-enable-load: load drawn (uA) from enabled supply
+ -- qcom,supply-disable-load: load drawn (uA) from disabled supply
+ -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on
+ -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
+ -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
+ -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
+
+Example:
+ mdss_spi: qcom,mdss_spi {
+ compatible = "qcom,mdss-spi-display";
+ label = "mdss spi panel";
+
+ qcom,mdss-fb-map = <&mdss_fb0>;
+ qcom,mdss-mdp = <&mdss_mdp>;
+ vdd-supply = <&pm8909_l17>;
+ vddio-supply = <&pm8909_l6>;
+ qcom,platform-spi-dc-gpio = <&msm_gpio 110 0>;
+
+ qcom,panel-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <2850000>;
+ qcom,supply-max-voltage = <2850000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 4fb47d6..a4e9ba7 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -127,6 +127,19 @@
mask - mask for the relevant bits in the efuse register.
shift - number of bits to right shift to get the speed bin
value.
+
+- qcom,gpu-speed-bin-vectors:
+ GPU speed bin vectors property is the series of all the vectors
+ in format specified below. Values from individual fuses are read,
+ masked and shifted to form a value. At the end all fuse values
+ are ordered together to form final speed bin value.
+ <offset mask shift>
+ <offset mask shift>
+ < .. .. .. >
+ offset - offset of the efuse register from the base.
+ mask - mask for the relevant bits in the efuse register.
+ shift - number of bits to right shift.
+
- qcom,gpu-disable-fuse: GPU disable fuse
<offset mask shift>
offset - offset of the efuse register from the base.
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index d9d3470..432c482 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -10,6 +10,7 @@
Required properties:
- compatible : should be "qcom,qpnp-vadc" for Voltage ADC device driver and
"qcom,qpnp-vadc-hc" for VADC_HC voltage ADC device driver.
+ should include "qcom,qpnp-adc-hc-pm5" for PMIC5.
- reg : offset and length of the PMIC Aribter register map.
- address-cells : Must be one.
- size-cells : Must be zero.
diff --git a/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt b/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt
new file mode 100644
index 0000000..27c304e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt
@@ -0,0 +1,28 @@
+The ICM20602 sensor is 6-axis gyroscope+accelerometer combo
+device which is made by InvenSense Inc.
+
+Required properties:
+
+ - compatible : Should be "invensense,icm20602".
+ - reg : the I2C address which depends on the AD0 pin.
+ - interrupt-parent : should be the phandle for the interrupt controller
+ - interrupts : interrupt mapping for GPIO IRQ
+
+Optional properties:
+ - invensense,icm20602-gpio: the irq gpio. This is not used if
+ using SMD to trigger. this is needed only if using the
+ device IRQ to trigger. Only using SMD to trigger can
+ support 8K sample rate.
+
+Example:
+ icm20602-i2c@068 {
+ compatible = "invensense,icm20602";
+ reg = <0x68>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <13 0>;
+ invensense,icm20602-gpio = <&msm_gpio 13 0x0>;
+ pinctrl-names = "imu_active","imu_suspend";
+ pinctrl-0 = <&imu_int_active>;
+ pinctrl-1 = <&imu_int_suspend>;
+ status = "okay";
+ };
diff --git a/Documentation/devicetree/bindings/input/gen_vkeys.txt b/Documentation/devicetree/bindings/input/gen_vkeys.txt
new file mode 100644
index 0000000..2f8d65e
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/gen_vkeys.txt
@@ -0,0 +1,28 @@
+Touchscreen Virtual Keys Device
+
+Generate virtual keys sysfs entry for Android
+
+Required properties:
+
+ - compatible : should be "qcom,gen-vkeys"
+ - label : name of the touch controller
+ - qcom,disp-maxx : Maximum x-coordinate of display
+ - qcom,disp-maxy : Maximum y-coordinate of display
+ - qcom,panel-maxx : Maximum x-coordinate of touch panel
+ - qcom,panel-maxy : Maximum y-coordinate of touch panel
+ - qcom,key-codes : Array of key codes for virtual keys
+
+Optional properties:
+ - qcom,y-offset : Offset of y-location for virtual keys, default 0
+
+Example:
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <720>;
+ qcom,disp-maxy = <1280>;
+ qcom,panel-maxx = <760>;
+ qcom,panel-maxy = <1424>;
+ qcom,key-codes = <158 139 102 217>;
+ qcom,y-offset = <35>;
+ };
diff --git a/Documentation/devicetree/bindings/input/misc/stmvl53l0x.txt b/Documentation/devicetree/bindings/input/misc/stmvl53l0x.txt
new file mode 100644
index 0000000..bf10122
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/misc/stmvl53l0x.txt
@@ -0,0 +1,36 @@
+STM VL53L0X Time-of-Flight ranging and gesture detection sensor driver
+
+Description:
+
+The VL53L0X is a new generation Time-of-Flight
+(ToF) laser-ranging module housed in the
+smallest package on the market today, providing
+accurate distance measurement whatever the
+target reflectances unlike conventional
+technologies. It can measure absolute distances
+up to 2m, setting a new benchmark in ranging
+performance levels, opening the door to various
+new applications.
+The VL53L0X integrates a leading-edge SPAD
+array (Single Photon Avalanche Diodes) and
+embeds ST’s second generation FlightSenseTM
+patented technology.
+The VL53L0X’s 940 nm VCSEL emitter (Vertical
+Cavity Surface-Emitting Laser), is totally invisible
+to the human eye, coupled with internal physical
+infrared filters, it enables longer ranging
+distances, higher immunity to ambient light, and
+better robustness to cover glass optical crosstalk.
+
+Required properties:
+
+ - compatible : Should be "st,stmvl53l0".
+ - reg : i2c slave address of the device.
+
+Example:
+ i2c@f9925000 {
+ vl53l0x@52 {
+ compatible = "st,stmvl53l0";
+ reg = <0x52>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/himax.txt b/Documentation/devicetree/bindings/input/touchscreen/himax.txt
index 258ffec..b54c859 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/himax.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/himax.txt
@@ -3,3 +3,20 @@
Required properties:
- compatible : Should be "himax,hxcommon"
+ - reg : i2c slave address of the device.
+ - interrupt-parent : parent of interrupt.
+ - himax,irq-gpio : irq gpio.
+ - himax,reset-gpio : reset gpio.
+ - vdd-supply : Power supply needed to power up the device.
+ - avdd-supply : Power source required to power up i2c bus.
+ - himax,panel-coords : panel coordinates for the chip in pixels.
+ It is a four tuple consisting of min x,
+ min y, max x and max y values.
+ - himax,display-coords : display coordinates in pixels. It is a four
+ tuple consisting of min x, min y, max x and
+ max y values
+
+Optional properties:
+ - himax,3v3-gpio : gpio acting as 3.3 v supply.
+ - report_type : Multi-touch protocol type. Default 0.
+ 0 for protocol A, 1 for protocol B.
diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
index 87a551ba..885be72 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
@@ -24,6 +24,7 @@
- synaptics,reset-on-state : reset gpio active state.
- synaptics,power-on-state : power switch active state.
- synaptics,ub-i2c-addr : microbootloader mode I2C slave address.
+ - synaptics,do-not-disable-regulators : If specified, regulators cannot be disabled/enabled during suspend/resume.
- synaptics,cap-button-codes : virtual key code mappings to be used.
- synaptics,vir-button-codes : virtual key code and the response region on panel.
- synaptics,wakeup-gestures-en: enable wakeup gestures.
@@ -33,6 +34,7 @@
- synaptics,reset-active-ms : reset active duration for controller (ms), default 100.
- synaptics,power-delay-ms : power delay for controller (ms), default 100.
- synaptics,max-y-for-2d : maximal y value of the panel.
+ - synaptics,bus-lpm-cur-uA : I2C bus idle mode current setting.
- synaptics,swap-axes : specify whether to swap axes.
- synaptics,resume-in-workqueue : specify whether to defer the resume to workqueue.
- clock-names : Clock names used for secure touch. They are: "iface_clk", "core_clk"
diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
index 4c29cda..fdc3418 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
@@ -57,6 +57,10 @@
occupied by the redistributors. Required if more than one such
region is present.
+- ignored-save-restore-irqs: Array of u32 elements, specifying the interrupts
+ which are ignored while doing gicd save/restore. Maximum of 10 elements
+ is supported at present.
+
Sub-nodes:
PPI affinity can be expressed as a single "ppi-partitions" node,
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
index 833b108..18e8cf5 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,mpm.txt
@@ -52,7 +52,7 @@
Example:
wakegic: wake-gic@7781b8 {
- compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953", "qcom,mpm-gic-msm8937";
+ compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8953", "qcom,mpm-gic-msm8937", "qcom,mpm-gic-msm8909";
interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
reg = <0x601d4 0x1000>,
<0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
@@ -85,7 +85,7 @@
Example:
wakegpio: wake-gpio {
- compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8953", "qcom,mpm-gpio-msm8937";
+ compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8953", "qcom,mpm-gpio-msm8937","qcom,mpm-gpio-msm8909";
interrupt-controller;
interrupt-parent = <&tlmm>;
#interrupt-cells = <2>;
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 78aa1d7..23e5f20 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -77,6 +77,16 @@
- qcom,tz-device-id : A string indicating the device ID for this SMMU known
to TZ. See msm_tz_smmu.c for a full list of mappings.
+- qcom,enable-static-cb:
+ A boolean indicating that the SMMU global register space,
+ as well as some context banks, are managed by secure software
+ and may not be modified by HLOS.
+
+- qcom,static-ns-cbs:
+ A list of u32.
+ When qcom,enable-static-cb is selected, indicates which
+ iommu context banks may be used by HLOS.
+
- qcom,skip-init : Disable resetting configuration for all context banks
during device reset. This is useful for targets where
some context banks are dedicated to other execution
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
index 1a76d5d..258504e 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
@@ -116,6 +116,11 @@
Definition: Short circuit debounce cycles for internal PWM.
Allowed values: 0, 8, 16 or 32.
+- vcc_pon-supply
+ Usage: optional
+ Value type: <phandle>
+ Definition: PON driver regulator required to force MBG_ON
+
Following properties are specific only to LRA vibrators.
- qcom,lra-auto-mode
diff --git a/Documentation/devicetree/bindings/mcd/mcd.txt b/Documentation/devicetree/bindings/mcd/mcd.txt
deleted file mode 100644
index 4077ee2..0000000
--- a/Documentation/devicetree/bindings/mcd/mcd.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-* MCD (MobiCore Driver)
-
-t-base is an operating system running in the secure world (TrustZone).
-The t-base implementation consists of several components in the
-secure world and the non-secure world (kernel and user space). The
-MobiCore driver communicates with the t-base operating system that
-exists in TrustZone.
-
-Required properties:
- - compatible: Should be "qcom,mcd"
- - qcom,ce-hw-instance: should contain crypto HW instance
- - qcom,ce-device: Device number
- - clocks: Array of <clock_controller_phandle clock_reference> listing
- all the clocks that are accesed by this subsystem.
- - qcom,ce-opp-freq: indicates the CE operating frequency in Hz, changes from target to target.
-
-Example:
- mcd {
- compatible = "qcom,mcd";
- qcom,ce-hw-instance = <0>;
- qcom,ce-device = <0>;
- clocks = <&clock_gcc clk_crypto_clk_src>,
- <&clock_gcc clk_gcc_crypto_clk>,
- <&clock_gcc clk_gcc_crypto_ahb_clk>,
- <&clock_gcc clk_gcc_crypto_axi_clk>;
- clock-names = "core_clk_src", "core_clk",
- "iface_clk", "bus_clk";
- qcom,ce-opp-freq = <100000000>;
- };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index 66eaae1..e1e486f 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -71,6 +71,11 @@
Value type: <u32>
Definition: CAM HW Version information.
+- camnoc-axi-min-ib-bw
+ Usage: optional
+ Value type: <u64>
+ Definition: Min camnoc axi bw for the given target.
+
- regulator-names
Usage: required
Value type: <string>
diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
index 8f3ad9a..b41d260 100644
--- a/Documentation/devicetree/bindings/mfd/axp20x.txt
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -28,6 +28,9 @@
regulator to drive the OTG VBus, rather then as an input pin
which signals whether the board is driving OTG VBus or not.
+- x-powers,master-mode: Boolean (axp806 only). Set this when the PMIC is
+ wired for master mode. The default is slave mode.
+
- <input>-supply: a phandle to the regulator supply node. May be omitted if
inputs are unregulated, such as using the IPSOUT output
from the PMIC.
diff --git a/Documentation/devicetree/bindings/misc/fpc,fpc1028.txt b/Documentation/devicetree/bindings/misc/fpc,fpc1028.txt
new file mode 100644
index 0000000..233c7cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/fpc,fpc1028.txt
@@ -0,0 +1,85 @@
+Fingerprint Cards AB. Fpc1028 driver
+
+The fpc1028 fingerprint sensor is connected to the host processor via SPI.
+The sensor will generates interrupts when the user touches the sensor.
+The host controller is expected to read data over SPI and pass the data to
+the rest of the system.
+
+This binding document describes the properties for this module.
+
+Properties:
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: It must be "fpc,fpc1020"
+
+- interrupts
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Peripheral interrupt specifier.
+
+- interrupt-parent
+ Usage: required
+ Value type: <phandle>
+ Definition: phandle of the interrupt controller which services the
+ summary interrupt.
+
+- fpc,gpio_rst
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: GPIO which connecting to the reset pin of fpc1028
+
+- fpc,gpio_irq
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Specifies the GPIO which connecting to the irq pin of fpc1028.
+
+- vcc_spi-supply
+ Usage: required
+ Value type: <phandle>
+ Definition: The phandle of the regulator which supplies fpc1028 spi bus core.
+
+- vcc_io-supply
+ Usage: required
+ Value type: <phandle>
+ Definition: The phandle of the regulator which supplies fpc1028 io pins.
+
+- vcc_ana-supply
+ Usage: required
+ Value type: <phandle>
+ Definition: The phandle of the regulator which supplies fpc1028 analog circuit.
+
+- pinctrl-names:
+ Usage: required
+ Value type: <string>
+ Definition: Pinctrl state names for each pin group configuration.
+ eg:"fpc1020_reset_reset", "fpc1020_reset_active", "fpc1020_irq_active".
+ refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+
+- pinctrl-n:
+ Usage: required
+ Value type: <string>
+ Definition: pinctrl state for each pin group
+ refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+
+
+Example:
+
+ fpc1020 {
+ compatible = "fpc,fpc1020";
+ interrupt-parent = <&tlmm>;
+ interrupts = <48 0>;
+ fpc,gpio_rst = <&tlmm 124 0x0>;
+ fpc,gpio_irq = <&tlmm 48 0>;
+ vcc_spi-supply = <&pm8953_l5>;
+ vdd_io-supply = <&pm8953_l5>;
+ vdd_ana-supply = <&pm8953_l5>;
+ fpc,enable-on-boot;
+ pinctrl-names = "fpc1020_reset_reset",
+ "fpc1020_reset_active",
+ "fpc1020_irq_active";
+ pinctrl-0 = <&msm_gpio_124>;
+ pinctrl-1 = <&msm_gpio_124_output_high>;
+ pinctrl-2 = <&msm_gpio_48>;
+ };
diff --git a/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt b/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt
index eff3d82..6f0cbfed 100644
--- a/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt
+++ b/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt
@@ -10,6 +10,7 @@
- reg: Offset and length of the register regions for the mac and io-macro
- interrupts: Interrupt number used by this controller
- io-macro-info: Internal io-macro-info
+- emac_emb_smmu: Internal emac smmu node
Optional:
- qcom,msm-bus,name: String representing the client-name
@@ -20,16 +21,25 @@
in KBps, instantaneous bandwidth in KBps
qcom,bus-vector-names: specifies string IDs for the corresponding bus vectors
in the same order as qcom,msm-bus,vectors-KBps property.
+- qcom,arm-smmu: Boolean, if present enables EMAC SMMU support in sdxpoorwills.
Internal io-macro-info:
- io-macro-bypass-mode: <0 or 1> internal or external delay configuration
- io-interface: <rgmii/mii/rmii> PHY interface used
+Internal emac_emb_smmu:
+- compatible: Should be "qcom,emac-smmu-embedded".
+- qcom,smmu-s1-bypass: Boolean, if present S1 bypass is enabled.
+- iommus: Includes the <&smmu_phandle stream_id size> pair for each context
+ bank.
+- qcom,iova-mapping: <starting_address size> of the smmu context bank.
+
Example:
soc {
emac_hw: qcom,emac@00020000 {
compatible = "qcom,emac-dwc-eqos";
+ qcom,arm-smmu;
reg = <0x20000 0x10000>,
<0x36000 0x100>;
reg-names = "emac-base", "rgmii-base";
@@ -57,5 +67,12 @@
io-macro-bypass-mode = <0>;
io-interface = "rgmii";
};
+
+ emac_emb_smmu: emac_emb_smmu {
+ compatible = "qcom,emac-smmu-embedded";
+ qcom,smmu-s1-bypass;
+ iommus = <&apps_smmu 0x220 0xf>;
+ qcom,iova-mapping = <0x80000000 0x40000000>;
+ };
};
}
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt
index caf297b..c28d4eb8 100644
--- a/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt
@@ -35,6 +35,15 @@
- ti,palmas-enable-dvfs2: Enable DVFS2. Configure pins for DVFS2 mode.
Selection primary or secondary function associated to GPADC_START
and SYSEN2 pin/pad for DVFS2 interface
+- ti,palmas-override-powerhold: This is applicable for PMICs for which
+ GPIO7 is configured in POWERHOLD mode which has higher priority
+ over DEV_ON bit and keeps the PMIC supplies on even after the DEV_ON
+ bit is turned off. This property enables driver to over ride the
+ POWERHOLD value to GPIO7 so as to turn off the PMIC in power off
+ scenarios. So for GPIO7 if ti,palmas-override-powerhold is set
+ then the GPIO_7 field should never be muxed to anything else.
+ It should be set to POWERHOLD by default and only in case of
+ power off scenarios the driver will over ride the mux value.
This binding uses the following generic properties as defined in
pinctrl-bindings.txt:
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index 73cd1db..917c2d0 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -37,6 +37,8 @@
compatible "qcom,ipa-smmu-uc-cb"
- qcom,use-a2-service: determine if A2 service will be used
- qcom,use-ipa-tethering-bridge: determine if tethering bridge will be used
+- qcom,use-ipa-in-mhi-mode: Boolean context flag to indicate whether
+ device booting in MHI config or not.
- qcom,use-ipa-bamdma-a2-bridge: determine if a2/ipa hw bridge will be used
- qcom,ee: which EE is assigned to (non-secure) APPS from IPA-BAM POV. This
is a number
@@ -116,6 +118,10 @@
controller phandle and "clk_ipa_clk" as macro for "iface_clk"
- clock-names: This property shall contain the clock input names used
by driver in same order as the clocks property.This should be "iface_clk"
+- emulator-bar0-offset: Specifies the offset, within PCIe BAR0, where
+ IPA/GSI programmable registers reside. This property is used only
+ with the IPA/GSI emulation system, which is connected to and
+ communicated with via PCIe.
IPA SMMU sub nodes
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
index f6a7a1b..1e44686 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
@@ -109,6 +109,10 @@
this. If this property is not specified,
low battery voltage threshold will be
configured to 4200 mV.
+- qcom,fg-rconn-mohm: Battery connector resistance (Rconn) in
+ milliohms. If Rconn is specified, then
+ Rslow values will be updated to account
+ it for an accurate ESR.
- qcom,cycle-counter-en: Boolean property which enables the cycle
counter feature. If this property is
present, then the following properties
@@ -143,6 +147,14 @@
battery voltage shadow and the current
predicted voltage in uV to initiate
capacity learning.
+- qcom,cl-max-limit-deciperc: The maximum percent that the capacity
+ cannot go above during any capacity
+ learning cycle. This property is in the
+ unit of .1% increments.
+- qcom,cl-min-limit-deciperc: The minimum percent that the capacity
+ cannot go below during any capacity
+ learning cycle. This property is in the
+ unit of .1% increments.
- qcom,capacity-estimation-on: A boolean property to have the fuel
gauge driver attempt to estimate the
battery capacity using battery
@@ -178,6 +190,97 @@
settings will be different from default.
Once SOC crosses 5%, ESR pulse timings
will be restored back to default.
+- qcom,fg-control-slope-limiter: A boolean property to specify if SOC
+ slope limiter coefficients needs to
+ be modified based on charging status
+ and battery temperature threshold.
+- qcom,fg-slope-limit-temp-threshold: Temperature threshold in decidegC used
+ for applying the slope coefficient based
+ on charging status and battery
+ temperature. If this property is not
+ specified, a default value of 100 (10C)
+ will be applied by default.
+- qcom,fg-slope-limit-low-temp-chg: When the temperature goes below the
+ specified temperature threshold and
+ battery is charging, slope coefficient
+ specified with this property will be
+ applied. If this property is not
+ specified, a default value of 45 will be
+ applied.
+- qcom,fg-slope-limit-low-temp-dischg: Same as "qcom,fg-slope-limit-low-temp-chg"
+ except this is when the battery is
+ discharging.
+- qcom,fg-slope-limit-high-temp-chg: When the temperature goes above the
+ specified temperature threshold and
+ battery is charging, slope coefficient
+ specified with this property will be
+ applied. If this property is not
+ specified, a default value of 2 will be
+ applied.
+- qcom,fg-slope-limit-high-temp-dischg: Same as "qcom,fg-slope-limit-high-temp-chg"
+ except this is when the battery is
+ discharging.
+- qcom,fg-dischg-voltage-gain-ctrl: A boolean property to specify if the
+ voltage gain needs to be modified
+ during discharging based on monotonic
+ soc.
+- qcom,fg-dischg-voltage-gain-soc: Array of monotonic SOC threshold values
+ to change the voltage gain settings
+ during discharge. This should be defined
+ in the ascending order and in the range
+ of 0-100. Array limit is set to 3.
+ If qcom,fg-dischg-voltage-gain-ctrl is
+ set, then this property should be
+ specified to apply the gain settings.
+- qcom,fg-dischg-med-voltage-gain: Array of voltage gain values that needs
+ to be applied to medC voltage gain when
+ the monotonic SOC goes below the SOC
+ threshold specified under
+ qcom,fg-dischg-voltage-gain-soc. Array
+ limit is set to 3.
+ If qcom,fg-dischg-voltage-gain-ctrl is
+ set, then this property should be
+ specified to apply the gain setting.
+- qcom,fg-dischg-high-voltage-gain: Array of voltage gain values that needs
+ to be applied to highC voltage gain when
+ the monotonic SOC goes below the SOC
+ threshold specified under
+ qcom,fg-dischg-voltage-gain-soc. Array
+ limit is set to 3.
+ If qcom,fg-dischg-voltage-gain-ctrl is
+ set, then this property should be
+ specified to apply the gain setting.
+- qcom,fg-use-vbat-low-empty-soc: A boolean property to specify whether
+ vbatt-low interrupt is used to handle
+ empty battery condition. If this is
+ not specified, empty battery condition
+ is detected by empty-soc interrupt.
+- qcom,fg-batt-temp-low-limit: Battery temperature (in decidegC) low
+ limit which will be used to validate
+ the battery temperature reading from FG.
+ If the battery temperature goes below
+ this limit, last read good temperature
+ will be notified to userspace. If this
+ limit is not specified, then the
+ default limit would be -60C.
+- qcom,fg-batt-temp-high-limit: Battery temperature (in decidegC) high
+ limit which will be used to validate
+ the battery temperature reading from FG.
+ If the battery temperature goes above
+ this limit, last read good temperature
+ will be notified to userspace. If this
+ limit is not specified, then the
+ default limit would be 150C.
+- qcom,fg-cc-soc-limit-pct: Percentage of CC_SOC before resetting
+ FG and restore the full CC_SOC value.
+- qcom,fg-restore-batt-info: A boolean property to specify whether
+ battery parameters needs to be
+ restored. If this feature is enabled,
+ then validating the battery parameters
+ by OCV/battery SOC, validation range
+ in percentage should be specified via
+ appropriate module parameters to make
+ it work properly.
qcom,fg-soc node required properties:
- reg : offset and length of the PMIC peripheral register map.
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt
new file mode 100644
index 0000000..6dccdd3
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt
@@ -0,0 +1,211 @@
+Qualcomm QPNP Linear Charger
+
+The charger module supports the linear battery charger peripherals on
+Qualcomm PMIC chips.
+
+There are four different peripherals in the charger module.
+Each of these peripherals are implemented as subnodes.
+
+- qcom,chgr: Supports charging control and status reporting
+- qcom,bat-if: Battery status reporting such as presence and
+ temperature reporting.
+- qcom,usb-chgpth: USB charge path detection and input current
+ limiting configuration.
+- qcom,chg-misc: Miscellaneous features such as comparator override
+ features etc.
+
+Parent node required properties:
+- qcom,vddmax-mv: Target voltage of battery in mV.
+- qcom,vddsafe-mv: Maximum Vdd voltage in mV.
+- qcom,vinmin-mv: Minimum input voltage in mV.
+- qcom,ibatsafe-ma: Safety battery current setting
+
+Parent node optional properties:
+- qcom,vbatweak-uv: Weak battery voltage threshold in uV,
+ above which fast charging can start.
+ The supported voltage range is from
+ 3000000uV to 3581250uV with a step
+ size of 18750000 uV.
+- qcom,charging-disabled: Set this property to disable charging
+ by default.
+- qcom,use-default-batt-values: Set this flag to force reporting of
+ fake battery.
+- qcom,warm-bat-decidegc: Warm battery temperature in decidegC.
+- qcom,cool-bat-decidegc: Cool battery temperature in decidegC.
+ Note that if both warm and cool
+ battery temperatures are set, the
+ corresponding ibatmax and bat-mv
+ properties are required to be set.
+- qcom,ibatmax-cool-ma: Maximum cool battery charge current.
+- qcom,ibatmax-warm-ma: Maximum warm battery charge current.
+- qcom,warm-bat-mv: Warm temperature battery target
+ voltage.
+- qcom,cool-bat-mv: Cool temperature battery target
+ voltage.
+- qcom,thermal-mitigation: Array of ibatmax values for different
+ system thermal mitigation level.
+- qcom,tchg-mins: Maximum total software initialized
+ charge time.
+- qcom,bpd-detection: Select a battery presence detection
+ scheme by specifying either "bpd_thm"
+ "bpd_id" or "bpd_thm_id". "bpd_thm"
+ selects the temperature pin, "bpd_id"
+ uses the id pin for battery presence
+ detection, "bpd_thm_id" selects both.
+ If the property is not set, the
+ temperatue pin will be used.
+- qcom,btc-disabled: If flag is set battery hot and cold
+ monitoring is disabled in hardware.
+ This monitoring is turned on by
+ default.
+- qcom,batt-hot-percentage: Specify a supported hot threshold
+ percentage.
+ Supported thresholds: 25% and 35%. If
+ none is specified hardware defaults
+ will be used.
+- qcom,batt-cold-percentage: Specify a supported cold threshold
+ percentage. Supported thresholds: 70%
+ and 80%. If none is specified
+ hardwaredefaults will be used.
+- qcom,chg-adc_tm Corresponding ADC TM device's phandle
+ to set recurring measurements and
+ receive notification for batt_therm.
+-qcom,float-charge If specified enable float charging.
+- qcom,chg-vadc Corresponding VADC device's phandle.
+- qcom,charger-detect-eoc If specified charger hardware will
+ detect end-of-charge.
+ If not specified charger driver
+ depends on BMSfor end-of-charge
+ detection.
+- qcom,disable-vbatdet-based-recharge If specified disable VBATDET irq
+ and charging can only be resumed
+ if charger is re-inserted or SOC
+ falls below resume SOC.
+ This property should always be used
+ along with the BMS property:
+ "qcom,disable-suspend-on-usb".
+- qcom,use-external-charger If specified the LBC module will
+ be disabled and the driver will not
+ register. It also enables BID for
+ BPD and disables BTC. Declare this node
+ only if you are using an external charger
+ and not the PMIC internal LBC.
+- qcom,chgr-led-support There is a current sink device in linear
+ charger module, it is used to control a
+ led which can act as a charger led as well
+ as a general notification led.
+- qcom,parallel-charger This is a bool property to indicate the
+ LBC will operate as a secondary charger
+ in the parallel mode. If this is enabled
+ the charging operations will be controlled by
+ the primary-charger.
+- qcom,collapsible-chgr-support If specified the collapsible charger feature
+ will be supported. LBC will disable VIN_MIN
+ comparator and use chg_gone interrupt to
+ detect charger removal.
+
+
+Sub node required structure:
+- A qcom,charger node must be a child of an SPMI node that has specified
+ the spmi-dev-container property. Each subnode reflects
+ a hardware peripheral which adds a unique set of features
+ to the collective charging device. For example USB detection
+ and the battery interface are each separate peripherals and
+ each should be their own subnode.
+
+Sub node required properties:
+- compatible: Must be "qcom,qpnp-linear-charger".
+- reg: Specifies the SPMI address and size for this
+ peripheral.
+- interrupts: Specifies the interrupt associated with the
+ peripheral.
+- interrupt-names: Specifies the interrupt names for the peripheral.
+ Every available interrupt needs to have an associated
+ name with it to indentify its purpose.
+
+ The following lists each subnode and their
+ corresponding required interrupt names:
+
+ qcom,usb-chgpth:
+ - usbin-valid
+
+ The following interrupts are available:
+
+ qcom,usb-chgpth:
+ - usbin-valid: Indicates valid USB
+ connection.
+ - coarse-det-usb: Coarse detect interrupt
+ triggers at low voltage on
+ USB_IN.
+ - chg-gone: Triggers on VCHG line.
+ - overtemp: Triggers on over temperature
+ condition
+
+ qcom,chgr:
+ - chg-done: Triggers on charge completion.
+ - chg-failed: Notifies of charge failures.
+ - fast-chg-on: Notifies of fast charging.
+ - vbat-det-lo: Triggers on vbat-det-lo
+ voltage.
+
+Example:
+ pm8916-chg: qcom,charger {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-linear-charger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,vddmax-mv = <4200>;
+ qcom,vddsafe-mv = <4200>;
+ qcom,vinmin-mv = <4200>;
+ qcom,ibatsafe-ma = <1440>;
+ qcom,vbatweak-uv = <3200>;
+ qcom,thermal-mitigation = <1500 700 600 325>;
+ qcom,cool-bat-decidegc = <100>;
+ qcom,warm-bat-decidegc = <450>;
+ qcom,cool-bat-mv = <4100>;
+ qcom,ibatmax-warm-ma = <360>;
+ qcom,ibatmax-cool-ma = <360>;
+ qcom,warm-bat-mv = <4100>;
+ qcom,batt-hot-percentage = <25>;
+ qcom,batt-cold-percentage = <85>;
+ qcom,tchg-mins = <152>;
+ qcom,resume-soc = <99>;
+ qcom,btc-disabled = <0>;
+ qcom,chg-vadc = <&pm8916_vadc>;
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x0 0x10 0x7>,
+ <0x0 0x10 0x6>,
+ <0x0 0x10 0x5>,
+ <0x0 0x10 0x0>;
+
+ interrupt-names = "chg-done",
+ "chg-failed",
+ "fast-chg-on",
+ "vbat-det-lo";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x0 0x12 0x1>,
+ <0x0 0x12 0x0>;
+
+ interrupt-names = "bat-temp-ok",
+ "batt-pres";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts = <0 0x13 0x2>,
+ <0 0x13 0x1>;
+
+ interrupt-names = "chg-gone",
+ "usbin-valid";
+ };
+
+ qcom,chg-misc@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index efa67f5..0da71a3 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -191,6 +191,116 @@
temperature specific configuration as applied. If not
specified, the default value is 0 degree centigrade.
+- qcom,cl-disable
+ Usage: optional
+ Value type: <empty>
+ Definition: A boolean property to disable the battery capacity
+ learning when charging.
+
+- qcom,cl-feedback-on
+ Usage: optional
+ Value type: <empty>
+ Definition: A boolean property to feedback the learned capacity into
+ the capacity lerning algorithm. This has to be used only if the
+ property "qcom,cl-disable" is not specified.
+
+- qcom,cl-max-start-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery SOC has to be below or equal to this value at the
+ start of a charge cycle to start the capacity learning.
+ If this is not specified, then the default value used
+ will be 15. Unit is in percentage.
+
+- qcom,cl-min-start-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: Battery SOC has to be above or equal to this value at the
+ start of a charge cycle to start the capacity learning.
+ If this is not specified, then the default value used
+ will be 10. Unit is in percentage.
+
+- qcom,cl-min-temp
+ Usage: optional
+ Value type: <u32>
+ Definition: Lower limit of battery temperature to start the capacity
+ learning. If this is not specified, then the default value
+ used will be 150 (15 C). Unit is in decidegC.
+
+- qcom,cl-max-temp
+ Usage: optional
+ Value type: <u32>
+ Definition: Upper limit of battery temperature to start the capacity
+ learning. If this is not specified, then the default value
+ used will be 500 (50 C). Unit is in decidegC.
+
+- qcom,cl-max-increment
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum capacity increment allowed per capacity learning
+ cycle. If this is not specified, then the default value
+ used will be 5 (0.5%). Unit is in decipercentage.
+
+- qcom,cl-max-decrement
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum capacity decrement allowed per capacity learning
+ cycle. If this is not specified, then the default value
+ used will be 100 (10%). Unit is in decipercentage.
+
+- qcom,cl-min-limit
+ Usage: optional
+ Value type: <u32>
+ Definition: Minimum limit that the capacity cannot go below in a
+ capacity learning cycle. If this is not specified, then
+ the default value is 0. Unit is in decipercentage.
+
+- qcom,cl-max-limit
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum limit that the capacity cannot go above in a
+ capacity learning cycle. If this is not specified, then
+ the default value is 0. Unit is in decipercentage.
+
+- qcom,esr-disable
+ Usage: optional
+ Value type: <bool>
+ Definition: Boolean property to disable ESR estimation. If not defined
+ ESR estimation stays enabled for charge-cycles.
+
+- qcom,esr-discharge-enable
+ Usage: optional
+ Value type: <bool>
+ Definition: Boolean property to enable ESR estimation during discharge.
+ Only valid if 'qcom,esr-disable' is not defined.
+
+- qcom,esr-qual-current-ua
+ Usage: optional
+ Value type: <u32>
+ Definition: Minimum current differential in uA to qualify an ESR
+ reading as valid. If not defined the value defaults
+ to 130mA.
+
+- qcom,esr-qual-vbatt-uv
+ Usage: optional
+ Value type: <u32>
+ Definition: Minimum vbatt differential in uV to qualify an ESR
+ reading as valid. If not defined the value defaults
+ to 7mV.
+
+- qcom,esr-disable-soc
+ Usage: optional
+ Value type: <u32>
+ Definition: Minimum battery SOC below which ESR will not be
+ attempted by QG. If not defined the value defaults
+ to 10%.
+
+- qcom,qg-ext-sns
+ Usage: optional
+ Value type: <bool>
+ Definition: Boolean property to support external-rsense based
+ configuration.
+
==========================================================
Second Level Nodes - Peripherals managed by QGAUGE driver
==========================================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index afa8009..9de24c3 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -193,6 +193,12 @@
to be get from these properties defined in battery profile:
qcom,step-chg-ranges, qcom,jeita-fcc-ranges, qcom,jeita-fv-ranges.
+- qcom,disable-stat-sw-override
+ Usage: optional
+ Value type: bool
+ Definition: Boolean flag which when present disables STAT pin default software
+ override configuration.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
index de273cd..8ee2749 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt
@@ -195,6 +195,12 @@
This is only applicable to certain PMICs like PMI632 which
has SCHGM_FLASH peripheral.
+- qcom,chg-vadc
+ Usage: optional
+ Value type: <phandle>
+ Definition: Phandle for the VADC node, it is used to obtain USBIN_V
+ and USBIN_I readings on PMIC632 based platform.
+
=============================================
Second Level Nodes - SMB5 Charger Peripherals
=============================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-vm-bms.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-vm-bms.txt
new file mode 100644
index 0000000..690a047
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-vm-bms.txt
@@ -0,0 +1,180 @@
+Qualcomm's QPNP Voltage-Mode(VM) PMIC Battery Management System
+
+QPNP PMIC VM BMS provides interface to clients to read properties related
+to the battery. Its main function is to calculate the SOC (state of charge)
+of the battery based on periodic sampling of the VBAT (battery voltage).
+
+Parent node required properties:
+- compatible : Must be "qcom,qpnp-vm-bms" for the BM driver.
+- reg : Offset and length of the PMIC peripheral register map.
+- interrupts : The interrupt mappings.
+ The format should be
+ <slave-id peripheral-id interrupt-number>.
+- interrupt-names : names for the mapped bms interrupt
+ The following interrupts are required:
+ 0 : leave CV state
+ 1 : enter CV state
+ 2 : good ocv generated
+ 3 : ocv_thr
+ 4 : fifo update
+ 5 : fsm state chnaged
+
+Additionally, optional subnodes may be included:
+- qcom,batt-pres-status : A subnode with a register address for the SMBB
+ battery interface's BATT_PRES_STATUS register. If this node is
+ added, then the BMS will try to detect offmode battery removal
+ via the battery interface's offmode battery removal circuit.
+- qcom,battery-data : A phandle to a node containing the available batterydata
+ profiles. See the batterydata bindings documentation for more
+ details.
+
+Parent node required properties:
+- qcom,v-cutoff-uv : cutoff voltage where the battery is considered dead in
+ micro-volts.
+- qcom,max-voltage-uv : maximum voltage for the battery in micro-volts.
+- qcom,r-conn-mohm : connector resistance in milli-ohms.
+- qcom,shutdown-soc-valid-limit : If the ocv upon restart is within this
+ distance of the shutdown ocv, the BMS will try to force
+ the new SoC to the old one to provide charge continuity.
+ That is to say,
+ if (abs(shutdown-soc - current-soc) < limit)
+ then use old SoC.
+- qcom,low-soc-calculate-soc-threshold : The SoC threshold for when
+ the periodic calculate_soc work speeds up. This ensures
+ SoC is updated in userspace constantly when we are near
+ shutdown.
+- qcom,low-voltage-threshold : The battery voltage threshold in micro-volts for
+ when the BMS tries to wake up and hold a wakelock to
+ ensure a clean shutdown.
+- qcom,low-voltage-calculate-soc-ms : The time period between subsequent
+ SoC recalculations when the current voltage is below
+ qcom,low-voltage threshold. This takes precedence over
+ qcom,low-soc-calculate-soc-ms.
+- qcom,low-soc-calculate-soc-ms : The time period between subsequent
+ SoC recalculations when the current SoC is below
+ qcom,low-soc-calculate-soc-threshold. This takes
+ precedence over qcom,calculate-soc-ms.
+- qcom,calculate-soc-ms : The time period between subsequent SoC
+ recalculations when the current SoC is above or equal
+ qcom,low-soc-calculate-soc-threshold.
+- qcom,volatge-soc-timeout-ms : The timeout period after which the module starts
+ reporting volage based SOC and does not use the VMBMS
+ algorithm for SOC calculation.
+- qcom,bms-vadc: Corresponding VADC device's phandle.
+- qcom,bms-adc_tm: Corresponding ADC_TM device's phandle to set recurring
+ measurements and receive notifications for vbatt.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node.
+
+Parent node Optional properties
+- qcom,s1-sample-interval-ms: The sampling rate in ms of the accumulator in state
+ S1. (i.e) the rate at which the accumulator is being
+ filled with vbat samples. Minimum value = 0 and
+ Maximum value = 2550ms.
+- qcom,s2-sample-interval-ms: The sampling rate in ms of the accumulator in state
+ S2. (i.e) the rate at which the accumulator is being
+ filled with vbat samples. Minimum value = 0 and
+ Maximum value = 2550ms.
+- qcom,s1-sample-count: The number of samples to be accululated for one FIFO in
+ state S1. Possible values are - 0, 4, 8, 16, 32, 64, 128,
+ 256.
+- qcom,s2-sample-count: The number of samples to be accululated for one FIFO in
+ state S2. Possible values are - 0, 4, 8, 16, 32, 64, 128,
+ 256.
+- qcom,s1-fifo-legth: Number of FIFO's to be filled in state S1, to generate
+ the fifo_update_done interrupt. Possile values - 0 to 8
+- qcom,s2-fifo-legth: Number of FIFO's to be filled in state S2, to generate
+ the fifo_update_done interrupt. Possible values- 0 to 8
+- qcom,force-s3-on-suspend : Bool property to force the BMS into S3 (sleep) state
+ while entering into system suspend.
+- qcom,force-bms-active-on-charger: Bool property to keep BMS FSM active
+ if charger is present.
+- qcom,report-charger-eoc : Bool property to indicate if BMS needs to indicate
+ EOC to charger.
+- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
+ try to force the startup SoC to be the same as the
+ shutdown SoC. Defining it will make BMS ignore the
+ shutdown SoC.
+- qcom,use-voltage-soc : A boolean that controls whether BMS will use
+ voltage-based SoC instead of a coulomb counter based
+ one. Voltage-based SoC will not guarantee linearity.
+- qcom,disable-bms : Bool property to disable the VMBMS hardware module.
+ Enable this property if BMS is not supported or an external
+ fuel gauge is used.
+- qcom,s3-ocv-tolerence-uv : The S3 state OCV tolerence threshold in uV. The
+ LSB value is 300uV and maximum value is 76500uV.
+- qcom,low-soc-fifo-length : The fifo length (of S2 STATE) to be used at lower
+ SOCs. If this value is not specified the system uses
+ default length.
+- qcom,resume-soc: Capacity in percent at which charging should resume
+ when a fully charged battery drops below this level.
+- qcom,low-temp-threshold : The temperature threshold below which the IBAT
+ averaging and UUC smoothening is disabled. This value
+ is in deci-degrees centigrade. If not specified it
+ defaults to 0.
+- qcom,ibat-avg-samples : The number of samples to be averaged for IBAT
+ estimation. If not specified it defaults to 16.
+ The possible values are 1 to 16.
+- qcom,batt-aging-comp : A boolean that defines if battery aging compensation
+ is enabled.
+- qcom,use-reported-soc : Bool property to enable the reported_soc logic. To
+ enable this feature, qcom,resume-soc must be defined as
+ a proper value. The BMS is also required to control the
+ charging, discharging and recharging.
+
+qcom,batt-pres-status node required properties:
+- reg : offset and length of the PMIC LBC battery interface BATT_PRES_STATUS
+ register.
+
+qcom,qpnp-chg-pres required properties:
+- reg : offset and length of the PMIC LBC charger interafce CHARGER_OPTION
+ register.
+
+Example:
+pm8916_bms: qcom,qpnp-vm-bms {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-vm-bms";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <18>;
+ qcom,shutdown-soc-valid-limit = <20>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-voltage-threshold = <3420000>;
+ qcom,low-voltage-calculate-soc-ms = <1000>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,low-soc-fifo-length = <2>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,s3-ocv-tolerence-uv = <1200>;
+ qcom,volatge-soc-timeout-ms = <60000>;
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,bms-vadc = <&pm8916_vadc>;
+ qcom,bms-adc_tm = <&pm8916_adc_tm>;
+
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ }
+
+ qcom,qpnp-chg-pres@1208 {
+ reg = <0x1108 0x1>;
+ }
+
+ qcom,bms-bms@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0>,
+ <0x0 0x40 0x1>,
+ <0x0 0x40 0x2>,
+ <0x0 0x40 0x3>,
+ <0x0 0x40 0x4>,
+ <0x0 0x40 0x5>,
+
+ interrupt-names = "leave_cv",
+ "enter_cv",
+ "good_ocv",
+ "ocv_thr",
+ "fifo_updtaed",
+ "fsm_state_change";
+ };
+};
diff --git a/Documentation/devicetree/bindings/power/smb1360-charger-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb1360-charger-fg.txt
similarity index 100%
rename from Documentation/devicetree/bindings/power/smb1360-charger-fg.txt
rename to Documentation/devicetree/bindings/power/supply/qcom/smb1360-charger-fg.txt
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
index ddd90e1..2a84dd6 100644
--- a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -20,7 +20,8 @@
Value type: <string>
Definition: The name of the register defined in the reg property.
It must have "lpg-base", "lut-base" is optional but
- it's required if any LPG channels support LUT mode.
+ it's required if any LPG channels support LUT mode
+ with a LUT module.
- #pwm-cells:
Usage: required
@@ -30,14 +31,47 @@
the PWM channel ID indexed from 0, and the second
cell is the PWM default period in nanoseconds.
+- nvmem-names:
+ Usage: optional
+ Value type: <string>
+ Definition: The nvmem device name for the SDAM module where the LUT
+ pattern is stored. It must be "ppg_sdam". This property
+ is required only when LUT mode is supported with a SDAM
+ module instead of a LUT module.
+
+- nvmem:
+ Usage: optional
+ Value type: <phandle>
+ Definition: Phandle of the nvmem device to access the LUT stored
+ in the SDAM module. This property is required only when
+ LUT mode is supported and the LUT pattern is stored in a
+ SDAM module instead of a LUT module.
+
+- qcom,pbs-client
+ Usage: optional
+ Value type: <phandle>
+ Definition: Phandle of the PBS client used for sending the PBS
+ trigger. This property is required when LUT mode is
+ supported and the LUT pattern is stored in a SDAM
+ module instead of a LUT module.
+
+- qcom,lut-sdam-base:
+ Usage: optional
+ Value type: <u32>
+ Definition: The register base of the LUT entries stored in SDAM. This
+ property is required only when LUT mode is supported and
+ the LUT pattern is stored in a SDAM module instead of a
+ LUT module.
+
- qcom,lut-patterns:
Usage: optional
Value type: <prop-encoded-array>
Definition: Duty ratios in percentages for LPG working at LUT mode.
These duty ratios will be translated into PWM values
- and stored in LUT module. The LUT module has resource
- to store 47 PWM values at max and shared for all LPG
- channels. This property is required if any LPG channels
+ and stored in LUT or SDAM module shared for all LPG
+ channels. The LUT module has resource to store 47 PWM
+ values at max while SDAM module can store upto 64 PWM
+ values. This property is required if any LPG channels
support LUT mode.
Subnode is optional if LUT mode is not required, it's required if any LPG
@@ -54,31 +88,37 @@
range is 1 - 8. Maximum value depends on the number of
channels supported on PMIC.
+- qcom,lpg-sdam-base:
+ Usage: optional
+ Value type: <u32>
+ Definition: Register base address for LPG configuration in SDAM for
+ the LPG channel specified under "qcom,lpg-chan-id".
+ This property is required if LUT mode is supported with
+ a SDAM module.
+
- qcom,ramp-step-ms:
Usage: required
Value type: <u32>
Definition: The step duration in milliseconds for LPG staying at each
- duty specified in the LUT pattern. Allowed range is
- 1 - 511.
+ duty specified in the LUT pattern. Allowed range:
+ 1 - 511 when LUT module is used, and 8 - 2000 when SDAM
+ is used.
- qcom,ramp-high-index:
Usage: required
Value type: <u32>
Definition: The high index of the LUT pattern where LPG ends up
- ramping to. Allowed range is 1 - 47.
+ ramping to. Allowed range: 1 - 47 when LUT module
+ is used, and 1 - 64 when SDAM module is used.
- qcom,ramp-low-index:
Usage: required
Value type: <u32>
Definition: The low index of the LUT pattern from where LPG begins
- ramping from. Allowed range is 0 - 46.
-
-- qcom,ramp-from-low-to-high:
- Usage: optional
- Value type: <empty>
- Definition: The flag to specify the LPG ramping direction. The ramping
- direction is from low index to high index of the LUT
- pattern if it's specified.
+ ramping from. The ramp-low-index should be always less
+ than ramp-high-index when SDAM module is used. Allowed
+ range: 0 - 46 when LUT module is used, and 0 - 63 when
+ SDAM module is used.
- qcom,ramp-pattern-repeat:
Usage: optional
@@ -86,34 +126,66 @@
Definition: The flag to specify if LPG would be ramping with the LUT
pattern repeatedly.
+- qcom,ramp-from-low-to-high:
+ Usage: optional
+ Value type: <empty>
+ Definition: The flag to specify the LPG ramping direction. The ramping
+ direction is from low index to high index of the LUT
+ pattern if it's specified. This property is not required
+ when SDAM module is used.
+
- qcom,ramp-toggle:
Usage: optional
Value type: <empty>
Definition: The flag to specify if LPG would toggle the LUT pattern
in ramping. If toggling enabled, LPG would return to the
low index when high index is reached, or return to the high
- index when low index is reached.
+ index when low index is reached. This property is not
+ required when SDAM module is used.
- qcom,ramp-pause-hi-count:
Usage: optional
Value type: <u32>
Definition: The step count that LPG stop the output when it ramped up
- to the high index of the LUT.
+ to the high index of the LUT. This property is not
+ required when SDAM module is used.
- qcom,ramp-pause-lo-count:
Usage: optional
Value type: <u32>
Definition: The step count that LPG stop the output when it ramped up
- to the low index of the LUT.
-Example:
+ to the low index of the LUT. This property is not
+ required when SDAM module is used.
- pmi8998_lpg: lpg@b100 {
+Example when LUT pattern is stored in a LUT module:
+
+ pm8150l_lpg: lpg@b100 {
compatible = "qcom,pwm-lpg";
reg = <0xb100 0x600>, <0xb000 0x100>;
reg-names = "lpg-base", "lut-base";
#pwm-cells = <2>;
qcom,lut-patterns = <0 14 28 42 56 70 84 100
100 84 70 56 42 28 14 0>;
+ lpg@1 {
+ qcom,lpg-chan-id = <1>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-pause-hi-count = <10>;
+ qcom,ramp-pause-lo-count = <10>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <15>;
+ qcom,ramp-from-low-to-high;
+ qcom,ramp-pattern-repeat;
+ };
+ lpg@2 {
+ qcom,lpg-chan-id = <2>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-pause-hi-count = <10>;
+ qcom,ramp-pause-lo-count = <10>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <15>;
+ qcom,ramp-from-low-to-high;
+ qcom,ramp-pattern-repeat;
+ };
lpg@3 {
qcom,lpg-chan-id = <3>;
qcom,ramp-step-ms = <200>;
@@ -124,24 +196,43 @@
qcom,ramp-from-low-to-high;
qcom,ramp-pattern-repeat;
};
- lpg@4 {
- qcom,lpg-chan-id = <4>;
+ };
+
+Example when LUT pattern is stored in a SDAM module:
+
+ pmi632_lpg: lpg@b100 {
+ compatible = "qcom,pwm-lpg";
+ reg = <0xb100 0x600>;
+ reg-names = "lpg-base";
+ #pwm-cells = <2>;
+ nvmem-names = "ppg_sdam";
+ nvmem = <&sdam7>;
+ qcom,pbs-client = <&pbs_client_3>;
+ qcom,lut-sdam-base = <0x80>;
+ qcom,lut-patterns = <0 14 28 42 56 70 84 100
+ 100 84 70 56 42 28 14 0>;
+ lpg@1 {
+ qcom,lpg-chan-id = <1>;
qcom,ramp-step-ms = <200>;
- qcom,ramp-pause-hi-count = <10>;
- qcom,ramp-pause-lo-count = <10>;
qcom,ramp-low-index = <0>;
qcom,ramp-high-index = <15>;
- qcom,ramp-from-low-to-high;
qcom,ramp-pattern-repeat;
+ qcom,lpg-sdam-base = <0x48>:
};
- lpg@5 {
- qcom,lpg-chan-id = <5>;
+ lpg@2 {
+ qcom,lpg-chan-id = <2>;
qcom,ramp-step-ms = <200>;
- qcom,ramp-pause-hi-count = <10>;
- qcom,ramp-pause-lo-count = <10>;
qcom,ramp-low-index = <0>;
qcom,ramp-high-index = <15>;
- qcom,ramp-from-low-to-high;
qcom,ramp-pattern-repeat;
+ qcom,lpg-sdam-base = <0x56>;
+ };
+ lpg@3 {
+ qcom,lpg-chan-id = <3>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <15>;
+ qcom,ramp-pattern-repeat;
+ qcom,lpg-sdam-base = <0x64>;
};
};
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
index 846bd22..b02d759 100644
--- a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
@@ -241,6 +241,15 @@
time in order to allow hardware closed-loop CPR voltage
change requests to reach the PMIC regulator.
+- qcom,cpr-thread-has-always-vote-en
+ Usage: optional; only meaningful for CPR4 controller
+ Value type: <empty>
+ Definition: Boolean value which indicates that the CPR controller should
+ be configured to keep thread vote always enabled. This
+ configuration allows the CPR controller to not consider
+ MID/DN recommendations from other thread when all sensors
+ mapped to a thread collapsed.
+
=================================================
Second Level Nodes - CPR Threads for a Controller
=================================================
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 7b8ae6f..20cb25b 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -300,6 +300,11 @@
- compatible : "qcom,msm-audio-apr"
This device is added to represent APR module.
+ - qcom,subsys-name: This value provides the subsystem name where codec
+ is present. It can be "apr_modem" or "apr_adsp". This
+ property enable apr driver to receive subsystem up/down
+ notification from modem/adsp.
+
Optional properties:
- compatible : "qcom,msm-audio-apr-dummy"
@@ -434,6 +439,9 @@
Required properties:
- compatible : "qcom,wcd-dsp-glink"
+ - qcom,msm-codec-glink-edge: Name of the glink edge which is used
+ for IPC.
+ If no name is set, it defaults to "wdsp"
* msm_ext_disp_audio_codec_rx
@@ -661,6 +669,8 @@
msm_audio_apr_dummy {
compatible = "qcom,msm-audio-apr-dummy";
};
+
+ qcom,subsys-name = "apr_adsp";
};
qcom,msm-ocmem-audio {
@@ -722,6 +732,7 @@
wcd_dsp_glink {
compatible = "qcom,wcd-dsp-glink";
+ qcom,msm-codec-glink-edge = "bg";
};
msm_ext_disp_audio_codec_rx {
@@ -1180,6 +1191,18 @@
When clock rate is set to zero,
then external clock is assumed.
+ - qcom,msm-cpudai-tdm-afe-ebit-unsupported: Notify if ebit
+ setting is needed.When this is
+ set, along with clock rate as
+ zero, then afe is not configured
+ for clock.
+
+ - qcom,msm-cpudai-tdm-sec-port-enable: For chipsets with the
+ limitation where we need to enable
+ both RX and TX AFE ports, this flag
+ is used to enable TX/RX port for
+ RX/TX streams.
+
- qcom,msm-cpudai-tdm-clk-internal: Clock Source.
0 - EBIT clock from clk tree
1 - IBIT clock from clk tree
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
index 97b71a7..d3f765c 100644
--- a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
+++ b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
@@ -13,6 +13,7 @@
Required properties:
- compatible : should be "qcom,qpnp-adc-tm-hc" for thermal ADC driver using
refreshed BTM peripheral.
+ should include "qcom,qpnp-adc-tm-hc-pm5" for PMIC5.
- reg : offset and length of the PMIC Aribter register map.
- address-cells : Must be one.
- size-cells : Must be zero.
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index 6ff6e9b..f25691a 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -20,6 +20,7 @@
should be "qcom,sdm845-tsens" for SDM845 TSENS driver.
should be "qcom,tsens24xx" for 2.4 TSENS controller.
should be "qcom,msm8937-tsens" for 8937 TSENS driver.
+ should be "qcom,msm8909-tsens" for 8909 TSENS driver.
The compatible property is used to identify the respective controller to use
for the corresponding SoC.
- reg : offset and length of the TSENS registers with associated property in reg-names
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 7f79f40..f075a2a 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -74,6 +74,7 @@
Defaults to 26 MHz if not specified.
- extcon: phandle to external connector (Refer Documentation/devicetree/bindings/extcon/extcon-gpio.txt for more details).
- non-removable : defines if the connected ufs device is not removable
+- force-ufshc-probe : For force probing UFS device (non removable) even if it is not the boot device.
Note: If above properties are not defined it can be assumed that the supply
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 5256edd..431c32a 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -189,6 +189,8 @@
point to external connector device, which provide "USB-HOST" cable events.
A single phandle may be specified if a single connector device provides
both "USB" and "USB-HOST" events.
+- qcom,phy-id-high-as-peripheral: If present, specifies device to switch to device mode
+ if PHY ID state is high or host mode if PHY ID state is low.
Example HSUSB OTG controller device node :
usb@f9690000 {
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 881f9ca..0cc8b00 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -82,6 +82,7 @@
- qcom,smmu-s1-bypass: If present, configure SMMU to bypass stage 1 translation.
- qcom,no-vbus-vote-with-type-C: If present, then do not try to get and enable VBUS
regulator in type-C host mode from dwc3-msm driver.
+- qcom,connector-type-uAB: HW platform is using micro-AB USB connector type.
Sub nodes:
- Sub node for "DWC3- USB3 controller".
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index e1be5fd..a22edb5 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -103,6 +103,7 @@
fcs Fairchild Semiconductor
firefly Firefly
focaltech FocalTech Systems Co.,Ltd
+fpc Fingerprint Cards AB.
friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
fsl Freescale Semiconductor
ge General Electric Company
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index e7fb61e..e85f9e1 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -171,6 +171,23 @@
offprjjquota Turn off project journelled quota.
quota Enable plain user disk quota accounting.
noquota Disable all plain disk quota option.
+whint_mode=%s Control which write hints are passed down to block
+ layer. This supports "off", "user-based", and
+ "fs-based". In "off" mode (default), f2fs does not pass
+ down hints. In "user-based" mode, f2fs tries to pass
+ down hints given by users. And in "fs-based" mode, f2fs
+ passes down hints with its policy.
+alloc_mode=%s Adjust block allocation policy, which supports "reuse"
+ and "default".
+fsync_mode=%s Control the policy of fsync. Currently supports "posix"
+ and "strict". In "posix" mode, which is default, fsync
+ will follow POSIX semantics and does a light operation
+ to improve the filesystem performance. In "strict" mode,
+ fsync will be heavy and behaves in line with xfs, ext4
+ and btrfs, where xfstest generic/342 will pass, but the
+ performance will regress.
+test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
+ context. The fake fscrypt context is used by xfstests.
================================================================================
DEBUGFS ENTRIES
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 0b8f21f..435a509 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2662,6 +2662,9 @@
noalign [KNL,ARM]
+ noaltinstr [S390] Disables alternative instructions patching
+ (CPU alternatives feature).
+
noapic [SMP,APIC] Tells the kernel to not make use of any
IOAPICs that may be present in the system.
@@ -2718,6 +2721,9 @@
allow data leaks with this option, which is equivalent
to spectre_v2=off.
+ nospec_store_bypass_disable
+ [HW] Disable all mitigations for the Speculative Store Bypass vulnerability
+
noxsave [BUGS=X86] Disables x86 extended register state save
and restore using xsave. The kernel will fallback to
enabling legacy floating-point and sse state.
@@ -3992,6 +3998,48 @@
Not specifying this option is equivalent to
spectre_v2=auto.
+ spec_store_bypass_disable=
+ [HW] Control Speculative Store Bypass (SSB) Disable mitigation
+ (Speculative Store Bypass vulnerability)
+
+ Certain CPUs are vulnerable to an exploit against a
+ a common industry wide performance optimization known
+ as "Speculative Store Bypass" in which recent stores
+ to the same memory location may not be observed by
+ later loads during speculative execution. The idea
+ is that such stores are unlikely and that they can
+ be detected prior to instruction retirement at the
+ end of a particular speculation execution window.
+
+ In vulnerable processors, the speculatively forwarded
+ store can be used in a cache side channel attack, for
+ example to read memory to which the attacker does not
+ directly have access (e.g. inside sandboxed code).
+
+ This parameter controls whether the Speculative Store
+ Bypass optimization is used.
+
+ on - Unconditionally disable Speculative Store Bypass
+ off - Unconditionally enable Speculative Store Bypass
+ auto - Kernel detects whether the CPU model contains an
+ implementation of Speculative Store Bypass and
+ picks the most appropriate mitigation. If the
+ CPU is not vulnerable, "off" is selected. If the
+ CPU is vulnerable the default mitigation is
+ architecture and Kconfig dependent. See below.
+ prctl - Control Speculative Store Bypass per thread
+ via prctl. Speculative Store Bypass is enabled
+ for a process by default. The state of the control
+ is inherited on fork.
+ seccomp - Same as "prctl" above, but all seccomp threads
+ will disable SSB unless they explicitly opt out.
+
+ Not specifying this option is equivalent to
+ spec_store_bypass_disable=auto.
+
+ Default mitigations:
+ X86: If CONFIG_SECCOMP=y "seccomp", otherwise "prctl"
+
spia_io_base= [HW,MTD]
spia_fio_base=
spia_pedr=
diff --git a/Documentation/spec_ctrl.txt b/Documentation/spec_ctrl.txt
new file mode 100644
index 0000000..32f3d55
--- /dev/null
+++ b/Documentation/spec_ctrl.txt
@@ -0,0 +1,94 @@
+===================
+Speculation Control
+===================
+
+Quite some CPUs have speculation-related misfeatures which are in
+fact vulnerabilities causing data leaks in various forms even across
+privilege domains.
+
+The kernel provides mitigation for such vulnerabilities in various
+forms. Some of these mitigations are compile-time configurable and some
+can be supplied on the kernel command line.
+
+There is also a class of mitigations which are very expensive, but they can
+be restricted to a certain set of processes or tasks in controlled
+environments. The mechanism to control these mitigations is via
+:manpage:`prctl(2)`.
+
+There are two prctl options which are related to this:
+
+ * PR_GET_SPECULATION_CTRL
+
+ * PR_SET_SPECULATION_CTRL
+
+PR_GET_SPECULATION_CTRL
+-----------------------
+
+PR_GET_SPECULATION_CTRL returns the state of the speculation misfeature
+which is selected with arg2 of prctl(2). The return value uses bits 0-3 with
+the following meaning:
+
+==== ===================== ===================================================
+Bit Define Description
+==== ===================== ===================================================
+0 PR_SPEC_PRCTL Mitigation can be controlled per task by
+ PR_SET_SPECULATION_CTRL.
+1 PR_SPEC_ENABLE The speculation feature is enabled, mitigation is
+ disabled.
+2 PR_SPEC_DISABLE The speculation feature is disabled, mitigation is
+ enabled.
+3 PR_SPEC_FORCE_DISABLE Same as PR_SPEC_DISABLE, but cannot be undone. A
+ subsequent prctl(..., PR_SPEC_ENABLE) will fail.
+==== ===================== ===================================================
+
+If all bits are 0 the CPU is not affected by the speculation misfeature.
+
+If PR_SPEC_PRCTL is set, then the per-task control of the mitigation is
+available. If not set, prctl(PR_SET_SPECULATION_CTRL) for the speculation
+misfeature will fail.
+
+PR_SET_SPECULATION_CTRL
+-----------------------
+
+PR_SET_SPECULATION_CTRL allows to control the speculation misfeature, which
+is selected by arg2 of :manpage:`prctl(2)` per task. arg3 is used to hand
+in the control value, i.e. either PR_SPEC_ENABLE or PR_SPEC_DISABLE or
+PR_SPEC_FORCE_DISABLE.
+
+Common error codes
+------------------
+======= =================================================================
+Value Meaning
+======= =================================================================
+EINVAL The prctl is not implemented by the architecture or unused
+ prctl(2) arguments are not 0.
+
+ENODEV arg2 is selecting a not supported speculation misfeature.
+======= =================================================================
+
+PR_SET_SPECULATION_CTRL error codes
+-----------------------------------
+======= =================================================================
+Value Meaning
+======= =================================================================
+0 Success
+
+ERANGE arg3 is incorrect, i.e. it's neither PR_SPEC_ENABLE nor
+ PR_SPEC_DISABLE nor PR_SPEC_FORCE_DISABLE.
+
+ENXIO Control of the selected speculation misfeature is not possible.
+ See PR_GET_SPECULATION_CTRL.
+
+EPERM Speculation was disabled with PR_SPEC_FORCE_DISABLE and caller
+ tried to enable it again.
+======= =================================================================
+
+Speculation misfeature controls
+-------------------------------
+- PR_SPEC_STORE_BYPASS: Speculative Store Bypass
+
+ Invocations:
+ * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, 0, 0, 0);
+ * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0);
+ * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0);
+ * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0);
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 1f5eab4..e46c14f 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2118,6 +2118,9 @@
ARM 64-bit FP registers have the following id bit patterns:
0x4030 0000 0012 0 <regno:12>
+ARM firmware pseudo-registers have the following bit pattern:
+ 0x4030 0000 0014 <regno:16>
+
arm64 registers are mapped using the lower 32 bits. The upper 16 of
that is the register group type, or coprocessor number:
@@ -2134,6 +2137,9 @@
arm64 system registers have the following id bit patterns:
0x6030 0000 0013 <op0:2> <op1:3> <crn:4> <crm:4> <op2:3>
+arm64 firmware pseudo-registers have the following bit pattern:
+ 0x6030 0000 0014 <regno:16>
+
MIPS registers are mapped using the lower 32 bits. The upper 16 of that is
the register group type:
@@ -2656,7 +2662,8 @@
and execute guest code when KVM_RUN is called.
- KVM_ARM_VCPU_EL1_32BIT: Starts the CPU in a 32bit mode.
Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only).
- - KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 for the CPU.
+ - KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 (or a future revision
+ backward compatible with v0.2) for the CPU.
Depends on KVM_CAP_ARM_PSCI_0_2.
- KVM_ARM_VCPU_PMU_V3: Emulate PMUv3 for the CPU.
Depends on KVM_CAP_ARM_PMU_V3.
diff --git a/Documentation/virtual/kvm/arm/psci.txt b/Documentation/virtual/kvm/arm/psci.txt
new file mode 100644
index 0000000..aafdab8
--- /dev/null
+++ b/Documentation/virtual/kvm/arm/psci.txt
@@ -0,0 +1,30 @@
+KVM implements the PSCI (Power State Coordination Interface)
+specification in order to provide services such as CPU on/off, reset
+and power-off to the guest.
+
+The PSCI specification is regularly updated to provide new features,
+and KVM implements these updates if they make sense from a virtualization
+point of view.
+
+This means that a guest booted on two different versions of KVM can
+observe two different "firmware" revisions. This could cause issues if
+a given guest is tied to a particular PSCI revision (unlikely), or if
+a migration causes a different PSCI version to be exposed out of the
+blue to an unsuspecting guest.
+
+In order to remedy this situation, KVM exposes a set of "firmware
+pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
+interface. These registers can be saved/restored by userspace, and set
+to a convenient value if required.
+
+The following register is defined:
+
+* KVM_REG_ARM_PSCI_VERSION:
+
+ - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
+ (and thus has already been initialized)
+ - Returns the current PSCI version on GET_ONE_REG (defaulting to the
+ highest PSCI version implemented by KVM and compatible with v0.2)
+ - Allows any PSCI version implemented by KVM and compatible with
+ v0.2 to be set with SET_ONE_REG
+ - Affects the whole VM (even if the register view is per-vcpu)
diff --git a/Makefile b/Makefile
index 7c1b91b..43a123a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 84
+SUBLEVEL = 103
EXTRAVERSION =
NAME = Roaring Lionus
@@ -349,6 +349,7 @@
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
REAL_CC = $(CROSS_COMPILE)gcc
+LDGOLD = $(CROSS_COMPILE)ld.gold
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
@@ -627,6 +628,20 @@
CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,)
export CFLAGS_GCOV CFLAGS_KCOV
+# Make toolchain changes before including arch/$(SRCARCH)/Makefile to ensure
+# ar/cc/ld-* macros return correct values.
+ifdef CONFIG_LTO_CLANG
+# use GNU gold with LLVMgold for LTO linking, and LD for vmlinux_link
+LDFINAL_vmlinux := $(LD)
+LD := $(LDGOLD)
+LDFLAGS += -plugin LLVMgold.so
+# use llvm-ar for building symbol tables from IR files, and llvm-dis instead
+# of objdump for processing symbol versions and exports
+LLVM_AR := llvm-ar
+LLVM_DIS := llvm-dis
+export LLVM_AR LLVM_DIS
+endif
+
# The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default
# values of the respective KBUILD_* variables
ARCH_CPPFLAGS :=
@@ -645,6 +660,53 @@
KBUILD_CFLAGS += $(call cc-option,-fdata-sections,)
endif
+ifdef CONFIG_LTO_CLANG
+lto-clang-flags := -flto -fvisibility=hidden
+
+# allow disabling only clang LTO where needed
+DISABLE_LTO_CLANG := -fno-lto -fvisibility=default
+export DISABLE_LTO_CLANG
+endif
+
+ifdef CONFIG_LTO
+lto-flags := $(lto-clang-flags)
+KBUILD_CFLAGS += $(lto-flags)
+
+DISABLE_LTO := $(DISABLE_LTO_CLANG)
+export DISABLE_LTO
+
+# LDFINAL_vmlinux and LDFLAGS_FINAL_vmlinux can be set to override
+# the linker and flags for vmlinux_link.
+export LDFINAL_vmlinux LDFLAGS_FINAL_vmlinux
+endif
+
+ifdef CONFIG_CFI_CLANG
+cfi-clang-flags += -fsanitize=cfi
+DISABLE_CFI_CLANG := -fno-sanitize=cfi
+ifdef CONFIG_MODULES
+cfi-clang-flags += -fsanitize-cfi-cross-dso
+DISABLE_CFI_CLANG += -fno-sanitize-cfi-cross-dso
+endif
+ifdef CONFIG_CFI_PERMISSIVE
+cfi-clang-flags += -fsanitize-recover=cfi -fno-sanitize-trap=cfi
+endif
+
+# also disable CFI when LTO is disabled
+DISABLE_LTO_CLANG += $(DISABLE_CFI_CLANG)
+# allow disabling only clang CFI where needed
+export DISABLE_CFI_CLANG
+endif
+
+ifdef CONFIG_CFI
+# cfi-flags are re-tested in prepare-compiler-check
+cfi-flags := $(cfi-clang-flags)
+KBUILD_CFLAGS += $(cfi-flags)
+
+DISABLE_CFI := $(DISABLE_CFI_CLANG)
+DISABLE_LTO += $(DISABLE_CFI)
+export DISABLE_CFI
+endif
+
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += $(call cc-option,-Oz,-Os)
KBUILD_CFLAGS += $(call cc-disable-warning,maybe-uninitialized,)
@@ -808,6 +870,15 @@
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
+# clang sets -fmerge-all-constants by default as optimization, but this
+# is non-conforming behavior for C and in fact breaks the kernel, so we
+# need to disable it here generally.
+KBUILD_CFLAGS += $(call cc-option,-fno-merge-all-constants)
+
+# for gcc -fno-merge-all-constants disables everything, but it is fine
+# to have actual conforming behavior enabled.
+KBUILD_CFLAGS += $(call cc-option,-fmerge-constants)
+
# Make sure -fstack-check isn't enabled (like gentoo apparently did)
KBUILD_CFLAGS += $(call cc-option,-fno-stack-check,)
@@ -1087,6 +1158,22 @@
# CC_STACKPROTECTOR_STRONG! Why did it build with _REGULAR?!")
PHONY += prepare-compiler-check
prepare-compiler-check: FORCE
+# Make sure we're using a supported toolchain with LTO_CLANG
+ifdef CONFIG_LTO_CLANG
+ ifneq ($(call clang-ifversion, -ge, 0500, y), y)
+ @echo Cannot use CONFIG_LTO_CLANG: requires clang 5.0 or later >&2 && exit 1
+ endif
+ ifneq ($(call gold-ifversion, -ge, 112000000, y), y)
+ @echo Cannot use CONFIG_LTO_CLANG: requires GNU gold 1.12 or later >&2 && exit 1
+ endif
+endif
+# Make sure compiler supports LTO flags
+ifdef lto-flags
+ ifeq ($(call cc-option, $(lto-flags)),)
+ @echo Cannot use CONFIG_LTO: $(lto-flags) not supported by compiler \
+ >&2 && exit 1
+ endif
+endif
# Make sure compiler supports requested stack protector flag.
ifdef stackp-name
ifeq ($(call cc-option, $(stackp-flag)),)
@@ -1101,6 +1188,11 @@
$(stackp-flag) available but compiler is broken >&2 && exit 1
endif
endif
+ifdef cfi-flags
+ ifeq ($(call cc-option, $(cfi-flags)),)
+ @echo Cannot use CONFIG_CFI: $(cfi-flags) not supported by compiler >&2 && exit 1
+ endif
+endif
@:
# Generate some files
@@ -1575,7 +1667,8 @@
-o -name modules.builtin -o -name '.tmp_*.o.*' \
-o -name '*.c.[012]*.*' \
-o -name '*.ll' \
- -o -name '*.gcno' \) -type f -print | xargs rm -f
+ -o -name '*.gcno' \
+ -o -name '*.*.symversions' \) -type f -print | xargs rm -f
# Generate tags for editors
# ---------------------------------------------------------------------------
diff --git a/arch/Kconfig b/arch/Kconfig
index a364ece..4fa799b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -503,6 +503,74 @@
sections (e.g., '.text.init'). Typically '.' in section names
is used to distinguish them from label names / C identifiers.
+config LTO
+ def_bool n
+
+config ARCH_SUPPORTS_LTO_CLANG
+ bool
+ help
+ An architecture should select this option it supports:
+ - compiling with clang,
+ - compiling inline assembly with clang's integrated assembler,
+ - and linking with either lld or GNU gold w/ LLVMgold.
+
+choice
+ prompt "Link-Time Optimization (LTO) (EXPERIMENTAL)"
+ default LTO_NONE
+ help
+ This option turns on Link-Time Optimization (LTO).
+
+config LTO_NONE
+ bool "None"
+
+config LTO_CLANG
+ bool "Use clang Link Time Optimization (LTO) (EXPERIMENTAL)"
+ depends on ARCH_SUPPORTS_LTO_CLANG
+ depends on !FTRACE_MCOUNT_RECORD || HAVE_C_RECORDMCOUNT
+ select LTO
+ select THIN_ARCHIVES
+ select LD_DEAD_CODE_DATA_ELIMINATION
+ help
+ This option enables clang's Link Time Optimization (LTO), which allows
+ the compiler to optimize the kernel globally at link time. If you
+ enable this option, the compiler generates LLVM IR instead of object
+ files, and the actual compilation from IR occurs at the LTO link step,
+ which may take several minutes.
+
+ If you select this option, you must compile the kernel with clang >=
+ 5.0 (make CC=clang) and GNU gold from binutils >= 2.27, and have the
+ LLVMgold plug-in in LD_LIBRARY_PATH.
+
+endchoice
+
+config CFI
+ bool
+
+config CFI_PERMISSIVE
+ bool "Use CFI in permissive mode"
+ depends on CFI
+ help
+ When selected, Control Flow Integrity (CFI) violations result in a
+ warning instead of a kernel panic. This option is useful for finding
+ CFI violations in drivers during development.
+
+config CFI_CLANG
+ bool "Use clang Control Flow Integrity (CFI) (EXPERIMENTAL)"
+ depends on LTO_CLANG
+ depends on KALLSYMS
+ select CFI
+ help
+ This option enables clang Control Flow Integrity (CFI), which adds
+ runtime checking for indirect function calls.
+
+config CFI_CLANG_SHADOW
+ bool "Use CFI shadow to speed up cross-module checks"
+ default y
+ depends on CFI_CLANG
+ help
+ If you select this option, the kernel builds a fast look-up table of
+ CFI check functions in loaded modules to reduce overhead.
+
config HAVE_ARCH_WITHIN_STACK_FRAMES
bool
help
diff --git a/arch/alpha/include/asm/futex.h b/arch/alpha/include/asm/futex.h
index f939794..5647469 100644
--- a/arch/alpha/include/asm/futex.h
+++ b/arch/alpha/include/asm/futex.h
@@ -29,18 +29,10 @@
: "r" (uaddr), "r"(oparg) \
: "memory")
-static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
pagefault_disable();
@@ -66,17 +58,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/alpha/kernel/console.c b/arch/alpha/kernel/console.c
index 6a61dee..ab228ed 100644
--- a/arch/alpha/kernel/console.c
+++ b/arch/alpha/kernel/console.c
@@ -20,6 +20,7 @@
struct pci_controller *pci_vga_hose;
static struct resource alpha_vga = {
.name = "alpha-vga+",
+ .flags = IORESOURCE_IO,
.start = 0x3C0,
.end = 0x3DF
};
diff --git a/arch/arc/include/asm/futex.h b/arch/arc/include/asm/futex.h
index 11e1b1f..eb887dd 100644
--- a/arch/arc/include/asm/futex.h
+++ b/arch/arc/include/asm/futex.h
@@ -73,20 +73,11 @@
#endif
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
- return -EFAULT;
-
#ifndef CONFIG_ARC_HAS_LLSC
preempt_disable(); /* to guarantee atomic r-m-w of futex op */
#endif
@@ -118,30 +109,9 @@
preempt_enable();
#endif
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ:
- ret = (oldval == cmparg);
- break;
- case FUTEX_OP_CMP_NE:
- ret = (oldval != cmparg);
- break;
- case FUTEX_OP_CMP_LT:
- ret = (oldval < cmparg);
- break;
- case FUTEX_OP_CMP_GE:
- ret = (oldval >= cmparg);
- break;
- case FUTEX_OP_CMP_LE:
- ret = (oldval <= cmparg);
- break;
- case FUTEX_OP_CMP_GT:
- ret = (oldval > cmparg);
- break;
- default:
- ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 41245ce..54e93a0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -574,6 +574,7 @@
select PINCTRL
select ARCH_WANT_KMAP_ATOMIC_FLUSH
select SND_SOC_COMPRESS
+ select SND_HWDEP
help
Support for Qualcomm MSM/QSD based systems. This runs on the
apps processor of the MSM/QSD and depends on a shared memory
@@ -1937,6 +1938,26 @@
Enabling this option will cause a concatenated zImage and list of
DTBs to be built by default (instead of a standalone zImage.)
The image will built in arch/arm/boot/zImage-dtb
+choice
+ prompt "Appended DTB Kernel Image name"
+ depends on BUILD_ARM_APPENDED_DTB_IMAGE
+ default ZIMG_DTB
+ help
+ Enabling this option will cause a specific kernel image Image or
+ Image.gz to be used for final image creation.
+ The image will built in arch/arm/boot/IMAGE-NAME-dtb
+
+ config ZIMG_DTB
+ bool "zImage-dtb"
+ config IMG_DTB
+ bool "Image-dtb"
+endchoice
+
+config BUILD_ARM_APPENDED_KERNEL_IMAGE_NAME
+ string
+ depends on BUILD_ARM_APPENDED_DTB_IMAGE
+ default "zImage-dtb" if ZIMG_DTB
+ default "Image-dtb" if IMG_DTB
config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES
string "Default dtb names"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index f42fcd4..f66f377 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -13,6 +13,10 @@
# Ensure linker flags are correct
LDFLAGS :=
+ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y)
+export DTC_FLAGS := -@
+endif
+
LDFLAGS_vmlinux :=-p --no-undefined -X --pic-veneer
ifeq ($(CONFIG_CPU_ENDIAN_BE8),y)
LDFLAGS_vmlinux += --be8
@@ -299,7 +303,7 @@
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y)
-KBUILD_IMAGE := zImage-dtb
+KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_KERNEL_IMAGE_NAME))
else
KBUILD_IMAGE := zImage
endif
@@ -352,6 +356,10 @@
zImage-dtb: vmlinux scripts dtbs
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) DTSSUBDIR=$(DTSSUBDIR) $(boot)/$@
+Image-dtb: vmlinux scripts dtbs
+ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) DTSSUBDIR=$(DTSSUBDIR) $(boot)/$@
+
+
# We use MRPROPER_FILES and CLEAN_FILES now
archclean:
$(Q)$(MAKE) $(clean)=$(boot)
diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore
index ad7a025..271e124 100644
--- a/arch/arm/boot/.gitignore
+++ b/arch/arm/boot/.gitignore
@@ -4,4 +4,6 @@
bootpImage
uImage
*.dtb
-zImage-dtb
\ No newline at end of file
+zImage-dtb
+Image-dtb-hdr
+Image-dtb
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index 4175dfe..52e8c50 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -34,10 +34,10 @@
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
ifneq ($(DTB_NAMES),)
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
-else
-DTB_LIST := $(dtb-y)
-endif
DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST))
+else
+DTB_OBJS := $(shell find $(obj)/dts/ -name \*.dtb)
+endif
ifeq ($(CONFIG_XIP_KERNEL),y)
@@ -59,6 +59,14 @@
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
+$(obj)/Image-dtb-hdr: $(obj)/Image FORCE
+ echo -n 'UNCOMPRESSED_IMG' > $@ && \
+ $(call size_append, $(filter-out FORCE,$^)) >> $@
+
+$(obj)/Image-dtb: $(obj)/Image-dtb-hdr $(obj)/Image $(DTB_OBJS) FORCE
+ $(call if_changed,cat)
+ @echo ' Kernel: $@ is ready'
+
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
index 6df7829..78bee26 100644
--- a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
+++ b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
@@ -204,6 +204,7 @@
interrupt-controller;
ti,system-power-controller;
+ ti,palmas-override-powerhold;
tps659038_pmic {
compatible = "ti,tps659038-pmic";
diff --git a/arch/arm/boot/dts/am57xx-idk-common.dtsi b/arch/arm/boot/dts/am57xx-idk-common.dtsi
index db858ff..1cc6272 100644
--- a/arch/arm/boot/dts/am57xx-idk-common.dtsi
+++ b/arch/arm/boot/dts/am57xx-idk-common.dtsi
@@ -57,6 +57,7 @@
#interrupt-cells = <2>;
interrupt-controller;
ti,system-power-controller;
+ ti,palmas-override-powerhold;
tps659038_pmic {
compatible = "ti,tps659038-pmic";
diff --git a/arch/arm/boot/dts/aspeed-ast2500-evb.dts b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
index 1b7a5ff..d7774d3 100644
--- a/arch/arm/boot/dts/aspeed-ast2500-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
@@ -15,7 +15,7 @@
bootargs = "console=ttyS4,115200 earlyprintk";
};
- memory {
+ memory@80000000 {
reg = <0x80000000 0x20000000>;
};
};
diff --git a/arch/arm/boot/dts/at91sam9g25.dtsi b/arch/arm/boot/dts/at91sam9g25.dtsi
index a7da0dd..0898213 100644
--- a/arch/arm/boot/dts/at91sam9g25.dtsi
+++ b/arch/arm/boot/dts/at91sam9g25.dtsi
@@ -21,7 +21,7 @@
atmel,mux-mask = <
/* A B C */
0xffffffff 0xffe0399f 0xc000001c /* pioA */
- 0x0007ffff 0x8000fe3f 0x00000000 /* pioB */
+ 0x0007ffff 0x00047e3f 0x00000000 /* pioB */
0x80000000 0x07c0ffff 0xb83fffff /* pioC */
0x003fffff 0x003f8000 0x00000000 /* pioD */
>;
diff --git a/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi b/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi
index 12c981e..9a0599f 100644
--- a/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi
+++ b/arch/arm/boot/dts/bcm283x-rpi-smsc9512.dtsi
@@ -1,6 +1,6 @@
/ {
aliases {
- ethernet = ðernet;
+ ethernet0 = ðernet;
};
};
diff --git a/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi b/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi
index 3f0a56e..dc7ae77 100644
--- a/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi
+++ b/arch/arm/boot/dts/bcm283x-rpi-smsc9514.dtsi
@@ -1,6 +1,6 @@
/ {
aliases {
- ethernet = ðernet;
+ ethernet0 = ðernet;
};
};
diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index 132f2be..56311fd 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -398,6 +398,8 @@
tps659038: tps659038@58 {
compatible = "ti,tps659038";
reg = <0x58>;
+ ti,palmas-override-powerhold;
+ ti,system-power-controller;
tps659038_pmic {
compatible = "ti,tps659038-pmic";
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 41ecd6d..75a6063 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -408,7 +408,7 @@
reg = <0>;
vdd3-supply = <&lcd_vdd3_reg>;
vci-supply = <&ldo25_reg>;
- reset-gpios = <&gpy4 5 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpf2 1 GPIO_ACTIVE_HIGH>;
power-on-delay= <50>;
reset-delay = <100>;
init-delay = <100>;
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index f7357d9..64de33d 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -640,7 +640,7 @@
power-domains = <&pd_gsc>;
clocks = <&clock CLK_GSCL0>;
clock-names = "gscl";
- iommu = <&sysmmu_gsc0>;
+ iommus = <&sysmmu_gsc0>;
};
gsc_1: gsc@13e10000 {
@@ -650,7 +650,7 @@
power-domains = <&pd_gsc>;
clocks = <&clock CLK_GSCL1>;
clock-names = "gscl";
- iommu = <&sysmmu_gsc1>;
+ iommus = <&sysmmu_gsc1>;
};
gsc_2: gsc@13e20000 {
@@ -660,7 +660,7 @@
power-domains = <&pd_gsc>;
clocks = <&clock CLK_GSCL2>;
clock-names = "gscl";
- iommu = <&sysmmu_gsc2>;
+ iommus = <&sysmmu_gsc2>;
};
gsc_3: gsc@13e30000 {
@@ -670,7 +670,7 @@
power-domains = <&pd_gsc>;
clocks = <&clock CLK_GSCL3>;
clock-names = "gscl";
- iommu = <&sysmmu_gsc3>;
+ iommus = <&sysmmu_gsc3>;
};
hdmi: hdmi@14530000 {
diff --git a/arch/arm/boot/dts/imx53-qsrb.dts b/arch/arm/boot/dts/imx53-qsrb.dts
index 96d7eed..036c9bd 100644
--- a/arch/arm/boot/dts/imx53-qsrb.dts
+++ b/arch/arm/boot/dts/imx53-qsrb.dts
@@ -23,7 +23,7 @@
imx53-qsrb {
pinctrl_pmic: pmicgrp {
fsl,pins = <
- MX53_PAD_CSI0_DAT5__GPIO5_23 0x1e4 /* IRQ */
+ MX53_PAD_CSI0_DAT5__GPIO5_23 0x1c4 /* IRQ */
>;
};
};
diff --git a/arch/arm/boot/dts/logicpd-som-lv.dtsi b/arch/arm/boot/dts/logicpd-som-lv.dtsi
index 4f2c5ec..e262fa9 100644
--- a/arch/arm/boot/dts/logicpd-som-lv.dtsi
+++ b/arch/arm/boot/dts/logicpd-som-lv.dtsi
@@ -97,6 +97,8 @@
};
&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
clock-frequency = <2600000>;
twl: twl@48 {
@@ -215,7 +217,12 @@
>;
};
-
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ >;
+ };
};
&omap3_pmx_wkup {
diff --git a/arch/arm/boot/dts/logicpd-torpedo-som.dtsi b/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
index efe5399..08f0a35 100644
--- a/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
+++ b/arch/arm/boot/dts/logicpd-torpedo-som.dtsi
@@ -100,6 +100,8 @@
};
&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
clock-frequency = <2600000>;
twl: twl@48 {
@@ -207,6 +209,12 @@
OMAP3_CORE1_IOPAD(0x21b8, PIN_INPUT | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */
>;
};
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ >;
+ };
};
&uart2 {
diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts
index 9408753..67b4de0 100644
--- a/arch/arm/boot/dts/ls1021a-qds.dts
+++ b/arch/arm/boot/dts/ls1021a-qds.dts
@@ -215,7 +215,7 @@
reg = <0x2a>;
VDDA-supply = <®_3p3v>;
VDDIO-supply = <®_3p3v>;
- clocks = <&sys_mclk 1>;
+ clocks = <&sys_mclk>;
};
};
};
diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts
index a8b148a..44715c8 100644
--- a/arch/arm/boot/dts/ls1021a-twr.dts
+++ b/arch/arm/boot/dts/ls1021a-twr.dts
@@ -187,7 +187,7 @@
reg = <0x0a>;
VDDA-supply = <®_3p3v>;
VDDIO-supply = <®_3p3v>;
- clocks = <&sys_mclk 1>;
+ clocks = <&sys_mclk>;
};
};
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index 368e219..825f6ea 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -146,7 +146,7 @@
};
esdhc: esdhc@1560000 {
- compatible = "fsl,esdhc";
+ compatible = "fsl,ls1021a-esdhc", "fsl,esdhc";
reg = <0x0 0x1560000 0x0 0x10000>;
interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <0>;
diff --git a/arch/arm/boot/dts/moxart-uc7112lx.dts b/arch/arm/boot/dts/moxart-uc7112lx.dts
index 10d088d..4a962a2 100644
--- a/arch/arm/boot/dts/moxart-uc7112lx.dts
+++ b/arch/arm/boot/dts/moxart-uc7112lx.dts
@@ -6,7 +6,7 @@
*/
/dts-v1/;
-/include/ "moxart.dtsi"
+#include "moxart.dtsi"
/ {
model = "MOXA UC-7112-LX";
diff --git a/arch/arm/boot/dts/moxart.dtsi b/arch/arm/boot/dts/moxart.dtsi
index 1fd27ed..64f2f44 100644
--- a/arch/arm/boot/dts/moxart.dtsi
+++ b/arch/arm/boot/dts/moxart.dtsi
@@ -6,6 +6,7 @@
*/
/include/ "skeleton.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "moxa,moxart";
@@ -36,8 +37,8 @@
ranges;
intc: interrupt-controller@98800000 {
- compatible = "moxa,moxart-ic";
- reg = <0x98800000 0x38>;
+ compatible = "moxa,moxart-ic", "faraday,ftintc010";
+ reg = <0x98800000 0x100>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-mask = <0x00080000>;
@@ -59,7 +60,7 @@
timer: timer@98400000 {
compatible = "moxa,moxart-timer";
reg = <0x98400000 0x42>;
- interrupts = <19 1>;
+ interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
clocks = <&clk_apb>;
};
@@ -80,7 +81,7 @@
dma: dma@90500000 {
compatible = "moxa,moxart-dma";
reg = <0x90500080 0x40>;
- interrupts = <24 0>;
+ interrupts = <24 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
};
@@ -93,7 +94,7 @@
sdhci: sdhci@98e00000 {
compatible = "moxa,moxart-sdhci";
reg = <0x98e00000 0x5C>;
- interrupts = <5 0>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_apb>;
dmas = <&dma 5>,
<&dma 5>;
@@ -120,7 +121,7 @@
mac0: mac@90900000 {
compatible = "moxa,moxart-mac";
reg = <0x90900000 0x90>;
- interrupts = <25 0>;
+ interrupts = <25 IRQ_TYPE_LEVEL_HIGH>;
phy-handle = <ðphy0>;
phy-mode = "mii";
status = "disabled";
@@ -129,7 +130,7 @@
mac1: mac@92000000 {
compatible = "moxa,moxart-mac";
reg = <0x92000000 0x90>;
- interrupts = <27 0>;
+ interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
phy-handle = <ðphy1>;
phy-mode = "mii";
status = "disabled";
@@ -138,7 +139,7 @@
uart0: uart@98200000 {
compatible = "ns16550a";
reg = <0x98200000 0x20>;
- interrupts = <31 8>;
+ interrupts = <31 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clock-frequency = <14745600>;
diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
index b7a24af..4b7d972 100644
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
@@ -154,10 +154,10 @@
i2c_0: i2c@78b7000 {
compatible = "qcom,i2c-qup-v2.2.1";
- reg = <0x78b7000 0x6000>;
+ reg = <0x78b7000 0x600>;
interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_BLSP1_AHB_CLK>,
- <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
+ <&gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
clock-names = "iface", "core";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 4642d0d..e6af69d 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -21,14 +21,18 @@
mdm9607-ttp.dtb
targets += dtbs
-targets += $(addprefix ../, $(dtb-y))
-
-$(obj)/../%.dtb: $(src)/%.dts FORCE
- $(call if_changed_dep,dtc)
include $(srctree)/arch/arm64/boot/dts/qcom/Makefile
-$(obj)/../%.dtb: $(src)/../../../../arm64/boot/dts/qcom/%.dts FORCE
+$(obj)/%.dtb: $(src)/../../../../arm64/boot/dts/qcom/%.dts FORCE
$(call if_changed_dep,dtc)
-dtbs: $(addprefix $(obj)/../,$(dtb-y))
+ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y)
+$(obj)/%.dtbo:$(src)/../../../../arm64/boot/dts/qcom/%.dts FORCE
+ $(call if_changed_dep,dtc)
+ $(call if_changed,dtbo_verify)
+
+dtbs: $(addprefix $(obj)/,$(dtb-y)) $(addprefix $(obj)/,$(dtbo-y))
+else
+dtbs: $(addprefix $(obj)/,$(dtb-y))
+endif
clean-files := *.dtb
diff --git a/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi
index 0fd3b34..d891a4b 100644
--- a/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi
+++ b/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -69,6 +69,11 @@
compatible = "qcom,msm-pcm-hostless";
};
+ audio_apr: qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ qcom,subsys-name = "apr_modem";
+ };
+
host_pcm: qcom,msm-voice-host-pcm {
compatible = "qcom,msm-voice-host-pcm";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi
index 6a3b391..519367c 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-atp.dtsi
@@ -23,3 +23,13 @@
&qnand_1 {
status = "ok";
};
+
+&vbus_detect {
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+ qcom,connector-type-uAB;
+ extcon = <0>, <0>, <0>, <&vbus_detect>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
index c652a44..9da25abd 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
@@ -520,6 +520,34 @@
<&tpda_modem_out_funnel_in1>;
};
};
+
+ port@4 {
+ reg = <0>;
+ funnel_in1_in_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm0_out_funnel_in1>;
+ };
+ };
+ };
+ };
+
+ etm0: etm@7002000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b956>;
+
+ reg = <0x7002000 0x1000>;
+ cpu = <&CPU0>;
+
+ coresight-name = "coresight-etm0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm0_out_funnel_in1: endpoint {
+ remote-endpoint = <&funnel_in1_in_etm0>;
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dts
index 6909ef5..6c6e640 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dts
@@ -28,3 +28,7 @@
&cnss_sdio {
status = "okay";
};
+
+&blsp1_uart2b_hs {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dtsi
index d447724..16f933f 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-dualwifi-cdp.dtsi
@@ -36,3 +36,11 @@
/delete-property/ qcom,devfreq,freq-table;
/delete-property/ cd-gpios;
};
+
+&soc {
+ bluetooth: bt_qca6174 {
+ compatible = "qca,qca6174";
+ qca,bt-reset-gpio = <&pmxpoorwills_gpios 4 0>; /* BT_EN */
+ qca,bt-vdd-pa-supply = <&vreg_wlan>;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
index 518d8a1..1428f37 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
@@ -18,13 +18,18 @@
&usb {
status = "okay";
- extcon = <&vbus_detect>;
+ qcom,connector-type-uAB;
+ extcon = <0>, <0>, <0>, <&vbus_detect>;
};
&pcie_ep {
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
index 518d8a1..81877b7 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
@@ -16,15 +16,14 @@
status = "okay";
};
-&usb {
- status = "okay";
- extcon = <&vbus_detect>;
-};
-
&pcie_ep {
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
index eb544e6..d7c0d13 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
@@ -18,13 +18,18 @@
&usb {
status = "okay";
- extcon = <&vbus_detect>;
+ qcom,connector-type-uAB;
+ extcon = <0>, <0>, <0>, <&vbus_detect>;
};
&pcie_ep {
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
index eb544e6..6ceac6e 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
@@ -16,15 +16,14 @@
status = "okay";
};
-&usb {
- status = "okay";
- extcon = <&vbus_detect>;
-};
-
&pcie_ep {
status = "okay";
};
+&ipa_hw {
+ qcom,use-ipa-in-mhi-mode;
+};
+
&pcie0 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi
index 77fc533..505f242 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi
@@ -98,4 +98,9 @@
reg = <0xC300000 0x1000>, <0xC370004 0x4>;
reg-names = "phys_addr_base", "offset_addr";
};
+
+ qcom,rpmh-master-stats@b211200 {
+ compatible = "qcom,rpmh-master-stats-v1";
+ reg = <0xb211200 0x60>;
+ };
};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 38ba7fc..d1b2050 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -19,6 +19,8 @@
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include <dt-bindings/clock/qcom,aop-qmp.h>
+#define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024))
+
/ {
model = "Qualcomm Technologies, Inc. SDX POORWILLS";
compatible = "qcom,sdxpoorwills";
@@ -75,7 +77,7 @@
dump_mem: mem_dump_region {
compatible = "shared-dma-pool";
reusable;
- size = <0 0x2400000>;
+ size = <0x400000>;
};
};
@@ -218,6 +220,31 @@
< 1497600 >;
};
+ cpubw: qcom,cpubw {
+ compatible = "qcom,devbw";
+ governor = "cpufreq";
+ qcom,src-dst-ports = <1 512>;
+ qcom,active-only;
+ qcom,bw-tbl =
+ < MHZ_TO_MBPS(200, 2) >, /* 381 MB/s */
+ < MHZ_TO_MBPS(470, 2) >, /* 896 MB/s */
+ < MHZ_TO_MBPS(547, 2) >, /* 1043 MB/s */
+ < MHZ_TO_MBPS(691, 2) >, /* 1317 MB/s */
+ < MHZ_TO_MBPS(806, 2) >, /* 1537 MB/s */
+ < MHZ_TO_MBPS(940, 2) >, /* 1792 MB/s */
+ < MHZ_TO_MBPS(1383, 2) >; /* 2637 MB/s */
+ };
+
+ devfreq_compute: qcom,devfreq-compute {
+ compatible = "qcom,arm-cpu-mon";
+ qcom,cpulist = <&CPU0>;
+ qcom,target-dev = <&cpubw>;
+ qcom,core-dev-table =
+ < 153600 MHZ_TO_MBPS(200, 2) >,
+ < 576000 MHZ_TO_MBPS(691, 2) >,
+ < 1497600 MHZ_TO_MBPS(1383, 2)>;
+ };
+
clock_gcc: qcom,gcc@100000 {
compatible = "qcom,gcc-sdxpoorwills", "syscon";
reg = <0x100000 0x1f0000>;
@@ -691,6 +718,34 @@
qcom,glinkpkt-dev-name = "glink_pkt_loopback";
};
+ qcom,glinkpkt-data5-cntl {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA5_CNTL";
+ qcom,glinkpkt-dev-name = "smdcntl0";
+ };
+
+ qcom,glinkpkt-data6-cntl {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA6_CNTL";
+ qcom,glinkpkt-dev-name = "smdcntl1";
+ };
+
+ qcom,glinkpkt-apr-apps2 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "apr_apps2";
+ qcom,glinkpkt-dev-name = "apr_apps2";
+ };
+
+ qcom,glinkpkt-data22 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA22";
+ qcom,glinkpkt-dev-name = "smd22";
+ };
+
qcom,glinkpkt-data40-cntl {
qcom,glinkpkt-transport = "smem";
qcom,glinkpkt-edge = "mpss";
@@ -718,6 +773,13 @@
qcom,glinkpkt-ch-name = "DATA11";
qcom,glinkpkt-dev-name = "smd11";
};
+
+ qcom,glinkpkt-data21 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA21";
+ qcom,glinkpkt-dev-name = "smd21";
+ };
};
pil_modem: qcom,mss@4080000 {
@@ -774,7 +836,7 @@
memory-region = <&dump_mem>;
rpmh_dump {
- qcom,dump-size = <0x2000000>;
+ qcom,dump-size = <0x300000>;
qcom,dump-id = <0xec>;
};
@@ -871,24 +933,24 @@
<1 676 0 0>,
<143 777 0 0>,
/* SVS2 */
- <90 512 3616000 7232000>,
+ <90 512 900000 1800000>,
<90 585 300000 600000>,
- <1 676 90000 180000>, /*gcc_config_noc_clk_src */
+ <1 676 90000 179000>, /*gcc_config_noc_clk_src */
<143 777 0 120>, /* IB defined for IPA2X_clk in MHz*/
/* SVS */
- <90 512 6640000 13280000>,
+ <90 512 1530000 3060000>,
<90 585 400000 800000>,
- <1 676 100000 200000>,
+ <1 676 100000 199000>,
<143 777 0 250>, /* IB defined for IPA2X_clk in MHz*/
/* NOMINAL */
- <90 512 10400000 20800000>,
+ <90 512 2592000 5184000>,
<90 585 800000 1600000>,
- <1 676 200000 400000>,
+ <1 676 200000 399000>,
<143 777 0 440>, /* IB defined for IPA2X_clk in MHz*/
/* TURBO */
- <90 512 10400000 20800000>,
+ <90 512 2592000 5184000>,
<90 585 960000 1920000>,
- <1 676 266000 532000>,
+ <1 676 266000 531000>,
<143 777 0 500>; /* IB defined for IPA clk in MHz*/
qcom,bus-vector-names = "MIN", "SVS2", "SVS", "NOMINAL",
"TURBO";
@@ -1004,7 +1066,6 @@
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x5E0 0x0>;
qcom,iova-mapping = <0x20000000 0x40000000>;
qcom,additional-mapping =
@@ -1015,7 +1076,6 @@
ipa_smmu_wlan: ipa_smmu_wlan {
compatible = "qcom,ipa-smmu-wlan-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x5E1 0x0>;
qcom,additional-mapping =
/* ipa-uc ram */
@@ -1024,7 +1084,6 @@
ipa_smmu_uc: ipa_smmu_uc {
compatible = "qcom,ipa-smmu-uc-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x5E2 0x0>;
qcom,iova-mapping = <0x40000000 0x20000000>;
};
@@ -1043,6 +1102,12 @@
#mbox-cells = <1>;
};
+ aop-msg-client {
+ compatible = "qcom,debugfs-qmp-client";
+ mboxes = <&qmp_aop 0>;
+ mbox-names = "aop";
+ };
+
vbus_detect: qcom,pmd-vbus-det {
compatible = "qcom,pmd-vbus-det";
interrupt-parent = <&spmi_bus>;
@@ -1202,6 +1267,7 @@
&soc {
emac_hw: qcom,emac@00020000 {
compatible = "qcom,emac-dwc-eqos";
+ qcom,arm-smmu;
reg = <0x20000 0x10000>,
<0x36000 0x100>,
<0x3900000 0x300000>;
@@ -1219,13 +1285,14 @@
"rx-ch0-intr", "rx-ch1-intr",
"rx-ch2-intr", "rx-ch3-intr";
qcom,msm-bus,name = "emac";
- qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,num-paths = <2>;
qcom,msm-bus,vectors-KBps =
+ <98 512 0 0>, <1 781 0 0>, /* No vote */
<98 512 1250 0>, <1 781 0 40000>, /* 10Mbps vote */
<98 512 12500 0>, <1 781 0 40000>, /* 100Mbps vote */
<98 512 125000 0>, <1 781 0 40000>; /* 1000Mbps vote */
- qcom,bus-vector-names = "10", "100", "1000";
+ qcom,bus-vector-names = "0", "10", "100", "1000";
clocks = <&clock_gcc GCC_ETH_AXI_CLK>,
<&clock_gcc GCC_ETH_PTP_CLK>,
<&clock_gcc GCC_ETH_RGMII_CLK>,
@@ -1242,6 +1309,12 @@
io-macro-bypass-mode = <0>;
io-interface = "rgmii";
};
+
+ emac_emb_smmu: emac_emb_smmu {
+ compatible = "qcom,emac-smmu-embedded";
+ iommus = <&apps_smmu 0x220 0xf>;
+ qcom,iova-mapping = <0x80000000 0x40000000>;
+ };
};
ess-instance {
diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi
index fb9ef9c..959e3ed 100644
--- a/arch/arm/boot/dts/r7s72100.dtsi
+++ b/arch/arm/boot/dts/r7s72100.dtsi
@@ -112,7 +112,7 @@
#clock-cells = <1>;
compatible = "renesas,r7s72100-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0xfcfe0430 4>;
- clocks = <&p0_clk>;
+ clocks = <&b_clk>;
clock-indices = <R7S72100_CLK_ETHER>;
clock-output-names = "ether";
};
diff --git a/arch/arm/boot/dts/r8a7740-armadillo800eva.dts b/arch/arm/boot/dts/r8a7740-armadillo800eva.dts
index 7885075..1788e18 100644
--- a/arch/arm/boot/dts/r8a7740-armadillo800eva.dts
+++ b/arch/arm/boot/dts/r8a7740-armadillo800eva.dts
@@ -266,7 +266,9 @@
lcd0_pins: lcd0 {
groups = "lcd0_data24_0", "lcd0_lclk_1", "lcd0_sync";
function = "lcd0";
+ };
+ lcd0_mux {
/* DBGMD/LCDC0/FSIA MUX */
gpio-hog;
gpios = <176 0>;
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index b6c6410..262a512 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -1437,8 +1437,11 @@
compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>;
clocks = <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7790_CLK_SSI_ALL>, <&mstp10_clks R8A7790_CLK_SSI_ALL>,
<&p_clk>,
<&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>,
<&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>,
diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
index f8a7d09..12841e9 100644
--- a/arch/arm/boot/dts/r8a7791-koelsch.dts
+++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
@@ -279,7 +279,7 @@
x2_clk: x2-clock {
compatible = "fixed-clock";
#clock-cells = <0>;
- clock-frequency = <148500000>;
+ clock-frequency = <74250000>;
};
x13_clk: x13-clock {
diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi
index 162b55c..59405eb 100644
--- a/arch/arm/boot/dts/r8a7791.dtsi
+++ b/arch/arm/boot/dts/r8a7791.dtsi
@@ -74,9 +74,8 @@
next-level-cache = <&L2_CA15>;
};
- L2_CA15: cache-controller@0 {
+ L2_CA15: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7791_PD_CA15_SCU>;
cache-unified;
cache-level = <2>;
@@ -1438,8 +1437,11 @@
compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>;
clocks = <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7791_CLK_SSI_ALL>, <&mstp10_clks R8A7791_CLK_SSI_ALL>,
<&p_clk>,
<&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>,
<&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>,
diff --git a/arch/arm/boot/dts/r8a7792.dtsi b/arch/arm/boot/dts/r8a7792.dtsi
index 713141d..0b50c67 100644
--- a/arch/arm/boot/dts/r8a7792.dtsi
+++ b/arch/arm/boot/dts/r8a7792.dtsi
@@ -58,9 +58,8 @@
next-level-cache = <&L2_CA15>;
};
- L2_CA15: cache-controller@0 {
+ L2_CA15: cache-controller-0 {
compatible = "cache";
- reg = <0>;
cache-unified;
cache-level = <2>;
power-domains = <&sysc R8A7792_PD_CA15_SCU>;
diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi
index 8d02aac..e9625cb 100644
--- a/arch/arm/boot/dts/r8a7793.dtsi
+++ b/arch/arm/boot/dts/r8a7793.dtsi
@@ -65,9 +65,8 @@
power-domains = <&sysc R8A7793_PD_CA15_CPU1>;
};
- L2_CA15: cache-controller@0 {
+ L2_CA15: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7793_PD_CA15_SCU>;
cache-unified;
cache-level = <2>;
@@ -1235,8 +1234,11 @@
compatible = "renesas,r8a7793-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>;
clocks = <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
+ <&mstp10_clks R8A7793_CLK_SSI_ALL>, <&mstp10_clks R8A7793_CLK_SSI_ALL>,
<&p_clk>,
<&mstp10_clks R8A7793_CLK_SCU_ALL>, <&mstp10_clks R8A7793_CLK_SCU_ALL>,
<&mstp10_clks R8A7793_CLK_SCU_ALL>, <&mstp10_clks R8A7793_CLK_SCU_ALL>,
diff --git a/arch/arm/boot/dts/r8a7794-silk.dts b/arch/arm/boot/dts/r8a7794-silk.dts
index cf880ac..8874451 100644
--- a/arch/arm/boot/dts/r8a7794-silk.dts
+++ b/arch/arm/boot/dts/r8a7794-silk.dts
@@ -425,7 +425,7 @@
status = "okay";
clocks = <&mstp7_clks R8A7794_CLK_DU0>,
- <&mstp7_clks R8A7794_CLK_DU0>,
+ <&mstp7_clks R8A7794_CLK_DU1>,
<&x2_clk>, <&x3_clk>;
clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1";
diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi
index 7e860d3..d8f4ca8 100644
--- a/arch/arm/boot/dts/r8a7794.dtsi
+++ b/arch/arm/boot/dts/r8a7794.dtsi
@@ -56,9 +56,8 @@
next-level-cache = <&L2_CA7>;
};
- L2_CA7: cache-controller@0 {
+ L2_CA7: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7794_PD_CA7_SCU>;
cache-unified;
cache-level = <2>;
@@ -917,7 +916,7 @@
interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp7_clks R8A7794_CLK_DU0>,
- <&mstp7_clks R8A7794_CLK_DU0>;
+ <&mstp7_clks R8A7794_CLK_DU1>;
clock-names = "du.0", "du.1";
status = "disabled";
@@ -1262,19 +1261,21 @@
clocks = <&mp_clk>, <&hp_clk>,
<&zs_clk>, <&p_clk>, <&p_clk>, <&zs_clk>,
<&zs_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>,
- <&zx_clk>;
+ <&zx_clk>, <&zx_clk>;
#clock-cells = <1>;
clock-indices = <
R8A7794_CLK_EHCI R8A7794_CLK_HSUSB
R8A7794_CLK_HSCIF2 R8A7794_CLK_SCIF5
R8A7794_CLK_SCIF4 R8A7794_CLK_HSCIF1 R8A7794_CLK_HSCIF0
R8A7794_CLK_SCIF3 R8A7794_CLK_SCIF2 R8A7794_CLK_SCIF1
- R8A7794_CLK_SCIF0 R8A7794_CLK_DU0
+ R8A7794_CLK_SCIF0
+ R8A7794_CLK_DU1 R8A7794_CLK_DU0
>;
clock-output-names =
"ehci", "hsusb",
"hscif2", "scif5", "scif4", "hscif1", "hscif0",
- "scif3", "scif2", "scif1", "scif0", "du0";
+ "scif3", "scif2", "scif1", "scif0",
+ "du1", "du0";
};
mstp8_clks: mstp8_clks@e6150990 {
compatible = "renesas,r8a7794-mstp-clocks", "renesas,cpg-mstp-clocks";
diff --git a/arch/arm/boot/dts/rk322x.dtsi b/arch/arm/boot/dts/rk322x.dtsi
index 9e6bf0e..2679dc8 100644
--- a/arch/arm/boot/dts/rk322x.dtsi
+++ b/arch/arm/boot/dts/rk322x.dtsi
@@ -617,9 +617,9 @@
<0 12 RK_FUNC_1 &pcfg_pull_none>,
<0 13 RK_FUNC_1 &pcfg_pull_none>,
<0 14 RK_FUNC_1 &pcfg_pull_none>,
- <1 2 RK_FUNC_1 &pcfg_pull_none>,
- <1 4 RK_FUNC_1 &pcfg_pull_none>,
- <1 5 RK_FUNC_1 &pcfg_pull_none>;
+ <1 2 RK_FUNC_2 &pcfg_pull_none>,
+ <1 4 RK_FUNC_2 &pcfg_pull_none>,
+ <1 5 RK_FUNC_2 &pcfg_pull_none>;
};
};
diff --git a/arch/arm/boot/dts/sama5d4.dtsi b/arch/arm/boot/dts/sama5d4.dtsi
index 65e725f..de0e189 100644
--- a/arch/arm/boot/dts/sama5d4.dtsi
+++ b/arch/arm/boot/dts/sama5d4.dtsi
@@ -1362,7 +1362,7 @@
pinctrl@fc06a000 {
#address-cells = <1>;
#size-cells = <1>;
- compatible = "atmel,at91sam9x5-pinctrl", "atmel,at91rm9200-pinctrl", "simple-bus";
+ compatible = "atmel,sama5d3-pinctrl", "atmel,at91sam9x5-pinctrl", "simple-bus";
ranges = <0xfc068000 0xfc068000 0x100
0xfc06a000 0xfc06a000 0x4000>;
/* WARNING: revisit as pin spec has changed */
diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig
index 79de828..e32b055 100644
--- a/arch/arm/configs/bcm2835_defconfig
+++ b/arch/arm/configs/bcm2835_defconfig
@@ -1,6 +1,5 @@
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
-CONFIG_FHANDLE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_BSD_PROCESS_ACCT=y
@@ -32,6 +31,7 @@
CONFIG_AEABI=y
CONFIG_KSM=y
CONFIG_CLEANCACHE=y
+CONFIG_CMA=y
CONFIG_SECCOMP=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
@@ -52,6 +52,7 @@
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
+CONFIG_DMA_CMA=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SCSI_CONSTANTS=y
@@ -62,7 +63,6 @@
CONFIG_ZD1211RW=y
CONFIG_INPUT_EVDEV=y
# CONFIG_LEGACY_PTYS is not set
-# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_TTY_PRINTK=y
diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig
new file mode 100644
index 0000000..453212e
--- /dev/null
+++ b/arch/arm/configs/msm8909-perf_defconfig
@@ -0,0 +1,546 @@
+CONFIG_LOCALVERSION="-perf"
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_SCHED_CORE_CTL=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
+# CONFIG_MEMBARRIER is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_ARCH_MMAP_RND_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8909=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_PROCESS_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TEE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_RPFILTER=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_HDCP_QSEECOM=y
+CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_QCOM=y
+CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CNSS=y
+CONFIG_CNSS_SDIO=y
+CONFIG_CLD_HL_SDIO_CORE=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_DIAG_CHAR=y
+CONFIG_DIAG_USES_SMD=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8909=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SMB1360_CHARGER_FG=y
+CONFIG_QPNP_VM_BMS=y
+CONFIG_QPNP_LINEAR_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_MFD_QCOM_RPM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_REGULATOR_QCOM_RPM=y
+CONFIG_REGULATOR_QCOM_SPMI=y
+CONFIG_REGULATOR_CPR=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_MSM_GFX_LDO=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISP_V1=y
+CONFIG_MSM_ISPIF=y
+CONFIG_QCOM_KGSL=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_GADGET=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_DUAL_ROLE_USB_INTF=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_VIBRATOR=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_QCOM_LAZY_MAPPING=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_CORE_HANG_DETECT=y
+CONFIG_MSM_GLADIATOR_HANG_DETECT=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_TZ_SMMU=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_CNSS_CRYPTO=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_QTI_MPM=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_WARN=2048
+CONFIG_PAGE_OWNER=y
+CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_WQ_WATCHDOG=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANIC_ON_SCHED_BUG=y
+CONFIG_PANIC_ON_RT_THROTTLING=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+CONFIG_LKDTM=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SINK_TPIU=y
+CONFIG_CORESIGHT_SOURCE_ETM3X=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCE=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA2_ARM_CE=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
+CONFIG_XZ_DEC=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig
new file mode 100644
index 0000000..accc814
--- /dev/null
+++ b/arch/arm/configs/msm8909_defconfig
@@ -0,0 +1,587 @@
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_SCHED_CORE_CTL=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_KPROBES=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8909=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+CONFIG_CMA_DEBUGFS=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TEE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_RPFILTER=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_DEBUGFS=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_HDCP_QSEECOM=y
+CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_QCOM=y
+CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CNSS=y
+CONFIG_CNSS_SDIO=y
+CONFIG_CLD_HL_SDIO_CORE=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_DIAG_CHAR=y
+CONFIG_DIAG_USES_SMD=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8909=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SMB1360_CHARGER_FG=y
+CONFIG_QPNP_VM_BMS=y
+CONFIG_QPNP_LINEAR_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_MFD_QCOM_RPM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_REGULATOR_QCOM_RPM=y
+CONFIG_REGULATOR_QCOM_SPMI=y
+CONFIG_REGULATOR_CPR=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_MSM_GFX_LDO=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISP_V1=y
+CONFIG_MSM_ISPIF=y
+CONFIG_QCOM_KGSL=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_GADGET=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_DUAL_ROLE_USB_INTF=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_MTP=y
+CONFIG_USB_CONFIGFS_F_PTP=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_LEDS_CLASS_FLASH=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_VIBRATOR=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_QCOM_LAZY_MAPPING=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_CORE_HANG_DETECT=y
+CONFIG_MSM_GLADIATOR_HANG_DETECT=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_TZ_SMMU=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_CNSS_CRYPTO=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_QTI_MPM=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_WARN=2048
+CONFIG_PAGE_OWNER=y
+CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_SLUB_DEBUG_PANIC_ON=y
+CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
+CONFIG_DEBUG_OBJECTS=y
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
+CONFIG_SLUB_DEBUG_ON=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_WQ_WATCHDOG=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANIC_ON_SCHED_BUG=y
+CONFIG_PANIC_ON_RT_THROTTLING=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_IPC_LOGGING=y
+CONFIG_QCOM_RTB=y
+CONFIG_QCOM_RTB_SEPARATE_CPUS=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_PREEMPT_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_LKDTM=y
+CONFIG_MEMTEST=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
+CONFIG_DEBUG_USER=y
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SINK_TPIU=y
+CONFIG_CORESIGHT_SOURCE_ETM3X=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA2_ARM_CE=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index 98fb09b..2b784c9 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -1,3 +1,4 @@
+# CONFIG_FHANDLE is not set
CONFIG_AUDIT=y
# CONFIG_AUDITSYSCALL is not set
CONFIG_NO_HZ=y
@@ -5,7 +6,6 @@
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
@@ -34,7 +34,7 @@
CONFIG_EMBEDDED=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -49,10 +49,10 @@
CONFIG_SCHED_MC=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
-CONFIG_HIGHMEM=y
CONFIG_CMA=y
CONFIG_ZSMALLOC=y
CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -108,14 +108,12 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CT=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
-CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
-CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
-CONFIG_NETFILTER_XT_TARGET_TEE=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
@@ -125,7 +123,6 @@
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_DSCP=y
-CONFIG_NETFILTER_XT_MATCH_ESP=y
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
CONFIG_NETFILTER_XT_MATCH_HELPER=y
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
@@ -139,6 +136,7 @@
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -146,6 +144,8 @@
CONFIG_NETFILTER_XT_MATCH_TIME=y
CONFIG_NETFILTER_XT_MATCH_U32=y
CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_DUP_IPV4=y
+CONFIG_NF_LOG_IPV4=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_MATCH_AH=y
CONFIG_IP_NF_MATCH_ECN=y
@@ -164,6 +164,8 @@
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_NF_DUP_IPV6=y
+CONFIG_NF_LOG_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
@@ -229,11 +231,9 @@
CONFIG_SCSI_UFSHCD=y
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
-CONFIG_SCSI_UFS_QCOM_ICE=y
CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
@@ -257,7 +257,6 @@
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
-CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
@@ -266,6 +265,8 @@
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_SMD=y
CONFIG_DIAG_CHAR=y
@@ -317,6 +318,8 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MSM_VIDC_3X_V4L2=y
+CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
@@ -346,11 +349,7 @@
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MONTEREY=y
CONFIG_HID_MULTITOUCH=y
-CONFIG_USB_DWC3=y
-CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -361,6 +360,7 @@
CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -369,11 +369,11 @@
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_CCID=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_RING_BUFFER=y
@@ -381,7 +381,6 @@
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -400,16 +399,18 @@
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
-CONFIG_QCOM_PM=y
CONFIG_MSM_SPM=y
CONFIG_MSM_L2_SPM=y
CONFIG_MSM_BOOT_STATS=y
@@ -436,6 +437,7 @@
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -457,22 +459,23 @@
CONFIG_QCOM_SPMI_VADC=y
CONFIG_QCOM_RRADC=y
CONFIG_PWM=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_STM=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
-CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
@@ -490,21 +493,12 @@
CONFIG_SCHEDSTATS=y
CONFIG_SCHED_STACK_END_CHECK=y
# CONFIG_DEBUG_PREEMPT is not set
-# CONFIG_FTRACE is not set
+CONFIG_IPC_LOGGING=y
CONFIG_LKDTM=y
CONFIG_PANIC_ON_DATA_CORRUPTION=y
# CONFIG_ARM_UNWIND is not set
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_REMOTE_ETM=y
-CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_TPDA=y
-CONFIG_CORESIGHT_TPDM=y
-CONFIG_CORESIGHT_CTI=y
-CONFIG_CORESIGHT_EVENT=y
-CONFIG_CORESIGHT_HWEVENT=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
@@ -513,13 +507,14 @@
CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_CTR=y
-CONFIG_CRYPTO_XTS=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_ARM_CRYPTO=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 9d1fc06..c9d7fdb 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -1,3 +1,4 @@
+# CONFIG_FHANDLE is not set
CONFIG_AUDIT=y
# CONFIG_AUDITSYSCALL is not set
CONFIG_NO_HZ=y
@@ -5,7 +6,6 @@
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
@@ -17,7 +17,6 @@
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
-CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_SCHEDTUNE=y
CONFIG_RT_GROUP_SCHED=y
@@ -28,13 +27,19 @@
CONFIG_SCHED_TUNE=y
CONFIG_DEFAULT_USE_ENERGY_AWARE=y
CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KALLSYMS_ALL=y
CONFIG_BPF_SYSCALL=y
CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_OPROFILE=m
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
@@ -49,11 +54,11 @@
CONFIG_SCHED_MC=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
-CONFIG_HIGHMEM=y
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_ZSMALLOC=y
CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -64,12 +69,10 @@
CONFIG_PM_AUTOSLEEP=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_LIMIT=0
-CONFIG_PM_DEBUG=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM_USER=y
-CONFIG_XFRM_STATISTICS=y
CONFIG_NET_KEY=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
@@ -110,14 +113,13 @@
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CT=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
-CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
-CONFIG_NETFILTER_XT_TARGET_TEE=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
@@ -127,7 +129,6 @@
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_DSCP=y
-CONFIG_NETFILTER_XT_MATCH_ESP=y
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
CONFIG_NETFILTER_XT_MATCH_HELPER=y
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
@@ -141,6 +142,7 @@
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -148,6 +150,7 @@
CONFIG_NETFILTER_XT_MATCH_TIME=y
CONFIG_NETFILTER_XT_MATCH_U32=y
CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_DUP_IPV4=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_MATCH_AH=y
CONFIG_IP_NF_MATCH_ECN=y
@@ -166,14 +169,13 @@
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_NF_DUP_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_MANGLE=y
CONFIG_IP6_NF_RAW=y
-CONFIG_BRIDGE_NF_EBTABLES=y
-CONFIG_BRIDGE_EBT_BROUTE=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
CONFIG_L2TP_V3=y
@@ -183,8 +185,6 @@
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
-CONFIG_NET_SCH_MULTIQ=y
-CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
CONFIG_CLS_U32_MARK=y
@@ -197,8 +197,6 @@
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
CONFIG_NET_ACT_GACT=y
-CONFIG_NET_ACT_MIRRED=y
-CONFIG_NET_ACT_SKBEDIT=y
CONFIG_DNS_RESOLVER=y
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
@@ -217,26 +215,11 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_QPNP_MISC=y
-CONFIG_SCSI=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_SG=y
-CONFIG_CHR_DEV_SCH=y
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_LOGGING=y
-CONFIG_SCSI_SCAN_ASYNC=y
-CONFIG_SCSI_UFSHCD=y
-CONFIG_SCSI_UFSHCD_PLATFORM=y
-CONFIG_SCSI_UFS_QCOM=y
-CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_VERITY=y
@@ -260,7 +243,6 @@
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
-CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
@@ -269,6 +251,8 @@
CONFIG_INPUT_MISC=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_MSM_HS=y
@@ -322,6 +306,8 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MSM_VIDC_3X_V4L2=y
+CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
@@ -337,25 +323,7 @@
CONFIG_SND_DYNAMIC_MINORS=y
CONFIG_SND_SOC=y
CONFIG_UHID=y
-CONFIG_HID_A4TECH=y
-CONFIG_HID_APPLE=y
-CONFIG_HID_BELKIN=y
-CONFIG_HID_CHERRY=y
-CONFIG_HID_CHICONY=y
-CONFIG_HID_CYPRESS=y
-CONFIG_HID_ELECOM=y
-CONFIG_HID_EZKEY=y
-CONFIG_HID_KENSINGTON=y
-CONFIG_HID_LOGITECH=y
-CONFIG_HID_MAGICMOUSE=y
-CONFIG_HID_MICROSOFT=y
-CONFIG_HID_MONTEREY=y
-CONFIG_HID_MULTITOUCH=y
-CONFIG_USB_DWC3=y
-CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -363,22 +331,18 @@
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
-CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_NCM=y
-CONFIG_USB_CONFIGFS_ECM=y
-CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
-CONFIG_USB_CONFIGFS_F_MTP=y
-CONFIG_USB_CONFIGFS_F_PTP=y
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_CCID=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_RING_BUFFER=y
@@ -405,16 +369,18 @@
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RMNET_IPA=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
-CONFIG_QCOM_PM=y
CONFIG_MSM_SPM=y
CONFIG_MSM_L2_SPM=y
CONFIG_MSM_BOOT_STATS=y
@@ -441,6 +407,7 @@
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_GLINK_PKT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -462,22 +429,22 @@
CONFIG_QCOM_SPMI_VADC=y
CONFIG_QCOM_RRADC=y
CONFIG_PWM=y
+CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
-CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
@@ -488,15 +455,7 @@
CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_SLUB_DEBUG_PANIC_ON=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
-CONFIG_SLUB_DEBUG_ON=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
@@ -533,8 +492,13 @@
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SINK_TPIU=y
+CONFIG_CORESIGHT_SOURCE_ETM3X=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
@@ -556,6 +520,8 @@
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_ARM_CRYPTO=y
diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig
index df466cf..b098e52 100644
--- a/arch/arm/configs/msm8937-perf_defconfig
+++ b/arch/arm/configs/msm8937-perf_defconfig
@@ -6,6 +6,9 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
@@ -17,6 +20,8 @@
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_BPF=y
CONFIG_SCHED_CORE_CTL=y
@@ -52,6 +57,8 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_MSM8917=y
+CONFIG_ARCH_SDM439=y
+CONFIG_ARCH_SDM429=y
# CONFIG_VDSO is not set
CONFIG_SMP=y
CONFIG_SCHED_MC=y
@@ -60,16 +67,17 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
+CONFIG_ARM_MODULE_PLTS=y
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_ZSMALLOC=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -140,6 +148,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -202,8 +211,6 @@
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
-CONFIG_NET_SCH_MULTIQ=y
-CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
CONFIG_CLS_U32_MARK=y
@@ -215,13 +222,12 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_NET_ACT_GACT=y
-CONFIG_NET_ACT_MIRRED=y
-CONFIG_NET_ACT_SKBEDIT=y
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -239,7 +245,7 @@
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
-CONFIG_MEMORY_STATE_TIME=y
+CONFIG_UID_SYS_STATS=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -251,10 +257,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -263,6 +267,14 @@
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
@@ -276,6 +288,21 @@
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_USB_USBNET=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
@@ -283,6 +310,12 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
@@ -314,7 +347,6 @@
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
@@ -322,6 +354,7 @@
CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -330,11 +363,20 @@
CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_THERMAL_TSENS=y
-CONFIG_MSM_BCL_PERIPHERAL_CTL=y
-CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_QTI_VIRTUAL_SENSOR=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_QTI_BCL_PMIC5=y
+CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -384,7 +426,6 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_RADIO_IRIS=y
@@ -397,7 +438,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -413,12 +454,9 @@
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
-CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
@@ -432,14 +470,10 @@
CONFIG_USB_STORAGE_ONETOUCH=y
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_DWC3=y
-CONFIG_USB_DWC3_MSM=y
CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -448,7 +482,6 @@
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
-CONFIG_USB_CONFIGFS_QCRNDIS=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
@@ -466,11 +499,12 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -530,6 +564,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -538,6 +573,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
@@ -551,6 +587,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -559,10 +596,10 @@
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -570,8 +607,6 @@
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
@@ -587,8 +622,11 @@
CONFIG_IPC_LOGGING=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
@@ -605,6 +643,7 @@
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_LZ4=y
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCRYPTO=y
diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig
index e1c655b..7cb806d 100644
--- a/arch/arm/configs/msm8937_defconfig
+++ b/arch/arm/configs/msm8937_defconfig
@@ -21,6 +21,8 @@
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_BPF=y
CONFIG_SCHED_CORE_CTL=y
@@ -58,6 +60,8 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_MSM8917=y
+CONFIG_ARCH_SDM439=y
+CONFIG_ARCH_SDM429=y
# CONFIG_VDSO is not set
CONFIG_SMP=y
CONFIG_SCHED_MC=y
@@ -66,16 +70,17 @@
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
+CONFIG_ARM_MODULE_PLTS=y
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_ZSMALLOC=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -147,6 +152,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -209,8 +215,6 @@
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
-CONFIG_NET_SCH_MULTIQ=y
-CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
CONFIG_CLS_U32_MARK=y
@@ -222,14 +226,13 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_NET_ACT_GACT=y
-CONFIG_NET_ACT_MIRRED=y
-CONFIG_NET_ACT_SKBEDIT=y
CONFIG_DNS_RESOLVER=y
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -248,7 +251,6 @@
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -260,10 +262,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -272,6 +272,14 @@
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
@@ -285,6 +293,21 @@
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_USB_USBNET=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
@@ -292,6 +315,12 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
+CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
@@ -325,7 +354,6 @@
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
@@ -333,6 +361,7 @@
CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -341,11 +370,20 @@
CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_THERMAL_TSENS=y
-CONFIG_MSM_BCL_PERIPHERAL_CTL=y
-CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_QTI_VIRTUAL_SENSOR=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_QTI_BCL_PMIC5=y
+CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -395,7 +433,6 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_RADIO_IRIS=y
@@ -409,7 +446,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -425,12 +462,9 @@
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
-CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
@@ -444,14 +478,10 @@
CONFIG_USB_STORAGE_ONETOUCH=y
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_DWC3=y
-CONFIG_USB_DWC3_MSM=y
CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -460,7 +490,6 @@
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
-CONFIG_USB_CONFIGFS_QCRNDIS=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
@@ -478,12 +507,13 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -529,6 +559,7 @@
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
+CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_QCOM_RUN_QUEUE_STATS=y
CONFIG_MSM_SPM=y
CONFIG_MSM_L2_SPM=y
@@ -537,6 +568,7 @@
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_DEBUG_LAR_UNLOCK=y
CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_SECURE_BUFFER=y
@@ -550,6 +582,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -558,9 +591,11 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
+# CONFIG_MSM_JTAGV8 is not set
CONFIG_MSM_BAM_DMUX=y
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
@@ -571,18 +606,20 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
+CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_QTI_MPM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -590,8 +627,6 @@
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
@@ -606,6 +641,8 @@
CONFIG_DEBUG_PAGEALLOC=y
CONFIG_SLUB_DEBUG_PANIC_ON=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
+CONFIG_PAGE_POISONING=y
+CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
@@ -647,11 +684,16 @@
CONFIG_MEMTEST=y
CONFIG_PANIC_ON_DATA_CORRUPTION=y
CONFIG_DEBUG_USER=y
+CONFIG_FORCE_PAGES=y
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
@@ -668,6 +710,7 @@
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_LZ4=y
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCRYPTO=y
diff --git a/arch/arm/configs/msm8953-batcam-perf_defconfig b/arch/arm/configs/msm8953-batcam-perf_defconfig
new file mode 100644
index 0000000..a6fe9b0
--- /dev/null
+++ b/arch/arm/configs/msm8953-batcam-perf_defconfig
@@ -0,0 +1,307 @@
+CONFIG_LOCALVERSION="-perf"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SCHED_WALT=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_BPF_SYSCALL=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8953=y
+CONFIG_ARCH_MSM8953_BOOT_ORDERING=y
+# CONFIG_VDSO is not set
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_VMSPLIT_3G_OPT=y
+CONFIG_NR_CPUS=8
+CONFIG_ARM_PSCI=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_CMA=y
+CONFIG_CMA_DEBUGFS=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_IMG_DTB=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_BOOST=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_FREQ_MSM=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HIBERNATION=y
+CONFIG_HIBERNATION_IMAGE_REUSE=y
+CONFIG_HIBERNATION_SKIP_CRC=y
+CONFIG_PM_STD_PARTITION="/dev/mmcblk0p49"
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_DMA_CMA=y
+# CONFIG_OF_KOBJ is not set
+CONFIG_QSEECOM=y
+CONFIG_SCSI=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_NETDEVICES=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8953=y
+CONFIG_PINCTRL_MSM8917=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_QPNP_FG=y
+CONFIG_SMB135X_CHARGER=y
+CONFIG_QPNP_SMB5=y
+CONFIG_QPNP_SMBCHARGER=y
+CONFIG_QPNP_TYPEC=y
+CONFIG_QPNP_QG=y
+CONFIG_MSM_APM=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_REGULATOR_CPR=y
+CONFIG_REGULATOR_CPR4_APSS=y
+CONFIG_REGULATOR_CPRH_KBSS=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_MSM_GFX_LDO=y
+CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_QPNP_LCDB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_FB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_MSM=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_DUAL_ROLE_USB_INTF=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_MSM_QUSB_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_MMC=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_CQ_HCI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QTI_TRI_LED=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
+CONFIG_LEDS_QPNP_WLED=y
+CONFIG_LEDS_QPNP_HAPTICS=y
+CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RNDIS_IPA=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_COINCELL=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_QCOM_LAZY_MAPPING=y
+CONFIG_QCOM_RUN_QUEUE_STATS=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_RPM_SMD=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_TZ_SMMU=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_ICNSS=y
+CONFIG_MSM_PERFORMANCE=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_PM=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_QCOM_BIMC_BWMON=y
+CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
+CONFIG_SPDM_SCM=y
+CONFIG_DEVFREQ_SPDM=y
+CONFIG_EXTCON_USB_GPIO=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_PWM_QTI_LPG=y
+CONFIG_QTI_MPM=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_FRAME_WARN=2048
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_STACKTRACE=y
+# CONFIG_FTRACE is not set
+CONFIG_SECURITY=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA2_ARM_CE=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/msm8953-batcam_defconfig b/arch/arm/configs/msm8953-batcam_defconfig
new file mode 100644
index 0000000..cd86b01
--- /dev/null
+++ b/arch/arm/configs/msm8953-batcam_defconfig
@@ -0,0 +1,308 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SCHED_WALT=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_BPF_SYSCALL=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8953=y
+CONFIG_ARCH_MSM8953_BOOT_ORDERING=y
+# CONFIG_VDSO is not set
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_VMSPLIT_3G_OPT=y
+CONFIG_NR_CPUS=8
+CONFIG_ARM_PSCI=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_CMA=y
+CONFIG_CMA_DEBUGFS=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_IMG_DTB=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_BOOST=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPU_FREQ_MSM=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HIBERNATION=y
+CONFIG_HIBERNATION_IMAGE_REUSE=y
+CONFIG_HIBERNATION_SKIP_CRC=y
+CONFIG_PM_STD_PARTITION="/dev/mmcblk0p49"
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_DMA_CMA=y
+# CONFIG_OF_KOBJ is not set
+CONFIG_QSEECOM=y
+CONFIG_SCSI=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_NETDEVICES=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
+CONFIG_INPUT_UINPUT=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_SMD=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8953=y
+CONFIG_PINCTRL_MSM8917=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_QPNP_FG=y
+CONFIG_SMB135X_CHARGER=y
+CONFIG_QPNP_SMB5=y
+CONFIG_QPNP_SMBCHARGER=y
+CONFIG_QPNP_TYPEC=y
+CONFIG_QPNP_QG=y
+CONFIG_MSM_APM=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
+CONFIG_REGULATOR_CPR=y
+CONFIG_REGULATOR_CPR4_APSS=y
+CONFIG_REGULATOR_CPRH_KBSS=y
+CONFIG_REGULATOR_MEM_ACC=y
+CONFIG_REGULATOR_MSM_GFX_LDO=y
+CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_QPNP_LCDB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_FB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_MSM=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_DUAL_ROLE_USB_INTF=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_MSM_QUSB_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_MMC=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_CQ_HCI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QTI_TRI_LED=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
+CONFIG_LEDS_QPNP_WLED=y
+CONFIG_LEDS_QPNP_HAPTICS=y
+CONFIG_LEDS_QPNP_VIBRATOR_LDO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_IPA=y
+CONFIG_RNDIS_IPA=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_COINCELL=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_QCOM_LAZY_MAPPING=y
+CONFIG_QCOM_RUN_QUEUE_STATS=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_RPM_SMD=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
+CONFIG_MSM_TZ_SMMU=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_ICNSS=y
+CONFIG_MSM_PERFORMANCE=y
+CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_PM=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_QCOM_BIMC_BWMON=y
+CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
+CONFIG_SPDM_SCM=y
+CONFIG_DEVFREQ_SPDM=y
+CONFIG_EXTCON_USB_GPIO=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_PWM_QTI_LPG=y
+CONFIG_QTI_MPM=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QFMT_V2=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_FRAME_WARN=2048
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_STACKTRACE=y
+# CONFIG_FTRACE is not set
+CONFIG_SECURITY=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA2_ARM_CE=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index 7627adb..78f90cb 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -145,6 +145,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -222,6 +223,8 @@
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -240,7 +243,6 @@
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -252,10 +254,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -308,6 +308,11 @@
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
+CONFIG_TOUCHSCREEN_HIMAX_CHIPSET=y
+CONFIG_TOUCHSCREEN_HIMAX_I2C=y
+CONFIG_TOUCHSCREEN_HIMAX_DEBUG=y
+CONFIG_HMX_DB=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
@@ -338,14 +343,13 @@
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
-CONFIG_POWER_RESET_SYSCON=y
CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -354,12 +358,22 @@
CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_THERMAL_TSENS=y
-CONFIG_MSM_BCL_PERIPHERAL_CTL=y
-CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_QTI_VIRTUAL_SENSOR=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_QTI_BCL_PMIC5=y
+CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PROXY_CONSUMER=y
@@ -408,7 +422,6 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_RADIO_IRIS=y
@@ -421,7 +434,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -488,11 +501,12 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -561,6 +575,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
@@ -574,6 +589,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -584,6 +600,9 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -591,8 +610,6 @@
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
@@ -608,21 +625,24 @@
CONFIG_IPC_LOGGING=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
CONFIG_CORESIGHT_CTI=y
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_PFK=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_LSM_MMAP_MIN_ADDR=4096
CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_CTR=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index e86e30d..9b82472 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -149,6 +149,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -227,6 +228,8 @@
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -245,7 +248,6 @@
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -257,10 +259,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -313,6 +313,11 @@
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_FT5X06=y
+CONFIG_TOUCHSCREEN_GEN_VKEYS=y
+CONFIG_TOUCHSCREEN_HIMAX_CHIPSET=y
+CONFIG_TOUCHSCREEN_HIMAX_I2C=y
+CONFIG_TOUCHSCREEN_HIMAX_DEBUG=y
+CONFIG_HMX_DB=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
@@ -345,14 +350,13 @@
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
-CONFIG_POWER_RESET_SYSCON=y
CONFIG_POWER_SUPPLY=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -361,12 +365,22 @@
CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_THERMAL_TSENS=y
-CONFIG_MSM_BCL_PERIPHERAL_CTL=y
-CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_QTI_VIRTUAL_SENSOR=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_QTI_BCL_PMIC5=y
+CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PROXY_CONSUMER=y
@@ -415,7 +429,6 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_RADIO_IRIS=y
@@ -429,7 +442,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -496,12 +509,13 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -547,6 +561,7 @@
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
+CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_QCOM_RUN_QUEUE_STATS=y
CONFIG_MSM_SPM=y
CONFIG_MSM_L2_SPM=y
@@ -555,6 +570,7 @@
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_DEBUG_LAR_UNLOCK=y
CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_SECURE_BUFFER=y
@@ -577,6 +593,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
@@ -590,6 +607,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -600,6 +618,9 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -607,8 +628,6 @@
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
@@ -623,6 +642,8 @@
CONFIG_DEBUG_PAGEALLOC=y
CONFIG_SLUB_DEBUG_PANIC_ON=y
CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
+CONFIG_PAGE_POISONING=y
+CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
@@ -664,24 +685,29 @@
CONFIG_MEMTEST=y
CONFIG_PANIC_ON_DATA_CORRUPTION=y
CONFIG_DEBUG_USER=y
+CONFIG_FORCE_PAGES=y
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
CONFIG_CORESIGHT_STM=y
CONFIG_CORESIGHT_TPDA=y
CONFIG_CORESIGHT_TPDM=y
CONFIG_CORESIGHT_CTI=y
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_PFK=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_LSM_MMAP_MIN_ADDR=4096
CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_CTR=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index e3c1c1a..cab3796 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -1,3 +1,4 @@
+CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
@@ -31,7 +32,9 @@
CONFIG_CMA=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_MSM=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
@@ -60,6 +63,7 @@
CONFIG_NETFILTER=y
CONFIG_NETFILTER_DEBUG=y
CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_TIMEOUT=y
CONFIG_NF_CONNTRACK_TIMESTAMP=y
@@ -77,6 +81,7 @@
CONFIG_NF_CT_NETLINK_TIMEOUT=y
CONFIG_NF_CT_NETLINK_HELPER=y
CONFIG_NETFILTER_NETLINK_GLUE_CT=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -84,6 +89,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
@@ -115,6 +121,7 @@
CONFIG_IP_NF_TARGET_ECN=y
CONFIG_IP_NF_TARGET_TTL=y
CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
@@ -217,6 +224,7 @@
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_LIBPS2=y
# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
@@ -253,6 +261,7 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_QPNP=y
@@ -374,6 +383,10 @@
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_ARM_MEMLAT_MON=y
+CONFIG_DEVFREQ_GOV_MEMLAT=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_EXTCON_QCOM_SPMI_MISC=y
CONFIG_IIO=y
CONFIG_PWM=y
@@ -392,12 +405,15 @@
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
CONFIG_MAGIC_SYSRQ=y
+CONFIG_PANIC_ON_RECURSIVE_FAULT=y
CONFIG_PANIC_TIMEOUT=5
# CONFIG_SCHED_DEBUG is not set
CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_IPC_LOGGING=y
CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_REMOTE_ETM=y
CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
@@ -407,6 +423,11 @@
CONFIG_CORESIGHT_CTI=y
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 77ae3e3..bd429ee 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -1,3 +1,4 @@
+CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
@@ -33,7 +34,9 @@
CONFIG_CMA=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_BOOST=y
CONFIG_CPU_FREQ_MSM=y
CONFIG_CPU_IDLE=y
CONFIG_VFP=y
@@ -62,6 +65,7 @@
CONFIG_NETFILTER=y
CONFIG_NETFILTER_DEBUG=y
CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_TIMEOUT=y
CONFIG_NF_CONNTRACK_TIMESTAMP=y
@@ -79,6 +83,7 @@
CONFIG_NF_CT_NETLINK_TIMEOUT=y
CONFIG_NF_CT_NETLINK_HELPER=y
CONFIG_NETFILTER_NETLINK_GLUE_CT=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -86,6 +91,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
@@ -117,6 +123,7 @@
CONFIG_IP_NF_TARGET_ECN=y
CONFIG_IP_NF_TARGET_TTL=y
CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
@@ -150,6 +157,8 @@
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_DEBUGFS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -221,7 +230,6 @@
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=m
-CONFIG_SLIMBUS=y
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
CONFIG_PTP_1588_CLOCK=y
@@ -290,6 +298,7 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_MSM=y
+CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_USB_MSM_SSPHY_QMP=y
CONFIG_MSM_HSUSB_PHY=y
@@ -380,6 +389,9 @@
CONFIG_QCOM_DCC_V2=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_ARM_MEMLAT_MON=y
+CONFIG_DEVFREQ_GOV_MEMLAT=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_EXTCON_QCOM_SPMI_MISC=y
CONFIG_IIO=y
@@ -405,8 +417,10 @@
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_PANIC_ON_RECURSIVE_FAULT=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
@@ -421,6 +435,7 @@
CONFIG_PREEMPT_TRACER=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
CONFIG_CORESIGHT_SOURCE_ETM3X=y
@@ -435,8 +450,11 @@
CONFIG_CORESIGHT_TGU=y
CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_DUMMY=y
-CONFIG_CRYPTO_CMAC=y
-CONFIG_CRYPTO_SHA256=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCEDEV=y
diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h
index a808829..4d1065c 100644
--- a/arch/arm/include/asm/arch_gicv3.h
+++ b/arch/arm/include/asm/arch_gicv3.h
@@ -242,6 +242,15 @@
writel_relaxed((u32)(val >> 32), addr + 4);
}
+static inline u64 gic_read_irouter(const volatile void __iomem *addr)
+{
+ u64 val;
+
+ val = readl_relaxed(addr);
+ val |= (u64)readl_relaxed(addr + 4) << 32;
+ return val;
+}
+
static inline u64 gic_read_typer(const volatile void __iomem *addr)
{
u64 val;
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 12f99fd..3aed449 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -534,4 +534,14 @@
#endif
.endm
+#ifdef CONFIG_KPROBES
+#define _ASM_NOKPROBE(entry) \
+ .pushsection "_kprobe_blacklist", "aw" ; \
+ .balign 4 ; \
+ .long entry; \
+ .popsection
+#else
+#define _ASM_NOKPROBE(entry)
+#endif
+
#endif /* __ASM_ASSEMBLER_H__ */
diff --git a/arch/arm/include/asm/dma-contiguous.h b/arch/arm/include/asm/dma-contiguous.h
index 4f8e9e5..2dbb8c6 100644
--- a/arch/arm/include/asm/dma-contiguous.h
+++ b/arch/arm/include/asm/dma-contiguous.h
@@ -1,14 +1,25 @@
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
#ifndef ASMARM_DMA_CONTIGUOUS_H
#define ASMARM_DMA_CONTIGUOUS_H
#ifdef __KERNEL__
-#ifdef CONFIG_DMA_CMA
#include <linux/types.h>
void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
#endif
-#endif
#endif
diff --git a/arch/arm/include/asm/etmv4x.h b/arch/arm/include/asm/etmv4x.h
new file mode 100644
index 0000000..7ad0a92
--- /dev/null
+++ b/arch/arm/include/asm/etmv4x.h
@@ -0,0 +1,387 @@
+/* Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ETMV4X_H
+#define __ASM_ETMV4X_H
+
+#include <linux/types.h>
+
+
+/* 32 bit register read for AArch32 */
+#define trc_readl(reg) RSYSL_##reg()
+#define trc_readq(reg) RSYSL_##reg()
+
+/* 32 bit register write for AArch32 */
+#define trc_write(val, reg) WSYS_##reg(val)
+
+#define MRC(op0, op1, crn, crm, op2) \
+({ \
+uint32_t val; \
+asm volatile("mrc p"#op0", "#op1", %0, "#crn", "#crm", "#op2 : "=r" (val)); \
+val; \
+})
+
+#define MCR(val, op0, op1, crn, crm, op2) \
+({ \
+asm volatile("mcr p"#op0", "#op1", %0, "#crn", "#crm", "#op2 : : "r" (val));\
+})
+
+/* Clock and Power Management Register */
+#define RSYSL_CPMR_EL1() MRC(15, 7, c15, c0, 5)
+#define WSYS_CPMR_EL1(val) MCR(val, 15, 7, c15, c0, 5)
+
+/*
+ * ETMv4 Registers
+ *
+ * Read only
+ * ETMAUTHSTATUS, ETMDEVARCH, ETMDEVID, ETMIDRn[0-13], ETMOSLSR, ETMSTATR
+ *
+ * Write only
+ * ETMOSLAR
+ */
+/* 32 bit registers */
+#define RSYSL_ETMAUTHSTATUS() MRC(14, 1, c7, c14, 6)
+#define RSYSL_ETMAUXCTLR() MRC(14, 1, c0, c6, 0)
+#define RSYSL_ETMCCCTLR() MRC(14, 1, c0, c14, 0)
+#define RSYSL_ETMCIDCCTLR0() MRC(14, 1, c3, c0, 2)
+#define RSYSL_ETMCNTCTLR0() MRC(14, 1, c0, c4, 5)
+#define RSYSL_ETMCNTCTLR1() MRC(14, 1, c0, c5, 5)
+#define RSYSL_ETMCNTCTLR2() MRC(14, 1, c0, c6, 5)
+#define RSYSL_ETMCNTCTLR3() MRC(14, 1, c0, c7, 5)
+#define RSYSL_ETMCNTRLDVR0() MRC(14, 1, c0, c0, 5)
+#define RSYSL_ETMCNTRLDVR1() MRC(14, 1, c0, c1, 5)
+#define RSYSL_ETMCNTRLDVR2() MRC(14, 1, c0, c2, 5)
+#define RSYSL_ETMCNTRLDVR3() MRC(14, 1, c0, c3, 5)
+#define RSYSL_ETMCNTVR0() MRC(14, 1, c0, c8, 5)
+#define RSYSL_ETMCNTVR1() MRC(14, 1, c0, c9, 5)
+#define RSYSL_ETMCNTVR2() MRC(14, 1, c0, c10, 5)
+#define RSYSL_ETMCNTVR3() MRC(14, 1, c0, c11, 5)
+#define RSYSL_ETMCONFIGR() MRC(14, 1, c0, c4, 0)
+#define RSYSL_ETMDEVARCH() MRC(14, 1, c7, c15, 6)
+#define RSYSL_ETMDEVID() MRC(14, 1, c7, c2, 7)
+#define RSYSL_ETMEVENTCTL0R() MRC(14, 1, c0, c8, 0)
+#define RSYSL_ETMEVENTCTL1R() MRC(14, 1, c0, c9, 0)
+#define RSYSL_ETMEXTINSELR() MRC(14, 1, c0, c8, 4)
+#define RSYSL_ETMIDR0() MRC(14, 1, c0, c8, 7)
+#define RSYSL_ETMIDR1() MRC(14, 1, c0, c9, 7)
+#define RSYSL_ETMIDR10() MRC(14, 1, c0, c2, 6)
+#define RSYSL_ETMIDR11() MRC(14, 1, c0, c3, 6)
+#define RSYSL_ETMIDR12() MRC(14, 1, c0, c4, 6)
+#define RSYSL_ETMIDR13() MRC(14, 1, c0, c5, 6)
+#define RSYSL_ETMIDR2() MRC(14, 1, c0, c10, 7)
+#define RSYSL_ETMIDR3() MRC(14, 1, c0, c11, 7)
+#define RSYSL_ETMIDR4() MRC(14, 1, c0, c12, 7)
+#define RSYSL_ETMIDR5() MRC(14, 1, c0, c13, 7)
+#define RSYSL_ETMIDR6() MRC(14, 1, c0, c14, 7)
+#define RSYSL_ETMIDR7() MRC(14, 1, c0, c15, 7)
+#define RSYSL_ETMIDR8() MRC(14, 1, c0, c0, 6)
+#define RSYSL_ETMIDR9() MRC(14, 1, c0, c1, 6)
+#define RSYSL_ETMIMSPEC0() MRC(14, 1, c0, c0, 7)
+#define RSYSL_ETMOSLSR() MRC(14, 1, c1, c1, 4)
+#define RSYSL_ETMPRGCTLR() MRC(14, 1, c0, c1, 0)
+#define RSYSL_ETMRSCTLR10() MRC(14, 1, c1, c10, 0)
+#define RSYSL_ETMRSCTLR11() MRC(14, 1, c1, c11, 0)
+#define RSYSL_ETMRSCTLR12() MRC(14, 1, c1, c12, 0)
+#define RSYSL_ETMRSCTLR13() MRC(14, 1, c1, c13, 0)
+#define RSYSL_ETMRSCTLR14() MRC(14, 1, c1, c14, 0)
+#define RSYSL_ETMRSCTLR15() MRC(14, 1, c1, c15, 0)
+#define RSYSL_ETMRSCTLR2() MRC(14, 1, c1, c2, 0)
+#define RSYSL_ETMRSCTLR3() MRC(14, 1, c1, c3, 0)
+#define RSYSL_ETMRSCTLR4() MRC(14, 1, c1, c4, 0)
+#define RSYSL_ETMRSCTLR5() MRC(14, 1, c1, c5, 0)
+#define RSYSL_ETMRSCTLR6() MRC(14, 1, c1, c6, 0)
+#define RSYSL_ETMRSCTLR7() MRC(14, 1, c1, c7, 0)
+#define RSYSL_ETMRSCTLR8() MRC(14, 1, c1, c8, 0)
+#define RSYSL_ETMRSCTLR9() MRC(14, 1, c1, c9, 0)
+#define RSYSL_ETMRSCTLR16() MRC(14, 1, c1, c0, 1)
+#define RSYSL_ETMRSCTLR17() MRC(14, 1, c1, c1, 1)
+#define RSYSL_ETMRSCTLR18() MRC(14, 1, c1, c2, 1)
+#define RSYSL_ETMRSCTLR19() MRC(14, 1, c1, c3, 1)
+#define RSYSL_ETMRSCTLR20() MRC(14, 1, c1, c4, 1)
+#define RSYSL_ETMRSCTLR21() MRC(14, 1, c1, c5, 1)
+#define RSYSL_ETMRSCTLR22() MRC(14, 1, c1, c6, 1)
+#define RSYSL_ETMRSCTLR23() MRC(14, 1, c1, c7, 1)
+#define RSYSL_ETMRSCTLR24() MRC(14, 1, c1, c8, 1)
+#define RSYSL_ETMRSCTLR25() MRC(14, 1, c1, c9, 1)
+#define RSYSL_ETMRSCTLR26() MRC(14, 1, c1, c10, 1)
+#define RSYSL_ETMRSCTLR27() MRC(14, 1, c1, c11, 1)
+#define RSYSL_ETMRSCTLR28() MRC(14, 1, c1, c12, 1)
+#define RSYSL_ETMRSCTLR29() MRC(14, 1, c1, c13, 1)
+#define RSYSL_ETMRSCTLR30() MRC(14, 1, c1, c14, 1)
+#define RSYSL_ETMRSCTLR31() MRC(14, 1, c1, c15, 1)
+#define RSYSL_ETMSEQEVR0() MRC(14, 1, c0, c0, 4)
+#define RSYSL_ETMSEQEVR1() MRC(14, 1, c0, c1, 4)
+#define RSYSL_ETMSEQEVR2() MRC(14, 1, c0, c2, 4)
+#define RSYSL_ETMSEQRSTEVR() MRC(14, 1, c0, c6, 4)
+#define RSYSL_ETMSEQSTR() MRC(14, 1, c0, c7, 4)
+#define RSYSL_ETMSTALLCTLR() MRC(14, 1, c0, c11, 0)
+#define RSYSL_ETMSTATR() MRC(14, 1, c0, c3, 0)
+#define RSYSL_ETMSYNCPR() MRC(14, 1, c0, c13, 0)
+#define RSYSL_ETMTRACEIDR() MRC(14, 1, c0, c0, 1)
+#define RSYSL_ETMTSCTLR() MRC(14, 1, c0, c12, 0)
+#define RSYSL_ETMVICTLR() MRC(14, 1, c0, c0, 2)
+#define RSYSL_ETMVIIECTLR() MRC(14, 1, c0, c1, 2)
+#define RSYSL_ETMVISSCTLR() MRC(14, 1, c0, c2, 2)
+#define RSYSL_ETMSSCCR0() MRC(14, 1, c1, c0, 2)
+#define RSYSL_ETMSSCCR1() MRC(14, 1, c1, c1, 2)
+#define RSYSL_ETMSSCCR2() MRC(14, 1, c1, c2, 2)
+#define RSYSL_ETMSSCCR3() MRC(14, 1, c1, c3, 2)
+#define RSYSL_ETMSSCCR4() MRC(14, 1, c1, c4, 2)
+#define RSYSL_ETMSSCCR5() MRC(14, 1, c1, c5, 2)
+#define RSYSL_ETMSSCCR6() MRC(14, 1, c1, c6, 2)
+#define RSYSL_ETMSSCCR7() MRC(14, 1, c1, c7, 2)
+#define RSYSL_ETMSSCSR0() MRC(14, 1, c1, c8, 2)
+#define RSYSL_ETMSSCSR1() MRC(14, 1, c1, c9, 2)
+#define RSYSL_ETMSSCSR2() MRC(14, 1, c1, c10, 2)
+#define RSYSL_ETMSSCSR3() MRC(14, 1, c1, c11, 2)
+#define RSYSL_ETMSSCSR4() MRC(14, 1, c1, c12, 2)
+#define RSYSL_ETMSSCSR5() MRC(14, 1, c1, c13, 2)
+#define RSYSL_ETMSSCSR6() MRC(14, 1, c1, c14, 2)
+#define RSYSL_ETMSSCSR7() MRC(14, 1, c1, c15, 2)
+#define RSYSL_ETMSSPCICR0() MRC(14, 1, c1, c0, 3)
+#define RSYSL_ETMSSPCICR1() MRC(14, 1, c1, c1, 3)
+#define RSYSL_ETMSSPCICR2() MRC(14, 1, c1, c2, 3)
+#define RSYSL_ETMSSPCICR3() MRC(14, 1, c1, c3, 3)
+#define RSYSL_ETMSSPCICR4() MRC(14, 1, c1, c4, 3)
+#define RSYSL_ETMSSPCICR5() MRC(14, 1, c1, c5, 3)
+#define RSYSL_ETMSSPCICR6() MRC(14, 1, c1, c6, 3)
+#define RSYSL_ETMSSPCICR7() MRC(14, 1, c1, c7, 3)
+
+/*
+ * 64 bit registers, ignore the upper 32bit
+ * A read from a 32-bit register location using a 64-bit access result
+ * in the upper 32bits being return as RES0.
+ */
+#define RSYSL_ETMACATR0() MRC(14, 1, c2, c0, 2)
+#define RSYSL_ETMACATR1() MRC(14, 1, c2, c2, 2)
+#define RSYSL_ETMACATR2() MRC(14, 1, c2, c4, 2)
+#define RSYSL_ETMACATR3() MRC(14, 1, c2, c6, 2)
+#define RSYSL_ETMACATR4() MRC(14, 1, c2, c8, 2)
+#define RSYSL_ETMACATR5() MRC(14, 1, c2, c10, 2)
+#define RSYSL_ETMACATR6() MRC(14, 1, c2, c12, 2)
+#define RSYSL_ETMACATR7() MRC(14, 1, c2, c14, 2)
+#define RSYSL_ETMACATR8() MRC(14, 1, c2, c0, 3)
+#define RSYSL_ETMACATR9() MRC(14, 1, c2, c2, 3)
+#define RSYSL_ETMACATR10() MRC(14, 1, c2, c4, 3)
+#define RSYSL_ETMACATR11() MRC(14, 1, c2, c6, 3)
+#define RSYSL_ETMACATR12() MRC(14, 1, c2, c8, 3)
+#define RSYSL_ETMACATR13() MRC(14, 1, c2, c10, 3)
+#define RSYSL_ETMACATR14() MRC(14, 1, c2, c12, 3)
+#define RSYSL_ETMACATR15() MRC(14, 1, c2, c14, 3)
+#define RSYSL_ETMCIDCVR0() MRC(14, 1, c3, c0, 0)
+#define RSYSL_ETMCIDCVR1() MRC(14, 1, c3, c2, 0)
+#define RSYSL_ETMCIDCVR2() MRC(14, 1, c3, c4, 0)
+#define RSYSL_ETMCIDCVR3() MRC(14, 1, c3, c6, 0)
+#define RSYSL_ETMCIDCVR4() MRC(14, 1, c3, c8, 0)
+#define RSYSL_ETMCIDCVR5() MRC(14, 1, c3, c10, 0)
+#define RSYSL_ETMCIDCVR6() MRC(14, 1, c3, c12, 0)
+#define RSYSL_ETMCIDCVR7() MRC(14, 1, c3, c14, 0)
+#define RSYSL_ETMACVR0() MRC(14, 1, c2, c0, 0)
+#define RSYSL_ETMACVR1() MRC(14, 1, c2, c2, 0)
+#define RSYSL_ETMACVR2() MRC(14, 1, c2, c4, 0)
+#define RSYSL_ETMACVR3() MRC(14, 1, c2, c6, 0)
+#define RSYSL_ETMACVR4() MRC(14, 1, c2, c8, 0)
+#define RSYSL_ETMACVR5() MRC(14, 1, c2, c10, 0)
+#define RSYSL_ETMACVR6() MRC(14, 1, c2, c12, 0)
+#define RSYSL_ETMACVR7() MRC(14, 1, c2, c14, 0)
+#define RSYSL_ETMACVR8() MRC(14, 1, c2, c0, 1)
+#define RSYSL_ETMACVR9() MRC(14, 1, c2, c2, 1)
+#define RSYSL_ETMACVR10() MRC(14, 1, c2, c4, 1)
+#define RSYSL_ETMACVR11() MRC(14, 1, c2, c6, 1)
+#define RSYSL_ETMACVR12() MRC(14, 1, c2, c8, 1)
+#define RSYSL_ETMACVR13() MRC(14, 1, c2, c10, 1)
+#define RSYSL_ETMACVR14() MRC(14, 1, c2, c12, 1)
+#define RSYSL_ETMACVR15() MRC(14, 1, c2, c14, 1)
+#define RSYSL_ETMVMIDCVR0() MRC(14, 1, c3, c0, 1)
+#define RSYSL_ETMVMIDCVR1() MRC(14, 1, c3, c2, 1)
+#define RSYSL_ETMVMIDCVR2() MRC(14, 1, c3, c4, 1)
+#define RSYSL_ETMVMIDCVR3() MRC(14, 1, c3, c6, 1)
+#define RSYSL_ETMVMIDCVR4() MRC(14, 1, c3, c8, 1)
+#define RSYSL_ETMVMIDCVR5() MRC(14, 1, c3, c10, 1)
+#define RSYSL_ETMVMIDCVR6() MRC(14, 1, c3, c12, 1)
+#define RSYSL_ETMVMIDCVR7() MRC(14, 1, c3, c14, 1)
+#define RSYSL_ETMDVCVR0() MRC(14, 1, c2, c0, 4)
+#define RSYSL_ETMDVCVR1() MRC(14, 1, c2, c4, 4)
+#define RSYSL_ETMDVCVR2() MRC(14, 1, c2, c8, 4)
+#define RSYSL_ETMDVCVR3() MRC(14, 1, c2, c12, 4)
+#define RSYSL_ETMDVCVR4() MRC(14, 1, c2, c0, 5)
+#define RSYSL_ETMDVCVR5() MRC(14, 1, c2, c4, 5)
+#define RSYSL_ETMDVCVR6() MRC(14, 1, c2, c8, 5)
+#define RSYSL_ETMDVCVR7() MRC(14, 1, c2, c12, 5)
+#define RSYSL_ETMDVCMR0() MRC(14, 1, c2, c0, 6)
+#define RSYSL_ETMDVCMR1() MRC(14, 1, c2, c4, 6)
+#define RSYSL_ETMDVCMR2() MRC(14, 1, c2, c8, 6)
+#define RSYSL_ETMDVCMR3() MRC(14, 1, c2, c12, 6)
+#define RSYSL_ETMDVCMR4() MRC(14, 1, c2, c0, 7)
+#define RSYSL_ETMDVCMR5() MRC(14, 1, c2, c4, 7)
+#define RSYSL_ETMDVCMR6() MRC(14, 1, c2, c8, 7)
+#define RSYSL_ETMDVCMR7() MRC(14, 1, c2, c12, 7)
+
+/*
+ * 32 and 64 bit registers
+ * A write to a 32-bit register location using a 64-bit access result
+ * in the upper 32bit of access
+ */
+#define WSYS_ETMAUXCTLR(val) MCR(val, 14, 1, c0, c6, 0)
+#define WSYS_ETMACATR0(val) MCR(val, 14, 1, c2, c0, 2)
+#define WSYS_ETMACATR1(val) MCR(val, 14, 1, c2, c2, 2)
+#define WSYS_ETMACATR2(val) MCR(val, 14, 1, c2, c4, 2)
+#define WSYS_ETMACATR3(val) MCR(val, 14, 1, c2, c6, 2)
+#define WSYS_ETMACATR4(val) MCR(val, 14, 1, c2, c8, 2)
+#define WSYS_ETMACATR5(val) MCR(val, 14, 1, c2, c10, 2)
+#define WSYS_ETMACATR6(val) MCR(val, 14, 1, c2, c12, 2)
+#define WSYS_ETMACATR7(val) MCR(val, 14, 1, c2, c14, 2)
+#define WSYS_ETMACATR8(val) MCR(val, 14, 1, c2, c0, 3)
+#define WSYS_ETMACATR9(val) MCR(val, 14, 1, c2, c2, 3)
+#define WSYS_ETMACATR10(val) MCR(val, 14, 1, c2, c4, 3)
+#define WSYS_ETMACATR11(val) MCR(val, 14, 1, c2, c6, 3)
+#define WSYS_ETMACATR12(val) MCR(val, 14, 1, c2, c8, 3)
+#define WSYS_ETMACATR13(val) MCR(val, 14, 1, c2, c10, 3)
+#define WSYS_ETMACATR14(val) MCR(val, 14, 1, c2, c12, 3)
+#define WSYS_ETMACATR15(val) MCR(val, 14, 1, c2, c14, 3)
+#define WSYS_ETMACVR0(val) MCR(val, 14, 1, c2, c0, 0)
+#define WSYS_ETMACVR1(val) MCR(val, 14, 1, c2, c2, 0)
+#define WSYS_ETMACVR2(val) MCR(val, 14, 1, c2, c4, 0)
+#define WSYS_ETMACVR3(val) MCR(val, 14, 1, c2, c6, 0)
+#define WSYS_ETMACVR4(val) MCR(val, 14, 1, c2, c8, 0)
+#define WSYS_ETMACVR5(val) MCR(val, 14, 1, c2, c10, 0)
+#define WSYS_ETMACVR6(val) MCR(val, 14, 1, c2, c12, 0)
+#define WSYS_ETMACVR7(val) MCR(val, 14, 1, c2, c14, 0)
+#define WSYS_ETMACVR8(val) MCR(val, 14, 1, c2, c0, 1)
+#define WSYS_ETMACVR9(val) MCR(val, 14, 1, c2, c2, 1)
+#define WSYS_ETMACVR10(val) MCR(val, 14, 1, c2, c4, 1)
+#define WSYS_ETMACVR11(val) MCR(val, 14, 1, c2, c6, 1)
+#define WSYS_ETMACVR12(val) MCR(val, 14, 1, c2, c8, 1)
+#define WSYS_ETMACVR13(val) MCR(val, 14, 1, c2, c10, 1)
+#define WSYS_ETMACVR14(val) MCR(val, 14, 1, c2, c12, 1)
+#define WSYS_ETMACVR15(val) MCR(val, 14, 1, c2, c14, 1)
+#define WSYS_ETMCCCTLR(val) MCR(val, 14, 1, c0, c14, 0)
+#define WSYS_ETMCIDCCTLR0(val) MCR(val, 14, 1, c3, c0, 2)
+#define WSYS_ETMCIDCVR0(val) MCR(val, 14, 1, c3, c0, 0)
+#define WSYS_ETMCIDCVR1(val) MCR(val, 14, 1, c3, c2, 0)
+#define WSYS_ETMCIDCVR2(val) MCR(val, 14, 1, c3, c4, 0)
+#define WSYS_ETMCIDCVR3(val) MCR(val, 14, 1, c3, c6, 0)
+#define WSYS_ETMCIDCVR4(val) MCR(val, 14, 1, c3, c8, 0)
+#define WSYS_ETMCIDCVR5(val) MCR(val, 14, 1, c3, c10, 0)
+#define WSYS_ETMCIDCVR6(val) MCR(val, 14, 1, c3, c12, 0)
+#define WSYS_ETMCIDCVR7(val) MCR(val, 14, 1, c3, c14, 0)
+#define WSYS_ETMCNTCTLR0(val) MCR(val, 14, 1, c0, c4, 5)
+#define WSYS_ETMCNTCTLR1(val) MCR(val, 14, 1, c0, c5, 5)
+#define WSYS_ETMCNTCTLR2(val) MCR(val, 14, 1, c0, c6, 5)
+#define WSYS_ETMCNTCTLR3(val) MCR(val, 14, 1, c0, c7, 5)
+#define WSYS_ETMCNTRLDVR0(val) MCR(val, 14, 1, c0, c0, 5)
+#define WSYS_ETMCNTRLDVR1(val) MCR(val, 14, 1, c0, c1, 5)
+#define WSYS_ETMCNTRLDVR2(val) MCR(val, 14, 1, c0, c2, 5)
+#define WSYS_ETMCNTRLDVR3(val) MCR(val, 14, 1, c0, c3, 5)
+#define WSYS_ETMCNTVR0(val) MCR(val, 14, 1, c0, c8, 5)
+#define WSYS_ETMCNTVR1(val) MCR(val, 14, 1, c0, c9, 5)
+#define WSYS_ETMCNTVR2(val) MCR(val, 14, 1, c0, c10, 5)
+#define WSYS_ETMCNTVR3(val) MCR(val, 14, 1, c0, c11, 5)
+#define WSYS_ETMCONFIGR(val) MCR(val, 14, 1, c0, c4, 0)
+#define WSYS_ETMEVENTCTL0R(val) MCR(val, 14, 1, c0, c8, 0)
+#define WSYS_ETMEVENTCTL1R(val) MCR(val, 14, 1, c0, c9, 0)
+#define WSYS_ETMEXTINSELR(val) MCR(val, 14, 1, c0, c8, 4)
+#define WSYS_ETMIMSPEC0(val) MCR(val, 14, 1, c0, c0, 7)
+#define WSYS_ETMOSLAR(val) MCR(val, 14, 1, c1, c0, 4)
+#define WSYS_ETMPRGCTLR(val) MCR(val, 14, 1, c0, c1, 0)
+#define WSYS_ETMRSCTLR10(val) MCR(val, 14, 1, c1, c10, 0)
+#define WSYS_ETMRSCTLR11(val) MCR(val, 14, 1, c1, c11, 0)
+#define WSYS_ETMRSCTLR12(val) MCR(val, 14, 1, c1, c12, 0)
+#define WSYS_ETMRSCTLR13(val) MCR(val, 14, 1, c1, c13, 0)
+#define WSYS_ETMRSCTLR14(val) MCR(val, 14, 1, c1, c14, 0)
+#define WSYS_ETMRSCTLR15(val) MCR(val, 14, 1, c1, c15, 0)
+#define WSYS_ETMRSCTLR2(val) MCR(val, 14, 1, c1, c2, 0)
+#define WSYS_ETMRSCTLR3(val) MCR(val, 14, 1, c1, c3, 0)
+#define WSYS_ETMRSCTLR4(val) MCR(val, 14, 1, c1, c4, 0)
+#define WSYS_ETMRSCTLR5(val) MCR(val, 14, 1, c1, c5, 0)
+#define WSYS_ETMRSCTLR6(val) MCR(val, 14, 1, c1, c6, 0)
+#define WSYS_ETMRSCTLR7(val) MCR(val, 14, 1, c1, c7, 0)
+#define WSYS_ETMRSCTLR8(val) MCR(val, 14, 1, c1, c8, 0)
+#define WSYS_ETMRSCTLR9(val) MCR(val, 14, 1, c1, c9, 0)
+#define WSYS_ETMRSCTLR16(val) MCR(val, 14, 1, c1, c0, 1)
+#define WSYS_ETMRSCTLR17(val) MCR(val, 14, 1, c1, c1, 1)
+#define WSYS_ETMRSCTLR18(val) MCR(val, 14, 1, c1, c2, 1)
+#define WSYS_ETMRSCTLR19(val) MCR(val, 14, 1, c1, c3, 1)
+#define WSYS_ETMRSCTLR20(val) MCR(val, 14, 1, c1, c4, 1)
+#define WSYS_ETMRSCTLR21(val) MCR(val, 14, 1, c1, c5, 1)
+#define WSYS_ETMRSCTLR22(val) MCR(val, 14, 1, c1, c6, 1)
+#define WSYS_ETMRSCTLR23(val) MCR(val, 14, 1, c1, c7, 1)
+#define WSYS_ETMRSCTLR24(val) MCR(val, 14, 1, c1, c8, 1)
+#define WSYS_ETMRSCTLR25(val) MCR(val, 14, 1, c1, c9, 1)
+#define WSYS_ETMRSCTLR26(val) MCR(val, 14, 1, c1, c10, 1)
+#define WSYS_ETMRSCTLR27(val) MCR(val, 14, 1, c1, c11, 1)
+#define WSYS_ETMRSCTLR28(val) MCR(val, 14, 1, c1, c12, 1)
+#define WSYS_ETMRSCTLR29(val) MCR(val, 14, 1, c1, c13, 1)
+#define WSYS_ETMRSCTLR30(val) MCR(val, 14, 1, c1, c14, 1)
+#define WSYS_ETMRSCTLR31(val) MCR(val, 14, 1, c1, c15, 1)
+#define WSYS_ETMSEQEVR0(val) MCR(val, 14, 1, c0, c0, 4)
+#define WSYS_ETMSEQEVR1(val) MCR(val, 14, 1, c0, c1, 4)
+#define WSYS_ETMSEQEVR2(val) MCR(val, 14, 1, c0, c2, 4)
+#define WSYS_ETMSEQRSTEVR(val) MCR(val, 14, 1, c0, c6, 4)
+#define WSYS_ETMSEQSTR(val) MCR(val, 14, 1, c0, c7, 4)
+#define WSYS_ETMSTALLCTLR(val) MCR(val, 14, 1, c0, c11, 0)
+#define WSYS_ETMSYNCPR(val) MCR(val, 14, 1, c0, c13, 0)
+#define WSYS_ETMTRACEIDR(val) MCR(val, 14, 1, c0, c0, 1)
+#define WSYS_ETMTSCTLR(val) MCR(val, 14, 1, c0, c12, 0)
+#define WSYS_ETMVICTLR(val) MCR(val, 14, 1, c0, c0, 2)
+#define WSYS_ETMVIIECTLR(val) MCR(val, 14, 1, c0, c1, 2)
+#define WSYS_ETMVISSCTLR(val) MCR(val, 14, 1, c0, c2, 2)
+#define WSYS_ETMVMIDCVR0(val) MCR(val, 14, 1, c3, c0, 1)
+#define WSYS_ETMVMIDCVR1(val) MCR(val, 14, 1, c3, c2, 1)
+#define WSYS_ETMVMIDCVR2(val) MCR(val, 14, 1, c3, c4, 1)
+#define WSYS_ETMVMIDCVR3(val) MCR(val, 14, 1, c3, c6, 1)
+#define WSYS_ETMVMIDCVR4(val) MCR(val, 14, 1, c3, c8, 1)
+#define WSYS_ETMVMIDCVR5(val) MCR(val, 14, 1, c3, c10, 1)
+#define WSYS_ETMVMIDCVR6(val) MCR(val, 14, 1, c3, c12, 1)
+#define WSYS_ETMVMIDCVR7(val) MCR(val, 14, 1, c3, c14, 1)
+#define WSYS_ETMDVCVR0(val) MCR(val, 14, 1, c2, c0, 4)
+#define WSYS_ETMDVCVR1(val) MCR(val, 14, 1, c2, c4, 4)
+#define WSYS_ETMDVCVR2(val) MCR(val, 14, 1, c2, c8, 4)
+#define WSYS_ETMDVCVR3(val) MCR(val, 14, 1, c2, c12, 4)
+#define WSYS_ETMDVCVR4(val) MCR(val, 14, 1, c2, c0, 5)
+#define WSYS_ETMDVCVR5(val) MCR(val, 14, 1, c2, c4, 5)
+#define WSYS_ETMDVCVR6(val) MCR(val, 14, 1, c2, c8, 5)
+#define WSYS_ETMDVCVR7(val) MCR(val, 14, 1, c2, c12, 5)
+#define WSYS_ETMDVCMR0(val) MCR(val, 14, 1, c2, c0, 6)
+#define WSYS_ETMDVCMR1(val) MCR(val, 14, 1, c2, c4, 6)
+#define WSYS_ETMDVCMR2(val) MCR(val, 14, 1, c2, c8, 6)
+#define WSYS_ETMDVCMR3(val) MCR(val, 14, 1, c2, c12, 6)
+#define WSYS_ETMDVCMR4(val) MCR(val, 14, 1, c2, c0, 7)
+#define WSYS_ETMDVCMR5(val) MCR(val, 14, 1, c2, c4, 7)
+#define WSYS_ETMDVCMR6(val) MCR(val, 14, 1, c2, c8, 7)
+#define WSYS_ETMDVCMR7(val) MCR(val, 14, 1, c2, c12, 7)
+#define WSYS_ETMSSCCR0(val) MCR(val, 14, 1, c1, c0, 2)
+#define WSYS_ETMSSCCR1(val) MCR(val, 14, 1, c1, c1, 2)
+#define WSYS_ETMSSCCR2(val) MCR(val, 14, 1, c1, c2, 2)
+#define WSYS_ETMSSCCR3(val) MCR(val, 14, 1, c1, c3, 2)
+#define WSYS_ETMSSCCR4(val) MCR(val, 14, 1, c1, c4, 2)
+#define WSYS_ETMSSCCR5(val) MCR(val, 14, 1, c1, c5, 2)
+#define WSYS_ETMSSCCR6(val) MCR(val, 14, 1, c1, c6, 2)
+#define WSYS_ETMSSCCR7(val) MCR(val, 14, 1, c1, c7, 2)
+#define WSYS_ETMSSCSR0(val) MCR(val, 14, 1, c1, c8, 2)
+#define WSYS_ETMSSCSR1(val) MCR(val, 14, 1, c1, c9, 2)
+#define WSYS_ETMSSCSR2(val) MCR(val, 14, 1, c1, c10, 2)
+#define WSYS_ETMSSCSR3(val) MCR(val, 14, 1, c1, c11, 2)
+#define WSYS_ETMSSCSR4(val) MCR(val, 14, 1, c1, c12, 2)
+#define WSYS_ETMSSCSR5(val) MCR(val, 14, 1, c1, c13, 2)
+#define WSYS_ETMSSCSR6(val) MCR(val, 14, 1, c1, c14, 2)
+#define WSYS_ETMSSCSR7(val) MCR(val, 14, 1, c1, c15, 2)
+#define WSYS_ETMSSPCICR0(val) MCR(val, 14, 1, c1, c0, 3)
+#define WSYS_ETMSSPCICR1(val) MCR(val, 14, 1, c1, c1, 3)
+#define WSYS_ETMSSPCICR2(val) MCR(val, 14, 1, c1, c2, 3)
+#define WSYS_ETMSSPCICR3(val) MCR(val, 14, 1, c1, c3, 3)
+#define WSYS_ETMSSPCICR4(val) MCR(val, 14, 1, c1, c4, 3)
+#define WSYS_ETMSSPCICR5(val) MCR(val, 14, 1, c1, c5, 3)
+#define WSYS_ETMSSPCICR6(val) MCR(val, 14, 1, c1, c6, 3)
+#define WSYS_ETMSSPCICR7(val) MCR(val, 14, 1, c1, c7, 3)
+
+#endif
diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h
index 6795368..cc41438 100644
--- a/arch/arm/include/asm/futex.h
+++ b/arch/arm/include/asm/futex.h
@@ -128,20 +128,10 @@
#endif /* !SMP */
static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret, tmp;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
-
#ifndef CONFIG_SMP
preempt_disable();
#endif
@@ -172,17 +162,9 @@
preempt_enable();
#endif
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/arm/include/asm/hardware/debugv8.h b/arch/arm/include/asm/hardware/debugv8.h
new file mode 100644
index 0000000..a8249cd
--- /dev/null
+++ b/arch/arm/include/asm/hardware/debugv8.h
@@ -0,0 +1,247 @@
+/* Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_HARDWARE_DEBUGV8_H
+#define __ASM_HARDWARE_DEBUGV8_H
+
+#include <linux/types.h>
+
+/* Accessors for CP14 registers */
+#define dbg_read(reg) RCP14_##reg()
+#define dbg_write(val, reg) WCP14_##reg(val)
+
+/* MRC14 registers */
+#define MRC14(op1, crn, crm, op2) \
+({ \
+uint32_t val; \
+asm volatile("mrc p14, "#op1", %0, "#crn", "#crm", "#op2 : "=r" (val)); \
+val; \
+})
+
+/* MCR14 registers */
+#define MCR14(val, op1, crn, crm, op2) \
+({ \
+asm volatile("mcr p14, "#op1", %0, "#crn", "#crm", "#op2 : : "r" (val));\
+})
+
+/*
+ * Debug Registers
+ *
+ * Read only
+ * DBGDIDR, DBGDSCRint, DBGDTRRXint, DBGDRAR, DBGOSLSR, DBGOSSRR, DBGDSAR,
+ * DBGAUTHSTATUS, DBGDEVID2, DBGDEVID1, DBGDEVID
+ *
+ * Write only
+ * DBGDTRTXint, DBGOSLAR
+ */
+#define RCP14_DBGDIDR() MRC14(0, c0, c0, 0)
+#define RCP14_DBGDSCRint() MRC14(0, c0, c1, 0)
+#define RCP14_DBGDCCINT() MRC14(0, c0, c2, 0)
+#define RCP14_DBGDTRRXint() MRC14(0, c0, c5, 0)
+#define RCP14_DBGWFAR() MRC14(0, c0, c6, 0)
+#define RCP14_DBGVCR() MRC14(0, c0, c7, 0)
+#define RCP14_DBGDTRRXext() MRC14(0, c0, c0, 2)
+#define RCP14_DBGDSCRext() MRC14(0, c0, c2, 2)
+#define RCP14_DBGDTRTXext() MRC14(0, c0, c3, 2)
+#define RCP14_DBGOSECCR() MRC14(0, c0, c6, 2)
+#define RCP14_DBGBVR0() MRC14(0, c0, c0, 4)
+#define RCP14_DBGBVR1() MRC14(0, c0, c1, 4)
+#define RCP14_DBGBVR2() MRC14(0, c0, c2, 4)
+#define RCP14_DBGBVR3() MRC14(0, c0, c3, 4)
+#define RCP14_DBGBVR4() MRC14(0, c0, c4, 4)
+#define RCP14_DBGBVR5() MRC14(0, c0, c5, 4)
+#define RCP14_DBGBVR6() MRC14(0, c0, c6, 4)
+#define RCP14_DBGBVR7() MRC14(0, c0, c7, 4)
+#define RCP14_DBGBVR8() MRC14(0, c0, c8, 4)
+#define RCP14_DBGBVR9() MRC14(0, c0, c9, 4)
+#define RCP14_DBGBVR10() MRC14(0, c0, c10, 4)
+#define RCP14_DBGBVR11() MRC14(0, c0, c11, 4)
+#define RCP14_DBGBVR12() MRC14(0, c0, c12, 4)
+#define RCP14_DBGBVR13() MRC14(0, c0, c13, 4)
+#define RCP14_DBGBVR14() MRC14(0, c0, c14, 4)
+#define RCP14_DBGBVR15() MRC14(0, c0, c15, 4)
+#define RCP14_DBGBCR0() MRC14(0, c0, c0, 5)
+#define RCP14_DBGBCR1() MRC14(0, c0, c1, 5)
+#define RCP14_DBGBCR2() MRC14(0, c0, c2, 5)
+#define RCP14_DBGBCR3() MRC14(0, c0, c3, 5)
+#define RCP14_DBGBCR4() MRC14(0, c0, c4, 5)
+#define RCP14_DBGBCR5() MRC14(0, c0, c5, 5)
+#define RCP14_DBGBCR6() MRC14(0, c0, c6, 5)
+#define RCP14_DBGBCR7() MRC14(0, c0, c7, 5)
+#define RCP14_DBGBCR8() MRC14(0, c0, c8, 5)
+#define RCP14_DBGBCR9() MRC14(0, c0, c9, 5)
+#define RCP14_DBGBCR10() MRC14(0, c0, c10, 5)
+#define RCP14_DBGBCR11() MRC14(0, c0, c11, 5)
+#define RCP14_DBGBCR12() MRC14(0, c0, c12, 5)
+#define RCP14_DBGBCR13() MRC14(0, c0, c13, 5)
+#define RCP14_DBGBCR14() MRC14(0, c0, c14, 5)
+#define RCP14_DBGBCR15() MRC14(0, c0, c15, 5)
+#define RCP14_DBGWVR0() MRC14(0, c0, c0, 6)
+#define RCP14_DBGWVR1() MRC14(0, c0, c1, 6)
+#define RCP14_DBGWVR2() MRC14(0, c0, c2, 6)
+#define RCP14_DBGWVR3() MRC14(0, c0, c3, 6)
+#define RCP14_DBGWVR4() MRC14(0, c0, c4, 6)
+#define RCP14_DBGWVR5() MRC14(0, c0, c5, 6)
+#define RCP14_DBGWVR6() MRC14(0, c0, c6, 6)
+#define RCP14_DBGWVR7() MRC14(0, c0, c7, 6)
+#define RCP14_DBGWVR8() MRC14(0, c0, c8, 6)
+#define RCP14_DBGWVR9() MRC14(0, c0, c9, 6)
+#define RCP14_DBGWVR10() MRC14(0, c0, c10, 6)
+#define RCP14_DBGWVR11() MRC14(0, c0, c11, 6)
+#define RCP14_DBGWVR12() MRC14(0, c0, c12, 6)
+#define RCP14_DBGWVR13() MRC14(0, c0, c13, 6)
+#define RCP14_DBGWVR14() MRC14(0, c0, c14, 6)
+#define RCP14_DBGWVR15() MRC14(0, c0, c15, 6)
+#define RCP14_DBGWCR0() MRC14(0, c0, c0, 7)
+#define RCP14_DBGWCR1() MRC14(0, c0, c1, 7)
+#define RCP14_DBGWCR2() MRC14(0, c0, c2, 7)
+#define RCP14_DBGWCR3() MRC14(0, c0, c3, 7)
+#define RCP14_DBGWCR4() MRC14(0, c0, c4, 7)
+#define RCP14_DBGWCR5() MRC14(0, c0, c5, 7)
+#define RCP14_DBGWCR6() MRC14(0, c0, c6, 7)
+#define RCP14_DBGWCR7() MRC14(0, c0, c7, 7)
+#define RCP14_DBGWCR8() MRC14(0, c0, c8, 7)
+#define RCP14_DBGWCR9() MRC14(0, c0, c9, 7)
+#define RCP14_DBGWCR10() MRC14(0, c0, c10, 7)
+#define RCP14_DBGWCR11() MRC14(0, c0, c11, 7)
+#define RCP14_DBGWCR12() MRC14(0, c0, c12, 7)
+#define RCP14_DBGWCR13() MRC14(0, c0, c13, 7)
+#define RCP14_DBGWCR14() MRC14(0, c0, c14, 7)
+#define RCP14_DBGWCR15() MRC14(0, c0, c15, 7)
+#define RCP14_DBGDRAR() MRC14(0, c1, c0, 0)
+#define RCP14_DBGBXVR0() MRC14(0, c1, c0, 1)
+#define RCP14_DBGBXVR1() MRC14(0, c1, c1, 1)
+#define RCP14_DBGBXVR2() MRC14(0, c1, c2, 1)
+#define RCP14_DBGBXVR3() MRC14(0, c1, c3, 1)
+#define RCP14_DBGBXVR4() MRC14(0, c1, c4, 1)
+#define RCP14_DBGBXVR5() MRC14(0, c1, c5, 1)
+#define RCP14_DBGBXVR6() MRC14(0, c1, c6, 1)
+#define RCP14_DBGBXVR7() MRC14(0, c1, c7, 1)
+#define RCP14_DBGBXVR8() MRC14(0, c1, c8, 1)
+#define RCP14_DBGBXVR9() MRC14(0, c1, c9, 1)
+#define RCP14_DBGBXVR10() MRC14(0, c1, c10, 1)
+#define RCP14_DBGBXVR11() MRC14(0, c1, c11, 1)
+#define RCP14_DBGBXVR12() MRC14(0, c1, c12, 1)
+#define RCP14_DBGBXVR13() MRC14(0, c1, c13, 1)
+#define RCP14_DBGBXVR14() MRC14(0, c1, c14, 1)
+#define RCP14_DBGBXVR15() MRC14(0, c1, c15, 1)
+#define RCP14_DBGOSLSR() MRC14(0, c1, c1, 4)
+#define RCP14_DBGOSSRR() MRC14(0, c1, c2, 4)
+#define RCP14_DBGOSDLR() MRC14(0, c1, c3, 4)
+#define RCP14_DBGPRCR() MRC14(0, c1, c4, 4)
+#define RCP14_DBGPRSR() MRC14(0, c1, c5, 4)
+#define RCP14_DBGDSAR() MRC14(0, c2, c0, 0)
+#define RCP14_DBGITCTRL() MRC14(0, c7, c0, 4)
+#define RCP14_DBGCLAIMSET() MRC14(0, c7, c8, 6)
+#define RCP14_DBGCLAIMCLR() MRC14(0, c7, c9, 6)
+#define RCP14_DBGAUTHSTATUS() MRC14(0, c7, c14, 6)
+#define RCP14_DBGDEVID2() MRC14(0, c7, c0, 7)
+#define RCP14_DBGDEVID1() MRC14(0, c7, c1, 7)
+#define RCP14_DBGDEVID() MRC14(0, c7, c2, 7)
+
+#define WCP14_DBGDCCINT(val) MCR14(val, 0, c0, c2, 0)
+#define WCP14_DBGDTRTXint(val) MCR14(val, 0, c0, c5, 0)
+#define WCP14_DBGWFAR(val) MCR14(val, 0, c0, c6, 0)
+#define WCP14_DBGVCR(val) MCR14(val, 0, c0, c7, 0)
+#define WCP14_DBGDTRRXext(val) MCR14(val, 0, c0, c0, 2)
+#define WCP14_DBGDSCRext(val) MCR14(val, 0, c0, c2, 2)
+#define WCP14_DBGDTRTXext(val) MCR14(val, 0, c0, c3, 2)
+#define WCP14_DBGOSECCR(val) MCR14(val, 0, c0, c6, 2)
+#define WCP14_DBGBVR0(val) MCR14(val, 0, c0, c0, 4)
+#define WCP14_DBGBVR1(val) MCR14(val, 0, c0, c1, 4)
+#define WCP14_DBGBVR2(val) MCR14(val, 0, c0, c2, 4)
+#define WCP14_DBGBVR3(val) MCR14(val, 0, c0, c3, 4)
+#define WCP14_DBGBVR4(val) MCR14(val, 0, c0, c4, 4)
+#define WCP14_DBGBVR5(val) MCR14(val, 0, c0, c5, 4)
+#define WCP14_DBGBVR6(val) MCR14(val, 0, c0, c6, 4)
+#define WCP14_DBGBVR7(val) MCR14(val, 0, c0, c7, 4)
+#define WCP14_DBGBVR8(val) MCR14(val, 0, c0, c8, 4)
+#define WCP14_DBGBVR9(val) MCR14(val, 0, c0, c9, 4)
+#define WCP14_DBGBVR10(val) MCR14(val, 0, c0, c10, 4)
+#define WCP14_DBGBVR11(val) MCR14(val, 0, c0, c11, 4)
+#define WCP14_DBGBVR12(val) MCR14(val, 0, c0, c12, 4)
+#define WCP14_DBGBVR13(val) MCR14(val, 0, c0, c13, 4)
+#define WCP14_DBGBVR14(val) MCR14(val, 0, c0, c14, 4)
+#define WCP14_DBGBVR15(val) MCR14(val, 0, c0, c15, 4)
+#define WCP14_DBGBCR0(val) MCR14(val, 0, c0, c0, 5)
+#define WCP14_DBGBCR1(val) MCR14(val, 0, c0, c1, 5)
+#define WCP14_DBGBCR2(val) MCR14(val, 0, c0, c2, 5)
+#define WCP14_DBGBCR3(val) MCR14(val, 0, c0, c3, 5)
+#define WCP14_DBGBCR4(val) MCR14(val, 0, c0, c4, 5)
+#define WCP14_DBGBCR5(val) MCR14(val, 0, c0, c5, 5)
+#define WCP14_DBGBCR6(val) MCR14(val, 0, c0, c6, 5)
+#define WCP14_DBGBCR7(val) MCR14(val, 0, c0, c7, 5)
+#define WCP14_DBGBCR8(val) MCR14(val, 0, c0, c8, 5)
+#define WCP14_DBGBCR9(val) MCR14(val, 0, c0, c9, 5)
+#define WCP14_DBGBCR10(val) MCR14(val, 0, c0, c10, 5)
+#define WCP14_DBGBCR11(val) MCR14(val, 0, c0, c11, 5)
+#define WCP14_DBGBCR12(val) MCR14(val, 0, c0, c12, 5)
+#define WCP14_DBGBCR13(val) MCR14(val, 0, c0, c13, 5)
+#define WCP14_DBGBCR14(val) MCR14(val, 0, c0, c14, 5)
+#define WCP14_DBGBCR15(val) MCR14(val, 0, c0, c15, 5)
+#define WCP14_DBGWVR0(val) MCR14(val, 0, c0, c0, 6)
+#define WCP14_DBGWVR1(val) MCR14(val, 0, c0, c1, 6)
+#define WCP14_DBGWVR2(val) MCR14(val, 0, c0, c2, 6)
+#define WCP14_DBGWVR3(val) MCR14(val, 0, c0, c3, 6)
+#define WCP14_DBGWVR4(val) MCR14(val, 0, c0, c4, 6)
+#define WCP14_DBGWVR5(val) MCR14(val, 0, c0, c5, 6)
+#define WCP14_DBGWVR6(val) MCR14(val, 0, c0, c6, 6)
+#define WCP14_DBGWVR7(val) MCR14(val, 0, c0, c7, 6)
+#define WCP14_DBGWVR8(val) MCR14(val, 0, c0, c8, 6)
+#define WCP14_DBGWVR9(val) MCR14(val, 0, c0, c9, 6)
+#define WCP14_DBGWVR10(val) MCR14(val, 0, c0, c10, 6)
+#define WCP14_DBGWVR11(val) MCR14(val, 0, c0, c11, 6)
+#define WCP14_DBGWVR12(val) MCR14(val, 0, c0, c12, 6)
+#define WCP14_DBGWVR13(val) MCR14(val, 0, c0, c13, 6)
+#define WCP14_DBGWVR14(val) MCR14(val, 0, c0, c14, 6)
+#define WCP14_DBGWVR15(val) MCR14(val, 0, c0, c15, 6)
+#define WCP14_DBGWCR0(val) MCR14(val, 0, c0, c0, 7)
+#define WCP14_DBGWCR1(val) MCR14(val, 0, c0, c1, 7)
+#define WCP14_DBGWCR2(val) MCR14(val, 0, c0, c2, 7)
+#define WCP14_DBGWCR3(val) MCR14(val, 0, c0, c3, 7)
+#define WCP14_DBGWCR4(val) MCR14(val, 0, c0, c4, 7)
+#define WCP14_DBGWCR5(val) MCR14(val, 0, c0, c5, 7)
+#define WCP14_DBGWCR6(val) MCR14(val, 0, c0, c6, 7)
+#define WCP14_DBGWCR7(val) MCR14(val, 0, c0, c7, 7)
+#define WCP14_DBGWCR8(val) MCR14(val, 0, c0, c8, 7)
+#define WCP14_DBGWCR9(val) MCR14(val, 0, c0, c9, 7)
+#define WCP14_DBGWCR10(val) MCR14(val, 0, c0, c10, 7)
+#define WCP14_DBGWCR11(val) MCR14(val, 0, c0, c11, 7)
+#define WCP14_DBGWCR12(val) MCR14(val, 0, c0, c12, 7)
+#define WCP14_DBGWCR13(val) MCR14(val, 0, c0, c13, 7)
+#define WCP14_DBGWCR14(val) MCR14(val, 0, c0, c14, 7)
+#define WCP14_DBGWCR15(val) MCR14(val, 0, c0, c15, 7)
+#define WCP14_DBGBXVR0(val) MCR14(val, 0, c1, c0, 1)
+#define WCP14_DBGBXVR1(val) MCR14(val, 0, c1, c1, 1)
+#define WCP14_DBGBXVR2(val) MCR14(val, 0, c1, c2, 1)
+#define WCP14_DBGBXVR3(val) MCR14(val, 0, c1, c3, 1)
+#define WCP14_DBGBXVR4(val) MCR14(val, 0, c1, c4, 1)
+#define WCP14_DBGBXVR5(val) MCR14(val, 0, c1, c5, 1)
+#define WCP14_DBGBXVR6(val) MCR14(val, 0, c1, c6, 1)
+#define WCP14_DBGBXVR7(val) MCR14(val, 0, c1, c7, 1)
+#define WCP14_DBGBXVR8(val) MCR14(val, 0, c1, c8, 1)
+#define WCP14_DBGBXVR9(val) MCR14(val, 0, c1, c9, 1)
+#define WCP14_DBGBXVR10(val) MCR14(val, 0, c1, c10, 1)
+#define WCP14_DBGBXVR11(val) MCR14(val, 0, c1, c11, 1)
+#define WCP14_DBGBXVR12(val) MCR14(val, 0, c1, c12, 1)
+#define WCP14_DBGBXVR13(val) MCR14(val, 0, c1, c13, 1)
+#define WCP14_DBGBXVR14(val) MCR14(val, 0, c1, c14, 1)
+#define WCP14_DBGBXVR15(val) MCR14(val, 0, c1, c15, 1)
+#define WCP14_DBGOSLAR(val) MCR14(val, 0, c1, c0, 4)
+#define WCP14_DBGOSSRR(val) MCR14(val, 0, c1, c2, 4)
+#define WCP14_DBGOSDLR(val) MCR14(val, 0, c1, c3, 4)
+#define WCP14_DBGPRCR(val) MCR14(val, 0, c1, c4, 4)
+#define WCP14_DBGITCTRL(val) MCR14(val, 0, c7, c0, 4)
+#define WCP14_DBGCLAIMSET(val) MCR14(val, 0, c7, c8, 6)
+#define WCP14_DBGCLAIMCLR(val) MCR14(val, 0, c7, c9, 6)
+
+#endif
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
index afcaf8b..e40bbc5 100644
--- a/arch/arm/include/asm/hw_breakpoint.h
+++ b/arch/arm/include/asm/hw_breakpoint.h
@@ -52,6 +52,7 @@
#define ARM_DEBUG_ARCH_V7_MM 4
#define ARM_DEBUG_ARCH_V7_1 5
#define ARM_DEBUG_ARCH_V8 6
+#define ARM_DEBUG_ARCH_V8_8 8
/* Breakpoint */
#define ARM_BREAKPOINT_EXECUTE 0
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index d5423ab..f4dab20 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -78,6 +78,9 @@
/* Interrupt controller */
struct vgic_dist vgic;
int max_vcpus;
+
+ /* Mandated version of PSCI */
+ u32 psci_version;
};
#define KVM_NR_MEM_OBJS 40
@@ -318,4 +321,10 @@
return -ENXIO;
}
+static inline bool kvm_arm_harden_branch_predictor(void)
+{
+ /* No way to detect it yet, pretend it is not there. */
+ return false;
+}
+
#endif /* __ARM_KVM_HOST_H__ */
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index d10e362..7f66b1b 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -223,6 +223,22 @@
return 8;
}
+/*
+ * We are not in the kvm->srcu critical section most of the time, so we take
+ * the SRCU read lock here. Since we copy the data from the user page, we
+ * can immediately drop the lock again.
+ */
+static inline int kvm_read_guest_lock(struct kvm *kvm,
+ gpa_t gpa, void *data, unsigned long len)
+{
+ int srcu_idx = srcu_read_lock(&kvm->srcu);
+ int ret = kvm_read_guest(kvm, gpa, data, len);
+
+ srcu_read_unlock(&kvm->srcu, srcu_idx);
+
+ return ret;
+}
+
static inline void *kvm_get_hyp_vector(void)
{
return kvm_ksym_ref(__kvm_hyp_vector);
diff --git a/arch/arm/include/asm/kvm_psci.h b/arch/arm/include/asm/kvm_psci.h
deleted file mode 100644
index 6bda945..0000000
--- a/arch/arm/include/asm/kvm_psci.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2012 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __ARM_KVM_PSCI_H__
-#define __ARM_KVM_PSCI_H__
-
-#define KVM_ARM_PSCI_0_1 1
-#define KVM_ARM_PSCI_0_2 2
-
-int kvm_psci_version(struct kvm_vcpu *vcpu);
-int kvm_psci_call(struct kvm_vcpu *vcpu);
-
-#endif /* __ARM_KVM_PSCI_H__ */
diff --git a/arch/arm/include/asm/xen/events.h b/arch/arm/include/asm/xen/events.h
index 71e473d..620dc75 100644
--- a/arch/arm/include/asm/xen/events.h
+++ b/arch/arm/include/asm/xen/events.h
@@ -16,7 +16,7 @@
return raw_irqs_disabled_flags(regs->ARM_cpsr);
}
-#define xchg_xen_ulong(ptr, val) atomic64_xchg(container_of((ptr), \
+#define xchg_xen_ulong(ptr, val) atomic64_xchg(container_of((long long*)(ptr),\
atomic64_t, \
counter), (val))
diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h
index b38c10c..0b8cf31 100644
--- a/arch/arm/include/uapi/asm/kvm.h
+++ b/arch/arm/include/uapi/asm/kvm.h
@@ -173,6 +173,12 @@
#define KVM_REG_ARM_VFP_FPINST 0x1009
#define KVM_REG_ARM_VFP_FPINST2 0x100A
+/* KVM-as-firmware specific pseudo-registers */
+#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \
+ KVM_REG_ARM_FW | ((r) & 0xffff))
+#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+
/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index 3f17594..414e60e 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -29,11 +29,6 @@
#endif
#ifdef CONFIG_DYNAMIC_FTRACE
-#ifdef CONFIG_OLD_MCOUNT
-#define OLD_MCOUNT_ADDR ((unsigned long) mcount)
-#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
-
-#define OLD_NOP 0xe1a00000 /* mov r0, r0 */
static int __ftrace_modify_code(void *data)
{
@@ -51,6 +46,12 @@
stop_machine(__ftrace_modify_code, &command, NULL);
}
+#ifdef CONFIG_OLD_MCOUNT
+#define OLD_MCOUNT_ADDR ((unsigned long) mcount)
+#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
+
+#define OLD_NOP 0xe1a00000 /* mov r0, r0 */
+
static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
{
return rec->arch.old_mcount ? OLD_NOP : NOP;
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 2d1d821..42d3974 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -150,10 +150,15 @@
}
c = irq_data_get_irq_chip(d);
- if (!c->irq_set_affinity)
+ if (!c->irq_set_affinity) {
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
- else if (c->irq_set_affinity(d, affinity, false) == IRQ_SET_MASK_OK && ret)
- cpumask_copy(irq_data_get_affinity_mask(d), affinity);
+ } else {
+ int r = irq_set_affinity_locked(d, affinity, false);
+
+ if (r)
+ pr_warn_ratelimited("IRQ%u: set affinity failed(%d).\n",
+ d->irq, r);
+ }
return ret;
}
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 4f9e2b5..342efa6 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1066,8 +1066,6 @@
{
unsigned long config_base = 0;
- if (attr->exclude_idle)
- return -EPERM;
if (attr->exclude_user)
config_base |= ARMV7_EXCLUDE_USER;
if (attr->exclude_kernel)
@@ -1184,7 +1182,48 @@
*nb_cnt += 1;
}
-static int armv7_probe_num_events(struct arm_pmu *arm_pmu)
+static void armv7_pmu_idle_update(struct arm_pmu *cpu_pmu)
+{
+ struct pmu_hw_events *hw_events;
+ struct perf_event *event;
+ int idx;
+
+ if (!cpu_pmu)
+ return;
+
+ hw_events = this_cpu_ptr(cpu_pmu->hw_events);
+ if (!hw_events)
+ return;
+
+ for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
+ event = hw_events->events[idx];
+
+ if (!event || !event->attr.exclude_idle ||
+ event->state != PERF_EVENT_STATE_ACTIVE)
+ continue;
+
+ cpu_pmu->pmu.read(event);
+ }
+}
+
+struct armv7_pmu_idle_nb {
+ struct arm_pmu *cpu_pmu;
+ struct notifier_block perf_cpu_idle_nb;
+};
+
+static int armv7_pmu_idle_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct armv7_pmu_idle_nb *pmu_idle_nb = container_of(nb,
+ struct armv7_pmu_idle_nb, perf_cpu_idle_nb);
+
+ if (action == IDLE_START)
+ armv7_pmu_idle_update(pmu_idle_nb->cpu_pmu);
+
+ return NOTIFY_OK;
+}
+
+static int armv7_probe_pmu(struct arm_pmu *arm_pmu)
{
return smp_call_function_any(&arm_pmu->supported_cpus,
armv7_read_num_pmnc_events,
@@ -1200,7 +1239,7 @@
&armv7_pmuv1_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] =
&armv7_pmu_format_attr_group;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1212,7 +1251,7 @@
&armv7_pmuv1_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] =
&armv7_pmu_format_attr_group;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1224,7 +1263,7 @@
&armv7_pmuv1_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] =
&armv7_pmu_format_attr_group;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1237,7 +1276,7 @@
&armv7_pmuv2_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] =
&armv7_pmu_format_attr_group;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1250,7 +1289,7 @@
&armv7_pmuv2_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] =
&armv7_pmu_format_attr_group;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1263,7 +1302,7 @@
&armv7_pmuv2_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] =
&armv7_pmu_format_attr_group;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1660,7 +1699,7 @@
cpu_pmu->disable = krait_pmu_disable_event;
cpu_pmu->get_event_idx = krait_pmu_get_event_idx;
cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
/*
@@ -1983,7 +2022,7 @@
cpu_pmu->disable = scorpion_pmu_disable_event;
cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx;
cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu)
@@ -1996,7 +2035,7 @@
cpu_pmu->disable = scorpion_pmu_disable_event;
cpu_pmu->get_event_idx = scorpion_pmu_get_event_idx;
cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx;
- return armv7_probe_num_events(cpu_pmu);
+ return armv7_probe_pmu(cpu_pmu);
}
static const struct of_device_id armv7_pmu_of_device_ids[] = {
@@ -2022,8 +2061,24 @@
static int armv7_pmu_device_probe(struct platform_device *pdev)
{
- return arm_pmu_device_probe(pdev, armv7_pmu_of_device_ids,
+ int ret;
+ struct armv7_pmu_idle_nb *pmu_idle_nb;
+
+ pmu_idle_nb = devm_kzalloc(&pdev->dev, sizeof(*pmu_idle_nb),
+ GFP_KERNEL);
+ if (!pmu_idle_nb)
+ return -ENOMEM;
+
+ ret = arm_pmu_device_probe(pdev, armv7_pmu_of_device_ids,
armv7_pmu_probe_table);
+ if (ret)
+ return ret;
+
+ pmu_idle_nb->cpu_pmu = (struct arm_pmu *) platform_get_drvdata(pdev);
+ pmu_idle_nb->perf_cpu_idle_nb.notifier_call = armv7_pmu_idle_notifier;
+ idle_notifier_register(&pmu_idle_nb->perf_cpu_idle_nb);
+
+ return 0;
}
static struct platform_driver armv7_pmu_driver = {
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index c6324b5..38ad8b9 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -129,13 +129,13 @@
for (j = 0; j < 8; j++) {
u32 data;
if (probe_kernel_address(p, data)) {
- printk(" ********");
+ pr_cont(" ********");
} else {
- printk(" %08x", data);
+ pr_cont(" %08x", data);
}
++p;
}
- printk("\n");
+ pr_cont("\n");
}
}
diff --git a/arch/arm/kernel/psci_smp.c b/arch/arm/kernel/psci_smp.c
index 29286fb..8ff6674 100644
--- a/arch/arm/kernel/psci_smp.c
+++ b/arch/arm/kernel/psci_smp.c
@@ -112,6 +112,12 @@
return 0;
}
+static bool psci_cpu_can_disable(unsigned int cpu)
+{
+ /*Hotplug of any CPU is supported*/
+ return true;
+}
+
#endif
bool __init psci_smp_available(void)
@@ -126,5 +132,6 @@
.cpu_disable = psci_cpu_disable,
.cpu_die = psci_cpu_die,
.cpu_kill = psci_cpu_kill,
+ .cpu_can_disable = psci_cpu_can_disable,
#endif
};
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index ab509d6..a03a99a 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -672,6 +672,8 @@
if ((unsigned)ipinr < NR_IPI)
trace_ipi_exit_rcuidle(ipi_types[ipinr]);
+
+ per_cpu(pending_ipi, cpu) = false;
set_irq_regs(old_regs);
}
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 1b30489..aa316a7 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -19,6 +19,7 @@
#include <linux/uaccess.h>
#include <linux/hardirq.h>
#include <linux/kdebug.h>
+#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/kexec.h>
#include <linux/bug.h>
@@ -415,7 +416,8 @@
raw_spin_unlock_irqrestore(&undef_lock, flags);
}
-static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
+static nokprobe_inline
+int call_undef_hook(struct pt_regs *regs, unsigned int instr)
{
struct undef_hook *hook;
unsigned long flags;
@@ -488,6 +490,7 @@
arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
}
+NOKPROBE_SYMBOL(do_undefinstr)
/*
* Handle FIQ similarly to NMI on x86 systems.
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index c38bfbe..ef6595c 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -29,6 +29,7 @@
#include <linux/kvm.h>
#include <trace/events/kvm.h>
#include <kvm/arm_pmu.h>
+#include <kvm/arm_psci.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -44,7 +45,6 @@
#include <asm/kvm_mmu.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
-#include <asm/kvm_psci.h>
#include <asm/sections.h>
#ifdef REQUIRES_VIRT
@@ -1088,7 +1088,7 @@
pgd_ptr = kvm_mmu_get_httbr();
stack_page = __this_cpu_read(kvm_arm_hyp_stack_page);
hyp_stack_ptr = stack_page + PAGE_SIZE;
- vector_ptr = (unsigned long)kvm_ksym_ref(__kvm_hyp_vector);
+ vector_ptr = (unsigned long)kvm_get_hyp_vector();
__cpu_init_hyp_mode(pgd_ptr, hyp_stack_ptr, vector_ptr);
__cpu_init_stage2();
@@ -1345,6 +1345,13 @@
goto out_err;
}
+
+ err = kvm_map_vectors();
+ if (err) {
+ kvm_err("Cannot map vectors\n");
+ goto out_err;
+ }
+
/*
* Map the Hyp stack pages
*/
diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c
index 9aca920..630117d 100644
--- a/arch/arm/kvm/guest.c
+++ b/arch/arm/kvm/guest.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <kvm/arm_psci.h>
#include <asm/cputype.h>
#include <asm/uaccess.h>
#include <asm/kvm.h>
@@ -176,6 +177,7 @@
unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
{
return num_core_regs() + kvm_arm_num_coproc_regs(vcpu)
+ + kvm_arm_get_fw_num_regs(vcpu)
+ NUM_TIMER_REGS;
}
@@ -196,6 +198,11 @@
uindices++;
}
+ ret = kvm_arm_copy_fw_reg_indices(vcpu, uindices);
+ if (ret)
+ return ret;
+ uindices += kvm_arm_get_fw_num_regs(vcpu);
+
ret = copy_timer_indices(vcpu, uindices);
if (ret)
return ret;
@@ -214,6 +221,9 @@
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
return get_core_reg(vcpu, reg);
+ if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
+ return kvm_arm_get_fw_reg(vcpu, reg);
+
if (is_timer_reg(reg->id))
return get_timer_reg(vcpu, reg);
@@ -230,6 +240,9 @@
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
return set_core_reg(vcpu, reg);
+ if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
+ return kvm_arm_set_fw_reg(vcpu, reg);
+
if (is_timer_reg(reg->id))
return set_timer_reg(vcpu, reg);
diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c
index 4e57ebc..de1aedc 100644
--- a/arch/arm/kvm/handle_exit.c
+++ b/arch/arm/kvm/handle_exit.c
@@ -21,7 +21,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_mmu.h>
-#include <asm/kvm_psci.h>
+#include <kvm/arm_psci.h>
#include <trace/events/kvm.h>
#include "trace.h"
@@ -36,7 +36,7 @@
kvm_vcpu_hvc_get_imm(vcpu));
vcpu->stat.hvc_exit_stat++;
- ret = kvm_psci_call(vcpu);
+ ret = kvm_hvc_call_handler(vcpu);
if (ret < 0) {
vcpu_set_reg(vcpu, 0, ~0UL);
return 1;
diff --git a/arch/arm/kvm/hyp/Makefile b/arch/arm/kvm/hyp/Makefile
index 92eab1d..6104921 100644
--- a/arch/arm/kvm/hyp/Makefile
+++ b/arch/arm/kvm/hyp/Makefile
@@ -6,6 +6,8 @@
KVM=../../../../virt/kvm
+CFLAGS_ARMV7VE :=$(call cc-option, -march=armv7ve)
+
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v2-sr.o
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v3-sr.o
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/timer-sr.o
@@ -14,7 +16,10 @@
obj-$(CONFIG_KVM_ARM_HOST) += cp15-sr.o
obj-$(CONFIG_KVM_ARM_HOST) += vfp.o
obj-$(CONFIG_KVM_ARM_HOST) += banked-sr.o
+CFLAGS_banked-sr.o += $(CFLAGS_ARMV7VE)
+
obj-$(CONFIG_KVM_ARM_HOST) += entry.o
obj-$(CONFIG_KVM_ARM_HOST) += hyp-entry.o
obj-$(CONFIG_KVM_ARM_HOST) += switch.o
+CFLAGS_switch.o += $(CFLAGS_ARMV7VE)
obj-$(CONFIG_KVM_ARM_HOST) += s2-setup.o
diff --git a/arch/arm/kvm/hyp/banked-sr.c b/arch/arm/kvm/hyp/banked-sr.c
index 111bda8..be4b8b0 100644
--- a/arch/arm/kvm/hyp/banked-sr.c
+++ b/arch/arm/kvm/hyp/banked-sr.c
@@ -20,6 +20,10 @@
#include <asm/kvm_hyp.h>
+/*
+ * gcc before 4.9 doesn't understand -march=armv7ve, so we have to
+ * trick the assembler.
+ */
__asm__(".arch_extension virt");
void __hyp_text __banked_save_state(struct kvm_cpu_context *ctxt)
diff --git a/arch/arm/kvm/hyp/switch.c b/arch/arm/kvm/hyp/switch.c
index 624a510..ebd2dd4 100644
--- a/arch/arm/kvm/hyp/switch.c
+++ b/arch/arm/kvm/hyp/switch.c
@@ -237,8 +237,10 @@
vcpu = (struct kvm_vcpu *)read_sysreg(HTPIDR);
host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+ __timer_save_state(vcpu);
__deactivate_traps(vcpu);
__deactivate_vm(vcpu);
+ __banked_restore_state(host_ctxt);
__sysreg_restore_state(host_ctxt);
}
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 2a35c19..7f868d9 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -1284,7 +1284,7 @@
return -EFAULT;
}
- if (vma_kernel_pagesize(vma) && !logging_active) {
+ if (vma_kernel_pagesize(vma) == PMD_SIZE && !logging_active) {
hugetlb = true;
gfn = (fault_ipa & PMD_MASK) >> PAGE_SHIFT;
} else {
diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c
index a08d7a9..8a9c654 100644
--- a/arch/arm/kvm/psci.c
+++ b/arch/arm/kvm/psci.c
@@ -15,16 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/arm-smccc.h>
#include <linux/preempt.h>
#include <linux/kvm_host.h>
+#include <linux/uaccess.h>
#include <linux/wait.h>
#include <asm/cputype.h>
#include <asm/kvm_emulate.h>
-#include <asm/kvm_psci.h>
#include <asm/kvm_host.h>
-#include <uapi/linux/psci.h>
+#include <kvm/arm_psci.h>
/*
* This is an implementation of the Power State Coordination Interface
@@ -33,6 +34,38 @@
#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1)
+static u32 smccc_get_function(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 0);
+}
+
+static unsigned long smccc_get_arg1(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 1);
+}
+
+static unsigned long smccc_get_arg2(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 2);
+}
+
+static unsigned long smccc_get_arg3(struct kvm_vcpu *vcpu)
+{
+ return vcpu_get_reg(vcpu, 3);
+}
+
+static void smccc_set_retval(struct kvm_vcpu *vcpu,
+ unsigned long a0,
+ unsigned long a1,
+ unsigned long a2,
+ unsigned long a3)
+{
+ vcpu_set_reg(vcpu, 0, a0);
+ vcpu_set_reg(vcpu, 1, a1);
+ vcpu_set_reg(vcpu, 2, a2);
+ vcpu_set_reg(vcpu, 3, a3);
+}
+
static unsigned long psci_affinity_mask(unsigned long affinity_level)
{
if (affinity_level <= 3)
@@ -75,7 +108,7 @@
unsigned long context_id;
phys_addr_t target_pc;
- cpu_id = vcpu_get_reg(source_vcpu, 1) & MPIDR_HWID_BITMASK;
+ cpu_id = smccc_get_arg1(source_vcpu) & MPIDR_HWID_BITMASK;
if (vcpu_mode_is_32bit(source_vcpu))
cpu_id &= ~((u32) 0);
@@ -88,14 +121,14 @@
if (!vcpu)
return PSCI_RET_INVALID_PARAMS;
if (!vcpu->arch.power_off) {
- if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1)
+ if (kvm_psci_version(source_vcpu, kvm) != KVM_ARM_PSCI_0_1)
return PSCI_RET_ALREADY_ON;
else
return PSCI_RET_INVALID_PARAMS;
}
- target_pc = vcpu_get_reg(source_vcpu, 2);
- context_id = vcpu_get_reg(source_vcpu, 3);
+ target_pc = smccc_get_arg2(source_vcpu);
+ context_id = smccc_get_arg3(source_vcpu);
kvm_reset_vcpu(vcpu);
@@ -114,7 +147,7 @@
* NOTE: We always update r0 (or x0) because for PSCI v0.1
* the general puspose registers are undefined upon CPU_ON.
*/
- vcpu_set_reg(vcpu, 0, context_id);
+ smccc_set_retval(vcpu, context_id, 0, 0, 0);
vcpu->arch.power_off = false;
smp_mb(); /* Make sure the above is visible */
@@ -134,8 +167,8 @@
struct kvm *kvm = vcpu->kvm;
struct kvm_vcpu *tmp;
- target_affinity = vcpu_get_reg(vcpu, 1);
- lowest_affinity_level = vcpu_get_reg(vcpu, 2);
+ target_affinity = smccc_get_arg1(vcpu);
+ lowest_affinity_level = smccc_get_arg2(vcpu);
/* Determine target affinity mask */
target_affinity_mask = psci_affinity_mask(lowest_affinity_level);
@@ -198,18 +231,10 @@
kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET);
}
-int kvm_psci_version(struct kvm_vcpu *vcpu)
-{
- if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features))
- return KVM_ARM_PSCI_0_2;
-
- return KVM_ARM_PSCI_0_1;
-}
-
static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
{
struct kvm *kvm = vcpu->kvm;
- unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0);
+ unsigned long psci_fn = smccc_get_function(vcpu);
unsigned long val;
int ret = 1;
@@ -219,7 +244,7 @@
* Bits[31:16] = Major Version = 0
* Bits[15:0] = Minor Version = 2
*/
- val = 2;
+ val = KVM_ARM_PSCI_0_2;
break;
case PSCI_0_2_FN_CPU_SUSPEND:
case PSCI_0_2_FN64_CPU_SUSPEND:
@@ -276,14 +301,56 @@
break;
}
- vcpu_set_reg(vcpu, 0, val);
+ smccc_set_retval(vcpu, val, 0, 0, 0);
+ return ret;
+}
+
+static int kvm_psci_1_0_call(struct kvm_vcpu *vcpu)
+{
+ u32 psci_fn = smccc_get_function(vcpu);
+ u32 feature;
+ unsigned long val;
+ int ret = 1;
+
+ switch(psci_fn) {
+ case PSCI_0_2_FN_PSCI_VERSION:
+ val = KVM_ARM_PSCI_1_0;
+ break;
+ case PSCI_1_0_FN_PSCI_FEATURES:
+ feature = smccc_get_arg1(vcpu);
+ switch(feature) {
+ case PSCI_0_2_FN_PSCI_VERSION:
+ case PSCI_0_2_FN_CPU_SUSPEND:
+ case PSCI_0_2_FN64_CPU_SUSPEND:
+ case PSCI_0_2_FN_CPU_OFF:
+ case PSCI_0_2_FN_CPU_ON:
+ case PSCI_0_2_FN64_CPU_ON:
+ case PSCI_0_2_FN_AFFINITY_INFO:
+ case PSCI_0_2_FN64_AFFINITY_INFO:
+ case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+ case PSCI_0_2_FN_SYSTEM_OFF:
+ case PSCI_0_2_FN_SYSTEM_RESET:
+ case PSCI_1_0_FN_PSCI_FEATURES:
+ case ARM_SMCCC_VERSION_FUNC_ID:
+ val = 0;
+ break;
+ default:
+ val = PSCI_RET_NOT_SUPPORTED;
+ break;
+ }
+ break;
+ default:
+ return kvm_psci_0_2_call(vcpu);
+ }
+
+ smccc_set_retval(vcpu, val, 0, 0, 0);
return ret;
}
static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
{
struct kvm *kvm = vcpu->kvm;
- unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0);
+ unsigned long psci_fn = smccc_get_function(vcpu);
unsigned long val;
switch (psci_fn) {
@@ -301,7 +368,7 @@
break;
}
- vcpu_set_reg(vcpu, 0, val);
+ smccc_set_retval(vcpu, val, 0, 0, 0);
return 1;
}
@@ -319,9 +386,11 @@
* Errors:
* -EINVAL: Unrecognized PSCI function
*/
-int kvm_psci_call(struct kvm_vcpu *vcpu)
+static int kvm_psci_call(struct kvm_vcpu *vcpu)
{
- switch (kvm_psci_version(vcpu)) {
+ switch (kvm_psci_version(vcpu, vcpu->kvm)) {
+ case KVM_ARM_PSCI_1_0:
+ return kvm_psci_1_0_call(vcpu);
case KVM_ARM_PSCI_0_2:
return kvm_psci_0_2_call(vcpu);
case KVM_ARM_PSCI_0_1:
@@ -330,3 +399,89 @@
return -EINVAL;
};
}
+
+int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
+{
+ u32 func_id = smccc_get_function(vcpu);
+ u32 val = PSCI_RET_NOT_SUPPORTED;
+ u32 feature;
+
+ switch (func_id) {
+ case ARM_SMCCC_VERSION_FUNC_ID:
+ val = ARM_SMCCC_VERSION_1_1;
+ break;
+ case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
+ feature = smccc_get_arg1(vcpu);
+ switch(feature) {
+ case ARM_SMCCC_ARCH_WORKAROUND_1:
+ if (kvm_arm_harden_branch_predictor())
+ val = 0;
+ break;
+ }
+ break;
+ default:
+ return kvm_psci_call(vcpu);
+ }
+
+ smccc_set_retval(vcpu, val, 0, 0, 0);
+ return 1;
+}
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
+{
+ return 1; /* PSCI version */
+}
+
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+{
+ if (put_user(KVM_REG_ARM_PSCI_VERSION, uindices))
+ return -EFAULT;
+
+ return 0;
+}
+
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+ if (reg->id == KVM_REG_ARM_PSCI_VERSION) {
+ void __user *uaddr = (void __user *)(long)reg->addr;
+ u64 val;
+
+ val = kvm_psci_version(vcpu, vcpu->kvm);
+ if (copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+ if (reg->id == KVM_REG_ARM_PSCI_VERSION) {
+ void __user *uaddr = (void __user *)(long)reg->addr;
+ bool wants_02;
+ u64 val;
+
+ if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)))
+ return -EFAULT;
+
+ wants_02 = test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features);
+
+ switch (val) {
+ case KVM_ARM_PSCI_0_1:
+ if (wants_02)
+ return -EINVAL;
+ vcpu->kvm->arch.psci_version = val;
+ return 0;
+ case KVM_ARM_PSCI_0_2:
+ case KVM_ARM_PSCI_1_0:
+ if (!wants_02)
+ return -EINVAL;
+ vcpu->kvm->arch.psci_version = val;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/arch/arm/lib/csumpartialcopyuser.S b/arch/arm/lib/csumpartialcopyuser.S
index 1712f13..b83fdc0 100644
--- a/arch/arm/lib/csumpartialcopyuser.S
+++ b/arch/arm/lib/csumpartialcopyuser.S
@@ -85,7 +85,11 @@
.pushsection .text.fixup,"ax"
.align 4
9001: mov r4, #-EFAULT
+#ifdef CONFIG_CPU_SW_DOMAIN_PAN
+ ldr r5, [sp, #9*4] @ *err_ptr
+#else
ldr r5, [sp, #8*4] @ *err_ptr
+#endif
str r4, [r5]
ldmia sp, {r1, r2} @ retrieve dst, len
add r2, r2, r1
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
index df73914..746e780 100644
--- a/arch/arm/lib/getuser.S
+++ b/arch/arm/lib/getuser.S
@@ -38,6 +38,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_1)
+_ASM_NOKPROBE(__get_user_1)
ENTRY(__get_user_2)
check_uaccess r0, 2, r1, r2, __get_user_bad
@@ -58,6 +59,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_2)
+_ASM_NOKPROBE(__get_user_2)
ENTRY(__get_user_4)
check_uaccess r0, 4, r1, r2, __get_user_bad
@@ -65,6 +67,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_4)
+_ASM_NOKPROBE(__get_user_4)
ENTRY(__get_user_8)
check_uaccess r0, 8, r1, r2, __get_user_bad8
@@ -78,6 +81,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_8)
+_ASM_NOKPROBE(__get_user_8)
#ifdef __ARMEB__
ENTRY(__get_user_32t_8)
@@ -91,6 +95,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_32t_8)
+_ASM_NOKPROBE(__get_user_32t_8)
ENTRY(__get_user_64t_1)
check_uaccess r0, 1, r1, r2, __get_user_bad8
@@ -98,6 +103,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_64t_1)
+_ASM_NOKPROBE(__get_user_64t_1)
ENTRY(__get_user_64t_2)
check_uaccess r0, 2, r1, r2, __get_user_bad8
@@ -114,6 +120,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_64t_2)
+_ASM_NOKPROBE(__get_user_64t_2)
ENTRY(__get_user_64t_4)
check_uaccess r0, 4, r1, r2, __get_user_bad8
@@ -121,6 +128,7 @@
mov r0, #0
ret lr
ENDPROC(__get_user_64t_4)
+_ASM_NOKPROBE(__get_user_64t_4)
#endif
__get_user_bad8:
@@ -131,6 +139,8 @@
ret lr
ENDPROC(__get_user_bad)
ENDPROC(__get_user_bad8)
+_ASM_NOKPROBE(__get_user_bad)
+_ASM_NOKPROBE(__get_user_bad8)
.pushsection __ex_table, "a"
.long 1b, __get_user_bad
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index a0e66d8..403db76 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -199,6 +199,7 @@
select BRCMSTB_L2_IRQ
select BCM7120_L2_IRQ
select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+ select ZONE_DMA if ARM_LPAE
select SOC_BRCMSTB
select SOC_BUS
help
diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
index add3771..9a22d40 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -821,6 +821,8 @@
.resource = da8xx_rproc_resources,
};
+static bool rproc_mem_inited __initdata;
+
#if IS_ENABLED(CONFIG_DA8XX_REMOTEPROC)
static phys_addr_t rproc_base __initdata;
@@ -859,6 +861,8 @@
ret = dma_declare_contiguous(&da8xx_dsp.dev, rproc_size, rproc_base, 0);
if (ret)
pr_err("%s: dma_declare_contiguous failed %d\n", __func__, ret);
+ else
+ rproc_mem_inited = true;
}
#else
@@ -873,6 +877,12 @@
{
int ret;
+ if (!rproc_mem_inited) {
+ pr_warn("%s: memory not reserved for DSP, not registering DSP device\n",
+ __func__);
+ return -ENOMEM;
+ }
+
ret = platform_device_register(&da8xx_dsp);
if (ret)
pr_err("%s: can't register DSP device: %d\n", __func__, ret);
diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c
index b3347d3..94906ed 100644
--- a/arch/arm/mach-imx/cpu.c
+++ b/arch/arm/mach-imx/cpu.c
@@ -131,6 +131,9 @@
case MXC_CPU_IMX6UL:
soc_id = "i.MX6UL";
break;
+ case MXC_CPU_IMX6ULL:
+ soc_id = "i.MX6ULL";
+ break;
case MXC_CPU_IMX7D:
soc_id = "i.MX7D";
break;
diff --git a/arch/arm/mach-imx/mxc.h b/arch/arm/mach-imx/mxc.h
index 34f2ff6..e00d626 100644
--- a/arch/arm/mach-imx/mxc.h
+++ b/arch/arm/mach-imx/mxc.h
@@ -39,6 +39,7 @@
#define MXC_CPU_IMX6SX 0x62
#define MXC_CPU_IMX6Q 0x63
#define MXC_CPU_IMX6UL 0x64
+#define MXC_CPU_IMX6ULL 0x65
#define MXC_CPU_IMX7D 0x72
#define IMX_DDR_TYPE_LPDDR2 1
@@ -73,6 +74,11 @@
return __mxc_cpu_type == MXC_CPU_IMX6UL;
}
+static inline bool cpu_is_imx6ull(void)
+{
+ return __mxc_cpu_type == MXC_CPU_IMX6ULL;
+}
+
static inline bool cpu_is_imx6q(void)
{
return __mxc_cpu_type == MXC_CPU_IMX6Q;
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 541647f..895c074 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -42,7 +42,7 @@
depends on ARCH_MULTI_V7
select ARMADA_370_XP_IRQ
select ARM_ERRATA_720789
- select ARM_ERRATA_753970
+ select PL310_ERRATA_753970
select ARM_GIC
select ARMADA_375_CLK
select HAVE_ARM_SCU
@@ -58,7 +58,7 @@
bool "Marvell Armada 380/385 boards"
depends on ARCH_MULTI_V7
select ARM_ERRATA_720789
- select ARM_ERRATA_753970
+ select PL310_ERRATA_753970
select ARM_GIC
select ARMADA_370_XP_IRQ
select ARMADA_38X_CLK
diff --git a/arch/arm/mach-omap2/clockdomains7xx_data.c b/arch/arm/mach-omap2/clockdomains7xx_data.c
index ef9ed36..e3416b4 100644
--- a/arch/arm/mach-omap2/clockdomains7xx_data.c
+++ b/arch/arm/mach-omap2/clockdomains7xx_data.c
@@ -524,7 +524,7 @@
.dep_bit = DRA7XX_PCIE_STATDEP_SHIFT,
.wkdep_srcs = pcie_wkup_sleep_deps,
.sleepdep_srcs = pcie_wkup_sleep_deps,
- .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .flags = CLKDM_CAN_SWSUP,
};
static struct clockdomain atl_7xx_clkdm = {
diff --git a/arch/arm/mach-omap2/omap-secure.c b/arch/arm/mach-omap2/omap-secure.c
index 9ff9205..fa7f308 100644
--- a/arch/arm/mach-omap2/omap-secure.c
+++ b/arch/arm/mach-omap2/omap-secure.c
@@ -73,6 +73,7 @@
return omap_secure_memblock_base;
}
+#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
u32 omap3_save_secure_ram(void __iomem *addr, int size)
{
u32 ret;
@@ -91,6 +92,7 @@
return ret;
}
+#endif
/**
* rx51_secure_dispatcher: Routine to dispatch secure PPA API calls
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 1ab1fbb..da48623 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -65,6 +65,17 @@
select HAVE_CLK_PREPARE
select COMMON_CLK_MSM
+config ARCH_MSM8953_BOOT_ORDERING
+ bool "Enable support for MSM8953 device boot ordering"
+ default n
+ help
+ Populate devices from devicetree at late_init, after
+ drivers for all platform devices have been registered.
+ This causes devices to be probed in the order they are
+ listed in devicetree. Thus it is possible to have
+ greater control over the probe ordering such that
+ overall boot time can be reduced.
+
config ARCH_MSM8937
bool "Enable support for MSM8937"
select CPU_V7
@@ -83,7 +94,7 @@
select MAY_HAVE_SPARSE_IRQ
select PINCTRL_MSM_TLMM
select USE_PINCTRL_IRQ
- select MSM_PM if PM
+ select MSM_PM_LEGACY if PM
select MSM_RPM_SMD
select MSM_RPM_STATS_LOG
select MSM_RPM_LOG
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index 5b93fa3..2f26f39 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_USE_OF) += board-dt.o
obj-$(CONFIG_SMP) += platsmp.o
+obj-$(CONFIG_MSM_PM_LEGACY) +=hotplug.o
obj-$(CONFIG_ARCH_SDXPOORWILLS) += board-poorwills.o
obj-$(CONFIG_ARCH_MSM8953) += board-msm8953.o
obj-$(CONFIG_ARCH_MSM8937) += board-msm8937.o
diff --git a/arch/arm/mach-qcom/board-msm8917.c b/arch/arm/mach-qcom/board-msm8917.c
index 63bc43b..0bd6984 100644
--- a/arch/arm/mach-qcom/board-msm8917.c
+++ b/arch/arm/mach-qcom/board-msm8917.c
@@ -17,6 +17,7 @@
static const char *msm8917_dt_match[] __initconst = {
"qcom,msm8917",
+ "qcom,apq8017",
NULL
};
diff --git a/arch/arm/mach-qcom/board-msm8953.c b/arch/arm/mach-qcom/board-msm8953.c
index 04b0bcc..9a82e3a 100644
--- a/arch/arm/mach-qcom/board-msm8953.c
+++ b/arch/arm/mach-qcom/board-msm8953.c
@@ -14,6 +14,7 @@
#include "board-dt.h"
#include <asm/mach/map.h>
#include <asm/mach/arch.h>
+#include <linux/of_platform.h>
static const char *msm8953_dt_match[] __initconst = {
"qcom,msm8953",
@@ -23,9 +24,25 @@
static void __init msm8953_init(void)
{
+ if (IS_ENABLED(CONFIG_ARCH_MSM8953_BOOT_ORDERING))
+ return;
board_dt_populate(NULL);
}
+#ifdef CONFIG_ARCH_MSM8953_BOOT_ORDERING
+static int __init msm8953_dt_populate(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+ /* Explicitly parent the /soc devices to the root node to preserve
+ * the kernel ABI (sysfs structure, etc) until userspace is updated
+ */
+ return of_platform_populate(of_find_node_by_path("/soc"),
+ of_default_bus_match_table, NULL, NULL);
+}
+late_initcall(msm8953_dt_populate);
+#endif
+
DT_MACHINE_START(MSM8953_DT,
"Qualcomm Technologies, Inc. MSM8953 (Flattened Device Tree)")
.init_machine = msm8953_init,
diff --git a/arch/arm/mach-qcom/board-sdm429.c b/arch/arm/mach-qcom/board-sdm429.c
index c648eaf..6bdf0f4 100644
--- a/arch/arm/mach-qcom/board-sdm429.c
+++ b/arch/arm/mach-qcom/board-sdm429.c
@@ -17,6 +17,7 @@
static const char *sdm429_dt_match[] __initconst = {
"qcom,sdm429",
+ "qcom,sda429",
NULL
};
diff --git a/arch/arm/mach-qcom/board-sdm439.c b/arch/arm/mach-qcom/board-sdm439.c
index 312f3a5..7b9aa0f 100644
--- a/arch/arm/mach-qcom/board-sdm439.c
+++ b/arch/arm/mach-qcom/board-sdm439.c
@@ -17,6 +17,7 @@
static const char *sdm439_dt_match[] __initconst = {
"qcom,sdm439",
+ "qcom,sda439",
NULL
};
diff --git a/arch/arm/mach-qcom/hotplug.c b/arch/arm/mach-qcom/hotplug.c
new file mode 100644
index 0000000..c038f4b
--- /dev/null
+++ b/arch/arm/mach-qcom/hotplug.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ * Copyright (c) 2011-2014, 2016, 2018, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm-legacy.h>
+#include <asm/smp_plat.h>
+#include "platsmp.h"
+#include <soc/qcom/jtag.h>
+
+static cpumask_t cpu_dying_mask;
+static DEFINE_PER_CPU(unsigned int, warm_boot_flag);
+
+static inline void cpu_enter_lowpower(void)
+{
+}
+
+static inline void cpu_leave_lowpower(void)
+{
+}
+
+static inline void platform_do_lowpower(unsigned int cpu)
+{
+ lpm_cpu_hotplug_enter(cpu);
+ /*
+ * getting here, means that we have come out of low power mode
+ * without having been woken up - this shouldn't happen
+ *
+ */
+ pr_err("%s: CPU%u has failed to Hotplug\n", __func__, cpu);
+}
+
+int qcom_cpu_kill_legacy(unsigned int cpu)
+{
+ int ret = 0;
+
+ if (cpumask_test_and_clear_cpu(cpu, &cpu_dying_mask))
+ ret = msm_pm_wait_cpu_shutdown(cpu);
+
+ return ret ? 0 : 1;
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void __ref qcom_cpu_die_legacy(unsigned int cpu)
+{
+ if (unlikely(cpu != smp_processor_id())) {
+ pr_crit("%s: running on %u, should be %u\n",
+ __func__, smp_processor_id(), cpu);
+ WARN_ON(cpu);
+ }
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ cpu_enter_lowpower();
+ platform_do_lowpower(cpu);
+
+ pr_debug("CPU%u: %s: normal wakeup\n", cpu, __func__);
+ cpu_leave_lowpower();
+}
+
+int msm_platform_secondary_init(unsigned int cpu)
+{
+ int ret;
+ unsigned int *warm_boot = this_cpu_ptr(&warm_boot_flag);
+
+ if (!(*warm_boot)) {
+ *warm_boot = 1;
+ /*
+ * All CPU0 boots are considered warm boots (restore needed)
+ * since CPU0 is the system boot CPU and never cold-booted
+ * by the kernel.
+ */
+ if (cpu)
+ return 0;
+ }
+ msm_jtag_restore_state();
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+
+ return ret;
+}
+
+static int hotplug_dying_cpu(unsigned int cpu)
+{
+ cpumask_set_cpu(cpu, &cpu_dying_mask);
+ return 0;
+}
+
+static int __init init_hotplug_dying(void)
+{
+ cpuhp_setup_state(CPUHP_AP_QCOM_SLEEP_STARTING,
+ "AP_QCOM_HOTPLUG_STARTING", NULL, hotplug_dying_cpu);
+
+ return 0;
+}
+early_initcall(init_hotplug_dying);
diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
index c422ac3..803804d 100644
--- a/arch/arm/mach-qcom/platsmp.c
+++ b/arch/arm/mach-qcom/platsmp.c
@@ -22,6 +22,9 @@
#include <asm/smp_plat.h>
#include <asm/fixmap.h>
#include "platsmp.h"
+#ifdef CONFIG_MSM_PM_LEGACY
+#include <soc/qcom/pm-legacy.h>
+#endif
#define MSM_APCS_IDR 0x0B011030
@@ -62,10 +65,18 @@
{
wfi();
}
+
+static bool qcom_cpu_can_disable(unsigned int cpu)
+{
+ return true; /*Hotplug of any CPU is supported */
+}
#endif
static void qcom_secondary_init(unsigned int cpu)
{
+#ifdef CONFIG_MSM_PM_LEGACY
+ WARN_ON(msm_platform_secondary_init(cpu));
+#endif
/*
* Synchronise with the boot thread.
*/
@@ -472,8 +483,14 @@
.smp_secondary_init = qcom_secondary_init,
.smp_boot_secondary = msm8909_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
+#ifdef CONFIG_MSM_PM_LEGACY
+ .cpu_die = qcom_cpu_die_legacy,
+ .cpu_kill = qcom_cpu_kill_legacy,
+#else
.cpu_die = qcom_cpu_die,
#endif
+ .cpu_can_disable = qcom_cpu_can_disable,
+#endif
};
CPU_METHOD_OF_DECLARE(qcom_smp_8909, "qcom,apss-8909", &msm8909_smp_ops);
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index b57aafc..f216025 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2372,6 +2372,7 @@
mapping->nr_bitmaps = 1;
mapping->extensions = extensions;
+ mapping->bits = BITS_PER_BYTE * bitmap_size;
spin_lock_init(&mapping->lock);
mapping->ops = &iommu_ops;
diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c
index bcdecc2..b2aa9b3 100644
--- a/arch/arm/probes/kprobes/opt-arm.c
+++ b/arch/arm/probes/kprobes/opt-arm.c
@@ -165,13 +165,14 @@
{
unsigned long flags;
struct kprobe *p = &op->kp;
- struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ struct kprobe_ctlblk *kcb;
/* Save skipped registers */
regs->ARM_pc = (unsigned long)op->kp.addr;
regs->ARM_ORIG_r0 = ~0UL;
local_irq_save(flags);
+ kcb = get_kprobe_ctlblk();
if (kprobe_running()) {
kprobes_inc_nmissed_count(&op->kp);
@@ -191,6 +192,7 @@
local_irq_restore(flags);
}
+NOKPROBE_SYMBOL(optimized_callback)
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig)
{
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index da0b33d..5629d75 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -648,7 +648,7 @@
*/
static int vfp_dying_cpu(unsigned int cpu)
{
- vfp_force_reload(cpu, current_thread_info());
+ vfp_current_hw_state[cpu] = NULL;
return 0;
}
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 7dd114f..9c01a31 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -16,6 +16,7 @@
select ARCH_HAS_SG_CHAIN
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_USE_CMPXCHG_LOCKREF
+ select ARCH_SUPPORTS_LTO_CLANG
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_NUMA_BALANCING
select ARCH_WANT_COMPAT_IPC_PARSE_VERSION
@@ -423,7 +424,7 @@
config ARM64_ERRATUM_843419
bool "Cortex-A53: 843419: A load or store might access an incorrect address"
- default y
+ default y if !LTO_CLANG
select ARM64_MODULE_CMODEL_LARGE if MODULES
help
This option links the kernel with '--fix-cortex-a53-843419' and
@@ -824,6 +825,18 @@
If unsure, say Y.
+config PSCI_BP_HARDENING
+ depends on HARDEN_BRANCH_PREDICTOR
+ bool "Use PSCI get version to enable branch predictor hardening"
+ help
+ If the mitigation for branch prediction is supported using psci
+ get version by the firmware then enable this option. Some older
+ versions of firmwares may not be using new SMCCC convention in
+ such cases use psci get version method to enable hardening for
+ branch prediction attacks.
+
+ If unsure, say N.
+
menuconfig ARMV8_DEPRECATED
bool "Emulate deprecated/obsolete ARMv8 instructions"
depends on COMPAT
@@ -1024,7 +1037,7 @@
config RANDOMIZE_MODULE_REGION_FULL
bool "Randomize the module region independently from the core kernel"
- depends on RANDOMIZE_BASE && !DYNAMIC_FTRACE
+ depends on RANDOMIZE_BASE && !DYNAMIC_FTRACE && !LTO_CLANG
default y
help
Randomizes the location of the module region without considering the
@@ -1150,6 +1163,29 @@
Space separated list of names of dtbs to append when
building a concatenated Image.gz-dtb.
+choice
+ prompt "Kernel compression method"
+ default BUILD_ARM64_KERNEL_COMPRESSION_GZIP
+ help
+ Allows choice between gzip compressed or uncompressed
+ kernel image
+
+config BUILD_ARM64_KERNEL_COMPRESSION_GZIP
+ bool "Build compressed kernel image"
+ help
+ Build a kernel image using gzip
+ compression with concatenated dtb.
+ gzip is based on the DEFLATE
+ algorithm.
+
+config BUILD_ARM64_UNCOMPRESSED_KERNEL
+ bool "Build uncompressed kernel image"
+ help
+ Build a kernel image without
+ compression and with
+ concatenated dtb.
+endchoice
+
config BUILD_ARM64_DT_OVERLAY
bool "enable DT overlay compilation support"
depends on OF
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 712352b..2b265a7 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -26,8 +26,17 @@
ifeq ($(call ld-option, --fix-cortex-a53-843419),)
$(warning ld does not support --fix-cortex-a53-843419; kernel may be susceptible to erratum)
else
+ ifeq ($(call gold-ifversion, -lt, 114000000, y), y)
+$(warning This version of GNU gold may generate incorrect code with --fix-cortex-a53-843419;\
+ see https://sourceware.org/bugzilla/show_bug.cgi?id=21491)
+ endif
LDFLAGS_vmlinux += --fix-cortex-a53-843419
endif
+else
+ ifeq ($(ld-name),gold)
+# Pass --no-fix-cortex-a53-843419 to ensure the erratum fix is disabled
+LDFLAGS += --no-fix-cortex-a53-843419
+ endif
endif
KBUILD_DEFCONFIG := defconfig
@@ -70,6 +79,10 @@
ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y)
KBUILD_CFLAGS_MODULE += -mcmodel=large
+ifeq ($(CONFIG_LTO_CLANG), y)
+# Code model is not stored in LLVM IR, so we need to pass it also to LLVMgold
+LDFLAGS += -plugin-opt=-code-model=large
+endif
endif
ifeq ($(CONFIG_ARM64_MODULE_PLTS),y)
@@ -109,11 +122,15 @@
libs-y := arch/arm64/lib/ $(libs-y)
core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
+ifeq ($(CONFIG_BUILD_ARM64_KERNEL_COMPRESSION_GZIP),y)
+KBUILD_IMAGE := Image.gz
+else
+KBUILD_IMAGE := Image
+endif
+
# Default target when executing plain make
ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y)
-KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME))
-else
-KBUILD_IMAGE := Image.gz
+KBUILD_IMAGE := $(addsuffix -dtb,$(KBUILD_IMAGE))
endif
KBUILD_DTBS := dtbs
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
index b97f1de..ccdfa1f 100644
--- a/arch/arm64/boot/Makefile
+++ b/arch/arm64/boot/Makefile
@@ -45,7 +45,11 @@
$(obj)/Image.bz2: $(obj)/Image FORCE
$(call if_changed,bzip2)
-$(obj)/Image-dtb: $(obj)/Image $(DTB_OBJS) FORCE
+$(obj)/Image-dtb-hdr: $(obj)/Image FORCE
+ echo -n 'UNCOMPRESSED_IMG' > $@ && \
+ $(call size_append, $(filter-out FORCE,$^)) >> $@
+
+$(obj)/Image-dtb: $(obj)/Image-dtb-hdr $(obj)/Image $(DTB_OBJS) FORCE
$(call if_changed,cat)
$(obj)/Image.gz: $(obj)/Image FORCE
diff --git a/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi b/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi
index af56793..573aaac 100644
--- a/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi
@@ -353,3 +353,52 @@
#include "msm8909-pm8916-pm.dtsi"
+&soc {
+ thermal-zones {
+ xo-therm-buf-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&pm8916_vadc 0x3c>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+
+ xo-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&pm8916_vadc 0x32>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ pa-therm0-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&pm8916_vadc 0x36>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
index 3947406..c8330bd 100644
--- a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
@@ -79,6 +79,31 @@
};
};
+&msm_gpio {
+ /delete-node/ mpu6050_int_pin;
+ /delete-node/ apds99xx_int_pin;
+ /delete-node/ ak8963_int_pin;
+
+ pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ config {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ };
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ config {
+ pins = "gpio25", "gpio37", "gpio59";
+ };
+ };
+ };
+};
+
&i2c_3 {
qcom,actuator@0 {
/delete-property/ cam_vaf-supply;
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 31d4b0d..3ad1c0e 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -135,17 +135,23 @@
sdm670-usbc-external-codec-pm660a-mtp-overlay.dtbo \
sda670-cdp-overlay.dtbo \
sda670-mtp-overlay.dtbo \
+ sda670-hdk-overlay.dtbo \
sda670-pm660a-cdp-overlay.dtbo \
sda670-pm660a-mtp-overlay.dtbo \
sdm670-tasha-codec-cdp-overlay.dtbo \
sdm670-pm660a-tasha-codec-cdp-overlay.dtbo \
sdm670-aqt1000-cdp-overlay.dtbo \
sdm670-pm660a-aqt1000-cdp-overlay.dtbo \
+ sxr1120-lc-mtp-overlay.dtbo \
+ sxr1120-lc-external-codec-mtp-overlay.dtbo \
+ sxr1120-lc-cdp-overlay.dtbo \
+ sxr1120-lc-external-codec-cdp-overlay.dtbo \
qcs605-cdp-overlay.dtbo \
qcs605-mtp-overlay.dtbo \
qcs605-360camera-overlay.dtbo \
qcs605-external-codec-mtp-overlay.dtbo \
qcs605-lc-mtp-overlay.dtbo \
+ qcs605-lc-cdp-overlay.dtbo \
sdm710-cdp-overlay.dtbo \
sdm710-mtp-overlay.dtbo \
sdm710-qrd-overlay.dtbo \
@@ -194,13 +200,19 @@
sdm670-pm660a-aqt1000-cdp-overlay.dtbo-base := sdm670.dtb
sda670-cdp-overlay.dtbo-base := sda670.dtb
sda670-mtp-overlay.dtbo-base := sda670.dtb
+sda670-hdk-overlay.dtbo-base := sda670.dtb
sda670-pm660a-cdp-overlay.dtbo-base := sda670.dtb
sda670-pm660a-mtp-overlay.dtbo-base := sda670.dtb
+sxr1120-lc-mtp-overlay.dtbo-base := sxr1120-lc.dtb
+sxr1120-lc-external-codec-mtp-overlay.dtbo-base := sxr1120-lc.dtb
+sxr1120-lc-cdp-overlay.dtbo-base := sxr1120-lc.dtb
+sxr1120-lc-external-codec-cdp-overlay.dtbo-base := sxr1120-lc.dtb
qcs605-cdp-overlay.dtbo-base := qcs605.dtb
qcs605-mtp-overlay.dtbo-base := qcs605.dtb
qcs605-external-codec-mtp-overlay.dtbo-base := qcs605.dtb
qcs605-lc-mtp-overlay.dtbo-base := qcs605-lc.dtb
qcs605-360camera-overlay.dtbo-base := qcs605.dtb
+qcs605-lc-cdp-overlay.dtbo-base := qcs605-lc-cdp-base.dtb
sdm710-cdp-overlay.dtbo-base := sdm710.dtb
sdm710-mtp-overlay.dtbo-base := sdm710.dtb
sdm710-qrd-overlay.dtbo-base := sdm710.dtb
@@ -245,16 +257,22 @@
sdm670-usbc-pm660a-cdp.dtb \
sdm670-usbc-pm660a-mtp.dtb \
sda670-mtp.dtb \
+ sda670-hdk.dtb \
sda670-cdp.dtb \
sdm670-tasha-codec-cdp.dtb \
sdm670-pm660a-tasha-codec-cdp.dtb \
sda670-pm660a-mtp.dtb \
sda670-pm660a-cdp.dtb \
+ sxr1120-lc-mtp.dtb \
+ sxr1120-lc-external-codec-mtp.dtb \
+ sxr1120-lc-cdp.dtb \
+ sxr1120-lc-external-codec-cdp.dtb \
qcs605-360camera.dtb \
qcs605-mtp.dtb \
qcs605-cdp.dtb \
qcs605-external-codec-mtp.dtb \
qcs605-lc-mtp.dtb \
+ qcs605-lc-cdp.dtb \
sdm710-mtp.dtb \
sdm710-cdp.dtb \
sdm710-qrd.dtb \
@@ -308,7 +326,9 @@
dtbo-$(CONFIG_ARCH_SDM439) += sdm439-mtp-overlay.dtbo \
sdm439-cdp-overlay.dtbo \
- sdm439-qrd-overlay.dtbo
+ sdm439-qrd-overlay.dtbo \
+ sdm439-external-codec-mtp-overlay.dtbo \
+ sdm439-rcm-overlay.dtbo
dtbo-$(CONFIG_ARCH_SDM429) += sdm429-mtp-overlay.dtbo \
sdm429-cdp-overlay.dtbo \
@@ -368,14 +388,20 @@
sdm632-pm8004.dtb
sdm439-mtp-overlay.dtbo-base := sdm439.dtb \
+ sda439.dtb \
msm8937-interposer-sdm439.dtb
sdm439-cdp-overlay.dtbo-base := sdm439.dtb \
+ sda439.dtb \
msm8937-interposer-sdm439.dtb
sdm439-qrd-overlay.dtbo-base := sdm439.dtb \
msm8937-interposer-sdm439.dtb
+sdm439-external-codec-mtp-overlay.dtbo-base := sdm439.dtb
+sdm439-rcm-overlay.dtbo-base := sdm439.dtb
sdm429-mtp-overlay.dtbo-base := sdm429.dtb \
+ sda429.dtb \
msm8937-interposer-sdm429.dtb
sdm429-cdp-overlay.dtbo-base := sdm429.dtb \
+ sda429.dtb \
msm8937-interposer-sdm429.dtb
sdm429-qrd-overlay.dtbo-base := sdm429.dtb \
msm8937-interposer-sdm429.dtb
@@ -398,6 +424,10 @@
apq8053-iot-mtp.dtb \
apq8053-lite-dragon-v1.0.dtb \
apq8053-lite-dragon-v2.0.dtb \
+ apq8053-lite-dragon-v2.1.dtb \
+ apq8053-lite-dragon-v2.2.dtb \
+ apq8053-lite-dragon-v2.3.dtb \
+ apq8053-lite-dragon-v2.4.dtb \
msm8953-pmi8940-cdp.dtb \
msm8953-pmi8940-mtp.dtb \
msm8953-pmi8937-cdp.dtb \
@@ -414,15 +444,31 @@
msm8937-interposer-sdm429-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8917) += msm8917-pmi8950-mtp.dtb \
+ msm8917-pmi8950-cdp.dtb \
+ msm8917-pmi8950-rcm.dtb \
+ msm8917-pmi8950-ext-codec-cdp.dtb \
+ msm8917-pmi8950-cdp-mirror-lake-touch.dtb \
+ apq8017-pmi8950-cdp-wcd-rome.dtb \
+ apq8017-pmi8950-mtp.dtb \
+ apq8017-pmi8950-cdp.dtb \
msm8917-pmi8937-qrd-sku5.dtb \
+ msm8917-pmi8937-mtp.dtb \
+ msm8917-pmi8937-cdp.dtb \
+ msm8917-pmi8937-rcm.dtb \
+ apq8017-pmi8937-mtp.dtb \
+ apq8017-pmi8937-cdp.dtb \
+ apq8017-pmi8937-cdp-wcd-rome.dtb \
msm8917-pmi8940-mtp.dtb \
- msm8917-pmi8937-mtp.dtb
+ msm8917-pmi8940-cdp.dtb \
+ msm8917-pmi8940-rcm.dtb
dtb-$(CONFIG_ARCH_MSM8909) += msm8909w-bg-wtp-v2.dtb \
apq8009w-bg-wtp-v2.dtb \
apq8009w-bg-alpha.dtb \
apq8009-mtp-wcd9326-refboard.dtb \
- apq8009-dragon.dtb
+ apq8009-robot-som-refboard.dtb \
+ apq8009-dragon.dtb \
+ apq8009-lat-v1.0.dtb
dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
sdm450-cdp.dtb \
@@ -459,7 +505,9 @@
sdm439-cdp.dtb \
sdm439-qrd.dtb \
sda439-mtp.dtb \
- sda439-cdp.dtb
+ sda439-cdp.dtb \
+ sdm439-external-codec-mtp.dtb \
+ sdm439-rcm.dtb
dtb-$(CONFIG_ARCH_SDM429) += sdm429-mtp.dtb \
sdm429-cdp.dtb \
diff --git a/arch/arm64/boot/dts/qcom/apq8009-audio-external_codec.dtsi b/arch/arm64/boot/dts/qcom/apq8009-audio-external_codec.dtsi
index d28e139..9261dfe 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-audio-external_codec.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8009-audio-external_codec.dtsi
@@ -19,6 +19,12 @@
};
&soc {
+ qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ msm_audio_apr_dummy {
+ compatible = "qcom,msm-audio-apr-dummy";
+ };
+ };
sound-9335 {
compatible = "qcom,apq8009-audio-i2s-codec";
qcom,model = "apq8009-tashalite-snd-card";
@@ -234,16 +240,17 @@
qcom,msm-cpudai-tdm-group-num-ports = <1>;
qcom,msm-cpudai-tdm-group-port-id = <36864>;
qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <1>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
qcom,msm-cpudai-tdm-sec-port-enable;
qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36864>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <1>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <1>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
};
@@ -254,17 +261,26 @@
qcom,msm-cpudai-tdm-group-num-ports = <1>;
qcom,msm-cpudai-tdm-group-port-id = <36865>;
qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-internal = <1>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
qcom,msm-cpudai-tdm-sec-port-enable;
qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36865>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <1>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <1>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
};
};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
index 1446c9c..ba12854 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
@@ -66,18 +66,102 @@
mdss_mdp: qcom,mdss_mdp@1a00000 {
status = "disabled";
};
+
+ bluetooth: bt_qca9379 {
+ compatible = "qca,qca9379";
+ qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */
+ };
+
+ cnss_sdio: qcom,cnss_sdio {
+ compatible = "qcom,cnss_sdio";
+ subsys-name = "AR6320";
+ /**
+ * There is no vdd-wlan on board and this is not for DSRC.
+ * IO and XTAL share the same vreg.
+ **/
+ vdd-wlan-io-supply = <&pm8916_l5>;
+ qcom,cap-tsf-gpio = <&msm_gpio 42 1>;
+ qcom,wlan-ramdump-dynamic = <0x200000>;
+ qcom,msm-bus,name = "msm-cnss";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <79 512 0 0>, /* No vote */
+ <79 512 6250 200000>, /* 50 Mbps */
+ <79 512 25000 200000>, /* 200 Mbps */
+ <79 512 2048000 4096000>; /* MAX */
+ };
};
-&sdhc_2 {
+&wcnss {
status = "disabled";
};
+&msm_gpio {
+ sdc2_wlan_gpio_on: sdc2_wlan_gpio_on {
+ mux {
+ pins = "gpio43";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio43";
+ drive-strength = <10>;
+ bias-pull-up;
+ output-high;
+ };
+ };
+
+ sdc2_wlan_gpio_off: sdc2_wlan_gpio_off {
+ mux {
+ pins = "gpio43";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio43";
+ drive-strength = <2>;
+ bias-disable;
+ output-low;
+ };
+ };
+};
+
+&sdhc_2 {
+ /delete-property/cd-gpios;
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &msm_gpio 40 0x1>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
+ qcom,vdd-voltage-level = <1800000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
+
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 50000>;
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on
+ &sdc2_wlan_gpio_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_wlan_gpio_off>;
+ qcom,nonremovable;
+ qcom,core_3_0v_support;
+ status = "ok";
+};
+
&usb_otg {
- interrupts = <0 134 0>,<0 140 0>,<0 136 0>;
- interrupt-names = "core_irq", "async_irq", "phy_irq";
+ interrupts = <0 134 0>,<0 140 0>,<0 136 0>;
+ interrupt-names = "core_irq", "async_irq", "phy_irq";
qcom,hsusb-otg-mode = <3>;
- qcom,switch-vbus-w-id;
+ qcom,switch-vbus-w-id;
vbus_otg-supply = <&vph_pwr_vreg>;
+ extcon = <&pm8916_chg>;
};
&external_image_mem {
@@ -111,3 +195,28 @@
status= "disabled";
};
};
+
+&firmware {
+ android {
+ compatible = "android,firmware";
+ fstab {
+ compatible = "android,fstab";
+ vendor_fstab: vendor {
+ fsmgr_flags = "wait,slotselect";
+ };
+ /delete-node/ system;
+ };
+ };
+};
+
+&pm8916_chg {
+ status = "ok";
+};
+
+&pm8916_bms {
+ status = "ok";
+};
+
+&blsp1_uart2_hs {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009-lat-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8009-lat-v1.0.dts
new file mode 100644
index 0000000..f81e369
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8009-lat-v1.0.dts
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8909-mtp.dtsi"
+#include "8909-pm8916.dtsi"
+#include "msm8909-pm8916-mtp.dtsi"
+#include "apq8009-audio-external_codec.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8009-lat-v1.0 Board";
+ compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp";
+ qcom,msm-id = <265 2>;
+ qcom,board-id= <10 0x1>;
+
+ bluetooth: bt_qca9379 {
+ compatible = "qca,qca9379";
+ qca,bt-reset-gpio = <&msm_gpio 47 0x0>; /* BT_EN */
+ };
+};
+
+&soc {
+ ext-codec {
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+ };
+
+ sound-9335 {
+ status = "disabled";
+ };
+
+ i2c@78b8000 {
+ wcd9xxx_codec@d {
+ status = "disabled";
+ };
+ };
+
+ vph_pwr_vreg: vph_pwr_vreg {
+ compatible = "regulator-fixed";
+ status = "ok";
+ regulator-name = "vph_pwr";
+ regulator-always-on;
+ };
+
+ mdss_mdp: qcom,mdss_mdp@1a00000 {
+ status = "disabled";
+ };
+
+ qcom,msm-thermal {
+ qcom,core-control-mask = <0xa>;
+ qcom,freq-mitigation-value = <1190400>;
+ qcom,freq-mitigation-control-mask = <0x05>;
+ };
+};
+
+&sdhc_2 {
+ status = "disabled";
+};
+
+&usb_otg {
+ interrupts = <0 134 0>, <0 140 0>, <0 136 0>;
+ interrupt-names = "core_irq", "async_irq", "phy_irq";
+ qcom,hsusb-otg-mode = <3>;
+ qcom,phy-id-high-as-peripheral;
+ vbus_otg-supply = <&vph_pwr_vreg>;
+};
+
+&external_image_mem {
+ reg = <0x0 0x87a00000 0x0 0x0600000>;
+};
+
+&modem_adsp_mem {
+ reg = <0x0 0x88000000 0x0 0x01e00000>;
+};
+
+&peripheral_mem {
+ reg = <0x0 0x89e00000 0x0 0x0700000>;
+};
+
+&i2c_4 {
+ smb1360_otg_supply: smb1360-chg-fg@14 {
+ compatible = "qcom,smb1360-chg-fg";
+ reg = <0x14>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <58 8>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default>;
+ qcom,charging-disabled;
+ qcom,empty-soc-disabled;
+ qcom,chg-inhibit-disabled;
+ qcom,float-voltage-mv = <4200>;
+ qcom,iterm-ma = <200>;
+ qcom,recharge-thresh-mv = <100>;
+ qcom,thermal-mitigation = <1500 700 600 0>;
+ regulator-name = "smb1360_otg_vreg";
+ status= "disabled";
+ };
+};
+
+&firmware {
+ android {
+ compatible = "android,firmware";
+ fstab {
+ compatible = "android,fstab";
+ vendor_fstab: vendor {
+ fsmgr_flags = "wait,slotselect";
+ };
+ /delete-node/ system;
+ };
+ };
+};
+
+&pm8916_chg {
+ status = "ok";
+};
+
+&pm8916_bms {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts
index 9efd808..2afd5ac 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts
@@ -18,6 +18,8 @@
#include "apq8009-audio-external_codec.dtsi"
#include "apq8009-memory.dtsi"
#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+#include "msm8909-pm8916-camera.dtsi"
+#include "msm8909-pm8916-camera-sensor-robot.dtsi"
/ {
model = "Qualcomm Technologies, Inc. APQ8009 WCD9326 Reference Board";
@@ -93,7 +95,7 @@
};
&soc {
- sound-9335 {
+ ext_codec: sound-9335 {
qcom,audio-routing =
"AIF4 VI", "MCLK",
"RX_BIAS", "MCLK",
@@ -110,12 +112,22 @@
"MIC BIAS3", "Digital Mic3",
"SpkrLeft IN", "SPK1 OUT",
"SpkrRight IN", "SPK2 OUT";
- };
- i2c@78b8000 {
- wcd9xxx_codec@d {
- qcom,cdc-reset-gpio = <&msm_gpio 27 0>;
- };
+ qcom,msm-gpios =
+ "us_eu_gpio";
+ qcom,pinctrl-names =
+ "all_off",
+ "us_eu_gpio_act";
+ pinctrl-names =
+ "all_off",
+ "us_eu_gpio_act";
+ pinctrl-0 = <&cross_conn_det_sus>;
+ pinctrl-1 = <&cross_conn_det_act>;
+ qcom,pri-mi2s-gpios = <&cdc_pri_mi2s_gpios>;
+ qcom,quat-mi2s-gpios = <&cdc_quat_mi2s_gpios>;
+
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+ "SpkrLeft", "SpkrRight";
};
i2c@78b9000 {
@@ -203,6 +215,36 @@
qcom,id-det-gpio = <&msm_gpio 110 0>;
qcom,dpdm_switch_gpio = <&pm8916_gpios 3 0>;
};
+
+ i2c@78b8000 {
+ wcd9xxx_codec@d {
+ status = "okay";
+ qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
+ };
+ };
+
+ cdc_pri_mi2s_gpios: msm_cdc_pinctrl_pri {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&pri_mi2s_active &pri_mi2s_ws_active
+ &pri_mi2s_dout_active &pri_mi2s_din_active>;
+ pinctrl-1 = <&pri_mi2s_sleep &pri_mi2s_ws_sleep
+ &pri_mi2s_dout_sleep &pri_mi2s_din_sleep>;
+ };
+
+ cdc_quat_mi2s_gpios: msm_cdc_pinctrl_quat {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>;
+ pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>;
+ };
+
+ wcd_rst_gpio: wcd_gpio_ctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&cdc_reset_active>;
+ pinctrl-1 = <&cdc_reset_sleep>;
+ };
};
&wcnss {
@@ -268,6 +310,7 @@
};
&i2c_4 {
+ status= "okay";
smb1360_otg_supply: smb1360-chg-fg@14 {
compatible = "qcom,smb1360-chg-fg";
reg = <0x14>;
@@ -283,15 +326,12 @@
qcom,recharge-thresh-mv = <100>;
qcom,thermal-mitigation = <1500 700 600 0>;
regulator-name = "smb1360_otg_vreg";
+ status= "okay";
};
};
&usb_otg {
- interrupts = <0 134 0>, <0 140 0>;
- interrupt-names = "core_irq", "async_irq";
-
- qcom,hsusb-otg-mode = <3>;
- vbus_otg-supply = <&vbus_otg_supply>;
+ extcon = <&smb1360_otg_supply>;
};
&mdss_fb0 {
@@ -327,4 +367,16 @@
status = "disabled";
};
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ status = "okay";
+};
+
+&blsp1_uart2_hs {
+ status = "disabled";
+};
+
/delete-node/ &cont_splash_mem;
diff --git a/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
new file mode 100644
index 0000000..1314129
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8909-mtp.dtsi"
+#include "8909-pm8916.dtsi"
+#include "msm8909-pm8916-mtp.dtsi"
+#include "apq8009-audio-external_codec.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8009 Robot SOM refboard";
+ compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp";
+ qcom,msm-id = <265 2>;
+ qcom,board-id = <8 0x15>;
+};
+
+&soc {
+ ext-codec {
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+ };
+
+ sound-9335 {
+ status = "disabled";
+ };
+
+ i2c@78b8000 {
+ wcd9xxx_codec@d {
+ status = "disabled";
+ };
+ };
+
+ vph_pwr_vreg: vph_pwr_vreg {
+ compatible = "regulator-fixed";
+ status = "ok";
+ regulator-name = "vph_pwr";
+ regulator-always-on;
+ };
+
+ mdss_mdp: qcom,mdss_mdp@1a00000 {
+ status = "disabled";
+ };
+
+ bluetooth: bt_qca9379 {
+ compatible = "qca,qca9379";
+ qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */
+ };
+ cnss_sdio: qcom,cnss_sdio {
+ compatible = "qcom,cnss_sdio";
+ subsys-name = "AR6320";
+ /**
+ * There is no vdd-wlan on board and this is not for DSRC.
+ * IO and XTAL share the same vreg.
+ */
+ vdd-wlan-io-supply = <&pm8916_l5>;
+ qcom,cap-tsf-gpio = <&msm_gpio 42 1>;
+ qcom,wlan-ramdump-dynamic = <0x200000>;
+ qcom,msm-bus,name = "msm-cnss";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <79 512 0 0>, /* No vote */
+ <79 512 6250 200000>, /* 50 Mbps */
+ <79 512 25000 200000>, /* 200 Mbps */
+ <79 512 2048000 4096000>; /* MAX */
+ };
+};
+
+&wcnss {
+ status = "disabled";
+};
+
+&msm_gpio {
+ sdc2_wlan_gpio_on: sdc2_wlan_gpio_on {
+ mux {
+ pins = "gpio43";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio43";
+ drive-strength = <10>;
+ bias-pull-up;
+ output-high;
+ };
+ };
+
+ sdc2_wlan_gpio_off: sdc2_wlan_gpio_off {
+ mux {
+ pins = "gpio43";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio43";
+ drive-strength = <2>;
+ bias-disable;
+ output-low;
+ };
+ };
+};
+
+&sdhc_2 {
+ /delete-property/cd-gpios;
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &msm_gpio 38 0>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
+ qcom,vdd-voltage-level = <1800000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
+
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 50000>;
+ qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on
+ &sdc2_wlan_gpio_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_wlan_gpio_off>;
+ qcom,nonremovable;
+ qcom,core_3_0v_support;
+ status = "ok";
+};
+
+&usb_otg {
+ interrupts = <0 134 0>,<0 140 0>,<0 136 0>;
+ interrupt-names = "core_irq", "async_irq", "phy_irq";
+ qcom,hsusb-otg-mode = <3>;
+ qcom,switch-vbus-w-id;
+ vbus_otg-supply = <&vph_pwr_vreg>;
+ extcon = <&pm8916_chg>;
+};
+
+&external_image_mem {
+ reg = <0x0 0x87900000 0x0 0x0700000>;
+};
+
+&modem_adsp_mem {
+ reg = <0x0 0x88000000 0x0 0x01e00000>;
+};
+
+&peripheral_mem {
+ status = "disabled";
+};
+
+&pm8916_chg {
+ status = "ok";
+};
+
+&pm8916_bms {
+ status = "ok";
+};
+
+&blsp1_uart2_hs {
+ status = "ok";
+};
+
+&i2c_1 {
+ status = "okay";
+ vl53l0x@52 {
+ compatible = "st,stmvl53l0";
+ reg = <0x29>;
+ status = "ok";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
index 57a28d0..20878c0 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
@@ -52,6 +52,7 @@
qcom,blackghost {
compatible = "qcom,pil-blackghost";
+ qcom,pil-force-shutdown;
qcom,firmware-name = "bg-wear";
/* GPIO inputs from blackghost */
qcom,bg2ap-status-gpio = <&msm_gpio 97 0>;
@@ -157,8 +158,11 @@
interrupts = <50 0>;
interrupt-names = "nfc_irq";
pinctrl-names = "nfc_active","nfc_suspend";
- pinctrl-0 = <&nfcw_int_active &nfcw_disable_active>;
+ pinctrl-0 = <&nfcw_int_active
+ &nfcw_disable_active
+ &nfc_clk_default>;
pinctrl-1 = <&nfcw_int_suspend &nfcw_disable_suspend>;
+ clocks = <&clock_rpm clk_bb_clk3_pin>;
clock-names = "ref_clk";
};
};
@@ -296,3 +300,9 @@
output-high;
};
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+ qcom,platform-enable-gpio = <&msm_gpio 59 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
index 454ba81..e7af39f 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
@@ -71,6 +71,7 @@
qcom,blackghost {
compatible = "qcom,pil-blackghost";
+ qcom,pil-force-shutdown;
qcom,firmware-name = "bg-wear";
/* GPIO inputs from blackghost */
qcom,bg2ap-status-gpio = <&msm_gpio 97 0>;
@@ -176,8 +177,11 @@
interrupts = <50 0>;
interrupt-names = "nfc_irq";
pinctrl-names = "nfc_active","nfc_suspend";
- pinctrl-0 = <&nfcw_int_active &nfcw_disable_active>;
+ pinctrl-0 = <&nfcw_int_active
+ &nfcw_disable_active
+ &nfc_clk_default>;
pinctrl-1 = <&nfcw_int_suspend &nfcw_disable_suspend>;
+ clocks = <&clock_rpm clk_bb_clk3_pin>;
clock-names = "ref_clk";
};
};
@@ -313,3 +317,9 @@
output-high;
};
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+ qcom,platform-enable-gpio = <&msm_gpio 59 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-audio.dtsi b/arch/arm64/boot/dts/qcom/apq8017-audio.dtsi
new file mode 100644
index 0000000..56f69ad
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-audio.dtsi
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&wsa881x_211 {
+ qcom,spkr-sd-n-gpio = <&tlmm 92 0>;
+};
+
+&wsa881x_212 {
+ qcom,spkr-sd-n-gpio = <&tlmm 92 0>;
+};
+
+&wsa881x_213 {
+ qcom,spkr-sd-n-gpio = <&tlmm 92 0>;
+};
+
+&wsa881x_214 {
+ qcom,spkr-sd-n-gpio = <&tlmm 92 0>;
+};
+
+&pm8937_gpios {
+ gpio@c000 {
+ status = "ok";
+ qcom,mode = <1>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <2>;
+ qcom,master-en = <1>;
+ qcom,out-strength = <2>;
+ };
+
+ gpio@c600 {
+ status = "ok";
+ qcom,mode = <1>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ qcom,out-strength = <2>;
+ };
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+ cdc-vdd-mic-bias-supply = <&pm8937_l9>;
+ qcom,cdc-vdd-mic-bias-voltage = <3300000 3300000>;
+ qcom,cdc-vdd-mic-bias-current = <15000>;
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ status = "okay";
+ qcom,msm-mbhc-hphl-swh = <1>;
+ qcom,msm-mbhc-gnd-swh = <1>;
+ qcom,tdm-audio-intf;
+ qcom,afe-rxtx-lb;
+ reg = <0xc051000 0x4>,
+ <0xc051004 0x4>,
+ <0xc055000 0x4>,
+ <0xc052000 0x4>,
+ <0x0c056000 0x4>,
+ <0x0c054000 0x4>,
+ <0x0c053000 0x4>;
+ reg-names = "csr_gp_io_mux_mic_ctl",
+ "csr_gp_io_mux_spkr_ctl",
+ "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel",
+ "csr_gp_io_mux_quin_ctl",
+ "csr_gp_io_lpaif_qui_pcm_sec_mode_muxsel",
+ "csr_gp_io_mux_mic_ext_clk_ctl",
+ "csr_gp_io_mux_sec_tlmm_ctl";
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "AIF4 VI", "MICBIAS_REGULATOR",
+ "RX_BIAS", "MCLK",
+ "BRIDGE RX OUT", "MCLK",
+ "BRIDGE TX IN", "MCLK",
+ "MADINPUT", "MCLK",
+ "AIF4 MAD", "MICBIAS_REGULATOR",
+ "AMIC1", "MIC BIAS3",
+ "MIC BIAS3", "Handset Mic",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "AMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Secondary Mic",
+ "AMIC4", "MIC BIAS3",
+ "MIC BIAS3", "Analog Mic4",
+ "AMIC5", "MIC BIAS4",
+ "MIC BIAS4", "Analog Mic7",
+ "AMIC6", "MIC BIAS4",
+ "MIC BIAS4", "Analog Mic6",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "DMIC5", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic5",
+ "MIC BIAS3", "MICBIAS_REGULATOR",
+ "MIC BIAS4", "MICBIAS_REGULATOR",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+ asoc-cpu = <&dai_pri_auxpcm>,
+ <&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s5>,
+ <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+ <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+ <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
+ <&afe_pcm_rx>, <&afe_pcm_tx>,
+ <&afe_proxy_rx>, <&afe_proxy_tx>,
+ <&incall_record_rx>, <&incall_record_tx>,
+ <&incall_music_rx>, <&incall_music_2_rx>,
+ <&sb_5_rx>, <&bt_sco_rx>,
+ <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
+ <&sb_6_rx>, <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>,
+ <&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>, <&afe_loopback_tx>;
+ asoc-cpu-names = "msm-dai-q6-auxpcm.1",
+ "msm-dai-q6-mi2s.2",
+ "msm-dai-q6-mi2s.3", "msm-dai-q6-mi2s.5",
+ "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+ "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+ "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+ "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+ "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+ "msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
+ "msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+ "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+ "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+ "msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394",
+ "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
+ "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
+ "msm-dai-q6-dev.16396", "msm-dai-q6-tdm.36864",
+ "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36880",
+ "msm-dai-q6-tdm.36881", "msm-dai-q6-dev.24577";
+ qcom,msm-gpios =
+ "quin_i2s",
+ "us_eu_gpio",
+ "quat_i2s";
+ qcom,pinctrl-names =
+ "all_off",
+ "quin_i2s_act",
+ "us_eu_gpio_act",
+ "quin_i2s_us_eu_gpio_act",
+ "quat_i2s_act",
+ "quat_i2s_quin_i2s_act",
+ "quat_i2s_us_eu_gpio_act",
+ "quat_i2s_us_eu_gpio_quin_i2s_act";
+ pinctrl-names =
+ "all_off",
+ "quin_i2s_act",
+ "us_eu_gpio_act",
+ "quin_i2s_us_eu_gpio_act",
+ "quat_i2s_act",
+ "quat_i2s_quin_i2s_act",
+ "quat_i2s_us_eu_gpio_act",
+ "quat_i2s_us_eu_gpio_quin_i2s_act";
+
+ pinctrl-0 = <&pri_tlmm_ws_sus
+ &cross_conn_det_sus &pri_mi2s_sd0_sleep
+ &pri_mi2s_sck_sleep &pri_mi2s_sd1_sleep
+ &sec_mi2s_ws_sleep &sec_mi2s_sck_sleep
+ &sec_mi2s_sd1_sleep &sec_mi2s_sd0_sleep>;
+ pinctrl-1 = <&pri_tlmm_ws_act
+ &cross_conn_det_sus &pri_mi2s_sd0_active
+ &pri_mi2s_sck_active &pri_mi2s_sd1_active
+ &sec_mi2s_ws_sleep &sec_mi2s_sck_sleep
+ &sec_mi2s_sd1_sleep &sec_mi2s_sd0_sleep>;
+ pinctrl-2 = <&pri_tlmm_ws_sus
+ &cross_conn_det_act &pri_mi2s_sd0_sleep
+ &pri_mi2s_sck_sleep &pri_mi2s_sd1_sleep
+ &sec_mi2s_ws_sleep &sec_mi2s_sck_sleep
+ &sec_mi2s_sd1_sleep &sec_mi2s_sd0_sleep>;
+ pinctrl-3 = <&pri_tlmm_ws_act
+ &cross_conn_det_act &pri_mi2s_sd0_active
+ &pri_mi2s_sck_active &pri_mi2s_sd1_active
+ &sec_mi2s_ws_sleep &sec_mi2s_sck_sleep
+ &sec_mi2s_sd1_sleep &sec_mi2s_sd0_sleep>;
+ pinctrl-4 = <&pri_tlmm_ws_sus
+ &cross_conn_det_sus &pri_mi2s_sd0_sleep
+ &pri_mi2s_sck_sleep &pri_mi2s_sd1_sleep
+ &sec_mi2s_ws_active &sec_mi2s_sck_active
+ &sec_mi2s_sd1_active &sec_mi2s_sd0_active>;
+ pinctrl-5 = <&pri_tlmm_ws_act
+ &cross_conn_det_sus &pri_mi2s_sd0_active
+ &pri_mi2s_sck_active &pri_mi2s_sd1_active
+ &sec_mi2s_ws_active &sec_mi2s_sck_active
+ &sec_mi2s_sd1_active &sec_mi2s_sd0_active>;
+ pinctrl-6 = <&pri_tlmm_ws_sus
+ &cross_conn_det_act &pri_mi2s_sd0_sleep
+ &pri_mi2s_sck_sleep &pri_mi2s_sd1_sleep
+ &sec_mi2s_ws_active &sec_mi2s_sck_active
+ &sec_mi2s_sd1_active &sec_mi2s_sd0_active>;
+ pinctrl-7 = <&pri_tlmm_ws_act
+ &cross_conn_det_act &pri_mi2s_sd0_active
+ &pri_mi2s_sck_active &pri_mi2s_sd1_active
+ &sec_mi2s_ws_active &sec_mi2s_sck_active
+ &sec_mi2s_sd1_active &sec_mi2s_sd0_active>;
+};
+
+&int_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
+
+&soc {
+ qcom,msm-dai-tdm-pri-rx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37120>;
+ qcom,msm-cpudai-tdm-group-num-ports = <1>;
+ qcom,msm-cpudai-tdm-group-port-id = <36864>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36864>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-pri-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37121>;
+ qcom,msm-cpudai-tdm-group-num-ports = <1>;
+ qcom,msm-cpudai-tdm-group-port-id = <36865>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36865>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-sec-rx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37136>;
+ qcom,msm-cpudai-tdm-group-num-ports = <1>;
+ qcom,msm-cpudai-tdm-group-port-id = <36880>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_sec_tdm_rx_0: qcom,msm-dai-q6-tdm-sec-rx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36880>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-sec-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37137>;
+ qcom,msm-cpudai-tdm-group-num-ports = <1>;
+ qcom,msm-cpudai-tdm-group-port-id = <36881>;
+ qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+ qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
+ dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36881>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+};
+
+&dai_pri_auxpcm {
+ qcom,msm-cpudai-afe-clk-ver = <2>;
+};
+
+&tlmm {
+ tlmm_gpio_key {
+ gpio_key_active: gpio_key_active {
+ mux {
+ pins = "gpio91", "gpio127", "gpio128";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio91", "gpio127", "gpio128";
+ };
+ };
+
+ gpio_key_suspend: gpio_key_suspend {
+ mux {
+ pins = "gpio91", "gpio127", "gpio128";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio91", "gpio127", "gpio128";
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8937-cdp-wcd-rome.dts b/arch/arm64/boot/dts/qcom/apq8017-pmi8937-cdp-wcd-rome.dts
new file mode 100644
index 0000000..19c24f8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8937-cdp-wcd-rome.dts
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8017.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8937.dtsi"
+#include "apq8017-rome.dtsi"
+#include "apq8017-audio.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8937 CDP \
+ with WCD codec/Rome card";
+ compatible = "qcom,apq8017-cdp", "qcom,apq8017", "qcom,cdp";
+ qcom,board-id= <1 2>;
+ qcom,pmic-id = <0x10019 0x020037 0x0 0x0>;
+};
+
+&blsp1_uart1 {
+ status = "ok";
+};
+
+&sdhc_2 {
+ /* device core power supply */
+ /delete-property/vdd-supply;
+ /delete-property/qcom,vdd-voltage-level;
+ /delete-property/qcom,vdd-current-level;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8937_l5>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ qcom,core_3_0v_support;
+ qcom,nonremovable;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on
+ &sdc2_wlan_gpio_active>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_wlan_gpio_sleep>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &tlmm 124 0x4>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
+ /delete-property/cd-gpios;
+ /delete-property/qcom,devfreq,freq-table;
+
+ status = "ok";
+
+};
+
+&mdss_fb0 {
+ /delete-node/ qcom,cont-splash-memory;
+};
+
+/delete-node/ &cont_splash_mem;
+
+&soc {
+ gpio_keys {
+ /delete-node/ home;
+ };
+};
+
+&modem_mem {
+ reg = <0x0 0x86800000 0x0 0x1500000>;
+};
+
+&adsp_fw_mem {
+ reg = <0x0 0x87d00000 0x0 0x1100000>;
+};
+
+&wcnss_fw_mem {
+ reg = <0x0 0x88e00000 0x0 0x700000>;
+};
+
+&secure_mem {
+ status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8937-cdp.dts b/arch/arm64/boot/dts/qcom/apq8017-pmi8937-cdp.dts
new file mode 100644
index 0000000..6faa413
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8937-cdp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8017.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8937.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8937 CDP";
+ compatible = "qcom,apq8017-cdp", "qcom,apq8017", "qcom,cdp";
+ qcom,board-id= <1 0>;
+ qcom,pmic-id = <0x10019 0x020037 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8937-mtp.dts b/arch/arm64/boot/dts/qcom/apq8017-pmi8937-mtp.dts
new file mode 100644
index 0000000..01305e6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8937-mtp.dts
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8017.dtsi"
+#include "msm8917-mtp.dtsi"
+#include "msm8917-pmi8937.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8937 MTP";
+ compatible = "qcom,apq8017-mtp", "qcom,apq8017", "qcom,mtp";
+ qcom,board-id= <8 0>;
+ qcom,pmic-id = <0x10019 0x020037 0x0 0x0>;
+};
+
+&blsp1_uart1 {
+ status = "ok";
+};
+
+&vendor {
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts
new file mode 100644
index 0000000..8ddb1fc
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8017.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8950.dtsi"
+#include "apq8017-rome.dtsi"
+#include "apq8017-audio.dtsi"
+#include "apq8017-pmi8950-cdp-wcd-rome.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 CDP \
+ with WCD codec/Rome card";
+ compatible = "qcom,apq8017-cdp", "qcom,apq8017", "qcom,cdp";
+ qcom,board-id= <1 2>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
+
+&blsp1_uart1 {
+ status = "ok";
+};
+
+&wcd9335 {
+ qcom,cdc-mic-unmute-delay = <50>;
+};
+
+&sdhc_2 {
+ /* device core power supply */
+ /delete-property/vdd-supply;
+ /delete-property/qcom,vdd-voltage-level;
+ /delete-property/qcom,vdd-current-level;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8937_l5>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ qcom,core_3_0v_support;
+ qcom,nonremovable;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on
+ &sdc2_wlan_gpio_active>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
+ &sdc2_wlan_gpio_sleep>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &tlmm 124 0x4>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
+ /delete-property/cd-gpios;
+ /delete-property/qcom,devfreq,freq-table;
+
+ status = "ok";
+
+};
+
+&mdss_fb0 {
+ /delete-node/ qcom,cont-splash-memory;
+};
+
+/delete-node/ &cont_splash_mem;
+
+&soc {
+ gpio_keys {
+ /delete-node/ home;
+ };
+};
+
+&usb_otg {
+ vbus_otg-supply = <0>;
+ qcom,vbus-low-as-hostmode;
+};
+
+&i2c_4 {
+ usb2533@2c {
+ status = "ok";
+ };
+};
+
+&i2c_2 {
+ /* DSI_TO_HDMI I2C configuration */
+ adv7533@39 {
+ compatible = "adv7533";
+ reg = <0x39>;
+ instance_id = <0>;
+ adi,video-mode = <3>; /* 3 = 1080p */
+ adi,main-addr = <0x39>;
+ adi,cec-dsi-addr = <0x3C>;
+ adi,enable-audio;
+ adi,irq-gpio = <&tlmm 0x29 0x2002>;
+ adi,power-down-gpio = <&tlmm 0x7D 0x0>;
+ adi,switch-gpio = <&pm8937_gpios 0x8 0x1>;
+ pinctrl-names = "pmx_adv7533_active",
+ "pmx_adv7533_suspend";
+ pinctrl-0 = <&adv7533_int_active>;
+ pinctrl-1 = <&adv7533_int_suspend>;
+ };
+
+ pericom-type-c@1d {
+ status = "disabled";
+ };
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_adv7533_1080p>;
+ qcom,platform-intf-mux-gpio = <&tlmm 115 0>;
+ status = "ok";
+ qcom,bridge-index = <0>;
+ qcom,pluggable;
+};
+
+&dsi_adv7533_1080p {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&modem_mem {
+ reg = <0x0 0x86800000 0x0 0x1500000>;
+};
+
+&adsp_fw_mem {
+ reg = <0x0 0x87d00000 0x0 0x1100000>;
+};
+
+&wcnss_fw_mem {
+ reg = <0x0 0x88e00000 0x0 0x700000>;
+};
+
+&other_ext_mem {
+ reg = <0x0 0x84f00000 0x0 0x1900000>;
+};
+
+&qcom_seecom {
+ reg = <0x84f00000 0x1400000>;
+};
+
+&secure_mem {
+ status = "disabled";
+};
+
+/* Warning, SPI6 & I2C6 cannot be enabled at the same time due to pin usage. */
+&spi_6 {
+ status = "disabled";
+};
+
+&i2c_6 {
+ status = "ok";
+ qcom,clk-freq-out = <100000>;
+
+ /* TI591XX LED Drivers */
+ tlc59116@60 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,tlc59116";
+ reg = <0x60>;
+ out0@0 {
+ label = "ledsec1_g";
+ reg = <0x0>;
+ };
+ out1@1 {
+ label = "ledsec1_r";
+ reg = <0x1>;
+ };
+ out2@2 {
+ label = "ledsec1_b";
+ reg = <0x2>;
+ };
+ out3@3 {
+ label = "ledsec2_g";
+ reg = <0x3>;
+ };
+ out4@4 {
+ label = "ledsec2_r";
+ reg = <0x4>;
+ };
+ out5@5 {
+ label = "ledsec2_b";
+ reg = <0x5>;
+ };
+ out6@6 {
+ label = "ledsec3_g";
+ reg = <0x6>;
+ };
+ out7@7 {
+ label = "ledsec3_r";
+ reg = <0x7>;
+ };
+ out8@8 {
+ label = "ledsec3_b";
+ reg = <0x8>;
+ };
+ out9@9 {
+ label = "ledsec4_g";
+ reg = <0x9>;
+ };
+ out10@10 {
+ label = "ledsec4_r";
+ reg = <0xa>;
+ };
+ out11@11 {
+ label = "ledsec4_b";
+ reg = <0xb>;
+ };
+ out12@12 {
+ label = "ledsec5_g";
+ reg = <0xc>;
+ };
+ out13@13 {
+ label = "ledsec5_r";
+ reg = <0xd>;
+ };
+ out14@14 {
+ label = "ledsec5_b";
+ reg = <0xe>;
+ };
+ };
+
+ tlc59116@61 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,tlc59116";
+ reg = <0x61>;
+ out0@0 {
+ label = "ledsec6_g";
+ reg = <0x0>;
+ };
+ out1@1 {
+ label = "ledsec6_r";
+ reg = <0x1>;
+ };
+ out2@2 {
+ label = "ledsec6_b";
+ reg = <0x2>;
+ };
+ out3@3 {
+ label = "ledsec7_g";
+ reg = <0x3>;
+ };
+ out4@4 {
+ label = "ledsec7_r";
+ reg = <0x4>;
+ };
+ out5@5 {
+ label = "ledsec7_b";
+ reg = <0x5>;
+ };
+ out6@6 {
+ label = "ledsec8_g";
+ reg = <0x6>;
+ };
+ out7@7 {
+ label = "ledsec8_r";
+ reg = <0x7>;
+ };
+ out8@8 {
+ label = "ledsec8_b";
+ reg = <0x8>;
+ };
+ out9@9 {
+ label = "ledsec9_g";
+ reg = <0x9>;
+ };
+ out10@10 {
+ label = "ledsec9_r";
+ reg = <0xa>;
+ };
+ out11@11 {
+ label = "ledsec9_b";
+ reg = <0xb>;
+ };
+ };
+};
+
+&soc {
+ pinctrl@1000000 {
+ i2c6{
+ tlc59116_reset: tlc59116_reset {
+ config {
+ pins = "gpio20";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi
new file mode 100644
index 0000000..3337add
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 CDP \
+ with WCD codec/Rome card";
+ compatible = "qcom,apq8017-cdp", "qcom,apq8017", "qcom,cdp";
+ qcom,board-id= <1 2>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+
+ aliases {
+ i2c6 = &i2c_6;
+ };
+};
+
+&soc {
+ i2c_6: i2c@7af6000 { /* BLSP2 QUP2 */
+ compatible = "qcom,i2c-msm-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ reg = <0x7af6000 0x600>;
+ interrupt-names = "qup_irq";
+ interrupts = <0 300 0>;
+ qcom,clk-freq-out = <400000>;
+ qcom,clk-freq-in = <19200000>;
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp2_qup2_i2c_apps_clk>;
+
+ pinctrl-names = "i2c_active", "i2c_sleep";
+ pinctrl-0 = <&i2c_6_active>;
+ pinctrl-1 = <&i2c_6_sleep>;
+ qcom,noise-rjct-scl = <0>;
+ qcom,noise-rjct-sda = <0>;
+ qcom,master-id = <84>;
+ dmas = <&dma_blsp2 6 64 0x20000020 0x20>,
+ <&dma_blsp2 7 32 0x20000020 0x20>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp.dts b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp.dts
new file mode 100644
index 0000000..5c8ef83
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-cdp.dts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8017.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 CDP";
+ compatible = "qcom,apq8017-cdp", "qcom,apq8017", "qcom,cdp";
+ qcom,board-id= <1 0>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
+
+&mdss_fb0 {
+ /delete-node/ qcom,cont-splash-memory;
+};
+
+/delete-node/ &cont_splash_mem;
diff --git a/arch/arm64/boot/dts/qcom/apq8017-pmi8950-mtp.dts b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-mtp.dts
new file mode 100644
index 0000000..b5b7667
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-pmi8950-mtp.dts
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8017.dtsi"
+#include "msm8917-mtp.dtsi"
+#include "msm8917-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 MTP";
+ compatible = "qcom,apq8017-mtp", "qcom,apq8017", "qcom,mtp";
+ qcom,board-id= <8 0>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
+
+&blsp1_uart1 {
+ status = "ok";
+};
+
+&vendor {
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&i2c_2 {
+ /* DSI_TO_HDMI I2C configuration */
+ adv7533@39 {
+ compatible = "adv7533";
+ reg = <0x39>;
+ instance_id = <0>;
+ adi,video-mode = <3>; /* 3 = 1080p */
+ adi,main-addr = <0x39>;
+ adi,cec-dsi-addr = <0x3C>;
+ adi,enable-audio;
+ adi,irq-gpio = <&tlmm 0x29 0x2002>;
+ adi,power-down-gpio = <&tlmm 0x7D 0x0>;
+ adi,switch-gpio = <&pm8937_gpios 0x8 0x1>;
+ pinctrl-names = "pmx_adv7533_active",
+ "pmx_adv7533_suspend";
+ pinctrl-0 = <&adv7533_int_active>;
+ pinctrl-1 = <&adv7533_int_suspend>;
+ };
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_adv7533_1080p>;
+ qcom,platform-intf-mux-gpio = <&tlmm 115 0>;
+ status = "ok";
+ qcom,bridge-index = <0>;
+ qcom,pluggable;
+};
+
+&dsi_adv7533_1080p {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017-rome.dtsi b/arch/arm64/boot/dts/qcom/apq8017-rome.dtsi
new file mode 100644
index 0000000..7dfa952
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017-rome.dtsi
@@ -0,0 +1,33 @@
+/*
+ * copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,cnss_sdio {
+ compatible = "qcom,cnss_sdio";
+ qcom,wlan-ramdump-dynamic = <0x200000>;
+ subsys-name = "AR6320";
+ qcom,cap-tsf-gpio = <&tlmm 126 1>;
+ };
+};
+
+&pm8937_gpios {
+ gpio@c100 { /* GPIO 2 - Rome Sleep Clock */
+ qcom,mode = <1>; /* Digital output */
+ qcom,vin-sel = <3>; /* VIN 3 */
+ qcom,src-sel = <2>; /* Function 2 */
+ qcom,out-strength = <2>; /* Medium */
+ qcom,pull = <4>;
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "okay";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8017.dtsi b/arch/arm64/boot/dts/qcom/apq8017.dtsi
new file mode 100644
index 0000000..fecc7ce8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8017.dtsi
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm8917.dtsi"
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8017";
+ compatible = "qcom,apq8017";
+ qcom,msm-id = <307 0x0>;
+};
+
+&tlmm {
+ pmx_adv7533_int: pmx_adv7533_int {
+ adv7533_int_active: adv7533_int_active {
+ mux {
+ pins = "gpio41";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio41";
+ function = "gpio";
+ drive-strength = <16>;
+ bias-pull-up; /* pull up */
+ };
+ };
+
+ adv7533_int_suspend: adv7533_int_suspend {
+ mux {
+ pins = "gpio41";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio41";
+ drive-strength = <16>;
+ bias-disable;
+ };
+ };
+ };
+};
+
+&mdss_dsi_active {
+ mux {
+ pins = "gpio60", "gpio98", "gpio115", "gpio133";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio60", "gpio98", "gpio115", "gpio133";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable = <0>; /* no pull */
+ output-high;
+ };
+};
+
+&mdss_dsi_suspend {
+ mux {
+ pins = "gpio60", "gpio98", "gpio115", "gpio133";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio60", "gpio98", "gpio115", "gpio133";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts b/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts
index 8f7e326..8782001 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts
@@ -26,3 +26,72 @@
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+&int_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
+
+&cdc_pri_mi2s_gpios {
+ status = "disabled";
+};
+
+&wsa881x_analog_vi_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_clk_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_reset_gpio {
+ status = "disabled";
+};
+
+&cdc_comp_gpios {
+ status = "disabled";
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ qcom,model = "msm8953-tasha-snd-card";
+ status = "okay";
+};
+
+&soc {
+ usb_detect: usb_detect {
+ compatible = "linux,extcon-usb-gpio";
+ pintctrl-names = "default";
+ pinctrl-0 = <&ssusb_mode_sel>;
+ id-gpio = <&tlmm 12 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
index d7836e5..55d8b7b 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
@@ -13,7 +13,6 @@
/dts-v1/;
-#include "apq8053-lite.dtsi"
#include "apq8053-lite-dragon-v2.0.dtsi"
/ {
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
index 947af4d..bb40018 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
@@ -12,68 +12,25 @@
*/
#include "apq8053-lite-dragon.dtsi"
-#include "msm8953-mdss-panels.dtsi"
-
-&i2c_3 {
- status = "okay";
- himax_ts@48 {
- compatible = "himax,hxcommon";
- reg = <0x48>;
- interrupt-parent = <&tlmm>;
- interrupts = <65 0x2>;
- vdd-supply = <&pm8953_l10>;
- avdd-supply = <&pm8953_l5>;
- pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
- "pmx_ts_release";
- pinctrl-0 = <&ts_int_active &ts_reset_active>;
- pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
- pinctrl-2 = <&ts_release>;
- himax,panel-coords = <0 800 0 1280>;
- himax,display-coords = <0 800 0 1280>;
- himax,irq-gpio = <&tlmm 65 0x2008>;
- report_type = <1>;
- };
-};
-
-&mdss_mdp {
- qcom,mdss-pref-prim-intf = "dsi";
-};
-
-&mdss_dsi {
- hw-config = "single_dsi";
-};
&mdss_dsi0 {
- qcom,dsi-pref-prim-pan = <&dsi_boyi_hx83100a_800p_video>;
- pinctrl-names = "mdss_default", "mdss_sleep";
pinctrl-0 = <&mdss_dsi_active &mdss_te_active &mdss_dsi_gpio>;
pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend &mdss_dsi_gpio>;
-
- vdd-supply = <&pm8953_l10>;
- vddio-supply = <&pm8953_l6>;
- lab-supply = <&lab_regulator>;
- ibb-supply = <&ibb_regulator>;
-
- qcom,platform-te-gpio = <&tlmm 24 0>;
- qcom,platform-reset-gpio = <&tlmm 61 0>;
- qcom,platform-bklight-en-gpio = <&tlmm 100 0>;
-};
-
-&mdss_dsi1 {
- status = "disabled";
-};
-
-&labibb {
- status = "okay";
- qpnp,qpnp-labibb-mode = "lcd";
-};
-
-&wled {
- qcom,cons-sync-write-delay-us = <1000>;
- qcom,led-strings-list = [00 01 02 03];
};
&pm8953_l4 {
status = "okay";
regulator-always-on;
};
+
+&camera0 {
+ qcom,mount-angle = <90>;
+};
+
+&camera1 {
+ qcom,mount-angle = <90>;
+};
+
+&camera2{
+ qcom,mount-angle = <90>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dts
new file mode 100644
index 0000000..6c9c266
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-dragon-v2.1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.1";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01010020 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
new file mode 100644
index 0000000..993799b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "apq8053-lite-dragon.dtsi"
+
+&mdss_dsi0 {
+ qcom,ext-vdd-gpio = <&tlmm 100 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 95 0>;
+
+ qcom,platform-lane-config = [00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 8f];
+};
+
+&eeprom0 {
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>,
+ <&tlmm 39 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA",
+ "CAM_STANDBY0";
+};
+
+&camera0 {
+ qcom,mount-angle = <270>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VDIG",
+ "CAM_VANA";
+};
+
+&camera1 {
+ qcom,mount-angle = <270>;
+};
+
+&camera2{
+ qcom,mount-angle = <270>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dts
new file mode 100644
index 0000000..ecc4fea
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-dragon-v2.2.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.2";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01010120 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
new file mode 100644
index 0000000..1744c90
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "apq8053-lite-dragon.dtsi"
+
+&i2c_3 {
+ status = "okay";
+ /delete-node/ himax_ts@48;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_boent51021_1200p_video>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ vdd-supply = <&pm8953_l10>;
+ vddio-supply = <&pm8953_l6>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,ext-vdd-gpio = <&tlmm 100 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 95 0>;
+
+ qcom,platform-lane-config = [00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 0f
+ 00 00 ff 8f];
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+};
+
+&eeprom0 {
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>,
+ <&tlmm 39 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA",
+ "CAM_STANDBY0";
+};
+
+&camera0 {
+ qcom,mount-angle = <270>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>,
+ <&tlmm 118 0>,
+ <&tlmm 119 0>;
+ qcom,gpio-vdig = <3>;
+ qcom,gpio-vana = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0",
+ "CAM_VDIG",
+ "CAM_VANA";
+};
+
+&camera1 {
+ qcom,mount-angle = <270>;
+};
+
+&camera2{
+ qcom,mount-angle = <270>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dts
new file mode 100644
index 0000000..e3f80be
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-dragon-v2.3.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.3";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01020020 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dtsi
new file mode 100644
index 0000000..5cf8ac0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dtsi
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "apq8053-lite-dragon.dtsi"
+
+&pm8953_l4 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&pm8953_l10 {
+ status = "okay";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+};
+
+&pm8953_l2 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&pm8953_l17 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&pm8953_l22 {
+ status = "okay";
+ regulator-always-on;
+};
+
+&i2c_3 {
+ status = "okay";
+ /delete-node/ himax_ts@48;
+ focaltech_ts@38 {
+ compatible = "focaltech,fts";
+ reg = <0x38>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2>;
+ vdd-supply = <&pm8953_l10>;
+ avdd-supply = <&pm8953_l6>;
+ pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
+ "pmx_ts_release";
+ pinctrl-0 = <&ts_int_active &ts_reset_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ pinctrl-2 = <&ts_release>;
+ focaltech,display-coords = <0 0 800 1280>;
+ focaltech,reset-gpio = <&tlmm 64 0x0>;
+ focaltech,irq-gpio = <&tlmm 65 0x2>;
+ focaltech,max-touch-number = <5>;
+ report_type = <1>;
+ };
+};
+
+&wled {
+ qcom,led-strings-list = [00 01 02];
+};
+
+&camera0 {
+ qcom,mount-angle = <90>;
+};
+
+&camera1 {
+ qcom,mount-angle = <90>;
+};
+
+&camera2{
+ qcom,mount-angle = <90>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dts
new file mode 100644
index 0000000..1f40ef8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-dragon-v2.4.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.4";
+ compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+ "qcom,dragonboard";
+ qcom,board-id= <0x01030020 0>;
+};
+
+&blsp2_uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dtsi
new file mode 100644
index 0000000..db0331e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dtsi
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "apq8053-lite-dragon.dtsi"
+
+&wled {
+ qcom,fs-curr-ua = <17500>;
+};
+
+&camera0 {
+ qcom,mount-angle = <180>;
+};
+
+&camera1 {
+ qcom,mount-angle = <180>;
+};
+
+&camera2 {
+ qcom,mount-angle = <180>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
index e48b3bb..bd48f09 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*/
+#include "apq8053-lite.dtsi"
#include "msm8953-pinctrl.dtsi"
#include "apq8053-camera-sensor-dragon.dtsi"
#include "pmi8950.dtsi"
@@ -185,39 +186,22 @@
&i2c_3 {
status = "okay";
- focaltech@38 {
- compatible = "focaltech,5x06";
- reg = <0x38>;
+ himax_ts@48 {
+ compatible = "himax,hxcommon";
+ reg = <0x48>;
interrupt-parent = <&tlmm>;
interrupts = <65 0x2>;
vdd-supply = <&pm8953_l10>;
- vcc_i2c-supply = <&pm8953_l5>;
- /* pins used by touchscreen */
+ avdd-supply = <&pm8953_l5>;
pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
- "pmx_ts_release";
+ "pmx_ts_release";
pinctrl-0 = <&ts_int_active &ts_reset_active>;
pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
pinctrl-2 = <&ts_release>;
- focaltech,name = "ft5606";
- focaltech,family-id = <0x08>;
- focaltech,reset-gpio = <&tlmm 64 0x0>;
- focaltech,irq-gpio = <&tlmm 65 0x2008>;
- focaltech,display-coords = <0 0 1919 1199>;
- focaltech,panel-coords = <0 0 1919 1199>;
- focaltech,no-force-update;
- focaltech,i2c-pull-up;
- focaltech,group-id = <1>;
- focaltech,hard-reset-delay-ms = <20>;
- focaltech,soft-reset-delay-ms = <200>;
- focaltech,num-max-touches = <5>;
- focaltech,fw-delay-aa-ms = <30>;
- focaltech,fw-delay-55-ms = <30>;
- focaltech,fw-upgrade-id1 = <0x79>;
- focaltech,fw-upgrade-id2 = <0x08>;
- focaltech,fw-delay-readid-ms = <10>;
- focaltech,fw-delay-era-flsh-ms = <2000>;
- focaltech,fw-auto-cal;
- focaltech,resume-in-workqueue;
+ himax,panel-coords = <0 800 0 1280>;
+ himax,display-coords = <0 800 0 1280>;
+ himax,irq-gpio = <&tlmm 65 0x2008>;
+ report_type = <1>;
};
};
@@ -229,6 +213,71 @@
status = "disabled";
};
+#include "msm8953-mdss-panels.dtsi"
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi_active {
+ mux {
+ pins = "gpio61", "gpio100";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61", "gpio100";
+ drive-strength = <8>;
+ bias-disable = <0>;
+ output-high;
+ };
+};
+
+&mdss_dsi_suspend {
+ mux {
+ pins = "gpio61", "gpio100";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio61", "gpio100";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_boyi_hx83100a_800p_video>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ vdd-supply = <&pm8953_l10>;
+ vddio-supply = <&pm8953_l6>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 100 0>;
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+};
+
+&labibb {
+ status = "okay";
+ qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&wled {
+ qcom,cons-sync-write-delay-us = <1000>;
+ qcom,led-strings-list = [00 01 02 03];
+};
&blsp1_uart0 {
status = "ok";
pinctrl-names = "default";
@@ -270,7 +319,7 @@
pins = "gpio75";
drive-strength = <10>;
bias-pull-up;
- output-high;
+ output-low;
};
};
sdc2_wlan_gpio_off: sdc2_wlan_gpio_off {
@@ -288,36 +337,21 @@
};
&sdhc_2 {
- /* device core power supply */
- vdd-supply = <&pm8953_l11>;
- qcom,vdd-voltage-level = <2950000 2950000>;
- qcom,vdd-current-level = <15000 800000>;
-
/* device communication power supply */
vdd-io-supply = <&pm8953_l12>;
qcom,vdd-io-always-on;
qcom,vdd-io-voltage-level = <1800000 1800000>;
qcom,vdd-io-current-level = <200 22000>;
+
qcom,core_3_0v_support;
qcom,nonremovable;
pinctrl-names = "active", "sleep";
- pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on
- &sdc2_wlan_gpio_on>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_wlan_gpio_on>;
pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
- &sdc2_wlan_gpio_off>;
+ &sdc2_wlan_gpio_off>;
- #address-cells = <0>;
- interrupt-parent = <&sdhc_2>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xffffffff>;
- interrupt-map = <0 &intc 0 125 0
- 1 &intc 0 221 0
- 2 &tlmm 133 0>;
- interrupt-names = "hc_irq", "pwr_irq", "status_irq";
-
- qcom,clk-rates = <400000 20000000 25000000 50000000>;
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
status = "ok";
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi
new file mode 100644
index 0000000..06fc5a4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi
@@ -0,0 +1,102 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_auo_390p_cmd: qcom,mdss_dsi_auo_390p_cmd {
+ qcom,mdss-dsi-panel-name = "AUO 390p command mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-framerate = <45>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <390>;
+ qcom,mdss-dsi-panel-height = <390>;
+ qcom,mdss-pan-physical-height-dimension = <29>;
+ qcom,mdss-pan-physical-width-dimension = <29>;
+ qcom,mdss-dsi-h-front-porch = <4>;
+ qcom,mdss-dsi-h-back-porch = <4>;
+ qcom,mdss-dsi-h-pulse-width = <4>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <8>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <8>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-tear-check-frame-rate = <4500>;
+ qcom,mdss-dsi-on-command = [
+ 15 01 00 00 00 00 02 fe 01
+ 15 01 00 00 00 00 02 0a f0
+ 15 01 00 00 00 00 02 fe 00
+ 15 01 00 00 00 00 02 35 00
+ 29 01 00 00 00 00 05 2a 00 04 01 89
+ 29 01 00 00 00 00 05 2b 00 00 01 85
+ 29 01 00 00 00 00 05 30 00 00 01 85
+ 29 01 00 00 00 00 05 31 00 04 01 89
+ 05 01 00 00 00 00 02 12 00
+ 15 01 00 00 00 00 02 53 20
+ 05 01 00 00 96 00 02 11 00
+ 05 01 00 00 00 00 02 29 00
+ 39 01 00 00 00 00 06 f0 55 aa 52 08 01
+ 39 01 00 00 00 00 07 ff 00 55 aa 52 08 01
+ ];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 00 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
+ ];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+
+ qcom,mdss-dsi-idle-on-command = [
+ 05 01 00 00 00 00 01 39 /* Idle-Mode On */
+ ];
+ qcom,mdss-dsi-idle-on-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-idle-off-command = [
+ 05 01 00 00 00 00 01 38 /* Idle-Mode Off */
+ /* Reset column start address*/
+ 29 01 00 00 00 00 05 2a 00 04 01 89
+ /* Reset row start address */
+ 29 01 00 00 00 00 05 2b 00 00 01 85
+ 15 01 00 00 00 00 02 fe 01
+ 15 01 00 00 00 00 02 04 00
+ 15 01 00 00 00 00 02 fe 00
+ 15 01 00 00 00 00 02 3a 77
+ ];
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-panel-timings = [5f 12 0a 00 32 34 10
+ 16 0f 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x05>;
+ qcom,mdss-dsi-t-clk-pre = <0x11>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>;
+ /* clk = totlaH * totalV * bpp* 66fps */
+ qcom,mdss-dsi-panel-clockrate = <276705792>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "te_signal_check";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-boent51021-1200p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-boent51021-1200p-video.dtsi
new file mode 100644
index 0000000..04accd8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-boent51021-1200p-video.dtsi
@@ -0,0 +1,92 @@
+/* Novatek Android Driver Sample Code for Novatek chipset
+ *
+ * Copyright (C) 2015-2018 Novatek Microelectronics Corp.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+&mdss_mdp {
+ dsi_boent51021_1200p_video: qcom,mdss_dsi_boent51021_1200p_video {
+ qcom,mdss-dsi-panel-name =
+ "boent51021 1200p video mode dsi panel";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1200>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <100>;
+ qcom,mdss-dsi-h-back-porch = <32>;
+ qcom,mdss-dsi-h-pulse-width = <1>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <14>;
+ qcom,mdss-dsi-v-front-porch = <25>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 23 01 00 00 01 00 02 8f a5
+ 23 01 00 00 00 00 02 83 00
+ 23 01 00 00 00 00 02 84 00
+ 23 01 00 00 00 00 02 8c 80
+ 23 01 00 00 00 00 02 cd 6c
+ 23 01 00 00 00 00 02 c8 fc
+ 23 01 00 00 00 00 02 97 00
+ 23 01 00 00 00 00 02 8b 10
+ 23 01 00 00 00 00 02 a9 20
+ 23 01 00 00 00 00 02 83 aa
+ 23 01 00 00 00 00 02 84 11
+ 23 01 00 00 00 00 02 a9 4b
+ 23 01 00 00 00 00 02 85 04
+ 23 01 00 00 00 00 02 86 08
+ 23 01 00 00 00 00 02 9c 10
+ 05 01 00 00 00 00 02 11 00
+ 23 01 00 00 00 00 02 8f 00
+ ];
+ qcom,mdss-dsi-off-command = [
+ 23 01 00 00 00 00 02 83 00
+ 23 01 00 00 78 00 02 84 00
+ 05 01 00 00 78 00 02 10 00
+ ];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings = [
+ f2 3a 28 00 6c 70 2c 3e 2e 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x0e>;
+ qcom,mdss-dsi-t-clk-pre = <0x33>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-force-clock-lane-hs;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-pan-physical-width-dimension = <135>;
+ qcom,mdss-pan-physical-height-dimension = <216>;
+ qcom,mdss-dsi-lp11-init;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
index 9b703fe..1ccde7b 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi
@@ -31,6 +31,8 @@
qcom,mdss-dsi-h-right-border = <0>;
qcom,mdss-dsi-v-top-border = <0>;
qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-pan-physical-width-dimension = <65>;
+ qcom,mdss-pan-physical-height-dimension = <129>;
qcom,mdss-dsi-bpp = <24>;
qcom,mdss-dsi-color-order = "rgb_swap_rgb";
qcom,mdss-dsi-underflow-color = <0xff>;
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-hd-plus-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-hd-plus-video.dtsi
index 237684e..91bf722 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-hd-plus-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-hx8399c-hd-plus-video.dtsi
@@ -40,21 +40,21 @@
b9 ff 83 99
39 01 00 00 00 00 02
d2 88
- 39 01 00 00 00 00 10
- b1 02 04 74 94 01 32 33
- 11 11 e6 5d 56 73 02 02
+ 39 01 00 00 00 00 0c
+ b1 02 04 72 92 01
+ 32 aa 11 11 52 57
39 01 00 00 00 00 10
b2 00 80 80 cc 05 07 5a
- 11 10 10 00 1e 70 03 D4
+ 11 10 10 00 1e 70 03 d4
39 01 00 00 00 00 2d
- b4 00 ff 59 59 0c ac 00
- 00 0c 00 07 0a 00 28 07
- 08 0c 21 03 00 00 00 ae
- 87 59 59 0c ac 00 00 0c
- 00 07 0a 00 28 07 08 0c
- 01 00 00 ae 01
+ b4 00 ff 59 59 01 ab 00
+ 00 09 00 03 05 00 28 03
+ 0b 0d 21 03 02 00 0c a3
+ 80 59 59 02 ab 00 00 09
+ 00 03 05 00 28 03 0b 0d
+ 02 00 0c a3 01
39 01 00 00 05 00 22
- d3 00 00 01 01 00 00 10
+ d3 00 0c 03 03 00 00 10
10 00 00 03 00 03 00 08
78 08 78 00 00 00 00 00
24 02 05 05 03 00 00 00
@@ -80,8 +80,8 @@
39 01 00 00 00 00 02
bd 01
39 01 00 00 00 00 11
- d8 82 ea aa aa 82 ea aa
- aa 82 ea aa aa 82 ea aa
+ d8 00 00 00 00 00 00 00
+ 00 82 ea aa aa 82 ea aa
aa
39 01 00 00 00 00 02
bd 02
@@ -90,25 +90,23 @@
3f
39 01 00 00 00 00 02
bd 00
- 39 01 00 00 00 00 02
- dd 03
39 01 00 00 05 00 37
- e0 08 2a 39 35 74 7c 87
- 7f 84 8a 8e 91 93 96 9b
- 9c 9e a5 a6 ae a1 af b2
- 5c 58 63 74 08 2a 39 35
- 74 7c 87 7f 84 8a 8e 91
- 93 96 9b 9c 9e a5 a6 ae
- a1 af b2 5c 58 63 74
+ e0 01 21 31 2d 66 6f 7b
+ 75 7a 81 86 89 8c 90 95
+ 97 9a a1 a2 aa 9e ad b0
+ 5b 57 63 7a 01 21 31 2d
+ 66 6f 7b 75 7a 81 86 89
+ 8c 90 95 97 9a a1 a2 aa
+ 9e ad b0 5b 57 63 7a
39 01 00 00 00 00 03
b6 7e 7e
39 01 00 00 00 00 02
cc 08
- 39 01 00 00 00 00 06
- c7 00 08 00 01 08
- 39 01 00 00 00 00 03
- c0 25 5a
- 05 01 00 00 78 00 02 11 00
+ 39 01 00 00 00 00 02
+ 35 00
+ 39 01 00 00 00 00 02
+ dd 03
+ 05 01 00 00 96 00 02 11 00
05 01 00 00 14 00 02 29 00];
qcom,mdss-dsi-off-command = [
05 01 00 00 14 00 02 28 00
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi
new file mode 100644
index 0000000..d50fe3b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi
@@ -0,0 +1,98 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_icn9706_720_1440_vid: qcom,mdss_dsi_icn9706_720_1440p_video {
+ qcom,mdss-dsi-panel-name =
+ "icn9706 720 1440p video mode dsi panel";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <720>;
+ qcom,mdss-dsi-panel-height = <1440>;
+ qcom,mdss-dsi-h-front-porch = <84>;
+ qcom,mdss-dsi-h-back-porch = <84>;
+ qcom,mdss-dsi-h-pulse-width = <24>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <20>;
+ qcom,mdss-dsi-v-front-porch = <24>;
+ qcom,mdss-dsi-v-pulse-width = <8>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings = [8b 1e 14 00 44 48 18 22 19
+ 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x04>;
+ qcom,mdss-dsi-t-clk-pre = <0x1c>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-on-command = [39 01 00 00 64 00 02 01 00
+ 39 01 00 00 00 00 03 f0 5a 5a
+ 39 01 00 00 00 00 03 f1 5a 5a
+ 39 01 00 00 00 00 03 f0 b4 4b
+ 39 01 00 00 00 00 03 b6 10 10
+ 39 01 00 00 00 00 15 b4 0a 08 12 10 0e 0c 00 00
+ 00 03 00 03 03 03 03 03 03 03 04 06
+ 39 01 00 00 00 00 15 b3 0b 09 13 11 0f 0d 00 00
+ 00 03 00 03 03 03 03 03 03 03 05 07
+ 39 01 00 00 00 00 0d b0 54 32 23 45 44 44 44 44
+ 60 01 60 01
+ 39 01 00 00 00 00 09 b1 32 84 02 83 15 01 57 01
+ 39 01 00 00 00 00 02 b2 33
+ 39 01 00 00 00 00 07 bd 54 14 6a 6a 20 19
+ 39 01 00 00 00 00 12 b7 01 01 09 11 0d 15 19 0d
+ 21 1d 00 00 20 00 02 ff 3c
+ 39 01 00 00 00 00 06 b8 23 01 30 34 53
+ 39 01 00 00 00 00 05 b9 a1 2c ff c4
+ 39 01 00 00 00 00 03 ba 88 23
+ 39 01 00 00 00 00 07 c1 16 16 04 0c 10 04
+ 39 01 00 00 00 00 03 c2 12 68
+ 39 01 00 00 00 00 04 c3 22 31 04
+ 39 01 00 00 00 00 06 c7 05 23 6b 41 00
+ 39 01 00 00 00 00 27 c8 7c 54 3d 2d 26 16 1b 08
+ 25 28 2d 4f 3e 48 3d 3d 35 25 06 7c 54 3d 2d
+ 26 16 1b 08 25 28 2d 4f 3e 48 3d 3d 35 25 06
+ 39 01 00 00 00 00 09 c6 00 00 68 00 00 60 36 00
+ 05 01 00 00 64 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+
+ qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00
+ 05 01 00 00 32 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 2>, <0 20>, <1 50>;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-pan-physical-width-dimension = <63>;
+ qcom,mdss-pan-physical-height-dimension = <112>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
index 2c54504..b2a541d 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,6 +37,7 @@
qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
qcom,mdss-pan-physical-width-dimension = <74>;
qcom,mdss-pan-physical-height-dimension = <131>;
+ qcom,mdss-dsi-dma-schedule-line = <5>;
qcom,mdss-dsi-display-timings {
timing@0{
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
index ab088b8..707875b 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,14 +27,14 @@
vdd-supply = <&gpu_cx_gdsc>;
interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 231 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 364 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 365 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 366 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 367 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 368 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 369 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 370 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
+ <GIC_SPI 364 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 365 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 366 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 367 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 368 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 369 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "gcc_gpu_memnoc_gfx_clk";
clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
attach-impl-defs =
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index e4fe2e3..0ac9c2a 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,14 +27,14 @@
vdd-supply = <&gpu_cx_gdsc>;
interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 231 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 364 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 365 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 366 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 367 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 368 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 369 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 370 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
+ <GIC_SPI 364 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 365 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 366 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 367 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 368 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 369 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "gcc_gpu_memnoc_gfx_clk";
clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
attach-impl-defs =
diff --git a/arch/arm64/boot/dts/qcom/msm-audio-lpass-msm8909.dtsi b/arch/arm64/boot/dts/qcom/msm-audio-lpass-msm8909.dtsi
new file mode 100644
index 0000000..fe9094f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm-audio-lpass-msm8909.dtsi
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ pcm0: qcom,msm-pcm {
+ compatible = "qcom,msm-pcm-dsp";
+ qcom,msm-pcm-dsp-id = <0>;
+ };
+
+ routing: qcom,msm-pcm-routing {
+ compatible = "qcom,msm-pcm-routing";
+ };
+
+ compr: qcom,msm-compr-dsp {
+ compatible = "qcom,msm-compr-dsp";
+ };
+
+ pcm1: qcom,msm-pcm-low-latency {
+ compatible = "qcom,msm-pcm-dsp";
+ qcom,msm-pcm-dsp-id = <1>;
+ qcom,msm-pcm-low-latency;
+ qcom,latency-level = "regular";
+ };
+
+ pcm2: qcom,msm-ultra-low-latency {
+ compatible = "qcom,msm-pcm-dsp";
+ qcom,msm-pcm-dsp-id = <2>;
+ qcom,msm-pcm-low-latency;
+ qcom,latency-level = "ultra";
+ };
+
+ compress: qcom,msm-compress-dsp {
+ compatible = "qcom,msm-compress-dsp";
+ };
+
+ voip: qcom,msm-voip-dsp {
+ compatible = "qcom,msm-voip-dsp";
+ };
+
+ voice: qcom,msm-pcm-voice {
+ compatible = "qcom,msm-pcm-voice";
+ qcom,destroy-cvd;
+ };
+
+ stub_codec: qcom,msm-stub-codec {
+ compatible = "qcom,msm-stub-codec";
+ };
+
+ qcom,msm-dai-fe {
+ compatible = "qcom,msm-dai-fe";
+ };
+
+ afe: qcom,msm-pcm-afe {
+ compatible = "qcom,msm-pcm-afe";
+ };
+
+ loopback: qcom,msm-pcm-loopback {
+ compatible = "qcom,msm-pcm-loopback";
+ };
+
+ lsm: qcom,msm-lsm-client {
+ compatible = "qcom,msm-lsm-client";
+ };
+
+ qcom,msm-dai-q6 {
+ compatible = "qcom,msm-dai-q6";
+ bt_sco_rx: qcom,msm-dai-q6-bt-sco-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12288>;
+ };
+
+ bt_sco_tx: qcom,msm-dai-q6-bt-sco-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12289>;
+ };
+
+ int_fm_rx: qcom,msm-dai-q6-int-fm-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12292>;
+ };
+
+ int_fm_tx: qcom,msm-dai-q6-int-fm-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <12293>;
+ };
+
+ afe_pcm_rx: qcom,msm-dai-q6-be-afe-pcm-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <224>;
+ };
+
+ afe_pcm_tx: qcom,msm-dai-q6-be-afe-pcm-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <225>;
+ };
+
+ afe_proxy_rx: qcom,msm-dai-q6-afe-proxy-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <241>;
+ };
+
+ afe_proxy_tx: qcom,msm-dai-q6-afe-proxy-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <240>;
+ };
+
+ incall_record_rx: qcom,msm-dai-q6-incall-record-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32771>;
+ };
+
+ incall_record_tx: qcom,msm-dai-q6-incall-record-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32772>;
+ };
+
+ incall_music_rx: qcom,msm-dai-q6-incall-music-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32773>;
+ };
+
+ incall_music_2_rx: qcom,msm-dai-q6-incall-music-2-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32770>;
+ };
+
+ usb_audio_rx: qcom,msm-dai-q6-usb-audio-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <28672>;
+ };
+
+ usb_audio_tx: qcom,msm-dai-q6-usb-audio-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <28673>;
+ };
+ };
+
+ hostless: qcom,msm-pcm-hostless {
+ compatible = "qcom,msm-pcm-hostless";
+ };
+
+};
diff --git a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
index 4606956..13e5187 100644
--- a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -302,6 +302,11 @@
compatible = "qcom,msm-pcm-hostless";
};
+ audio_apr: qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ qcom,subsys-name = "apr_adsp";
+ };
+
dai_pri_auxpcm: qcom,msm-pri-auxpcm {
compatible = "qcom,msm-auxpcm-dev";
qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm64/boot/dts/qcom/msm8909-audio-bg_codec.dtsi
index f2cea32..fa6498e 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-audio-bg_codec.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-audio-bg_codec.dtsi
@@ -9,8 +9,15 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+#include "msm-audio-lpass-msm8909.dtsi"
&soc {
+ qcom,msm-audio-apr {
+ compatible = "qcom,msm-audio-apr";
+ msm_audio_apr_dummy {
+ compatible = "qcom,msm-audio-apr-dummy";
+ };
+ };
audio_codec_bg: sound {
status = "disabled";
compatible = "qcom,msm-bg-audio-codec";
@@ -28,6 +35,8 @@
qcom,msm-ext-pa = "primary";
qcom,tdm-audio-intf;
qcom,msm-afe-clk-ver = <1>;
+ qcom,split-a2dp;
+ qcom,pri-mi2s-gpios = <&cdc_dmic_gpios>;
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
<&afe>, <&lsm>, <&routing>, <&lpa>,
@@ -69,7 +78,12 @@
asoc-codec = <&stub_codec>;
asoc-codec-names = "msm-stub-codec.1";
};
-
+ cdc_dmic_gpios: cdc_dmic_pinctrl {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>;
+ pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>;
+ };
pri_tdm_rx: qcom,msm-dai-tdm-pri-rx {
compatible = "qcom,msm-dai-tdm";
qcom,msm-cpudai-tdm-group-id = <37120>;
@@ -77,49 +91,32 @@
qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>;
qcom,msm-cpudai-tdm-clk-rate = <0>;
qcom,msm-cpudai-tdm-afe-ebit-unsupported;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-sec-port-enable;
qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
- pinctrl-names = "default", "sleep";
- pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>;
- pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>;
dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36864>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36866>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36868>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36870>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
};
@@ -131,49 +128,32 @@
qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>;
qcom,msm-cpudai-tdm-clk-rate = <0>;
qcom,msm-cpudai-tdm-afe-ebit-unsupported;
+ qcom,msm-cpudai-tdm-clk-internal = <0>;
+ qcom,msm-cpudai-tdm-sync-mode = <0>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-sec-port-enable;
qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
- pinctrl-names = "default", "sleep";
- pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>;
- pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>;
dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36865>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36867>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36869>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36871>;
- qcom,msm-cpudai-tdm-sync-mode = <0>;
- qcom,msm-cpudai-tdm-sync-src = <0>;
- qcom,msm-cpudai-tdm-data-out = <0>;
- qcom,msm-cpudai-tdm-invert-sync = <0>;
- qcom,msm-cpudai-tdm-data-delay = <0>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
};
@@ -187,6 +167,7 @@
status = "disabled";
compatible = "qcom,bg-codec";
qcom,subsys-name = "modem";
+ qcom,bg-speaker-connected;
qcom,bg-glink {
compatible = "qcom,bg-cdc-glink";
qcom,msm-glink-channels = <4>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909-coresight.dtsi b/arch/arm64/boot/dts/qcom/msm8909-coresight.dtsi
index 4e7d6f8..9d35b06 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-coresight.dtsi
@@ -1,413 +1,694 @@
-/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
+ * it under the terms of the GNU General Public License version 2 an
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
&soc {
tmc_etr: tmc@826000 {
- compatible = "arm,coresight-tmc";
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
reg = <0x826000 0x1000>,
<0x884000 0x15000>;
reg-names = "tmc-base", "bam-base";
+
interrupts = <0 166 0>;
interrupt-names = "byte-cntr-irq";
- qcom,memory-size = <0x100000>;
- qcom,sg-enable;
+ arm,buffer-size = <0x100000>;
+ arm,sg-enable;
- coresight-id = <0>;
coresight-name = "coresight-tmc-etr";
- coresight-nr-inports = <1>;
coresight-ctis = <&cti0 &cti8>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- tpiu: tpiu@820000 {
- compatible = "arm,coresight-tpiu";
- reg = <0x820000 0x1000>,
- <0x1100000 0xb0000>;
- reg-names = "tpiu-base", "nidnt-base";
-
- coresight-id = <1>;
- coresight-name = "coresight-tpiu";
- coresight-nr-inports = <1>;
-
- qcom,nidnthw;
- qcom,nidnt-swduart;
- qcom,nidnt-swdtrc;
- qcom,nidnt-jtag;
- qcom,nidnt-spmi;
- nidnt-gpio = <38>;
- nidnt-gpio-polarity = <1>;
-
- interrupts = <0 82 0>;
- interrupt-names = "nidnt-irq";
-
- vdd-supply = <&pm8909_l11>;
- qcom,vdd-voltage-level = <2950000 2950000>;
- qcom,vdd-current-level = <15000 400000>;
-
- vdd-io-supply = <&pm8909_l12>;
- qcom,vdd-io-voltage-level = <2950000 2950000>;
- qcom,vdd-io-current-level = <200 50000>;
+ coresight-csr = <&csr>;
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- replicator: replicator@824000 {
- compatible = "qcom,coresight-replicator";
- reg = <0x824000 0x1000>;
- reg-names = "replicator-base";
-
- coresight-id = <2>;
- coresight-name = "coresight-replicator";
- coresight-nr-inports = <1>;
- coresight-outports = <0 1>;
- coresight-child-list = <&tmc_etr &tpiu>;
- coresight-child-ports = <0 0>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+ port {
+ tmc_etr_in_replicator: endpoint {
+ slave-mode;
+ remote-endpoint = <&replicator_out_tmc_etr>;
+ };
+ };
};
tmc_etf: tmc@825000 {
- compatible = "arm,coresight-tmc";
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
reg = <0x825000 0x1000>;
reg-names = "tmc-base";
- coresight-id = <3>;
coresight-name = "coresight-tmc-etf";
- coresight-nr-inports = <1>;
- coresight-outports = <0>;
- coresight-child-list = <&replicator>;
- coresight-child-ports = <0>;
- coresight-default-sink;
+
+ arm,default-sink;
coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ tmc_etf_out_replicator:endpoint {
+ remote-endpoint =
+ <&replicator_in_tmc_etf>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tmc_etf_in_funnel_in0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_in0_out_tmc_etf>;
+ };
+ };
+ };
+ };
+
+ replicator: replicator@824000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b909>;
+
+ reg = <0x824000 0x1000>;
+ reg-names = "replicator-base";
+
+ coresight-name = "coresight-replicator";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ replicator_out_tmc_etr: endpoint {
+ remote-endpoint =
+ <&tmc_etr_in_replicator>;
+ };
+ };
+ port@1 {
+ reg = <0>;
+ replicator_in_tmc_etf: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tmc_etf_out_replicator>;
+ };
+ };
+ };
};
funnel_in0: funnel@821000 {
- compatible = "arm,coresight-funnel";
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
reg = <0x821000 0x1000>;
reg-names = "funnel-base";
- coresight-id = <4>;
coresight-name = "coresight-funnel-in0";
- coresight-nr-inports = <8>;
- coresight-outports = <0>;
- coresight-child-list = <&tmc_etf>;
- coresight-child-ports = <0>;
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ funnel_in0_out_tmc_etf: endpoint {
+ remote-endpoint =
+ <&tmc_etf_in_funnel_in0>;
+ };
+ };
+ port@1 {
+ reg = <7>;
+ funnel_in0_in_stm: endpoint {
+ slave-mode;
+ remote-endpoint = <&stm_out_funnel_in0>;
+ };
+ };
+
+ port@2 {
+ reg = <3>;
+ funnel_in0_in_funnel_center: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_center_out_funnel_in0>;
+ };
+ };
+
+ port@3 {
+ reg = <4>;
+ funnel_in0_in_funnel_right: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_right_out_funnel_in0>;
+ };
+ };
+
+ port@4 {
+
+ reg = <0>;
+ funnel_in0_in_rpm_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&rpm_etm0_out_funnel_center>;
+ };
+ };
+
+ port@5 {
+ reg = <1>;
+ funnel_in0_in_modem_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&modem_etm0_out_funnel_right>;
+ };
+ };
+ };
};
- funnel_in2: funnel@869000 {
- compatible = "arm,coresight-funnel";
+ funnel_center: funnel@869000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
reg = <0x869000 0x1000>;
reg-names = "funnel-base";
- coresight-id = <5>;
- coresight-name = "coresight-funnel-in2";
- coresight-nr-inports = <8>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in0>;
- coresight-child-ports = <6>;
+ coresight-name = "coresight-funnel-center";
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_center_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_center>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ funnel_center_in_dbgui: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&dbgui_out_funnel_center>;
+ };
+ };
+ };
};
- funnel_in3: funnel@868000 {
- compatible = "arm,coresight-funnel";
+ funnel_right: funnel@868000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
reg = <0x868000 0x1000>;
reg-names = "funnel-base";
- coresight-id = <6>;
- coresight-name = "coresight-funnel-in3";
- coresight-nr-inports = <2>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in2>;
- coresight-child-ports = <7>;
+ coresight-name = "coresight-funnel-right";
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_right_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_right>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ funnel_right_in_funnel_apss: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_apss_out_funnel_right>;
+ };
+ };
+
+ port@3 {
+ reg = <0>;
+ funnel_right_in_wcn_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&wcn_etm0_out_funnel_mm>;
+ };
+ };
+ };
};
- cti0: cti@810000 {
- compatible = "arm,coresight-cti";
- reg = <0x810000 0x1000>;
- reg-names = "cti-base";
+ funnel_apss: funnel@855000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
- coresight-id = <7>;
- coresight-name = "coresight-cti0";
- coresight-nr-inports = <0>;
+ reg = <0x855000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-apss";
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_apss_out_funnel_right: endpoint {
+ remote-endpoint =
+ <&funnel_right_in_funnel_apss>;
+ };
+ };
+
+ port@1 {
+ reg = <4>;
+ funnel_apss0_in_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm0_out_funnel_apss0>;
+ };
+ };
+
+ port@2 {
+ reg = <5>;
+ funnel_apss0_in_etm1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm1_out_funnel_apss0>;
+ };
+ };
+
+ port@3 {
+ reg = <6>;
+ funnel_apss0_in_etm2: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm2_out_funnel_apss0>;
+ };
+ };
+
+ port@4 {
+ reg = <7>;
+ funnel_apss0_in_etm3: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm3_out_funnel_apss0>;
+ };
+ };
+
+ };
+
};
- cti1: cti@811000 {
- compatible = "arm,coresight-cti";
- reg = <0x811000 0x1000>;
- reg-names = "cti-base";
+ tpiu: tpiu@820000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b912>;
+ coresight-name = "coresight-tpiu";
- coresight-id = <8>;
- coresight-name = "coresight-cti1";
- coresight-nr-inports = <0>;
+ reg = <0x820000 0x1000>,
+ <0x1100000 0xb0000>;
+ reg-names = "tpiu-base", "nidnt-base";
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
};
- cti2: cti@812000 {
- compatible = "arm,coresight-cti";
- reg = <0x812000 0x1000>;
- reg-names = "cti-base";
+ etm0: etm@84c000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b955>;
- coresight-id = <9>;
- coresight-name = "coresight-cti2";
- coresight-nr-inports = <0>;
+ reg = <0x84c000 0x1000>;
+ cpu = <&CPU0>;
+ coresight-name = "coresight-etm0";
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ port {
+ etm0_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm0>;
+ };
+ };
};
- cti3: cti@813000 {
- compatible = "arm,coresight-cti";
- reg = <0x813000 0x1000>;
- reg-names = "cti-base";
+ etm1: etm@84d000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b955>;
- coresight-id = <10>;
- coresight-name = "coresight-cti3";
- coresight-nr-inports = <0>;
+ reg = <0x84d000 0x1000>;
+ cpu = <&CPU1>;
+ coresight-name = "coresight-etm1";
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ port {
+ etm1_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm1>;
+ };
+ };
};
- cti4: cti@814000 {
- compatible = "arm,coresight-cti";
- reg = <0x814000 0x1000>;
- reg-names = "cti-base";
+ etm2: etm@84e000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b955>;
- coresight-id = <11>;
- coresight-name = "coresight-cti4";
- coresight-nr-inports = <0>;
+ reg = <0x84e000 0x1000>;
+ cpu = <&CPU2>;
+ coresight-name = "coresight-etm2";
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ port {
+ etm2_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm2>;
+ };
+ };
};
- cti5: cti@815000 {
- compatible = "arm,coresight-cti";
- reg = <0x815000 0x1000>;
- reg-names = "cti-base";
+ etm3: etm@84f000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b955>;
- coresight-id = <12>;
- coresight-name = "coresight-cti5";
- coresight-nr-inports = <0>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti6: cti@816000 {
- compatible = "arm,coresight-cti";
- reg = <0x816000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <13>;
- coresight-name = "coresight-cti6";
- coresight-nr-inports = <0>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
-
- qcom,cti-gpio-trigout = <2>;
- pinctrl-names = "cti-trigout-pctrl";
- pinctrl-0 = <&trigout_a0>;
- };
-
- cti7: cti@817000 {
- compatible = "arm,coresight-cti";
- reg = <0x817000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <14>;
- coresight-name = "coresight-cti7";
- coresight-nr-inports = <0>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti8: cti@818000 {
- compatible = "arm,coresight-cti";
- reg = <0x818000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <15>;
- coresight-name = "coresight-cti8";
- coresight-nr-inports = <0>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_cpu0: cti@851000 {
- compatible = "arm,coresight-cti";
- reg = <0x851000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <16>;
- coresight-name = "coresight-cti-cpu0";
- coresight-nr-inports = <0>;
- coresight-cti-cpu = <&CPU0>;
-
- qcom,cti-save;
+ reg = <0x84f000 0x1000>;
+ coresight-name = "coresight-etm3";
+ cpu = <&CPU3>;
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
+ clock-names = "apb_pclk";
- cti_cpu1: cti@852000 {
- compatible = "arm,coresight-cti";
- reg = <0x852000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <17>;
- coresight-name = "coresight-cti-cpu1";
- coresight-nr-inports = <0>;
- coresight-cti-cpu = <&CPU1>;
-
- qcom,cti-save;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_cpu2: cti@853000 {
- compatible = "arm,coresight-cti";
- reg = <0x853000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <18>;
- coresight-name = "coresight-cti-cpu2";
- coresight-nr-inports = <0>;
- coresight-cti-cpu = <&CPU2>;
-
- qcom,cti-save;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_cpu3: cti@854000 {
- compatible = "arm,coresight-cti";
- reg = <0x854000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <19>;
- coresight-name = "coresight-cti-cpu3";
- coresight-nr-inports = <0>;
- coresight-cti-cpu = <&CPU3>;
-
- qcom,cti-save;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_rpm_cpu0: cti@83c000 {
- compatible = "arm,coresight-cti";
- reg = <0x83c000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <20>;
- coresight-name = "coresight-cti-rpm-cpu0";
- coresight-nr-inports = <0>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_modem_cpu0: cti@838000 {
- compatible = "arm,coresight-cti";
- reg = <0x838000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <21>;
- coresight-name = "coresight-cti-modem-cpu0";
- coresight-nr-inports = <0>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_wcn_cpu0: cti@835000 {
- compatible = "arm,coresight-cti";
- reg = <0x835000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <22>;
- coresight-name = "coresight-cti-wcn-cpu0";
- coresight-nr-inports = <0>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_video_cpu0: cti@830000 {
- compatible = "arm,coresight-cti";
- reg = <0x830000 0x1000>;
- reg-names = "cti-base";
-
- coresight-id = <23>;
- coresight-name = "coresight-cti-video-cpu0";
- coresight-nr-inports = <0>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ port {
+ etm3_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm3>;
+ };
+ };
};
stm: stm@802000 {
- compatible = "arm,coresight-stm";
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b962>;
+
reg = <0x802000 0x1000>,
<0x9280000 0x180000>;
- reg-names = "stm-base", "stm-data-base";
+ reg-names = "stm-base", "stm-stimulus-base";
- coresight-id = <24>;
coresight-name = "coresight-stm";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in0>;
- coresight-child-ports = <7>;
+
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ port {
+ stm_out_funnel_in0: endpoint {
+ remote-endpoint = <&funnel_in0_in_stm>;
+ };
+ };
+ };
+
+ cti0: cti@810000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x810000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti0";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti1: cti@811000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x811000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti1";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti2: cti@812000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x812000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti2";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti3: cti@813000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x813000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti3";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti4: cti@814000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x814000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti4";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti5: cti@815000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x815000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti5";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti6: cti@816000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x816000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti6";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti7: cti@817000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x817000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti7";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti8: cti@818000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x818000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti8";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu0: cti@851000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x851000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu0";
+ cpu = <&CPU0>;
+ qcom,cti-save;
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu1: cti@852000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x852000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu1";
+ cpu = <&CPU1>;
+ qcom,cti-save;
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu2: cti@853000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x853000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu2";
+ cpu = <&CPU2>;
+ qcom,cti-save;
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu3: cti@854000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x854000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu3";
+ cpu = <&CPU3>;
+ qcom,cti-save;
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_modem_cpu0: cti@838000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x838000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-modem-cpu0";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* Venus CTI */
+ cti_video_cpu0: cti@830000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x830000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-video-cpu0";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* Pronto CTI */
+ cti_wcn_cpu0: cti@835000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x835000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-wcn-cpu0";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ wcn_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-wcn-etm0";
+ qcom,inst-id = <3>;
+
+ port {
+ wcn_etm0_out_funnel_mm: endpoint {
+ remote-endpoint = <&funnel_right_in_wcn_etm0>;
+ };
+ };
+ };
+
+ /* MSS_SCL */
+ modem_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-modem-etm0";
+ qcom,inst-id = <11>;
+
+ port {
+ modem_etm0_out_funnel_right: endpoint {
+ remote-endpoint = <&funnel_in0_in_modem_etm0>;
+ };
+ };
+ };
+
+ rpm_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-rpm-etm0";
+ qcom,inst-id = <4>;
+
+ port {
+ rpm_etm0_out_funnel_center: endpoint {
+ remote-endpoint = <&funnel_in0_in_rpm_etm0>;
+ };
+ };
};
csr: csr@801000 {
@@ -415,209 +696,52 @@
reg = <0x801000 0x1000>;
reg-names = "csr-base";
- coresight-id = <25>;
coresight-name = "coresight-csr";
- coresight-nr-inports = <0>;
+ qcom,usb-bam-support;
+ qcom,hwctrl-set-support;
+ qcom,set-byte-cntr-support;
qcom,blk-size = <1>;
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- funnel_apss: funnel@855000 {
- compatible = "arm,coresight-funnel";
- reg = <0x855000 0x1000>;
- reg-names = "funnel-base";
-
- coresight-id = <26>;
- coresight-name = "coresight-funnel-apss";
- coresight-nr-inports = <4>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in0>;
- coresight-child-ports = <4>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- etm0: etm@84c000 {
- compatible = "arm,coresight-etm";
- reg = <0x84c000 0x1000>;
- reg-names = "etm-base";
-
- coresight-id = <27>;
- coresight-name = "coresight-etm0";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_apss>;
- coresight-child-ports = <0>;
- coresight-etm-cpu = <&CPU0>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- etm1: etm@84d000 {
- compatible = "arm,coresight-etm";
- reg = <0x84d000 0x1000>;
- reg-names = "etm-base";
-
- coresight-id = <28>;
- coresight-name = "coresight-etm1";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_apss>;
- coresight-child-ports = <1>;
- coresight-etm-cpu = <&CPU1>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- etm2: etm@84e000 {
- compatible = "arm,coresight-etm";
- reg = <0x84e000 0x1000>;
- reg-names = "etm-base";
-
- coresight-id = <29>;
- coresight-name = "coresight-etm2";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_apss>;
- coresight-child-ports = <2>;
- coresight-etm-cpu = <&CPU2>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- etm3: etm@84f000 {
- compatible = "arm,coresight-etm";
- reg = <0x84f000 0x1000>;
- reg-names = "etm-base";
-
- coresight-id = <30>;
- coresight-name = "coresight-etm3";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_apss>;
- coresight-child-ports = <3>;
- coresight-etm-cpu = <&CPU3>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- hwevent: hwevent@86c000 {
- compatible = "qcom,coresight-hwevent";
- reg = <0x86c000 0x108>,
- <0x86cfb0 0x4>,
- <0x78c5010 0x4>,
- <0x7885010 0x4>;
- reg-names = "wrapper-mux", "wrapper-lockaccess", "usbbam-mux",
- "blsp-mux";
- coresight-id = <31>;
- coresight-name = "coresight-hwevent";
- coresight-nr-inports = <0>;
-
- clocks = <&clock_rpm clk_qdss_clk>,
- <&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- rpm_etm0 {
- compatible = "qcom,coresight-remote-etm";
-
- coresight-id = <32>;
- coresight-name = "coresight-rpm-etm0";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in0>;
- coresight-child-ports = <0>;
-
- qcom,inst-id = <4>;
- };
-
- wcn_etm0 {
- compatible = "qcom,coresight-remote-etm";
-
- coresight-id = <33>;
- coresight-name = "coresight-wcn-etm0";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in3>;
- coresight-child-ports = <0>;
-
- qcom,inst-id = <3>;
- };
-
- modem_etm0 {
- compatible = "qcom,coresight-remote-etm";
-
- coresight-id = <34>;
- coresight-name = "coresight-modem-etm0";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in0>;
- coresight-child-ports = <2>;
-
- qcom,inst-id = <2>;
- };
-
- fuse: fuse@5e01c {
- compatible = "arm,coresight-fuse-v2";
- reg = <0x5e01c 0x8>,
- <0x58040 0x4>,
- <0x5e00c 0x4>;
- reg-names = "fuse-base", "nidnt-fuse-base", "qpdi-fuse-base";
-
- coresight-id = <35>;
- coresight-name = "coresight-fuse";
- coresight-nr-inports = <0>;
- };
-
- qpdi: qpdi@1941000 {
- compatible = "qcom,coresight-qpdi";
- reg = <0x1941000 0x4>;
- reg-names = "qpdi-base";
-
- coresight-id = <36>;
- coresight-name = "coresight-qpdi";
- coresight-nr-inports = <0>;
-
- vdd-supply = <&pm8909_l11>;
- qcom,vdd-voltage-level = <2800000 2950000>;
- qcom,vdd-current-level = <15000 400000>;
-
- vdd-io-supply = <&pm8909_l12>;
- qcom,vdd-io-voltage-level = <1800000 2950000>;
- qcom,vdd-io-current-level = <200 50000>;
};
dbgui: dbgui@86d000 {
compatible = "qcom,coresight-dbgui";
reg = <0x86d000 0x1000>;
reg-names = "dbgui-base";
-
- coresight-id = <37>;
coresight-name = "coresight-dbgui";
- coresight-nr-inports = <0>;
- coresight-outports = <0>;
- coresight-child-list = <&funnel_in2>;
- coresight-child-ports = <2>;
qcom,dbgui-addr-offset = <0x30>;
- qcom,dbgui-data-offset = <0xB0>;
- qcom,dbgui-size = <32>;
+ qcom,dbgui-data-offset = <0x130>;
+ qcom,dbgui-size = <64>;
clocks = <&clock_rpm clk_qdss_clk>,
<&clock_rpm clk_qdss_a_clk>;
- clock-names = "core_clk", "core_a_clk";
+ clock-names = "apb_pclk";
+
+ port {
+ dbgui_out_funnel_center: endpoint {
+ remote-endpoint = <&funnel_center_in_dbgui>;
+ };
+ };
};
+
+ hwevent: hwevent@86c000 {
+ compatible = "qcom,coresight-hwevent";
+
+ reg = <0x86c000 0x108>,
+ <0x86cfb0 0x4>,
+ <0x78c5010 0x4>,
+ <0x7885010 0x4>;
+
+ reg-names = "center-wrapper-mux", "center-wrapper-lockaccess",
+ "right-wrapper-mux", "right-wrapper-lockaccess";
+
+ coresight-csr = <&csr>;
+ coresight-name = "coresight-hwevent";
+
+ clocks = <&clock_rpm clk_qdss_clk>,
+ <&clock_rpm clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-gpu.dtsi b/arch/arm64/boot/dts/qcom/msm8909-gpu.dtsi
index dc95570..180d6c3 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-gpu.dtsi
@@ -19,26 +19,15 @@
/* To use BIMC based bus governor */
gpubw: qcom,gpubw {
compatible = "qcom,devbw";
- governor = "bw_hwmon";
+ governor = "bw_vbif";
qcom,src-dst-ports = <26 512>;
qcom,bw-tbl =
< 0 >, /* 9.6 MHz */
- < 381 >, /* 50.0 MHz */
- < 762 >, /* 100.0 MHz */
< 1525 >, /* 200.0 MHz */
< 3051 >, /* 400.0 MHz */
< 4066 >; /* 533.0 MHz */
};
- qcom,gpu-bwmon@410000 {
- compatible = "qcom,bimc-bwmon2";
- reg = <0x00410000 0x300>, <0x00401000 0x200>;
- reg-names = "base", "global_base";
- interrupts = <0 183 4>;
- qcom,mport = <2>;
- qcom,target-dev = <&gpubw>;
- };
-
msm_gpu: qcom,kgsl-3d0@01c00000 {
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
@@ -103,24 +92,32 @@
reg = <0>;
qcom,gpu-freq = <456000000>;
qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <3>;
};
qcom,gpu-pwrlevel@1 {
reg = <1>;
qcom,gpu-freq = <307200000>;
qcom,bus-freq = <2>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <3>;
};
qcom,gpu-pwrlevel@2 {
reg = <2>;
qcom,gpu-freq = <200000000>;
- qcom,bus-freq = <1>;
+ qcom,bus-freq = <2>;
+ qcom,bus-min = <1>;
+ qcom,bus-max = <2>;
};
qcom,gpu-pwrlevel@3 {
reg = <3>;
qcom,gpu-freq = <19200000>;
qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
index 858429b..cd21e6e 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
@@ -33,12 +33,6 @@
qcom,ion-heap-type = "DMA";
};
- modem_heap: qcom,ion-heap@26 { /* MODEM HEAP */
- reg = <26>;
- memory-region = <&modem_adsp_mem>;
- qcom,ion-heap-type = "DMA";
- };
-
qcom,ion-heap@19 { /* QSEECOM TA HEAP */
reg = <19>;
memory-region = <&qseecom_ta_mem>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8909-mdss-panels.dtsi
new file mode 100644
index 0000000..0a657da
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8909-mdss-panels.dtsi
@@ -0,0 +1,75 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "dsi-panel-390p-auo-cmd.dtsi"
+
+&soc {
+ dsi_panel_pwr_supply: dsi_panel_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <2850000>;
+ qcom,supply-max-voltage = <2850000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+
+ dsi_pm660_panel_pwr_supply: dsi_pm660_panel_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <3000000>;
+ qcom,supply-max-voltage = <3000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "bklt";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+};
+
+&dsi_auo_390p_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi b/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi
index 0d824e0..67c6d06 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-mdss.dtsi
@@ -38,9 +38,11 @@
smmu_mdp_unsec: qcom,smmu_mdp_unsec_cb {
compatible = "qcom,smmu_mdp_unsec";
+ iommus = <&apps_iommu 0xC00 0>; /* For NS ctx bank */
};
smmu_mdp_sec: qcom,smmu_mdp_sec_cb {
compatible = "qcom,smmu_mdp_sec";
+ iommus = <&apps_iommu 0xC01 0>; /* For SEC Ctx Bank */
};
};
@@ -51,8 +53,8 @@
#size-cells = <1>;
qcom,mdss-fb-map-prim = <&mdss_fb0>;
gdsc-supply = <&gdsc_mdss>;
- vdda-supply = <&pm8909_l2>;
- vddio-supply = <&pm8909_l6>;
+ vdda-supply = <&pm660_l2>;
+ vddio-supply = <&pm660_l6>;
/* Bus Scale Settings */
qcom,msm-bus,name = "mdss_dsi";
@@ -133,8 +135,8 @@
interrupts = <0 80 0>;
qcom,mdss-mdp = <&mdss_mdp>;
- vdd-supply = <&pm8909_l17>;
- vddio-supply = <&pm8909_l6>;
+ vdd-supply = <&pm660_l17>;
+ vddio-supply = <&pm660_l6>;
clocks = <&clock_gcc_mdss clk_gcc_mdss_byte0_clk>,
<&clock_gcc_mdss clk_gcc_mdss_pclk0_clk>,
@@ -157,4 +159,4 @@
};
};
-/* #include "msm8909-mdss-panels.dtsi" */
+#include "msm8909-mdss-panels.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/msm8909-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8909-mtp.dtsi
index 18fc575..e2fbc9e 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-mtp.dtsi
@@ -410,27 +410,3 @@
};
};
};
-
-/* CoreSight */
-&tpiu {
- pinctrl-names = "sdcard", "trace", "swduart",
- "swdtrc", "jtag", "spmi";
- /* NIDnT */
- pinctrl-0 = <&qdsd_clk_sdcard &qdsd_cmd_sdcard
- &qdsd_data0_sdcard &qdsd_data1_sdcard
- &qdsd_data2_sdcard &qdsd_data3_sdcard>;
- pinctrl-1 = <&qdsd_clk_trace &qdsd_cmd_trace
- &qdsd_data0_trace &qdsd_data1_trace
- &qdsd_data2_trace &qdsd_data3_trace>;
- pinctrl-2 = <&qdsd_cmd_swduart &qdsd_data0_swduart
- &qdsd_data1_swduart &qdsd_data2_swduart
- &qdsd_data3_swduart>;
- pinctrl-3 = <&qdsd_clk_swdtrc &qdsd_cmd_swdtrc
- &qdsd_data0_swdtrc &qdsd_data1_swdtrc
- &qdsd_data2_swdtrc &qdsd_data3_swdtrc>;
- pinctrl-4 = <&qdsd_cmd_jtag &qdsd_data0_jtag
- &qdsd_data1_jtag &qdsd_data2_jtag
- &qdsd_data3_jtag>;
- pinctrl-5 = <&qdsd_clk_spmi &qdsd_cmd_spmi
- &qdsd_data0_spmi &qdsd_data3_spmi>;
-};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi
index 25688ff..eda92c5 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-pinctrl.dtsi
@@ -15,9 +15,11 @@
compatible = "qcom,msm8909-pinctrl";
reg = <0x1000000 0x300000>;
interrupts = <0 208 0>;
+ interrupts-extended = <&wakegic GIC_SPI 208 IRQ_TYPE_NONE>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
+ interrupt-parent = <&wakegpio>;
#interrupt-cells = <2>;
/* sensors */
@@ -1888,6 +1890,34 @@
};
};
+ cdc_reset_ctrl {
+ cdc_reset_sleep: cdc_reset_sleep {
+ mux {
+ pins = "gpio27";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio27";
+ drive-strength = <16>;
+ bias-disable;
+ output-low;
+ };
+ };
+ cdc_reset_active:cdc_reset_active {
+ mux {
+ pins = "gpio27";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio27"; /* gpio67 old */
+ drive-strength = <16>;
+ bias-pull-down;
+ output-high;
+ };
+ };
+ };
+
+
cdc-dmic-lines {
cdc_dmic0_clk_act: dmic0_clk_on {
mux {
diff --git a/arch/arm64/boot/dts/qcom/msm8909-pm8916-camera-sensor-robot.dtsi b/arch/arm64/boot/dts/qcom/msm8909-pm8916-camera-sensor-robot.dtsi
new file mode 100644
index 0000000..6f6655a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8909-pm8916-camera-sensor-robot.dtsi
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&i2c_3 {
+ status = "ok";
+};
+
+&i2c_3 {
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x2>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ cam_vdig-supply = <&pm8916_l2>;
+ cam_vana-supply = <&pm8916_l17>;
+ cam_vio-supply = <&pm8916_l6>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1200000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep>;
+ gpios = <&msm_gpio 26 0>,
+ <&msm_gpio 35 0>,
+ <&msm_gpio 34 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ cam_vana-supply = <&pm8916_l17>;
+ cam_vio-supply = <&pm8916_l6>;
+ qcom,cam-vreg-name = "cam_vio","cam_vana";
+ qcom,cam-vreg-min-voltage = <1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1800000 2850000>;
+ qcom,cam-vreg-op-mode = <0 80000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep>;
+ gpios = <&msm_gpio 26 0>,
+ <&msm_gpio 35 0>,
+ <&msm_gpio 34 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-pm8916-camera.dtsi b/arch/arm64/boot/dts/qcom/msm8909-pm8916-camera.dtsi
new file mode 100644
index 0000000..0b648ec
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8909-pm8916-camera.dtsi
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,msm-cam@1800000{
+ compatible = "qcom,msm-cam";
+ reg = <0x1b00000 0x40000>;
+ reg-names = "msm-cam";
+ status = "ok";
+ bus-vectors = "suspend", "svs", "nominal", "turbo";
+ qcom,bus-votes = <0 320000000 640000000 640000000>;
+ };
+
+ qcom,csiphy@1b0ac00 {
+ cell-index = <0>;
+ compatible = "qcom,csiphy-v3.1", "qcom,csiphy";
+ reg = <0x1b0ac00 0x200>,
+ <0x1b00030 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
+ interrupts = <0 78 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_csi0phytimer_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0phytimer_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0phy_clk>,
+ <&clock_gcc clk_gcc_camss_csi1phy_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csiphy_timer_src_clk", "csiphy_timer_clk",
+ "camss_ahb_src", "csi0_phy_clk", "csi1_phy_clk",
+ "camss_ahb_clk";
+ qcom,clock-rates = <0 0 200000000 0 0 0 0 0>;
+ };
+
+ qcom,csid@1b08000 {
+ cell-index = <0>;
+ compatible = "qcom,csid-v3.1", "qcom,csid";
+ reg = <0x1b08000 0x100>;
+ reg-names = "csid";
+ interrupts = <0 49 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8916_l2>;
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi0_ahb_clk>,
+ <&clock_gcc clk_csi0_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0_clk>,
+ <&clock_gcc clk_gcc_camss_csi0pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi0rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "ispif_ahb_clk", "camss_top_ahb_clk",
+ "csi_ahb_clk", "csi_src_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <40000000 0 0 200000000 0 0 0 0>;
+ };
+
+ qcom,csid@1b08400 {
+ cell-index = <1>;
+ compatible = "qcom,csid-v3.1", "qcom,csid";
+ reg = <0x1b08400 0x100>;
+ reg-names = "csid";
+ interrupts = <0 50 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8916_l2>;
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi1_ahb_clk>,
+ <&clock_gcc clk_csi1_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1_clk>,
+ <&clock_gcc clk_gcc_camss_csi1pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi1rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi1phy_clk>;
+ clock-names = "ispif_ahb_clk", "camss_top_ahb_clk",
+ "csi_ahb_clk", "csi_src_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk", "camss_csi1_phy";
+ qcom,clock-rates = <40000000 0 0 200000000 0 0 0 0 0>;
+ };
+
+ qcom,ispif@1b0a000 {
+ cell-index = <0>;
+ compatible = "qcom,ispif";
+ reg = <0x1b0a000 0x500>,
+ <0x1b00020 0x10>;
+ reg-names = "ispif", "csi_clk_mux";
+ interrupts = <0 51 0>;
+ interrupt-names = "ispif";
+ qcom,num-isps = <0x1>;
+ vfe0_vdd_supply = <&gdsc_vfe>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+
+ <&clock_gcc clk_csi0_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0_clk>,
+ <&clock_gcc clk_gcc_camss_csi0rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi0pix_clk>,
+ <&clock_gcc clk_csi1_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1_clk>,
+ <&clock_gcc clk_gcc_camss_csi1rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi1pix_clk>,
+ <&clock_gcc clk_vfe0_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>;
+
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csi0_src_clk", "csi0_clk",
+ "csi0_rdi_clk", "csi0_pix_clk",
+ "csi1_src_clk", "csi1_clk",
+ "csi1_rdi_clk", "csi1_pix_clk",
+ "vfe0_clk_src", "camss_vfe_vfe0_clk",
+ "camss_csi_vfe0_clk";
+ qcom,clock-rates = <0 40000000
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 0 0 0>;
+ qcom,clock-control = "NO_SET_RATE", "SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE", "NO_SET_RATE", "NO_SET_RATE";
+ };
+
+ qcom,vfe@1b10000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe32";
+ reg = <0x1b10000 0x830>,
+ <0x1b40000 0x200>;
+ reg-names = "vfe", "vfe_vbif";
+ interrupts = <0 52 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe>;
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_vfe0_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_axi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "vfe_clk_src",
+ "camss_vfe_vfe_clk", "camss_csi_vfe_clk", "iface_clk",
+ "bus_clk", "camss_ahb_clk", "ispif_ahb_clk";
+ qcom,clock-rates = <40000000 266670000 0 0 0 0 0 0>;
+
+ qos-entries = <8>;
+ qos-regs = <0x7BC 0x7C0 0x7C4 0x7C8 0x7CC 0x7D0
+ 0x7D4 0x798>;
+ qos-settings = <0xAAA5AAA5 0xAAA5AAA5 0xAAA5AAA5
+ 0xAAA5AAA5 0xAAA5AAA5 0xAAA5AAA5
+ 0xAAA5AAA5 0x00010000>;
+ vbif-entries = <1>;
+ vbif-regs = <0x04>;
+ vbif-settings = <0x1>;
+ ds-entries = <15>;
+ ds-regs = <0x7D8 0x7DC 0x7E0 0x7E4 0x7E8
+ 0x7EC 0x7F0 0x7F4 0x7F8 0x7FC 0x800
+ 0x804 0x808 0x80C 0x810>;
+ ds-settings = <0xCCCC1111 0xCCCC1111 0xCCCC1111
+ 0xCCCC1111 0xCCCC1111 0xCCCC1111
+ 0xCCCC1111 0xCCCC1111 0xCCCC1111
+ 0xCCCC1111 0xCCCC1111 0xCCCC1111
+ 0xCCCC1111 0xCCCC1111 0x00000103>;
+
+ bus-util-factor = <1024>;
+ };
+
+ qcom,cam_smmu {
+ status = "ok";
+ compatible = "qcom,msm-cam-smmu";
+ msm_cam_smmu_cb1: msm_cam_smmu_cb1 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x400 0x00>;
+ label = "vfe";
+ qcom,scratch-buf-support;
+ };
+ };
+
+ qcom,irqrouter@1b00000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,irqrouter";
+ reg = <0x1b00000 0x100>;
+ reg-names = "irqrouter";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8909-vidc.dtsi
new file mode 100644
index 0000000..5eeaa21
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8909-vidc.dtsi
@@ -0,0 +1,163 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,vidc@1d00000 {
+ compatible = "qcom,msm-vidc";
+ reg = <0x01d00000 0xff000>;
+ interrupts = <0 44 0>;
+ qcom,hfi-version = "3xx";
+ venus-supply = <&gdsc_venus>;
+ venus-core0-supply = <&gdsc_venus_core0>;
+ clocks = <&clock_gcc clk_gcc_venus0_vcodec0_clk>,
+ <&clock_gcc clk_gcc_venus0_core0_vcodec0_clk>,
+ <&clock_gcc clk_gcc_venus0_ahb_clk>,
+ <&clock_gcc clk_gcc_venus0_axi_clk>;
+ clock-names = "core_clk", "core0_clk", "iface_clk", "bus_clk";
+ qcom,clock-configs = <0x1 0x0 0x0 0x0>;
+ qcom,sw-power-collapse;
+ qcom,slave-side-cp;
+ qcom,hfi = "venus";
+ qcom,reg-presets = <0xe0020 0x05555556>,
+ <0xe0024 0x05555556>,
+ <0x80124 0x00000003>;
+ qcom,qdss-presets = <0x826000 0x1000>,
+ <0x827000 0x1000>,
+ <0x822000 0x1000>,
+ <0x803000 0x1000>,
+ <0x9180000 0x1000>,
+ <0x9181000 0x1000>;
+ qcom,max-hw-load = <244800>; /* 1080p@30 + 720p@30 */
+ qcom,firmware-name = "venus";
+ qcom,allowed-clock-rates = <307200000 266670000 133330000>;
+ qcom,clock-freq-tbl {
+ qcom,profile-enc {
+ qcom,codec-mask = <0x55555555>;
+ qcom,cycles-per-mb = <2316>;
+ qcom,low-power-mode-factor = <32768>;
+ };
+ qcom,profile-dec {
+ qcom,codec-mask = <0xf3ffffff>;
+ qcom,cycles-per-mb = <788>;
+ };
+ qcom,profile-hevcdec {
+ qcom,codec-mask = <0x0c000000>;
+ qcom,cycles-per-mb = <1015>;
+ };
+ };
+
+ /* MMUs */
+ non_secure_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_ns";
+ iommus = <&apps_iommu 0x800 0x00>,
+ <&apps_iommu 0x807 0x00>,
+ <&apps_iommu 0x808 0x27>,
+ <&apps_iommu 0x811 0x0>;
+ buffer-types = <0xfff>;
+ virtual-addr-pool = <0x5dc00000 0x8f000000>;
+ };
+
+ secure_bitstream_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_bitstream";
+ iommus = <&apps_iommu 0x90c 0x20>;
+ buffer-types = <0x241>;
+ virtual-addr-pool = <0x4b000000 0x12c00000>;
+ qcom,secure-context-bank;
+ };
+
+ secure_pixel_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_pixel";
+ iommus = <&apps_iommu 0x900 0x00>,
+ <&apps_iommu 0x909 0x20>,
+ <&apps_iommu 0x90a 0x00>,
+ <&apps_iommu 0x90b 0x20>,
+ <&apps_iommu 0x90e 0x00>;
+ buffer-types = <0x106>;
+ virtual-addr-pool = <0x25800000 0x25800000>;
+ qcom,secure-context-bank;
+ };
+
+ secure_non_pixel_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_non_pixel";
+ iommus = <&apps_iommu 0x9c0 0x00>,
+ <&apps_iommu 0x907 0x00>,
+ <&apps_iommu 0x908 0x20>,
+ <&apps_iommu 0x90d 0x20>,
+ <&apps_iommu 0x90f 0x00>;
+ buffer-types = <0x480>;
+ virtual-addr-pool = <0x1000000 0x24800000>;
+ qcom,secure-context-bank;
+ };
+
+ /* Buses */
+ venus_bus_ddr {
+ compatible = "qcom,msm-vidc,bus";
+ label = "venus-ddr";
+ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+ qcom,bus-governor = "venus-ddr-gov";
+ qcom,bus-range-kbps = <1000 917000>;
+ };
+
+ arm9_bus_ddr {
+ compatible = "qcom,msm-vidc,bus";
+ label = "venus-arm9-ddr";
+ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+ qcom,bus-governor = "performance";
+ qcom,bus-range-kbps = <1 1>;
+ };
+ };
+
+ venus-ddr-gov {
+ compatible = "qcom,msm-vidc,governor,table";
+ name = "venus-ddr-gov";
+ status = "ok";
+ qcom,bus-freq-table {
+ qcom,profile-enc {
+ qcom,codec-mask = <0x55555555>;
+ qcom,load-busfreq-tbl =
+ <244800 841000>, /* 1080p30E */
+ <216000 740000>, /* 720p60E */
+ <194400 680000>, /* FWVGA120E */
+ <144000 496000>, /* VGA120E */
+ <108000 370000>, /* 720p30E */
+ <97200 340000>, /* FWVGA60E */
+ <48600 170000>, /* FWVGA30E */
+ <72000 248000>, /* VGA60E */
+ <36000 124000>, /* VGA30E */
+ <18000 70000>, /* QVGA60E */
+ <9000 35000>, /* QVGA30E */
+ <0 0>;
+ };
+ qcom,profile-dec {
+ qcom,codec-mask = <0xffffffff>;
+ qcom,load-busfreq-tbl =
+ <244800 605000>, /* 1080p30D */
+ <216000 540000>, /* 720p60D */
+ <194400 484000>, /* FWVGA120D */
+ <144000 360000>, /* VGA120D */
+ <108000 270000>, /* 720p30D */
+ <97200 242000>, /* FWVGA60D */
+ <48600 121000>, /* FWVGA30D */
+ <72000 180000>, /* VGA60D */
+ <36000 90000>, /* VGA30D */
+ <18000 45000>, /* HVGA30D */
+ <0 0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909.dtsi b/arch/arm64/boot/dts/qcom/msm8909.dtsi
index eed3623..32735efe 100644
--- a/arch/arm64/boot/dts/qcom/msm8909.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909.dtsi
@@ -21,7 +21,7 @@
<258 0>,
<265 0>,
<275 0>;
- interrupt-parent = <&intc>;
+ interrupt-parent = <&wakegic>;
aliases {
/* smdtty devices */
@@ -77,7 +77,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu0_slp_sts>;
- qcom,limits-info = <&mitigation_profile0>;
};
CPU1: cpu@1 {
@@ -87,7 +86,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu1_slp_sts>;
- qcom,limits-info = <&mitigation_profile2>;
};
CPU2: cpu@2 {
@@ -97,7 +95,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu2_slp_sts>;
- qcom,limits-info = <&mitigation_profile1>;
};
CPU3: cpu@3 {
@@ -107,7 +104,6 @@
efficiency = <1024>;
sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
qcom,sleep-status = <&cpu3_slp_sts>;
- qcom,limits-info = <&mitigation_profile2>;
};
};
@@ -239,6 +235,7 @@
#include "msm8909-gpu.dtsi"
#include "msm8909-coresight.dtsi"
#include "msm8909-bus.dtsi"
+#include "msm8909-vidc.dtsi"
#include "msm8909-mdss.dtsi"
#include "msm8909-mdss-pll.dtsi"
@@ -251,11 +248,30 @@
intc: interrupt-controller@b000000 {
compatible = "qcom,msm-qgic2";
interrupt-controller;
+ interrupt-parent = <&intc>;
#interrupt-cells = <3>;
reg = <0x0b000000 0x1000>,
<0x0b002000 0x1000>;
};
+ wakegic: wake-gic@601d0 {
+ compatible = "qcom,mpm-gic-msm8909", "qcom,mpm-gic";
+ interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
+ reg = <0x601d0 0x1000>,
+ <0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ interrupt-controller;
+ interrupt-parent = <&intc>;
+ #interrupt-cells = <3>;
+ };
+
+ wakegpio: wake-gpio {
+ compatible = "qcom,mpm-gpio-msm8909", "qcom,mpm-gpio";
+ interrupt-controller;
+ interrupt-parent = <&intc>;
+ #interrupt-cells = <2>;
+ };
+
restart@4ab000 {
compatible = "qcom,pshold";
reg = <0x4ab000 0x4>,
@@ -337,6 +353,7 @@
reg = <0x1800000 0x80000>,
<0xb016000 0x00040>;
reg-names = "cc_base", "apcs_base";
+ qcom,gfx3d_clk_src-opp-store-vcorner = <&msm_gpu>;
vdd_dig-supply = <&pm8909_s1_corner>;
vdd_sr2_dig-supply = <&pm8909_s1_corner_ao>;
vdd_sr2_pll-supply = <&pm8909_l7_ao>;
@@ -539,79 +556,6 @@
};
- qcom,sensor-information {
- compatible = "qcom,sensor-information";
- sensor_information0: qcom,sensor-information-0 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor0";
- qcom,alias-name = "pop_mem";
- };
-
- sensor_information1: qcom,sensor-information-1 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor1";
- };
-
- sensor_information2: qcom,sensor-information-2 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor2";
- };
-
- sensor_information3: qcom,sensor-information-3 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor3";
- };
-
- sensor_information4: qcom,sensor-information-4 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor4";
- };
-
- sensor_information5: qcom,sensor-information-5 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "pa_therm0";
- };
-
- sensor_information6: qcom,sensor-information-6 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "case_therm";
- };
-
- sensor_information7: qcom,sensor-information-7 {
- qcom,sensor-type = "alarm";
- qcom,sensor-name = "pm8909_tz";
- qcom,scaling-factor = <1000>;
- };
-
- sensor_information8: qcom,sensor-information-8 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "xo_therm";
- };
-
- sensor_information9: qcom,sensor-information-9 {
- qcom,sensor-type = "adc";
- qcom,sensor-name = "xo_therm_buf";
- };
- };
-
- mitigation_profile0: qcom,limit_info-0 {
- qcom,temperature-sensor = <&sensor_information3>;
- qcom,boot-frequency-mitigate;
- qcom,emergency-frequency-mitigate;
- };
-
- mitigation_profile1: qcom,limit_info-1 {
- qcom,temperature-sensor = <&sensor_information3>;
- qcom,boot-frequency-mitigate;
- qcom,hotplug-mitigation-enable;
- };
-
- mitigation_profile2: qcom,limit_info-2 {
- qcom,temperature-sensor = <&sensor_information4>;
- qcom,boot-frequency-mitigate;
- qcom,hotplug-mitigation-enable;
- };
-
qcom,ipc-spinlock@1905000 {
compatible = "qcom,ipc-spinlock-sfpb";
reg = <0x1905000 0x8000>;
@@ -681,6 +625,16 @@
rpm-channel-type = <15>; /* SMD_APPS_RPM */
};
+ qcom,bam_dmux@4044000 {
+ compatible = "qcom,bam_dmux";
+ reg = <0x4044000 0x19000>;
+ interrupts = <0 29 1>;
+ qcom,rx-ring-size = <32>;
+ qcom,max-rx-mtu = <4096>;
+ qcom,fast-shutdown;
+ qcom,no-cpu-affinity;
+ };
+
qcom,smdtty {
compatible = "qcom,smdtty";
@@ -1918,19 +1872,6 @@
memory-region = <&modem_adsp_mem>;
};
- mcd {
- compatible = "qcom,mcd";
- qcom,ce-hw-instance = <0>;
- qcom,ce-device = <0>;
- clocks = <&clock_gcc clk_crypto_clk_src>,
- <&clock_gcc clk_gcc_crypto_clk>,
- <&clock_gcc clk_gcc_crypto_ahb_clk>,
- <&clock_gcc clk_gcc_crypto_axi_clk>;
- clock-names = "core_clk_src", "core_clk",
- "iface_clk", "bus_clk";
- qcom,ce-opp-freq = <100000000>;
- };
-
cpu0_slp_sts: cpu-sleep-status@b088008 {
reg = <0xb088008 0x100>;
qcom,sleep-status-mask= <0x80000>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
index d9fa6fb..255c146 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
@@ -54,6 +54,8 @@
synaptics,power-delay-ms = <200>;
synaptics,reset-delay-ms = <200>;
synaptics,max-y-for-2d = <389>;
+ synaptics,bus-lpm-cur-uA = <450>;
+ synaptics,do-not-disable-regulators;
synaptics,wakeup-gestures-en;
synaptics,resume-in-workqueue;
synaptics,x-flip;
@@ -71,6 +73,7 @@
qcom,blackghost {
compatible = "qcom,pil-blackghost";
+ qcom,pil-force-shutdown;
qcom,firmware-name = "bg-wear";
/* GPIO inputs from blackghost */
qcom,bg2ap-status-gpio = <&msm_gpio 97 0>;
@@ -221,6 +224,7 @@
&nfcw_disable_active
&nfc_clk_default>;
pinctrl-1 = <&nfcw_int_suspend &nfcw_disable_suspend>;
+ clocks = <&clock_rpm clk_bb_clk3_pin>;
clock-names = "ref_clk";
};
};
@@ -241,6 +245,33 @@
status = "disabled";
};
+&sdc1_clk_off {
+ config {
+ pins = "sdc1_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <2>; /* 2 MA */
+ output-low;
+ };
+};
+
+&sdc1_cmd_off {
+ config {
+ pins = "sdc1_cmd";
+ bias-disable; /* NO pull */
+ drive-strength = <2>; /* 2 MA */
+ output-low;
+ };
+};
+
+&sdc1_data_off {
+ config {
+ pins = "sdc1_data";
+ bias-disable; /* NO pull */
+ drive-strength = <2>; /* 2 MA */
+ output-low;
+ };
+};
+
&sdhc_2 {
status = "disabled";
};
@@ -316,3 +347,9 @@
output-high;
};
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+ qcom,platform-enable-gpio = <&msm_gpio 59 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909w.dtsi b/arch/arm64/boot/dts/qcom/msm8909w.dtsi
index 707b56e..c2e28d1 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909w.dtsi
@@ -94,3 +94,14 @@
<55 512 196800 196800>,
<55 512 393600 393600>;
};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_auo_cx_qvga_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-te-gpio = <&msm_gpio 24 0>;
+ qcom,platform-reset-gpio = <&msm_gpio 25 0>;
+ qcom,platform-bklight-en-gpio = <&msm_gpio 37 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-audio-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8917-audio-cdp.dtsi
new file mode 100644
index 0000000..465859a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-audio-cdp.dtsi
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&int_codec {
+ status = "okay";
+ qcom,msm-hs-micbias-type = "external";
+
+ asoc-wsa-codec-names = "wsa881x-i2c-codec.2-000f";
+ asoc-wsa-codec-prefixes = "SpkrMono";
+
+ msm-vdd-wsa-switch-supply = <&pm8937_l13>;
+ qcom,msm-vdd-wsa-switch-voltage = <3075000>;
+ qcom,msm-vdd-wsa-switch-current = <5000>;
+};
+
+&wsa881x_i2c_f {
+ status = "okay";
+};
+
+&wsa881x_i2c_45 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-camera-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8917-camera-pinctrl.dtsi
new file mode 100644
index 0000000..33cc8ae
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-camera-pinctrl.dtsi
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+cci {
+ cci0_active: cci0_active {
+ /* cci0 active state */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio29", "gpio30";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio29", "gpio30";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable; /* No PULL */
+ };
+ };
+
+ cci0_suspend: cci0_suspend {
+ /* cci0 suspended state */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio29", "gpio30";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio29", "gpio30";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable; /* No PULL */
+ };
+ };
+
+ cci1_active: cci1_active {
+ /* cci1 active state */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio31", "gpio32";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio31", "gpio32";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable; /* No PULL */
+ };
+ };
+
+ cci1_suspend: cci1_suspend {
+ /* cci1 suspended state */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio31", "gpio32";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio31", "gpio32";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable; /* No PULL */
+ };
+ };
+};
+
+/*sensors */
+cam_sensor_mclk0_default: cam_sensor_mclk0_default {
+ /* MCLK0 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio26";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio26";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_mclk0_sleep: cam_sensor_mclk0_sleep {
+ /* MCLK0 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio26";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio26";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_rear_default: cam_sensor_rear_default {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio36", "gpio35";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio36","gpio35";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_rear_sleep: cam_sensor_rear_sleep {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio36","gpio35";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio36","gpio35";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_rear_vdig: cam_sensor_rear_vdig {
+ /* VDIG */
+ mux {
+ pins = "gpio62";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio62";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_rear_vdig_sleep: cam_sensor_rear_vdig_sleep {
+ /* VDIG */
+ mux {
+ pins = "gpio62";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio62";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_mclk1_default: cam_sensor_mclk1_default {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio27";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio27";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_mclk1_sleep: cam_sensor_mclk1_sleep {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio27";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio27";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_front_default: cam_sensor_front_default {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio38","gpio50";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio38","gpio50";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_front_sleep: cam_sensor_front_sleep {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio38","gpio50";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio38","gpio50";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_mclk2_default: cam_sensor_mclk2_default {
+ /* MCLK2 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio28";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio28";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_mclk2_sleep: cam_sensor_mclk2_sleep {
+ /* MCLK2 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio28";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio28";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_front1_default: cam_sensor_front1_default {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio40", "gpio39";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio40", "gpio39";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+cam_sensor_front1_sleep: cam_sensor_front1_sleep {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio40", "gpio39";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio40", "gpio39";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-camera-sensor-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8917-camera-sensor-qrd.dtsi
new file mode 100644
index 0000000..dbaccfa
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-camera-sensor-qrd.dtsi
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&pm8937_l17>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2850000>;
+ qcom,cam-vreg-max-voltage = <2850000>;
+ qcom,cam-vreg-op-mode = <80000>;
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ compatible = "qcom,eeprom";
+ qcom,cci-master = <0>;
+ reg = <0x0>;
+ cam_vdig-supply = <&pm8937_l23>;
+ cam_vana-supply = <&pm8937_l22>;
+ cam_vio-supply = <&pm8937_l6>;
+ cam_vaf-supply = <&pm8937_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 36 0>,
+ <&tlmm 35 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0";
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ compatible = "qcom,eeprom";
+ reg = <0x02>;
+ cam_vdig-supply = <&pm8937_l23>;
+ cam_vana-supply = <&pm8937_l22>;
+ cam_vio-supply = <&pm8937_l6>;
+ cam_vaf-supply = <&pm8937_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <19200000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ qcom,actuator-src = <&actuator0>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,eeprom-src = <&eeprom0>;
+ cam_vdig-supply = <&pm8937_l23>;
+ cam_vana-supply = <&pm8937_l22>;
+ cam_vio-supply = <&pm8937_l6>;
+ cam_vaf-supply = <&pm8937_l17>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_vaf";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default
+ &cam_sensor_rear_default>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep
+ &cam_sensor_rear_sleep>;
+ gpios = <&tlmm 26 0>,
+ <&tlmm 36 0>,
+ <&tlmm 35 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,mount-angle = <270>;
+ cam_vdig-supply = <&pm8937_l23>;
+ cam_vana-supply = <&pm8937_l22>;
+ cam_vio-supply = <&pm8937_l6>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_default
+ &cam_sensor_front1_default>;
+ pinctrl-1 = <&cam_sensor_mclk2_sleep
+ &cam_sensor_front1_sleep>;
+ gpios = <&tlmm 28 0>,
+ <&tlmm 40 0>,
+ <&tlmm 39 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ clocks = <&clock_gcc clk_mclk2_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-camera.dtsi b/arch/arm64/boot/dts/qcom/msm8917-camera.dtsi
new file mode 100644
index 0000000..4991ff7
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-camera.dtsi
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,msm-cam@1b00000 {
+ compatible = "qcom,msm-cam";
+ reg = <0x1b00000 0x40000>;
+ reg-names = "msm-cam";
+ status = "ok";
+ bus-vectors = "suspend", "svs", "nominal", "turbo";
+ qcom,bus-votes = <0 160000000 320000000 320000000>;
+ };
+
+ qcom,csiphy@1b34000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,csiphy-v3.4.2", "qcom,csiphy";
+ reg = <0x1b34000 0x1000>,
+ <0x1b00030 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
+ interrupts = <0 78 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_csi0phytimer_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0phytimer_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0phy_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csiphy_timer_src_clk", "csiphy_timer_clk",
+ "camss_ahb_src", "csi_phy_clk",
+ "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 200000000 0 0 0 0>;
+ };
+
+ qcom,csiphy@1b35000 {
+ status = "ok";
+ cell-index = <1>;
+ compatible = "qcom,csiphy-v3.4.2", "qcom,csiphy";
+ reg = <0x1b35000 0x1000>,
+ <0x1b00038 0x4>;
+ reg-names = "csiphy", "csiphy_clk_mux";
+ interrupts = <0 79 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_csi1phytimer_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1phytimer_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1phy_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "ispif_ahb_clk",
+ "csiphy_timer_src_clk", "csiphy_timer_clk",
+ "camss_ahb_src", "csi_phy_clk",
+ "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 200000000 0 0 0 0>;
+ };
+
+ qcom,csid@1b30000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,csid-v3.4.3", "qcom,csid";
+ reg = <0x1b30000 0x400>;
+ reg-names = "csid";
+ interrupts = <0 51 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8937_l2>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi0_ahb_clk>,
+ <&clock_gcc clk_csi0_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0_clk>,
+ <&clock_gcc clk_gcc_camss_csi0pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi0rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csi_ahb_clk", "csi_src_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 0 200000000 0 0 0 0>;
+ };
+
+ qcom,csid@1b30400 {
+ status = "ok";
+ cell-index = <1>;
+ compatible = "qcom,csid-v3.4.3", "qcom,csid";
+ reg = <0x1b30400 0x400>;
+ reg-names = "csid";
+ interrupts = <0 52 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8937_l2>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi1_ahb_clk>,
+ <&clock_gcc clk_csi1_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1_clk>,
+ <&clock_gcc clk_gcc_camss_csi1pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi1rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csi_ahb_clk", "csi_src_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 0 200000000 0 0 0 0>;
+ };
+
+ qcom,csid@1b30800 {
+ status = "ok";
+ cell-index = <2>;
+ compatible = "qcom,csid-v3.4.3", "qcom,csid";
+ reg = <0x1b30800 0x400>;
+ reg-names = "csid";
+ interrupts = <0 153 0>;
+ interrupt-names = "csid";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8937_l2>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_csi2_ahb_clk>,
+ <&clock_gcc clk_csi2_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi2_clk>,
+ <&clock_gcc clk_gcc_camss_csi2pix_clk>,
+ <&clock_gcc clk_gcc_camss_csi2rdi_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "camss_top_ahb_clk",
+ "ispif_ahb_clk", "csi_ahb_clk", "csi_src_clk",
+ "csi_clk", "csi_pix_clk",
+ "csi_rdi_clk", "camss_ahb_clk";
+ qcom,clock-rates = <0 61540000 0 200000000 0 0 0 0>;
+ };
+
+ qcom,ispif@1b31000 {
+ cell-index = <0>;
+ compatible = "qcom,ispif-v3.0", "qcom,ispif";
+ reg = <0x1b31000 0x500>,
+ <0x1b00020 0x10>;
+ reg-names = "ispif", "csi_clk_mux";
+ interrupts = <0 55 0>;
+ interrupt-names = "ispif";
+ qcom,num-isps = <0x2>;
+ vfe0-vdd-supply = <&gdsc_vfe>;
+ vfe1-vdd-supply = <&gdsc_vfe1>;
+ qcom,vdd-names = "vfe0-vdd", "vfe1-vdd";
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_camss_top_ahb_clk_src>,
+ <&clock_gcc clk_csi0_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi0_clk>,
+ <&clock_gcc clk_gcc_camss_csi0rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi0pix_clk>,
+ <&clock_gcc clk_csi1_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi1_clk>,
+ <&clock_gcc clk_gcc_camss_csi1rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi1pix_clk>,
+ <&clock_gcc clk_csi2_clk_src>,
+ <&clock_gcc clk_gcc_camss_csi2_clk>,
+ <&clock_gcc clk_gcc_camss_csi2rdi_clk>,
+ <&clock_gcc clk_gcc_camss_csi2pix_clk>,
+ <&clock_gcc clk_vfe0_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>,
+ <&clock_gcc clk_vfe1_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe1_clk>;
+ clock-names = "ispif_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_src",
+ "csi0_src_clk", "csi0_clk",
+ "csi0_rdi_clk", "csi0_pix_clk",
+ "csi1_src_clk", "csi1_clk",
+ "csi1_rdi_clk", "csi1_pix_clk",
+ "csi2_src_clk", "csi2_clk",
+ "csi2_rdi_clk", "csi2_pix_clk",
+ "vfe0_clk_src", "camss_vfe_vfe0_clk",
+ "camss_csi_vfe0_clk", "vfe1_clk_src",
+ "camss_vfe_vfe1_clk", "camss_csi_vfe1_clk";
+ qcom,clock-rates = <61540000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 200000000 0 0 0
+ 0 0 0
+ 0 0 0>;
+ qcom,clock-cntl-support;
+ qcom,clock-control = "SET_RATE","NO_SET_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "SET_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "NO_SET_RATE", "SET_RATE",
+ "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "SET_RATE", "NO_SET_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "INIT_RATE", "NO_SET_RATE",
+ "NO_SET_RATE", "INIT_RATE", "NO_SET_RATE",
+ "NO_SET_RATE";
+ };
+
+ vfe0: qcom,vfe0@1b10000 {
+ cell-index = <0>;
+ compatible = "qcom,vfe40";
+ reg = <0x1b10000 0x1000>,
+ <0x1b40000 0x200>;
+ reg-names = "vfe", "vfe_vbif";
+ interrupts = <0 57 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_vfe0_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe0_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_vfe_axi_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>;
+ clock-names = "camss_top_ahb_clk", "camss_ahb_clk",
+ "vfe_clk_src", "camss_vfe_vfe_clk",
+ "camss_csi_vfe_clk", "iface_clk",
+ "bus_clk", "iface_ahb_clk";
+ qcom,clock-rates = <0 0 266670000 0 0 0 0 0>;
+ qos-entries = <8>;
+ qos-regs = <0x2c4 0x2c8 0x2cc 0x2d0 0x2d4 0x2d8
+ 0x2dc 0x2e0>;
+ qos-settings = <0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55>;
+ vbif-entries = <1>;
+ vbif-regs = <0x124>;
+ vbif-settings = <0x3>;
+ ds-entries = <17>;
+ ds-regs = <0x988 0x98c 0x990 0x994 0x998
+ 0x99c 0x9a0 0x9a4 0x9a8 0x9ac 0x9b0
+ 0x9b4 0x9b8 0x9bc 0x9c0 0x9c4 0x9c8>;
+ ds-settings = <0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0x00000110>;
+ max-clk-nominal = <400000000>;
+ max-clk-turbo = <432000000>;
+ };
+
+ vfe1: qcom,vfe1@1b14000 {
+ cell-index = <1>;
+ compatible = "qcom,vfe40";
+ reg = <0x1b14000 0x1000>,
+ <0x1ba0000 0x200>;
+ reg-names = "vfe", "vfe_vbif";
+ interrupts = <0 29 0>;
+ interrupt-names = "vfe";
+ vdd-supply = <&gdsc_vfe1>;
+ clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_vfe1_clk_src>,
+ <&clock_gcc clk_gcc_camss_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_csi_vfe1_clk>,
+ <&clock_gcc clk_gcc_camss_vfe1_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_vfe1_axi_clk>,
+ <&clock_gcc clk_gcc_camss_ispif_ahb_clk>;
+ clock-names = "camss_top_ahb_clk" , "camss_ahb_clk",
+ "vfe_clk_src", "camss_vfe_vfe_clk",
+ "camss_csi_vfe_clk", "iface_clk",
+ "bus_clk", "iface_ahb_clk";
+ qcom,clock-rates = <0 0 266670000 0 0 0 0 0>;
+ qos-entries = <8>;
+ qos-regs = <0x2c4 0x2c8 0x2cc 0x2d0 0x2d4 0x2d8
+ 0x2dc 0x2e0>;
+ qos-settings = <0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55 0xaa55aa55
+ 0xaa55aa55>;
+ vbif-entries = <1>;
+ vbif-regs = <0x124>;
+ vbif-settings = <0x3>;
+ ds-entries = <17>;
+ ds-regs = <0x988 0x98c 0x990 0x994 0x998
+ 0x99c 0x9a0 0x9a4 0x9a8 0x9ac 0x9b0
+ 0x9b4 0x9b8 0x9bc 0x9c0 0x9c4 0x9c8>;
+ ds-settings = <0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0xcccc1111
+ 0xcccc1111 0x00000110>;
+ max-clk-nominal = <400000000>;
+ max-clk-turbo = <432000000>;
+ };
+
+ qcom,vfe {
+ compatible = "qcom,vfe";
+ num_child = <2>;
+ };
+
+ qcom,cam_smmu {
+ status = "ok";
+ compatible = "qcom,msm-cam-smmu";
+ msm_cam_smmu_cb1: msm_cam_smmu_cb1 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x400 0x00>,
+ <&apps_iommu 0x2400 0x00>;
+ label = "vfe";
+ qcom,scratch-buf-support;
+ };
+
+ msm_cam_smmu_cb2: msm_cam_smmu_cb2 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ label = "vfe_secure";
+ qcom,secure-context;
+ };
+
+ msm_cam_smmu_cb3: msm_cam_smmu_cb3 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x1c00 0x00>;
+ label = "cpp";
+ };
+
+ msm_cam_smmu_cb4: msm_cam_smmu_cb4 {
+ compatible = "qcom,msm-cam-smmu-cb";
+ iommus = <&apps_iommu 0x1800 0x00>;
+ label = "jpeg_enc0";
+ };
+ };
+
+ qcom,jpeg@1b1c000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,jpeg";
+ reg = <0x1b1c000 0x400>,
+ <0x1b60000 0xc30>;
+ reg-names = "jpeg_hw", "jpeg_vbif";
+ interrupts = <0 59 0>;
+ interrupt-names = "jpeg";
+ vdd-supply = <&gdsc_jpeg>;
+ qcom,vdd-names = "vdd";
+ clock-names = "core_clk", "iface_clk", "bus_clk0",
+ "camss_top_ahb_clk", "camss_ahb_clk";
+ clocks = <&clock_gcc clk_gcc_camss_jpeg0_clk>,
+ <&clock_gcc clk_gcc_camss_jpeg_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_jpeg_axi_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ qcom,clock-rates = <266670000 0 0 0 0>;
+ qcom,qos-reg-settings = <0x28 0x0000555e>,
+ <0xc8 0x00005555>;
+ qcom,msm-bus,name = "msm_camera_jpeg0";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps = <62 512 0 0>,
+ <62 512 800000 800000>;
+ qcom,vbif-reg-settings = <0xc0 0x10101000>,
+ <0xb0 0x10100010>;
+ };
+
+ qcom,irqrouter@1b00000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,irqrouter";
+ reg = <0x1b00000 0x100>;
+ reg-names = "irqrouter";
+ };
+
+ qcom,cpp@1b04000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,cpp";
+ reg = <0x1b04000 0x100>,
+ <0x1b80000 0x200>,
+ <0x1b18000 0x018>,
+ <0x1858078 0x4>;
+ reg-names = "cpp", "cpp_vbif", "cpp_hw", "camss_cpp";
+ interrupts = <0 49 0>;
+ interrupt-names = "cpp";
+ vdd-supply = <&gdsc_cpp>;
+ qcom,vdd-names = "vdd";
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_cpp_clk_src>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_cpp_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_cpp_axi_clk>,
+ <&clock_gcc clk_gcc_camss_cpp_clk>,
+ <&clock_gcc clk_gcc_camss_micro_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>;
+ clock-names = "ispif_ahb_clk", "cpp_core_clk",
+ "camss_top_ahb_clk", "camss_vfe_cpp_ahb_clk",
+ "camss_vfe_cpp_axi_clk", "camss_vfe_cpp_clk",
+ "micro_iface_clk", "camss_ahb_clk";
+ qcom,clock-rates = <61540000 180000000 0 0 0 180000000 0 0>;
+ qcom,min-clock-rate = <133000000>;
+ resets = <&clock_gcc GCC_CAMSS_MICRO_BCR>;
+ reset-names = "micro_iface_reset";
+ qcom,bus-master = <1>;
+ qcom,msm-bus,name = "msm_camera_cpp";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <106 512 0 0>,
+ <106 512 0 0>;
+ qcom,msm-bus-vector-dyn-vote;
+ qcom,micro-reset;
+ qcom,cpp-fw-payload-info {
+ qcom,stripe-base = <156>;
+ qcom,plane-base = <141>;
+ qcom,stripe-size = <27>;
+ qcom,plane-size = <5>;
+ qcom,fe-ptr-off = <5>;
+ qcom,we-ptr-off = <11>;
+ };
+ };
+
+ cci: qcom,cci@1b0c000 {
+ status = "ok";
+ cell-index = <0>;
+ compatible = "qcom,cci";
+ reg = <0x1b0c000 0x4000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "cci";
+ interrupts = <0 50 0>;
+ interrupt-names = "cci";
+ clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>,
+ <&clock_gcc clk_cci_clk_src>,
+ <&clock_gcc clk_gcc_camss_cci_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_cci_clk>,
+ <&clock_gcc clk_gcc_camss_ahb_clk>,
+ <&clock_gcc clk_gcc_camss_top_ahb_clk>;
+ clock-names = "ispif_ahb_clk", "cci_src_clk",
+ "cci_ahb_clk", "camss_cci_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk";
+ qcom,clock-rates = <61540000 19200000 0 0 0 0>,
+ <61540000 37500000 0 0 0 0>;
+ pinctrl-names = "cci_default", "cci_suspend";
+ pinctrl-0 = <&cci0_active &cci1_active>;
+ pinctrl-1 = <&cci0_suspend &cci1_suspend>;
+ gpios = <&tlmm 29 0>,
+ <&tlmm 30 0>,
+ <&tlmm 31 0>,
+ <&tlmm 32 0>;
+ qcom,gpio-tbl-num = <0 1 2 3>;
+ qcom,gpio-tbl-flags = <1 1 1 1>;
+ qcom,gpio-tbl-label = "CCI_I2C_DATA0",
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
+ i2c_freq_100Khz: qcom,i2c_standard_mode {
+ status = "disabled";
+ };
+ i2c_freq_400Khz: qcom,i2c_fast_mode {
+ status = "disabled";
+ };
+ i2c_freq_custom: qcom,i2c_custom_mode {
+ status = "disabled";
+ };
+
+ i2c_freq_1Mhz: qcom,i2c_fast_plus_mode {
+ status = "disabled";
+ };
+ };
+};
+
+&i2c_freq_100Khz {
+ qcom,hw-thigh = <78>;
+ qcom,hw-tlow = <114>;
+ qcom,hw-tsu-sto = <28>;
+ qcom,hw-tsu-sta = <28>;
+ qcom,hw-thd-dat = <10>;
+ qcom,hw-thd-sta = <77>;
+ qcom,hw-tbuf = <118>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <1>;
+};
+
+&i2c_freq_400Khz {
+ qcom,hw-thigh = <20>;
+ qcom,hw-tlow = <28>;
+ qcom,hw-tsu-sto = <21>;
+ qcom,hw-tsu-sta = <21>;
+ qcom,hw-thd-dat = <13>;
+ qcom,hw-thd-sta = <18>;
+ qcom,hw-tbuf = <32>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ status = "ok";
+};
+
+&i2c_freq_custom {
+ qcom,hw-thigh = <15>;
+ qcom,hw-tlow = <28>;
+ qcom,hw-tsu-sto = <21>;
+ qcom,hw-tsu-sta = <21>;
+ qcom,hw-thd-dat = <13>;
+ qcom,hw-thd-sta = <18>;
+ qcom,hw-tbuf = <25>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ status = "ok";
+};
+
+&i2c_freq_1Mhz {
+ qcom,hw-thigh = <16>;
+ qcom,hw-tlow = <22>;
+ qcom,hw-tsu-sto = <17>;
+ qcom,hw-tsu-sta = <18>;
+ qcom,hw-thd-dat = <16>;
+ qcom,hw-thd-sta = <15>;
+ qcom,hw-tbuf = <19>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <3>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi b/arch/arm64/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi
new file mode 100644
index 0000000..1f19e20
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm8917-pinctrl.dtsi"
+/* #include "msm8917-camera-sensor-cdp.dtsi"*/
+
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active", "tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&tlmm 128 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ debounce-interval = <15>;
+ };
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&tlmm 127 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 91 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+
+ home {
+ label = "home";
+ gpios = <&tlmm 86 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ debounce-interval = <15>;
+ };
+ };
+
+ hbtp {
+ compatible = "qcom,hbtp-input";
+ vcc_ana-supply = <&pm8937_l10>;
+ vcc_dig-supply = <&pm8937_l5>;
+ qcom,afe-load = <50000>;
+ qcom,afe-vtg-min = <2850000>;
+ qcom,afe-vtg-max = <2850000>;
+ qcom,dig-load = <15000>;
+ qcom,dig-vtg-min = <1800000>;
+ qcom,dig-vtg-max = <1800000>;
+ };
+
+ usb_detect {
+ compatible = "qcom,gpio-usbdetect";
+ interrupt-names = "vbus_det_irq";
+ interrupt-parent = <&tlmm>;
+ interrupts = <130 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb_mode_select>;
+ qcom,gpio-mode-sel = <&tlmm 130 0>;
+ qcom,notify-host-mode;
+ status = "disabled";
+ };
+};
+
+&flash_led {
+ compatible = "qcom,qpnp-flash-led";
+ reg = <0xd300 0x100>;
+ pinctrl-names = "flash_led_enable","flash_led_disable";
+ pinctrl-0 = <&rear_flash_led_enable>;
+ pinctrl-1 = <&rear_flash_led_disable>;
+ qcom,follow-otst2-rb-disabled;
+};
+
+&wled {
+ qcom,cons-sync-write-delay-us = <1000>;
+};
+
+&pmi_haptic{
+ qcom,actuator-type = "lra";
+ qcom,wave-play-rate-us = <4165>;
+ qcom,lra-auto-res-mode = "qwd";
+ qcom,lra-high-z = "opt1";
+ qcom,lra-res-cal-period = <0>;
+};
+
+&blsp1_uart2 {
+ status = "ok";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart_console_active>;
+};
+
+#include "msm8937-mdss-panels.dtsi"
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_truly_720_vid>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 60 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 98 0>;
+};
+
+&dsi_truly_720_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_truly_720_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,ulps-enabled;
+ qcom,partial-update-enabled;
+ qcom,panel-roi-alignment = <2 2 2 2 2 2>;
+};
+
+&dsi_icn9706_720_1440_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
+&tlmm {
+ tlmm_gpio_key {
+ gpio_key_active: gpio_key_active {
+ mux {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ };
+ };
+
+ gpio_key_suspend: gpio_key_suspend {
+ mux {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ };
+ };
+ };
+};
+
+&sdhc_1 {
+ /* device core power supply */
+ vdd-supply = <&pm8937_l8>;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <200 570000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8937_l5>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 192000000
+ 384000000>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ /* device core power supply */
+ vdd-supply = <&pm8937_l11>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 800000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8937_l12>;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &tlmm 67 0>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&tlmm 67 0x1>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000
+ 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ status = "ok";
+};
+
+&i2c_3 {
+ status = "okay";
+ synaptics@22 {
+ compatible = "synaptics,dsx";
+ reg = <0x22>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2008>;
+ avdd-supply = <&pm8937_l10>;
+ vdd-supply = <&pm8937_l5>;
+ synaptics,vdd-voltage = <1880000 1880000>;
+ synaptics,avdd-voltage = <3008000 3008000>;
+ synaptics,vdd-current = <40000>;
+ synaptics,avdd-current = <20000>;
+ pinctrl-names = "pmx_ts_active","pmx_ts_suspend";
+ pinctrl-0 = <&ts_int_active &ts_reset_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ synaptics,display-coords = <0 0 719 1439>;
+ synaptics,panel-coords = <0 0 719 1439>;
+ synaptics,reset-gpio = <&tlmm 64 0x00>;
+ synaptics,irq-gpio = <&tlmm 65 0x2008>;
+ synaptics,disable-gpios;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8917-cdp.dtsi
new file mode 100644
index 0000000..fde4847
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-cdp.dtsi
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "msm8917-pinctrl.dtsi"
+/* #include "msm8917-camera-sensor-cdp.dtsi"*/
+
+&soc {
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
+ pinctrl-0 = <&gpio_key_active>;
+ pinctrl-1 = <&gpio_key_suspend>;
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&tlmm 128 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ debounce-interval = <15>;
+ };
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&tlmm 127 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&tlmm 91 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ debounce-interval = <15>;
+ };
+
+ home {
+ label = "home";
+ gpios = <&tlmm 86 0x1>;
+ linux,input-type = <1>;
+ linux,code = <102>;
+ debounce-interval = <15>;
+ };
+ };
+
+ hbtp {
+ compatible = "qcom,hbtp-input";
+ vcc_ana-supply = <&pm8937_l10>;
+ vcc_dig-supply = <&pm8937_l5>;
+ qcom,afe-load = <50000>;
+ qcom,afe-vtg-min = <2850000>;
+ qcom,afe-vtg-max = <2850000>;
+ qcom,dig-load = <15000>;
+ qcom,dig-vtg-min = <1800000>;
+ qcom,dig-vtg-max = <1800000>;
+ };
+
+ usb_detect {
+ compatible = "qcom,gpio-usbdetect";
+ interrupt-names = "vbus_det_irq";
+ interrupt-parent = <&tlmm>;
+ interrupts = <130 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb_mode_select>;
+ qcom,gpio-mode-sel = <&tlmm 130 0>;
+ qcom,notify-host-mode;
+ status = "disabled";
+ };
+};
+
+&blsp1_uart2 {
+ status = "ok";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart_console_active>;
+};
+
+#include "msm8937-mdss-panels.dtsi"
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_truly_720_vid>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 60 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 98 0>;
+};
+
+&dsi_truly_720_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_truly_720_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,ulps-enabled;
+ qcom,partial-update-enabled;
+ qcom,panel-roi-alignment = <2 2 2 2 2 2>;
+};
+
+&tlmm {
+ tlmm_gpio_key {
+ gpio_key_active: gpio_key_active {
+ mux {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ };
+ };
+
+ gpio_key_suspend: gpio_key_suspend {
+ mux {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio86", "gpio91", "gpio127", "gpio128";
+ };
+ };
+ };
+};
+
+&sdhc_1 {
+ /* device core power supply */
+ vdd-supply = <&pm8937_l8>;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <200 570000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8937_l5>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <200 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 192000000
+ 384000000>;
+ qcom,nonremovable;
+ qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+ status = "ok";
+};
+
+&sdhc_2 {
+ /* device core power supply */
+ vdd-supply = <&pm8937_l11>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 800000>;
+
+ /* device communication power supply */
+ vdd-io-supply = <&pm8937_l12>;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_2>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0
+ 2 &tlmm 67 0>;
+ interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+ cd-gpios = <&tlmm 67 0x1>;
+
+ qcom,clk-rates = <400000 20000000 25000000 50000000 100000000
+ 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ status = "ok";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8917-coresight.dtsi b/arch/arm64/boot/dts/qcom/msm8917-coresight.dtsi
new file mode 100644
index 0000000..87303c5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-coresight.dtsi
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ tmc_etr: tmc@6028000 {
+ compatible = "arm,primecell";
+ reg = <0x6028000 0x1000>,
+ <0x6044000 0x15000>;
+ reg-names = "tmc-base", "bam-base";
+
+ interrupts = <0 166 0>;
+ interrupt-names = "byte-cntr-irq";
+
+ arm,buffer-size = <0x100000>;
+ arm,sg-enable;
+ qcom,force-reg-dump;
+
+ coresight-name = "coresight-tmc-etr";
+ coresight-csr = <&csr>;
+ coresight-ctis = <&cti0 &cti8>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ tmc_etr_in_replicator: endpoint {
+ slave-mode;
+ remote-endpoint = <&replicator_out_tmc_etr>;
+ };
+ };
+ };
+
+ replicator: replicator@6026000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b909>;
+
+ reg = <0x6026000 0x1000>;
+ reg-names = "replicator-base";
+
+ coresight-name = "coresight-replicator";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ replicator_out_tmc_etr: endpoint {
+ remote-endpoint =
+ <&tmc_etr_in_replicator>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ replicator_in_tmc_etf: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tmc_etf_out_replicator>;
+ };
+ };
+ };
+ };
+
+ tmc_etf: tmc@6027000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
+ reg = <0x6027000 0x1000>;
+ reg-names = "tmc-base";
+
+ coresight-name = "coresight-tmc-etf";
+ coresight-csr = <&csr>;
+
+ arm,default-sink;
+ qcom,force-reg-dump;
+
+ coresight-ctis = <&cti0 &cti8>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ tmc_etf_out_replicator:endpoint {
+ remote-endpoint =
+ <&replicator_in_tmc_etf>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tmc_etf_in_funnel_in0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_in0_out_tmc_etf>;
+ };
+ };
+ };
+ };
+
+ funnel_in0: funnel@6021000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6021000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-in0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_in0_out_tmc_etf: endpoint {
+ remote-endpoint =
+ <&tmc_etf_in_funnel_in0>;
+ };
+ };
+
+ port@1 {
+ reg = <7>;
+ funnel_in0_in_stm: endpoint {
+ slave-mode;
+ remote-endpoint = <&stm_out_funnel_in0>;
+ };
+ };
+
+ port@2 {
+ reg = <6>;
+ funnel_in0_in_tpda: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpda_out_funnel_in0>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ funnel_in0_in_funnel_center: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_center_out_funnel_in0>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+ funnel_in0_in_funnel_right: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_right_out_funnel_in0>;
+ };
+ };
+
+ port@5 {
+ reg = <5>;
+ funnel_in0_in_funnel_mm: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_mm_out_funnel_in0>;
+ };
+ };
+ };
+ };
+
+ funnel_mm: funnel@6130000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6130000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-mm";
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_mm_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_mm>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_mm_in_wcn_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&wcn_etm0_out_funnel_mm>;
+ };
+ };
+
+ port@2 {
+ reg = <4>;
+ funnel_mm_in_funnel_cam: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_cam_out_funnel_mm>;
+ };
+ };
+
+ port@3 {
+ reg = <5>;
+ funnel_mm_in_audio_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&audio_etm0_out_funnel_mm>;
+ };
+ };
+ };
+ };
+
+ funnel_center: funnel@6100000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6100000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-center";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_center_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_center>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_center_in_rpm_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&rpm_etm0_out_funnel_center>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ funnel_center_in_dbgui: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&dbgui_out_funnel_center>;
+ };
+ };
+ };
+ };
+
+ funnel_right: funnel@6120000 {
+ compatible = "arm,primecell";
+
+ reg = <0x6120000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-right";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_right_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_right>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ funnel_right_in_modem_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&modem_etm0_out_funnel_right>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ funnel_right_in_funnel_apss: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_apss_out_funnel_right>;
+ };
+ };
+ };
+ };
+
+ funnel_cam: funnel@6132000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6132000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-cam";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ funnel_cam_out_funnel_mm: endpoint {
+ remote-endpoint = <&funnel_mm_in_funnel_cam>;
+ };
+ };
+ };
+
+ funnel_apss: funnel@61a1000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x61a1000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-apss";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ funnel_apss_out_funnel_right: endpoint {
+ remote-endpoint =
+ <&funnel_right_in_funnel_apss>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_apss0_in_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm0_out_funnel_apss0>;
+ };
+ };
+
+ port@2 {
+ reg = <1>;
+ funnel_apss0_in_etm1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm1_out_funnel_apss0>;
+ };
+ };
+
+ port@3 {
+ reg = <2>;
+ funnel_apss0_in_etm2: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm2_out_funnel_apss0>;
+ };
+ };
+
+ port@4 {
+ reg = <3>;
+ funnel_apss0_in_etm3: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm3_out_funnel_apss0>;
+ };
+ };
+ };
+ };
+
+ etm0: etm@61bc000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x61bc000 0x1000>;
+ cpu = <&CPU0>;
+ reg-names = "etm-base";
+
+ coresight-name = "coresight-etm0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ port {
+ etm0_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm0>;
+ };
+ };
+ };
+
+ etm1: etm@61bd000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x61bd000 0x1000>;
+ cpu = <&CPU1>;
+ coresight-name = "coresight-etm1";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm1_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm1>;
+ };
+ };
+ };
+
+ etm2: etm@61be000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x61be000 0x1000>;
+ cpu = <&CPU2>;
+ coresight-name = "coresight-etm2";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm2_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm2>;
+ };
+ };
+ };
+
+ etm3: etm@61bf000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x61bf000 0x1000>;
+ cpu = <&CPU3>;
+ coresight-name = "coresight-etm3";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm3_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm3>;
+ };
+ };
+ };
+
+ stm: stm@6002000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b962>;
+
+ reg = <0x6002000 0x1000>,
+ <0x9280000 0x180000>;
+ reg-names = "stm-base", "stm-data-base";
+
+ coresight-name = "coresight-stm";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ stm_out_funnel_in0: endpoint {
+ remote-endpoint = <&funnel_in0_in_stm>;
+ };
+ };
+ };
+
+ cti0: cti@6010000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6010000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti1: cti@6011000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6011000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti1";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti2: cti@6012000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6012000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti2";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti3: cti@6013000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6013000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti3";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti4: cti@6014000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6014000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti4";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti5: cti@6015000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6015000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti5";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti6: cti@6016000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6016000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti6";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti7: cti@6017000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6017000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti7";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti8: cti@6018000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6018000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti8";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti9: cti@6019000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6019000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti9";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti10: cti@601a000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x601a000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti10";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti11: cti@601b000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x601b000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti11";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti12: cti@601c000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x601c000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti12";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti13: cti@601d000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x601d000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti13";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti14: cti@601e000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x601e000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti14";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti15: cti@601f000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x601f000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti15";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu0: cti@61b8000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b8000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-cpu0";
+ cpu = <&CPU0>;
+ qcom,cti-save;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu1: cti@61b9000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b9000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-cpu1";
+ cpu = <&CPU1>;
+ qcom,cti-save;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu2: cti@61ba000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61ba000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-cpu2";
+ cpu = <&CPU2>;
+ qcom,cti-save;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu3: cti@61bb000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61bb000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-cpu3";
+ cpu = <&CPU3>;
+ qcom,cti-save;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_modem_cpu0: cti@6124000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6124000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-modem-cpu0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* Proto CTI */
+ cti_wcn_cpu0: cti@6139000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6139000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-wcn-cpu0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* Venus CTI */
+ cti_video_cpu0: cti@6134000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x6134000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-video-cpu0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* LPASS CTI */
+ cti_audio_cpu0: cti@613c000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x613c000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-audio-cpu0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* RPM CTI */
+ cti_rpm_cpu0: cti@610c000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x610c000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-rpm-cpu0";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ /* Proto ETM */
+ wcn_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-wcn-etm0";
+ qcom,inst-id = <3>;
+
+ port {
+ wcn_etm0_out_funnel_mm: endpoint {
+ remote-endpoint = <&funnel_mm_in_wcn_etm0>;
+ };
+ };
+ };
+
+ rpm_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-rpm-etm0";
+ qcom,inst-id = <4>;
+
+ port {
+ rpm_etm0_out_funnel_center: endpoint {
+ remote-endpoint = <&funnel_center_in_rpm_etm0>;
+ };
+ };
+ };
+
+ /* LPASS ETM */
+ audio_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-audio-etm0";
+ qcom,inst-id = <5>;
+
+ port {
+ audio_etm0_out_funnel_mm: endpoint {
+ remote-endpoint = <&funnel_mm_in_audio_etm0>;
+ };
+ };
+ };
+
+ modem_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+ coresight-name = "coresight-modem-etm0";
+ qcom,inst-id = <11>;
+
+ port {
+ modem_etm0_out_funnel_right: endpoint {
+ remote-endpoint = <&funnel_right_in_modem_etm0>;
+ };
+ };
+ };
+
+ csr: csr@6001000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6001000 0x1000>;
+ reg-names = "csr-base";
+
+ coresight-name = "coresight-csr";
+
+ qcom,usb-bam-support;
+ qcom,hwctrl-set-support;
+ qcom,set-byte-cntr-support;
+
+ qcom,blk-size = <1>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ dbgui: dbgui@6108000 {
+ compatible = "qcom,coresight-dbgui";
+ reg = <0x6108000 0x1000>;
+ reg-names = "dbgui-base";
+
+ coresight-name = "coresight-dbgui";
+
+ qcom,dbgui-addr-offset = <0x30>;
+ qcom,dbgui-data-offset = <0x130>;
+ qcom,dbgui-size = <32>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ dbgui_out_funnel_center: endpoint {
+ remote-endpoint = <&funnel_center_in_dbgui>;
+ };
+ };
+ };
+
+ tpda: tpda@6003000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+
+ reg = <0x6003000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda";
+
+ qcom,tpda-atid = <64>;
+ qcom,cmb-elem-size = <0 32>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ tpda_out_funnel_in0: endpoint {
+ remote-endpoint = <&funnel_in0_in_tpda>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_in_tpdm_dcc: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_dcc_out_tpda>;
+ };
+ };
+ };
+ };
+
+ tpdm_dcc: tpdm@6110000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+
+ reg = <0x6110000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-dcc";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ tpdm_dcc_out_tpda: endpoint {
+ remote-endpoint = <&tpda_in_tpdm_dcc>;
+ };
+ };
+ };
+
+ hwevent: hwevent@6101000 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0x6101000 0x148>,
+ <0x6101fb0 0x4>,
+ <0x6121000 0x148>,
+ <0x6121fb0 0x4>,
+ <0x6131000 0x148>,
+ <0x6131fb0 0x4>,
+ <0x78c5010 0x4>,
+ <0x7885010 0x4>;
+ reg-names = "center-wrapper-mux", "center-wrapper-lockaccess",
+ "right-wrapper-mux", "right-wrapper-lockaccess",
+ "mm-wrapper-mux", "mm-wrapper-lockaccess",
+ "usbbam-mux", "blsp-mux";
+
+ coresight-name = "coresight-hwevent";
+ coresight-csr = <&csr>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-cpu.dtsi b/arch/arm64/boot/dts/qcom/msm8917-cpu.dtsi
index 792d5d1..5a242db 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-cpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-cpu.dtsi
@@ -117,3 +117,46 @@
};
};
+
+&soc {
+ cpuss_dump {
+ compatible = "qcom,cpuss-dump";
+ qcom,l2_dump1 {
+ /* L2 cache dump for A53 cluster */
+ qcom,dump-node = <&L2_1>;
+ qcom,dump-id = <0xC1>;
+ };
+ qcom,l1_i_cache100 {
+ qcom,dump-node = <&L1_I_100>;
+ qcom,dump-id = <0x60>;
+ };
+ qcom,l1_i_cache101 {
+ qcom,dump-node = <&L1_I_101>;
+ qcom,dump-id = <0x61>;
+ };
+ qcom,l1_i_cache102 {
+ qcom,dump-node = <&L1_I_102>;
+ qcom,dump-id = <0x62>;
+ };
+ qcom,l1_i_cache103 {
+ qcom,dump-node = <&L1_I_103>;
+ qcom,dump-id = <0x63>;
+ };
+ qcom,l1_d_cache100 {
+ qcom,dump-node = <&L1_D_100>;
+ qcom,dump-id = <0x80>;
+ };
+ qcom,l1_d_cache101 {
+ qcom,dump-node = <&L1_D_101>;
+ qcom,dump-id = <0x81>;
+ };
+ qcom,l1_d_cache102 {
+ qcom,dump-node = <&L1_D_102>;
+ qcom,dump-id = <0x82>;
+ };
+ qcom,l1_d_cache103 {
+ qcom,dump-node = <&L1_D_103>;
+ qcom,dump-id = <0x83>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8917-mtp.dtsi
index 800ea1c..164b781 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-mtp.dtsi
@@ -68,6 +68,20 @@
status = "ok";
};
+&soc {
+ hbtp {
+ compatible = "qcom,hbtp-input";
+ vcc_ana-supply = <&pm8937_l10>;
+ vcc_dig-supply = <&pm8937_l5>;
+ qcom,afe-load = <50000>;
+ qcom,afe-vtg-min = <2850000>;
+ qcom,afe-vtg-max = <2850000>;
+ qcom,dig-load = <15000>;
+ qcom,dig-vtg-min = <1800000>;
+ qcom,dig-vtg-max = <1800000>;
+ };
+};
+
#include "msm8937-mdss-panels.dtsi"
&mdss_mdp {
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
index b9229e1..858936d 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
@@ -15,13 +15,14 @@
tlmm: pinctrl@1000000 {
compatible = "qcom,msm8917-pinctrl";
reg = <0x1000000 0x300000>;
- interrupts = <0 208 0>;
+ interrupts-extended = <&wakegic GIC_SPI 208 IRQ_TYPE_NONE>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
+ interrupt-parent = <&wakegpio>;
#interrupt-cells = <2>;
-
+#include "msm8917-camera-pinctrl.dtsi"
/* add pingrp for touchscreen */
pmx_ts_int_active {
ts_int_active: ts_int_active {
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi
index 6200b4e..a3b4679 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi
@@ -31,7 +31,6 @@
qcom,lpm-levels {
compatible = "qcom,lpm-levels";
- qcom,use-psci;
#address-cells = <1>;
#size-cells = <0>;
@@ -40,8 +39,6 @@
#address-cells = <1>;
#size-cells = <0>;
label = "perf";
- qcom,spm-device-names = "l2";
- qcom,default-level=<0>;
qcom,psci-mode-shift = <4>;
qcom,psci-mode-mask = <0xf>;
@@ -98,11 +95,12 @@
#size-cells = <0>;
qcom,psci-mode-shift = <0>;
qcom,psci-mode-mask = <0xf>;
+ qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
qcom,pm-cpu-level@0 {
reg = <0>;
- qcom,psci-cpu-mode = <0>;
label = "wfi";
+ qcom,psci-cpu-mode = <1>;
qcom,latency-us = <12>;
qcom,ss-power = <463>;
qcom,energy-overhead = <23520>;
@@ -111,8 +109,8 @@
qcom,pm-cpu-level@1 {
reg = <1>;
- qcom,psci-cpu-mode = <3>;
label = "pc";
+ qcom,psci-cpu-mode = <3>;
qcom,latency-us = <180>;
qcom,ss-power = <429>;
qcom,energy-overhead = <162991>;
@@ -125,7 +123,17 @@
};
};
- qcom,cpu-sleep-status {
- compatible = "qcom,cpu-sleep-status";
+ qcom,rpm-stats@29dba0 {
+ compatible = "qcom,rpm-stats";
+ reg = <0x200000 0x1000>, <0x290014 0x4>;
+ reg-names = "phys_addr_base", "offset_addr";
+ };
+
+ qcom,rpm-master-stats@60150 {
+ compatible = "qcom,rpm-master-stats";
+ reg = <0x60150 0x5000>;
+ qcom,masters = "APSS", "MPSS", "PRONTO", "TZ", "LPASS";
+ qcom,master-stats-version = <2>;
+ qcom,master-offset = <4096>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-cdp.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-cdp.dts
new file mode 100644
index 0000000..9d8a2eb
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-cdp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8937.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8937 CDP";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id= <1 0>;
+ qcom,pmic-id = <0x10019 0x020037 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
index ab86021..832a0ab 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
@@ -103,4 +103,24 @@
focaltech,resume-in-workqueue;
};
};
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "ft5x06_ts";
+ qcom,disp-maxx = <720>;
+ qcom,disp-maxy = <1280>;
+ qcom,panel-maxx = <720>;
+ qcom,panel-maxy = <1400>;
+ qcom,key-codes = <139 172 158>;
+ qcom,y-offset = <0>;
+ };
+
+ led_flash0: qcom,camera-flash {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-type = <1>;
+ qcom,flash-source = <&pmi8937_flash0>;
+ qcom,torch-source = <&pmi8937_torch0>;
+ qcom,switch-source = <&pmi8937_switch>;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-rcm.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-rcm.dts
new file mode 100644
index 0000000..c7fe115
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-rcm.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8937.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8937 RCM";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id= <21 0>;
+ qcom,pmic-id = <0x10019 0x020037 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8940-cdp.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8940-cdp.dts
new file mode 100644
index 0000000..9785a4f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8940-cdp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8940.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8940 CDP";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id = <1 0>;
+ qcom,pmic-id = <0x10019 0x020040 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8940-rcm.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8940-rcm.dts
new file mode 100644
index 0000000..2cab716
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8940-rcm.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8940.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8940 RCM";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id = <21 0>;
+ qcom,pmic-id = <0x10019 0x020040 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts
new file mode 100644
index 0000000..ea6b24a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-pmi8950-cdp-mirror-lake-touch.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8950 CDP ML Touch";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id = <1 4>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi
new file mode 100644
index 0000000..4701101
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "pmi8950.dtsi"
+#include "msm8917-cdp-mirror-lake-touch.dtsi"
+#include "msm8917-audio-cdp.dtsi"
+
+&soc {
+ led_flash0: qcom,camera-flash {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-type = <1>;
+ qcom,flash-source = <&pmi8950_flash0 &pmi8950_flash1>;
+ qcom,torch-source = <&pmi8950_torch0 &pmi8950_torch1>;
+ qcom,switch-source = <&pmi8950_switch>;
+ };
+
+ bluetooth: bt_qca6174 {
+ compatible = "qca,qca6174";
+ qca,bt-reset-gpio = <&tlmm 129 0>; /* BT_EN */
+ };
+};
+
+&qpnp_smbcharger {
+ /delete-property/ dpdm-supply;
+};
+
+&pm8937_gpios {
+ gpio@c400 {
+ qcom,mode = <0>;
+ qcom,output-type = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <2>;
+ qcom,out-strength = <3>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 */
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 130 0x00>;
+ qcom,nq-clkreq = <&pm8937_gpios 5 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
+&mdss_dsi0 {
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+};
+
+&labibb {
+ status = "ok";
+ qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&ibb_regulator {
+ qcom,qpnp-ibb-discharge-resistor = <32>;
+};
+
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <20>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp.dts
new file mode 100644
index 0000000..a32e2b3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-cdp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8950 CDP";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id= <1 0>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-ext-codec-cdp.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-ext-codec-cdp.dts
new file mode 100644
index 0000000..91c4f3a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-ext-codec-cdp.dts
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917 External Audio Codec CDP";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id= <1 1>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
+
+&pm8937_gpios {
+ gpio@c000 {
+ status = "ok";
+ qcom,mode = <1>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <2>;
+ qcom,master-en = <1>;
+ qcom,out-strength = <2>;
+ };
+
+ gpio@c600 {
+ status = "ok";
+ qcom,mode = <1>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ qcom,out-strength = <2>;
+ };
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ status = "okay";
+};
+
+&int_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-mtp.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-mtp.dts
index 3fe60a3..5b458b2 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-mtp.dts
@@ -14,7 +14,8 @@
/dts-v1/;
#include "msm8917.dtsi"
-#include "msm8917-pmi8950-mtp.dtsi"
+#include "msm8917-mtp.dtsi"
+#include "msm8917-pmi8950.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM8917-PMI8950 MTP";
@@ -22,3 +23,23 @@
qcom,board-id= <8 0>;
qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
};
+
+&blsp1_uart1 {
+ status = "ok";
+};
+
+&vendor {
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950-rcm.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-rcm.dts
new file mode 100644
index 0000000..a91bea1
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950-rcm.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8917.dtsi"
+#include "msm8917-cdp.dtsi"
+#include "msm8917-pmi8950.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8917-PMI8950 RCM";
+ compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp";
+ qcom,board-id= <21 0>;
+ qcom,pmic-id = <0x10019 0x010011 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8950.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pmi8950.dtsi
new file mode 100644
index 0000000..9543133
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8950.dtsi
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "pmi8950.dtsi"
+
+&qpnp_smbcharger {
+ qcom,chg-led-sw-controls;
+ qcom,chg-led-support;
+ /delete-property/ dpdm-supply;
+};
+
+&usb_otg {
+ extcon = <&qpnp_smbcharger>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
index f897b59..431a5e5 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
@@ -12,6 +12,7 @@
*/
#include "msm8917-pinctrl.dtsi"
+#include "msm8917-camera-sensor-qrd.dtsi"
&blsp1_uart2 {
status = "ok";
@@ -78,6 +79,12 @@
};
&soc {
+ int_codec: sound {
+ status = "okay";
+ qcom,msm-mbhc-hphl-swh = <1>;
+ qcom,msm-hs-micbias-type = "internal";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
diff --git a/arch/arm64/boot/dts/qcom/msm8917-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8917-vidc.dtsi
new file mode 100644
index 0000000..c3a93af
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8917-vidc.dtsi
@@ -0,0 +1,167 @@
+/* Copyright (c) 2015-2016, 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,vidc@1d00000 {
+ compatible = "qcom,msm-vidc";
+ reg = <0x01d00000 0xff000>;
+ interrupts = <0 44 0>;
+ qcom,hfi-version = "3xx";
+ venus-supply = <&gdsc_venus>;
+ venus-core0-supply = <&gdsc_venus_core0>;
+ clocks = <&clock_gcc clk_gcc_venus0_vcodec0_clk>,
+ <&clock_gcc clk_gcc_venus0_core0_vcodec0_clk>,
+ <&clock_gcc clk_gcc_venus0_ahb_clk>,
+ <&clock_gcc clk_gcc_venus0_axi_clk>;
+ clock-names = "core_clk", "core0_clk", "iface_clk", "bus_clk";
+ qcom,clock-configs = <0x1 0x0 0x0 0x0>;
+ qcom,sw-power-collapse;
+ qcom,slave-side-cp;
+ qcom,dcvs-tbl =
+ <108000 108000 244800 0x00000004>, /* Encoder */
+ <108000 108000 244800 0x0c000000>; /* Decoder */
+ qcom,dcvs-limit =
+ <8160 30>, /* Encoder */
+ <8160 30>; /* Decoder */
+ qcom,hfi = "venus";
+ qcom,reg-presets = <0xe0020 0x05555556>,
+ <0xe0024 0x05555556>,
+ <0x80124 0x00000003>;
+ qcom,qdss-presets = <0x826000 0x1000>,
+ <0x827000 0x1000>,
+ <0x822000 0x1000>,
+ <0x803000 0x1000>,
+ <0x9180000 0x1000>,
+ <0x9181000 0x1000>;
+ qcom,max-hw-load = <352800>; /* 1080p@30 + 720p@30 */
+ qcom,pm-qos-latency-us = <651>;
+ qcom,firmware-name = "venus";
+ qcom,allowed-clock-rates = <360000000 329140000
+ 308570000 270000000 200000000>;
+ qcom,clock-freq-tbl {
+ qcom,profile-enc {
+ qcom,codec-mask = <0x55555555>;
+ qcom,cycles-per-mb = <2470>;
+ qcom,low-power-mode-factor = <32768>;
+ };
+ qcom,profile-dec {
+ qcom,codec-mask = <0xf3ffffff>;
+ qcom,cycles-per-mb = <788>;
+ };
+ qcom,profile-hevcdec {
+ qcom,codec-mask = <0x0c000000>;
+ qcom,cycles-per-mb = <1015>;
+ };
+ };
+ /* MMUs */
+ non_secure_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_ns";
+ iommus = <&apps_iommu 0x800 0x00>,
+ <&apps_iommu 0x807 0x00>,
+ <&apps_iommu 0x808 0x27>,
+ <&apps_iommu 0x811 0x20>;
+ buffer-types = <0xfff>;
+ virtual-addr-pool = <0x5dc00000 0x7f000000
+ 0xdcc00000 0x1000000>;
+ };
+
+ secure_bitstream_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_bitstream";
+ iommus = <&apps_iommu 0x900 0x00>,
+ <&apps_iommu 0x90a 0x04>,
+ <&apps_iommu 0x909 0x22>;
+ buffer-types = <0x241>;
+ virtual-addr-pool = <0x4b000000 0x12c00000>;
+ qcom,secure-context-bank;
+ };
+
+ secure_pixel_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_pixel";
+ iommus = <&apps_iommu 0x90c 0x20>;
+ buffer-types = <0x106>;
+ virtual-addr-pool = <0x25800000 0x25800000>;
+ qcom,secure-context-bank;
+ };
+
+ secure_non_pixel_cb {
+ compatible = "qcom,msm-vidc,context-bank";
+ label = "venus_sec_non_pixel";
+ iommus = <&apps_iommu 0x940 0x00>,
+ <&apps_iommu 0x907 0x08>,
+ <&apps_iommu 0x908 0x20>,
+ <&apps_iommu 0x90d 0x20>;
+ buffer-types = <0x480>;
+ virtual-addr-pool = <0x1000000 0x24800000>;
+ qcom,secure-context-bank;
+ };
+
+ venus_bus_ddr {
+ compatible = "qcom,msm-vidc,bus";
+ label = "venus-ddr";
+ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+ qcom,bus-governor = "venus-ddr-gov";
+ qcom,bus-range-kbps = <1000 917000>;
+ };
+
+ arm9_bus_ddr {
+ compatible = "qcom,msm-vidc,bus";
+ label = "venus-arm9-ddr";
+ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+ qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+ qcom,bus-governor = "performance";
+ qcom,bus-range-kbps = <1 1>;
+ };
+ };
+
+ venus-ddr-gov {
+ compatible = "qcom,msm-vidc,governor,table";
+ name = "venus-ddr-gov";
+ status = "ok";
+ qcom,bus-freq-table {
+ qcom,profile-enc {
+ qcom,codec-mask = <0x55555555>;
+ qcom,load-busfreq-tbl =
+ <244800 841000>, /* 1080p30E */
+ <216000 740000>, /* 720p60E */
+ <194400 680000>, /* FWVGA120E */
+ <144000 496000>, /* VGA120E */
+ <108000 370000>, /* 720p30E */
+ <97200 340000>, /* FWVGA60E */
+ <48600 170000>, /* FWVGA30E */
+ <72000 248000>, /* VGA60E */
+ <36000 124000>, /* VGA30E */
+ <18000 70000>, /* QVGA60E */
+ <9000 35000>, /* QVGA30E */
+ <0 0>;
+ };
+ qcom,profile-dec {
+ qcom,codec-mask = <0xffffffff>;
+ qcom,load-busfreq-tbl =
+ <244800 605000>, /* 1080p30D */
+ <216000 540000>, /* 720p60D */
+ <194400 484000>, /* FWVGA120D */
+ <144000 360000>, /* VGA120D */
+ <108000 270000>, /* 720p30D */
+ <97200 242000>, /* FWVGA60D */
+ <48600 121000>, /* FWVGA30D */
+ <72000 180000>, /* VGA60D */
+ <36000 90000>, /* VGA30D */
+ <18000 45000>, /* HVGA30D */
+ <0 0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8917.dtsi b/arch/arm64/boot/dts/qcom/msm8917.dtsi
index 78410b3..c4b1ec3 100644
--- a/arch/arm64/boot/dts/qcom/msm8917.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917.dtsi
@@ -21,7 +21,7 @@
model = "Qualcomm Technologies, Inc. MSM8917";
compatible = "qcom,msm8917";
qcom,msm-id = <303 0x0>, <308 0x0>, <309 0x0>;
- interrupt-parent = <&intc>;
+ interrupt-parent = <&wakegic>;
chosen {
bootargs = "sched_enable_hmp=1";
@@ -157,15 +157,18 @@
};
#include "msm8917-pinctrl.dtsi"
+#include "msm8917-camera.dtsi"
#include "msm8917-cpu.dtsi"
#include "msm8917-pm.dtsi"
#include "msm8917-ion.dtsi"
#include "msm8917-smp2p.dtsi"
+#include "msm8917-coresight.dtsi"
#include "msm8917-bus.dtsi"
#include "msm8917-mdss.dtsi"
#include "msm8917-mdss-pll.dtsi"
#include "msm-arm-smmu-8917.dtsi"
#include "msm8917-gpu.dtsi"
+#include "msm8917-vidc.dtsi"
&soc {
#address-cells = <1>;
@@ -182,20 +185,32 @@
<0x0b002000 0x1000>;
};
+ dcc: dcc@b3000 {
+ compatible = "qcom,dcc";
+ reg = <0xb3000 0x1000>,
+ <0xb4000 0x2000>;
+ reg-names = "dcc-base", "dcc-ram-base";
+
+ clocks = <&clock_gcc clk_gcc_dcc_clk>;
+ clock-names = "apb_pclk";
+
+ qcom,save-reg;
+ };
+
wakegic: wake-gic {
- compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8937";
+ compatible = "qcom,mpm-gic-msm8937", "qcom,mpm-gic";
interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
reg = <0x601d0 0x1000>,
<0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
reg-names = "vmpm", "ipc";
- qcom,num-mpm-irqs = <96>;
+ qcom,num-mpm-irqs = <64>;
interrupt-controller;
interrupt-parent = <&intc>;
#interrupt-cells = <3>;
};
wakegpio: wake-gpio {
- compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8937";
+ compatible = "qcom,mpm-gpio-msm8937", "qcom,mpm-gpio";
interrupt-controller;
interrupt-parent = <&intc>;
#interrupt-cells = <2>;
@@ -481,6 +496,23 @@
#clock-cells = <1>;
};
+ msm_cpufreq: qcom,msm-cpufreq {
+ compatible = "qcom,msm-cpufreq";
+ clock-names = "cpu0_clk", "cpu1_clk", "cpu2_clk",
+ "cpu3_clk";
+ clocks = <&clock_cpu clk_a53_bc_clk>,
+ <&clock_cpu clk_a53_bc_clk>,
+ <&clock_cpu clk_a53_bc_clk>,
+ <&clock_cpu clk_a53_bc_clk>;
+
+ qcom,cpufreq-table =
+ < 960000 >,
+ < 1094400 >,
+ < 1248000 >,
+ < 1401000 >,
+ < 1497600 >;
+ };
+
i2c_2: i2c@78b6000 { /* BLSP1 QUP2 */
compatible = "qcom,i2c-msm-v2";
#address-cells = <1>;
@@ -712,6 +744,24 @@
qcom,target-dev = <&cpubw>;
};
+ devfreq-cpufreq {
+ cpubw-cpufreq {
+ target-dev = <&cpubw>;
+ cpu-to-dev-map =
+ < 998400 4248 >,
+ < 1094400 4541 >,
+ < 1497600 5645 >;
+ };
+
+ mincpubw-cpufreq {
+ target-dev = <&mincpubw>;
+ cpu-to-dev-map =
+ < 998400 2270 >,
+ < 1094400 4248 >,
+ < 1497600 4248 >;
+ };
+ };
+
qcom,wdt@b017000 {
compatible = "qcom,msm-watchdog";
reg = <0xb017000 0x1000>;
@@ -723,6 +773,32 @@
qcom,wakeup-enable;
};
+ qcom,memshare {
+ compatible = "qcom,memshare";
+
+ qcom,client_1 {
+ compatible = "qcom,memshare-peripheral";
+ qcom,peripheral-size = <0x200000>;
+ qcom,client-id = <0>;
+ qcom,allocate-boot-time;
+ label = "modem";
+ };
+
+ qcom,client_2 {
+ compatible = "qcom,memshare-peripheral";
+ qcom,peripheral-size = <0x300000>;
+ qcom,client-id = <2>;
+ label = "modem";
+ };
+
+ mem_client_3_size: qcom,client_3 {
+ compatible = "qcom,memshare-peripheral";
+ qcom,peripheral-size = <0x0>;
+ qcom,client-id = <1>;
+ label = "modem";
+ };
+ };
+
spmi_bus: qcom,spmi@200f000 {
compatible = "qcom,spmi-pmic-arb";
reg = <0x200f000 0x1000>,
@@ -781,6 +857,64 @@
};
+ jtag_fuse: jtagfuse@a601c {
+ compatible = "qcom,jtag-fuse-v2";
+ reg = <0xa601c 0x8>;
+ reg-names = "fuse-base";
+ };
+
+ jtag_mm0: jtagmm@61bc000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bc000 0x1000>,
+ <0x61b0000 0x1000>;
+ reg-names = "etm-base", "debug-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ jtag_mm1: jtagmm@61bd000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bd000 0x1000>,
+ <0x61b2000 0x1000>;
+ reg-names = "etm-base", "debug-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU1>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ jtag_mm2: jtagmm@61be000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61be000 0x1000>,
+ <0x61b4000 0x1000>;
+ reg-names = "etm-base", "debug-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU2>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ jtag_mm3: jtagmm@61bf000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bf000 0x1000>,
+ <0x61b6000 0x1000>;
+ reg-names = "etm-base", "debug-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU3>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
qcom,ipc-spinlock@1905000 {
compatible = "qcom,ipc-spinlock-sfpb";
reg = <0x1905000 0x8000>;
@@ -1140,6 +1274,10 @@
qcom,ce-opp-freq = <100000000>;
};
+ qcom,iris-fm {
+ compatible = "qcom,iris_fm";
+ };
+
qcom,mss@4080000 {
compatible = "qcom,pil-q6v55-mss";
reg = <0x04080000 0x100>,
@@ -1422,6 +1560,86 @@
status = "disabled";
};
+ qcom,wcnss-wlan@a000000 {
+ compatible = "qcom,wcnss_wlan";
+ reg = <0xa000000 0x280000>,
+ <0xb011008 0x4>,
+ <0xa21b000 0x3000>,
+ <0x3204000 0x100>,
+ <0x3200800 0x200>,
+ <0xa100400 0x200>,
+ <0xa205050 0x200>,
+ <0xa219000 0x20>,
+ <0xa080488 0x8>,
+ <0xa080fb0 0x8>,
+ <0xa08040c 0x8>,
+ <0xa0120a8 0x8>,
+ <0xa012448 0x8>,
+ <0xa080c00 0x1>;
+
+ reg-names = "wcnss_mmio", "wcnss_fiq",
+ "pronto_phy_base", "riva_phy_base",
+ "riva_ccu_base", "pronto_a2xb_base",
+ "pronto_ccpu_base", "pronto_saw2_base",
+ "wlan_tx_phy_aborts","wlan_brdg_err_source",
+ "wlan_tx_status", "alarms_txctl",
+ "alarms_tactl", "pronto_mcu_base";
+
+ interrupts = <0 145 0 0 146 0>;
+ interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
+
+ qcom,pronto-vddmx-supply = <&pm8937_l3_level_ao>;
+ qcom,pronto-vddcx-supply = <&pm8937_s2_level>;
+ qcom,pronto-vddpx-supply = <&pm8937_l5>;
+ qcom,iris-vddxo-supply = <&pm8937_l7>;
+ qcom,iris-vddrfa-supply = <&pm8937_l19>;
+ qcom,iris-vddpa-supply = <&pm8937_l9>;
+ qcom,iris-vdddig-supply = <&pm8937_l5>;
+
+ qcom,iris-vddxo-voltage-level = <1800000 0 1800000>;
+ qcom,iris-vddrfa-voltage-level = <1300000 0 1300000>;
+ qcom,iris-vddpa-voltage-level = <3300000 0 3300000>;
+ qcom,iris-vdddig-voltage-level = <1800000 0 1800000>;
+
+ qcom,vddmx-voltage-level = <RPM_SMD_REGULATOR_LEVEL_TURBO
+ RPM_SMD_REGULATOR_LEVEL_NONE
+ RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,vddcx-voltage-level = <RPM_SMD_REGULATOR_LEVEL_NOM
+ RPM_SMD_REGULATOR_LEVEL_NONE
+ RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,vddpx-voltage-level = <1800000 0 1800000>;
+
+ qcom,iris-vddxo-current = <10000>;
+ qcom,iris-vddrfa-current = <100000>;
+ qcom,iris-vddpa-current = <515000>;
+ qcom,iris-vdddig-current = <10000>;
+
+ qcom,pronto-vddmx-current = <0>;
+ qcom,pronto-vddcx-current = <0>;
+ qcom,pronto-vddpx-current = <0>;
+
+ pinctrl-names = "wcnss_default", "wcnss_sleep",
+ "wcnss_gpio_default";
+ pinctrl-0 = <&wcnss_default>;
+ pinctrl-1 = <&wcnss_sleep>;
+ pinctrl-2 = <&wcnss_gpio_default>;
+
+ gpios = <&tlmm 76 0>, <&tlmm 77 0>, <&tlmm 78 0>,
+ <&tlmm 79 0>, <&tlmm 80 0>;
+
+ clocks = <&clock_gcc clk_xo_wlan_clk>,
+ <&clock_gcc clk_rf_clk2>,
+ <&clock_debug clk_gcc_debug_mux_8937>,
+ <&clock_gcc clk_wcnss_m_clk>;
+
+ clock-names = "xo", "rf_clk", "measure", "wcnss_debug";
+
+ qcom,has-autodetect-xo;
+ qcom,is-pronto-v3;
+ qcom,has-pronto-hw;
+ qcom,has-vsys-adc-channel;
+ qcom,wcnss-adc_tm = <&pm8937_adc_tm>;
+ };
};
#include "pm8937-rpm-regulator.dtsi"
@@ -1494,3 +1712,141 @@
qcom,clk-dis-wait-val = <0x5>;
status = "okay";
};
+
+/* GPU overrides */
+&msm_gpu {
+
+ qcom,gpu-speed-bin = <0x6018 0x80000000 31>;
+ /delete-node/qcom,gpu-pwrlevels;
+
+ qcom,gpu-pwrlevel-bins {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible="qcom,gpu-pwrlevel-bins";
+
+ qcom,gpu-pwrlevels-0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <0>;
+ qcom,initial-pwrlevel = <3>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <598000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <7>;
+ };
+
+ /* NOM+ */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <523200000>;
+ qcom,bus-freq = <6>;
+ qcom,bus-min = <5>;
+ qcom,bus-max = <7>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <484800000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <6>;
+ };
+
+ /* SVS+ */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <270000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <1>;
+ qcom,bus-max = <3>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ qcom,gpu-pwrlevels-1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <1>;
+ qcom,initial-pwrlevel = <3>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <650000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <8>;
+ };
+
+ /* NOM+ */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <523200000>;
+ qcom,bus-freq = <6>;
+ qcom,bus-min = <5>;
+ qcom,bus-max = <7>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <484800000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <6>;
+ };
+
+ /* SVS+ */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <270000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <1>;
+ qcom,bus-max = <3>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-audio.dtsi b/arch/arm64/boot/dts/qcom/msm8937-audio.dtsi
index 2e4d38e..ffc266f 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-audio.dtsi
@@ -240,16 +240,23 @@
"SpkrRight IN", "SPK2 OUT";
qcom,tasha-mclk-clk-freq = <9600000>;
+ qcom,cdc-us-euro-gpios = <&tlmm 63 0>;
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,msm-mbhc-gnd-swh = <0>;
+ qcom,cdc-us-eu-gpios = <&cdc_us_euro_sw>;
+ qcom,quin-mi2s-gpios = <&cdc_quin_mi2s_gpios>;
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>;
+ <&afe>, <&lsm>, <&routing>, <&cpe>,
+ <&pcm_noirq>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
- "msm-pcm-routing";
+ "msm-pcm-routing", "msm-cpe-lsm",
+ "msm-pcm-dsp-noirq";
asoc-cpu = <&dai_pri_auxpcm>,
<&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s5>,
@@ -281,9 +288,6 @@
asoc-codec = <&stub_codec>, <&hdmi_dba>;
asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-dba-codec-rx";
- qcom,cdc-us-euro-gpios = <&tlmm 63 0>;
- qcom,msm-mbhc-hphl-swh = <0>;
- qcom,msm-mbhc-gnd-swh = <0>;
qcom,wsa-max-devs = <2>;
qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
@@ -292,11 +296,19 @@
"SpkrLeft", "SpkrRight";
};
+ cpe: qcom,msm-cpe-lsm {
+ compatible = "qcom,msm-cpe-lsm";
+ };
+
wcd9xxx_intc: wcd9xxx-irq {
status = "disabled";
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
interrupt-parent = <&tlmm>;
- interrupts = <73 0>;
qcom,gpio-connect = <&tlmm 73 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&wcd_intr_default>;
};
clock_audio: audio_ext_clk {
@@ -306,28 +318,61 @@
qcom,node_has_rpm_clock;
#clock-cells = <1>;
qcom,audio-ref-clk-gpio = <&pm8937_gpios 1 0>;
- qcom,lpass-mclk-id = "pri_mclk";
clocks = <&clock_gcc clk_div_clk2>;
pinctrl-0 = <&cdc_mclk2_sleep>;
pinctrl-1 = <&cdc_mclk2_active>;
};
- wcd_rst_gpio: wcd_gpio_ctrl {
+ wcd_rst_gpio: msm_cdc_pinctrl {
status = "disabled";
- qcom,cdc-rst-n-gpio = <&tlmm 68 0>;
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&cdc_reset_active>;
+ pinctrl-1 = <&cdc_reset_sleep>;
};
};
&slim_msm {
status = "disabled";
+
+ dai_slim: msm_dai_slim {
+ status = "disabled";
+ compatible = "qcom,msm-dai-slim";
+ elemental-addr = [ff ff ff fe 17 02];
+ };
+
wcd9335: tasha_codec {
status = "disabled";
compatible = "qcom,tasha-slim-pgd";
+ elemental-addr = [00 01 A0 01 17 02];
+
+ qcom,cdc-slim-ifd = "tasha-slim-ifd";
+ qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 01 17 02];
+
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29 30>;
+
+ qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
+
clock-names = "wcd_clk", "wcd_native_clk";
clocks = <&clock_audio clk_audio_pmi_clk>,
<&clock_audio clk_audio_ap_clk2>;
- qcom,cdc-reset-gpio = <&tlmm 68 0>;
+ qcom,cdc-static-supplies =
+ "cdc-vdd-buck",
+ "cdc-buck-sido",
+ "cdc-vdd-tx-h",
+ "cdc-vdd-rx-h",
+ "cdc-vdd-px";
+ qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
+ qcom,cdc-micbias1-mv = <1800>;
+ qcom,cdc-micbias2-mv = <1800>;
+ qcom,cdc-micbias3-mv = <1800>;
+ qcom,cdc-micbias4-mv = <1800>;
+
+ qcom,cdc-dmic-sample-rate = <2400000>;
+ qcom,cdc-mclk-clk-rate = <9600000>;
cdc-vdd-buck-supply = <&eldo2_pm8937>;
qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
@@ -355,18 +400,6 @@
};
};
-&pm8937_gpios {
- gpio@c000 {
- status = "ok";
- qcom,mode = <1>;
- qcom,pull = <5>;
- qcom,vin-sel = <0>;
- qcom,src-sel = <2>;
- qcom,master-en = <1>;
- qcom,out-strength = <2>;
- };
-};
-
&pm8937_1 {
pmic_analog_codec: analog-codec@f000 {
status = "okay";
diff --git a/arch/arm64/boot/dts/qcom/msm8937-coresight.dtsi b/arch/arm64/boot/dts/qcom/msm8937-coresight.dtsi
index b952908..e64af14 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-coresight.dtsi
@@ -462,6 +462,8 @@
reg = <0x619c000 0x1000>;
cpu = <&CPU4>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm4";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -481,6 +483,8 @@
reg = <0x619d000 0x1000>;
cpu = <&CPU5>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm5";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -500,6 +504,8 @@
reg = <0x619e000 0x1000>;
cpu = <&CPU6>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm6";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -519,6 +525,8 @@
reg = <0x619f000 0x1000>;
cpu = <&CPU7>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm7";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -538,6 +546,8 @@
reg = <0x61bc000 0x1000>;
cpu = <&CPU0>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm0";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -557,6 +567,8 @@
reg = <0x61bd000 0x1000>;
cpu = <&CPU1>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm1";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -576,6 +588,8 @@
reg = <0x61be000 0x1000>;
cpu = <&CPU2>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm2";
clocks = <&clock_gcc clk_qdss_clk>,
@@ -594,6 +608,8 @@
arm,primecell-periphid = <0x000bb95d>;
reg = <0x61bf000 0x1000>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm3";
cpu = <&CPU3>;
@@ -957,26 +973,13 @@
clock-names = "apb_pclk";
};
- cti_modem_cpu0: cti@6128000 {
- compatible = "arm,primecell";
- arm,primecell-periphid = <0x0003b966>;
-
- reg = <0x6128000 0x1000>;
- reg-names = "cti-base";
- coresight-name = "coresight-cti-modem-cpu0";
-
- clocks = <&clock_gcc clk_qdss_clk>,
- <&clock_gcc clk_qdss_a_clk>;
- clock-names = "apb_pclk";
- };
-
- cti_modem_cpu1: cti@6124000{
+ cti_modem_cpu0: cti@6124000{
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b966>;
reg = <0x6124000 0x1000>;
reg-names = "cti-base";
- coresight-name = "coresight-cti-modem-cpu1";
+ coresight-name = "coresight-cti-modem-cpu0";
clocks = <&clock_gcc clk_qdss_clk>,
<&clock_gcc clk_qdss_a_clk>;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi
index 5ae93333..433ed7c 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi
@@ -23,6 +23,10 @@
/delete-node/ cti@61b9000;
/delete-node/ cti@61ba000;
/delete-node/ cti@61bb000;
+ /delete-node/ jtagmm@619c000;
+ /delete-node/ jtagmm@619d000;
+ /delete-node/ jtagmm@619e000;
+ /delete-node/ jtagmm@619f000;
qcom,spm@b1d2000 {
qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
@@ -55,4 +59,13 @@
};
/delete-node/ cpuss0-step;
+
+ quiet-therm-step {
+ cooling-maps {
+ /delete-node/ skin_cpu4;
+ /delete-node/ skin_cpu5;
+ /delete-node/ skin_cpu6;
+ /delete-node/ skin_cpu7;
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439-qrd.dts b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439-qrd.dts
index 71157e2..2bad28b 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439-qrd.dts
@@ -22,10 +22,3 @@
qcom,board-id = <0xb 2>;
qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
};
-
-&pmi632_vadc {
- chan@4a {
- qcom,scale-function = <22>;
- };
-};
-
diff --git a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
index 716ef73..ad6511c 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
@@ -177,4 +177,5 @@
&mdss_dsi {
vdda-supply = <&pm8953_l2>;
+ vddio-supply = <&pm8953_l6>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
index 436b8a7..528e8aa 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi
@@ -23,6 +23,10 @@
#include "dsi-adv7533-720p.dtsi"
#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
#include "dsi-panel-hx8399c-hd-plus-video.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-video.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi"
+#include "dsi-panel-icn9706-720-1440p-video.dtsi"
+
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi
index 42f1b5c..90685e9 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi
@@ -152,5 +152,45 @@
linux,can-disable;
gpio-key,wakeup;
};
+
+ };
+};
+
+&pm8937_gpios {
+ nfc_clk {
+ nfc_clk_default: nfc_clk_default {
+ pins = "gpio5";
+ function = "normal";
+ input-enable;
+ power-source = <1>;
+ };
+ };
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+ status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 17 0x00>;
+ qcom,nq-ven = <&tlmm 16 0x00>;
+ qcom,nq-firm = <&tlmm 130 0x00>;
+ qcom,nq-clkreq = <&pm8937_gpios 5 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK2";
+ interrupts = <17 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active
+ &nfc_clk_default>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ clocks = <&clock_gcc clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
+&thermal_zones {
+ quiet-therm-step {
+ status = "disabled";
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi
index 86e41a3..a730287 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-pinctrl.dtsi
@@ -319,6 +319,88 @@
};
};
+ spi7 {
+ spi7_default: spi7_default {
+ mux {
+ pins = "gpio85", "gpio86", "gpio88";
+ function = "blsp_spi7";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio88";
+ drive-strength = <16>;
+ bias-disable = <0>;
+ };
+ };
+
+ spi7_sleep: spi7_sleep {
+ mux {
+ pins = "gpio85", "gpio86", "gpio88";
+ function = "blsp_spi7";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio88";
+ drive-strength = <16>;
+ bias-disable = <0>;
+ };
+ };
+ spi7_cs0_active: cs0_active {
+ mux {
+ pins = "gpio87";
+ function = "blsp_spi7_cs0";
+ };
+
+ config {
+ pins = "gpio87";
+ drive-strength = <2>;
+ bias-disable = <0>;
+ };
+ };
+ };
+
+ fpc_reset_int {
+ fpc_reset_low: reset_low {
+ mux {
+ pins = "gpio124";
+ function = "fpc_reset_gpio_low";
+ };
+
+ config {
+ pins = "gpio124";
+ drive-strength = <2>;
+ bias-disable;
+ output-low;
+ };
+ };
+
+ fpc_reset_high: reset_high {
+ mux {
+ pins = "gpio124";
+ function = "fpc_reset_gpio_high";
+ };
+
+ config {
+ pins = "gpio124";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ fpc_int_low: int_low {
+ mux {
+ pins = "gpio48";
+ };
+ config {
+ pins = "gpio48";
+ drive-strength = <2>;
+ bias-pull-down;
+ input-enable;
+ };
+ };
+ };
+
wcnss_pmux_5wire {
/* Active configuration of bus pins */
wcnss_default: wcnss_default {
diff --git a/arch/arm64/boot/dts/qcom/msm8937-regulator.dtsi b/arch/arm64/boot/dts/qcom/msm8937-regulator.dtsi
index 44bdfc9..d6f24d9 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-regulator.dtsi
@@ -62,7 +62,7 @@
pm8937_cx_cdev: regulator-cx-cdev {
compatible = "qcom,regulator-cooling-device";
regulator-cdev-supply = <&pm8937_s2_floor_level>;
- regulator-levels = <RPM_SMD_REGULATOR_LEVEL_NOM
+ regulator-levels = <RPM_SMD_REGULATOR_LEVEL_NOM_PLUS
RPM_SMD_REGULATOR_LEVEL_RETENTION>;
#cooling-cells = <2>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi
index c4d5b90..ce6692d 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi
@@ -78,7 +78,9 @@
secure_bitstream_cb {
compatible = "qcom,msm-vidc,context-bank";
label = "venus_sec_bitstream";
- iommus = <&apps_iommu 0x90c 0x20>;
+ iommus = <&apps_iommu 0x900 0x00>,
+ <&apps_iommu 0x90a 0x04>,
+ <&apps_iommu 0x909 0x22>;
buffer-types = <0x241>;
virtual-addr-pool = <0x4b000000 0x12c00000>;
qcom,secure-context-bank;
@@ -87,10 +89,7 @@
secure_pixel_cb {
compatible = "qcom,msm-vidc,context-bank";
label = "venus_sec_pixel";
- iommus = <&apps_iommu 0x940 0x00>,
- <&apps_iommu 0x907 0x08>,
- <&apps_iommu 0x908 0x20>,
- <&apps_iommu 0x90d 0x20>;
+ iommus = <&apps_iommu 0x90c 0x20>;
buffer-types = <0x106>;
virtual-addr-pool = <0x25800000 0x25800000>;
qcom,secure-context-bank;
@@ -99,9 +98,10 @@
secure_non_pixel_cb {
compatible = "qcom,msm-vidc,context-bank";
label = "venus_sec_non_pixel";
- iommus = <&apps_iommu 0x900 0x00>,
- <&apps_iommu 0x90a 0x04>,
- <&apps_iommu 0x909 0x22>;
+ iommus = <&apps_iommu 0x940 0x00>,
+ <&apps_iommu 0x907 0x08>,
+ <&apps_iommu 0x908 0x20>,
+ <&apps_iommu 0x90d 0x20>;
buffer-types = <0x480>;
virtual-addr-pool = <0x1000000 0x24800000>;
qcom,secure-context-bank;
diff --git a/arch/arm64/boot/dts/qcom/msm8937.dtsi b/arch/arm64/boot/dts/qcom/msm8937.dtsi
index 7b9f74b..85e3043 100644
--- a/arch/arm64/boot/dts/qcom/msm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937.dtsi
@@ -30,6 +30,11 @@
firmware: firmware {
android {
compatible = "android,firmware";
+ vbmeta {
+ compatible = "android,vbmeta";
+ parts = "vbmeta,boot,system,vendor,dtbo,recovery";
+ };
+
fstab {
compatible = "android,fstab";
vendor {
@@ -37,7 +42,7 @@
dev = "/dev/block/platform/soc/7824900.sdhci/by-name/vendor";
type = "ext4";
mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait";
+ fsmgr_flags = "wait,avb";
status = "ok";
};
system {
@@ -45,7 +50,7 @@
dev = "/dev/block/platform/soc/7824900.sdhci/by-name/system";
type = "ext4";
mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait";
+ fsmgr_flags = "wait,avb";
status = "ok";
};
@@ -127,7 +132,7 @@
dump_mem: mem_dump_region {
compatible = "shared-dma-pool";
reusable;
- size = <0 0x2400000>;
+ size = <0x400000>;
};
};
@@ -208,7 +213,7 @@
reg = <0x601d0 0x1000>,
<0xb011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
reg-names = "vmpm", "ipc";
- qcom,num-mpm-irqs = <96>;
+ qcom,num-mpm-irqs = <64>;
interrupt-controller;
interrupt-parent = <&intc>;
#interrupt-cells = <3>;
@@ -325,16 +330,6 @@
compatible = "qcom,mem-dump";
memory-region = <&dump_mem>;
- rpmh_dump {
- qcom,dump-size = <0x2000000>;
- qcom,dump-id = <0xec>;
- };
-
- fcm_dump {
- qcom,dump-size = <0x8400>;
- qcom,dump-id = <0xee>;
- };
-
rpm_sw_dump {
qcom,dump-size = <0x28000>;
qcom,dump-id = <0xea>;
@@ -433,6 +428,7 @@
<0x00a6018 0x00004>;
reg-names = "cc_base", "apcs_c1_base",
"apcs_c0_base", "efuse";
+ qcom,gfx3d_clk_src-opp-store-vcorner = <&msm_gpu>;
vdd_dig-supply = <&pm8937_s2_level>;
vdd_sr2_dig-supply = <&pm8937_s2_level_ao>;
vdd_sr2_pll-supply = <&pm8937_l7_ao>;
@@ -1002,7 +998,7 @@
reg-names = "wdt-base";
interrupts = <0 3 0>, <0 4 0>;
qcom,bark-time = <11000>;
- qcom,pet-time = <10000>;
+ qcom,pet-time = <9360>;
qcom,ipi-ping;
qcom,wakeup-enable;
status = "okay";
@@ -1111,6 +1107,94 @@
};
};
+ jtag_mm0: jtagmm@61bc000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bc000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm1: jtagmm@61bd000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bd000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU1>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm2: jtagmm@61be000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61be000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU2>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm3: jtagmm@61bf000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bf000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU3>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm4: jtagmm@619c000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619c000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU4>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm5: jtagmm@619d000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619d000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU5>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm6: jtagmm@619e000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619e000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU6>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm7: jtagmm@619f000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619f000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>;
+ clock-names = "core_clk";
+ };
+
qcom,smdtty {
compatible = "qcom,smdtty";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi
index 6e961b1..d47dd75 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-cdp.dtsi
@@ -207,8 +207,8 @@
cam_v_custom1-supply = <&pm8953_l23>;
qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf",
"cam_vana", "cam_v_custom1";
- qcom,cam-vreg-min-voltage = <0 1200000 2850000 2800000 1220000>;
- qcom,cam-vreg-max-voltage = <0 1200000 2850000 2800000 1220000>;
+ qcom,cam-vreg-min-voltage = <0 1200000 2850000 2800000 1200000>;
+ qcom,cam-vreg-max-voltage = <0 1200000 2850000 2800000 1200000>;
qcom,cam-vreg-op-mode = <0 105000 100000 80000 105000>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk0_default
diff --git a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi
index 6e961b1..d47dd75 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-camera-sensor-mtp.dtsi
@@ -207,8 +207,8 @@
cam_v_custom1-supply = <&pm8953_l23>;
qcom,cam-vreg-name = "cam_vio", "cam_vdig", "cam_vaf",
"cam_vana", "cam_v_custom1";
- qcom,cam-vreg-min-voltage = <0 1200000 2850000 2800000 1220000>;
- qcom,cam-vreg-max-voltage = <0 1200000 2850000 2800000 1220000>;
+ qcom,cam-vreg-min-voltage = <0 1200000 2850000 2800000 1200000>;
+ qcom,cam-vreg-max-voltage = <0 1200000 2850000 2800000 1200000>;
qcom,cam-vreg-op-mode = <0 105000 100000 80000 105000>;
pinctrl-names = "cam_default", "cam_suspend";
pinctrl-0 = <&cam_sensor_mclk0_default
diff --git a/arch/arm64/boot/dts/qcom/msm8953-coresight.dtsi b/arch/arm64/boot/dts/qcom/msm8953-coresight.dtsi
index 196a526..d3c2e26 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-coresight.dtsi
@@ -505,6 +505,8 @@
reg = <0x619c000 0x1000>;
cpu = <&CPU0>;
+
+ qcom,tupwr-disable;
coresight-name = "coresight-etm0";
clocks = <&clock_gcc clk_qdss_clk>,
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
index b182a25..67ed197 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
@@ -23,6 +23,24 @@
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+&vendor {
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "batterydata-itech-3000mah.dtsi"
+ #include "batterydata-ascent-3450mAh.dtsi"
+ };
+};
+
+&qpnp_fg {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+ qcom,battery-data = <&mtp_batterydata>;
+ qcom,chg-led-sw-controls;
+ qcom,chg-led-support;
+};
+
&int_codec {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
index b80583e..e3a5b4a 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
@@ -25,3 +25,75 @@
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+&int_codec {
+ status = "disabled";
+};
+
+&pmic_analog_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
+
+&cdc_pri_mi2s_gpios {
+ status = "disabled";
+};
+
+&wsa881x_analog_vi_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_clk_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_reset_gpio {
+ status = "disabled";
+};
+
+&cdc_comp_gpios {
+ status = "disabled";
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&cdc_us_euro_sw {
+ status = "okay";
+};
+
+&cdc_quin_mi2s_gpios {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ qcom,model = "msm8953-tasha-snd-card";
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-gpu.dtsi b/arch/arm64/boot/dts/qcom/msm8953-gpu.dtsi
index f82b68d..14c4d2b 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-gpu.dtsi
@@ -231,8 +231,8 @@
qcom,gpu-pwrlevel@5 {
reg = <5>;
qcom,gpu-freq = <216000000>;
- qcom,bus-freq = <1>;
- qcom,bus-min = <1>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <2>;
qcom,bus-max = <4>;
};
@@ -240,7 +240,7 @@
qcom,gpu-pwrlevel@6 {
reg = <6>;
qcom,gpu-freq = <133300000>;
- qcom,bus-freq = <1>;
+ qcom,bus-freq = <3>;
qcom,bus-min = <1>;
qcom,bus-max = <4>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
index 39c76cc..c3d4cc5 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
@@ -25,3 +25,18 @@
qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
};
+&kgsl_smmu {
+ qcom,hibernation-support;
+ qcom,static-ns-cbs = <0>;
+ /delete-property/ qcom,skip-init;
+};
+
+&apps_iommu {
+ qcom,hibernation-support;
+ qcom,static-ns-cbs =
+ <15 16 17 18 19>,
+ <20 21 22 23 24 25 26 27 28 29 30>,
+ <31>;
+
+ /delete-property/ qcom,skip-init;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
index f7671dc..a80b4fe 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
@@ -25,6 +25,7 @@
#include "dsi-panel-lt8912-1080p-video.dtsi"
#include "dsi-panel-hx8399c-fhd-plus-video.dtsi"
#include "dsi-panel-hx83100a-800p-video.dtsi"
+#include "dsi-panel-boent51021-1200p-video.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -75,7 +76,13 @@
23 1e 08 09 05 03 04 a0
23 1a 08 09 05 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x1c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x1c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
@@ -86,7 +93,13 @@
23 1e 08 09 05 03 04 a0
23 1a 08 09 05 03 04 a0];
qcom,esd-check-enabled;
- qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x1c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x1c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
};
&dsi_r69006_1080p_video {
@@ -157,3 +170,11 @@
1f 1c 05 06 03 03 04 a0
1f 10 05 06 03 03 04 a0];
};
+
+&dsi_boent51021_1200p_video {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [25 20 08 0a 06 03 04 a0
+ 25 20 08 0a 06 03 04 a0
+ 25 20 08 0a 06 03 04 a0
+ 25 20 08 0a 06 03 04 a0
+ 25 1d 08 0a 06 03 04 a0];
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts
index c6ae512..e4897bc 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp-overlay.dts
@@ -21,7 +21,7 @@
qcom,board-id = <8 0>;
};
-/{
+&vendor {
mtp_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
#include "batterydata-itech-3000mah.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
index 97c6db3..539ac59 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
@@ -37,6 +37,13 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&pmi_haptic{
+ qcom,lra-auto-res-mode="qwd";
+ qcom,lra-high-z="opt1";
+ qcom,lra-res-cal-period = <0>;
+ qcom,wave-play-rate-us = <4165>;
+};
+
&qpnp_smbcharger {
qcom,battery-data = <&mtp_batterydata>;
qcom,chg-led-sw-controls;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
index cc3c392..e8de4ef 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -1731,5 +1731,19 @@
bias-disable;
};
};
+
+ ssusb_mode_sel: ssusb_mode_sel {
+ mux {
+ pins = "gpio12";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12";
+ drive-strength = <2>;
+ bias-disable;
+ input-disable;
+ };
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8953-vidc.dtsi
index cb8cdf2..1558010 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-vidc.dtsi
@@ -88,8 +88,8 @@
<&apps_iommu 0x82c 0x01>,
<&apps_iommu 0x821 0x10>;
buffer-types = <0xfff>;
- virtual-addr-pool = <0x5dc00000 0x7f000000
- 0xdcc00000 0x1000000>;
+ virtual-addr-pool = <0x79000000 0x28000000
+ 0xa1000000 0xc9000000>;
};
secure_bitstream_cb {
@@ -102,7 +102,7 @@
<&apps_iommu 0x926 0x0>,
<&apps_iommu 0x929 0x2>;
buffer-types = <0x241>;
- virtual-addr-pool = <0x4b000000 0x12c00000>;
+ virtual-addr-pool = <0x51000000 0x28000000>;
qcom,secure-context-bank;
};
@@ -113,7 +113,7 @@
<&apps_iommu 0x910 0x0>,
<&apps_iommu 0x92c 0x0>;
buffer-types = <0x106>;
- virtual-addr-pool = <0x25800000 0x25800000>;
+ virtual-addr-pool = <0x29000000 0x28000000>;
qcom,secure-context-bank;
};
@@ -125,7 +125,7 @@
<&apps_iommu 0x925 0x8>,
<&apps_iommu 0x928 0x0>;
buffer-types = <0x480>;
- virtual-addr-pool = <0x1000000 0x24800000>;
+ virtual-addr-pool = <0x1000000 0x28000000>;
qcom,secure-context-bank;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 64e7664..c3178e0 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -17,6 +17,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
#include <dt-bindings/clock/msm-clocks-8953.h>
+#include <dt-bindings/msm/msm-bus-ids.h>
/ {
model = "Qualcomm Technologies, Inc. MSM8953";
@@ -29,9 +30,21 @@
bootargs = "core_ctl_disable_cpumask=0-7 kpti=0";
};
+ vendor: vendor {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0xffffffff>;
+ compatible = "simple-bus";
+ };
+
firmware: firmware {
android {
compatible = "android,firmware";
+ vbmeta {
+ compatible = "android,vbmeta";
+ parts = "vbmeta,boot,system,vendor,dtbo,recovery";
+ };
+
fstab {
compatible = "android,fstab";
vendor {
@@ -39,7 +52,7 @@
dev = "/dev/block/platform/soc/7824900.sdhci/by-name/vendor";
type = "ext4";
mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait";
+ fsmgr_flags = "wait,avb";
status = "ok";
};
system {
@@ -47,7 +60,7 @@
dev = "/dev/block/platform/soc/7824900.sdhci/by-name/system";
type = "ext4";
mnt_flags = "ro,barrier=1,discard";
- fsmgr_flags = "wait";
+ fsmgr_flags = "wait,avb";
status = "ok";
};
@@ -68,7 +81,7 @@
modem_mem: modem_region@0 {
compatible = "removed-dma-pool";
- no-map-fixup;
+ no-map;
reg = <0x0 0x86c00000 0x0 0x6a00000>;
};
@@ -96,7 +109,7 @@
compatible = "shared-dma-pool";
reusable;
alignment = <0 0x400000>;
- size = <0 0x09800000>;
+ size = <0 0x0b400000>;
};
qseecom_mem: qseecom_region@0 {
@@ -142,7 +155,7 @@
dump_mem: mem_dump_region {
compatible = "shared-dma-pool";
reusable;
- size = <0 0x2400000>;
+ size = <0x400000>;
};
};
@@ -168,7 +181,18 @@
spi3 = &spi_3;
};
- soc: soc { };
+ soc: soc {
+ /*
+ * The ordering of these devices is important to boot time
+ * for iot projects.
+ */
+ smem: qcom,smem@86300000 {};
+ rpm_bus: qcom,rpm-smd {};
+ clock_gcc: qcom,gcc@1800000 {};
+ ad_hoc_bus: ad-hoc-bus@580000 {};
+ tlmm: pinctrl@1000000 {};
+ sdhc_1: sdhci@7824900 {};
+ };
};
@@ -350,11 +374,6 @@
compatible = "qcom,mem-dump";
memory-region = <&dump_mem>;
- fcm_dump {
- qcom,dump-size = <0x8400>;
- qcom,dump-id = <0xee>;
- };
-
rpm_sw_dump {
qcom,dump-size = <0x28000>;
qcom,dump-id = <0xea>;
@@ -1223,7 +1242,7 @@
reg-names = "wdt-base";
interrupts = <0 3 0>, <0 4 0>;
qcom,bark-time = <11000>;
- qcom,pet-time = <10000>;
+ qcom,pet-time = <9360>;
qcom,ipi-ping;
qcom,wakeup-enable;
qcom,scandump-size = <0x40000>;
@@ -1322,6 +1341,103 @@
label = "modem";
};
};
+
+ jtag_mm0: jtagmm@619c000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619c000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU0>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm1: jtagmm@619d000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619d000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU1>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm2: jtagmm@619e000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619e000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU2>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm3: jtagmm@619f000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x619f000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU3>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm4: jtagmm@61bc000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bc000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU4>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm5: jtagmm@61bd000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bd000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU5>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm6: jtagmm@61be000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61be000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU6>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
+ jtag_mm7: jtagmm@61bf000 {
+ compatible = "qcom,jtagv8-mm";
+ reg = <0x61bf000 0x1000>;
+ reg-names = "etm-base";
+
+ qcom,coresight-jtagmm-cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "core_clk";
+ };
+
sdcc1_ice: sdcc1ice@7803000 {
compatible = "qcom,ice";
reg = <0x7803000 0x8000>;
diff --git a/arch/arm64/boot/dts/qcom/pm8916.dtsi b/arch/arm64/boot/dts/qcom/pm8916.dtsi
index 7d38151..5204137 100644
--- a/arch/arm64/boot/dts/qcom/pm8916.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8916.dtsi
@@ -101,12 +101,13 @@
reg = <0x3100 0x100>;
#address-cells = <1>;
#size-cells = <0>;
- interrupts = <0x0 0x31 0x0 IRQ_TYPE_NONE>;
+ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eoc-int-en-set";
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
qcom,pmic-revid = <&pm8916_revid>;
+ #thermal-sensor-cells = <1>;
chan@8 {
label = "die_temp";
@@ -145,11 +146,12 @@
pm8916_tz: qcom,temp-alarm@2400 {
compatible = "qcom,qpnp-temp-alarm";
reg = <0x2400 0x100>;
- interrupts = <0x0 0x24 0x0 IRQ_TYPE_NONE>;
+ interrupts = <0x0 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
label = "pm8916_tz";
qcom,channel-num = <8>;
qcom,threshold-set = <0>;
qcom,temp_alarm-vadc = <&pm8916_vadc>;
+ #thermal-sensor-cells = <0>;
};
pm8916_adc_tm: vadc@3400 {
@@ -157,9 +159,9 @@
reg = <0x3400 0x100>;
#address-cells = <1>;
#size-cells = <0>;
- interrupts = <0x0 0x34 0x0 IRQ_TYPE_NONE>,
- <0x0 0x34 0x3 IRQ_TYPE_NONE>,
- <0x0 0x34 0x4 IRQ_TYPE_NONE>;
+ interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x34 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x34 0x4 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eoc-int-en-set",
"high-thr-en-set",
"low-thr-en-set";
@@ -169,6 +171,121 @@
qcom,pmic-revid = <&pm8916_revid>;
};
+ pm8916_chg: qcom,charger {
+ compatible = "qcom,qpnp-linear-charger";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,vddmax-mv = <4200>;
+ qcom,vddsafe-mv = <4200>;
+ qcom,vinmin-mv = <4308>;
+ qcom,ibatsafe-ma = <1440>;
+ qcom,thermal-mitigation = <1440 720 630 0>;
+ qcom,cool-bat-decidegc = <100>;
+ qcom,warm-bat-decidegc = <450>;
+ qcom,cool-bat-mv = <4100>;
+ qcom,warm-bat-mv = <4100>;
+ qcom,ibatmax-warm-ma = <360>;
+ qcom,ibatmax-cool-ma = <360>;
+ qcom,batt-hot-percentage = <25>;
+ qcom,batt-cold-percentage = <80>;
+ qcom,tchg-mins = <232>;
+ qcom,resume-soc = <99>;
+ qcom,chg-vadc = <&pm8916_vadc>;
+ qcom,chg-adc_tm = <&pm8916_adc_tm>;
+
+ status = "disabled";
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts =
+ <0x0 0x10 0x7 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x0 0x10 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x10 0x0 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-names = "chg-done",
+ "chg-failed",
+ "fast-chg-on",
+ "vbat-det-lo";
+ };
+
+ qcom,bat-if@1200 {
+ reg = <0x1200 0x100>;
+ interrupts = <0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x0 0x12 0x0 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "bat-temp-ok",
+ "batt-pres";
+ };
+
+ qcom,usb-chgpth@1300 {
+ reg = <0x1300 0x100>;
+ interrupts =
+ <0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0 0x13 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "usb-over-temp",
+ "chg-gone",
+ "usbin-valid";
+ };
+
+ qcom,chg-misc@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
+
+ pm8916_bms: qcom,vmbms {
+ compatible = "qcom,qpnp-vm-bms";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <0>;
+ qcom,shutdown-soc-valid-limit = <100>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-voltage-calculate-soc-ms = <1000>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,volatge-soc-timeout-ms = <60000>;
+ qcom,low-voltage-threshold = <3450000>;
+ qcom,s3-ocv-tolerence-uv = <1200>;
+ qcom,s2-fifo-length = <5>;
+ qcom,low-soc-fifo-length = <2>;
+ qcom,bms-vadc = <&pm8916_vadc>;
+ qcom,bms-adc_tm = <&pm8916_adc_tm>;
+ qcom,pmic-revid = <&pm8916_revid>;
+
+ qcom,force-s3-on-suspend;
+ qcom,force-s2-in-charging;
+ qcom,report-charger-eoc;
+
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ };
+
+ qcom,qpnp-chg-pres@1008 {
+ reg = <0x1008 0x1>;
+ };
+
+ qcom,vm-bms@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x1 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x2 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x3 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x4 IRQ_TYPE_NONE>,
+ <0x0 0x40 0x5 IRQ_TYPE_NONE>;
+
+ interrupt-names = "leave_cv",
+ "enter_cv",
+ "good_ocv",
+ "ocv_thr",
+ "fifo_update_done",
+ "fsm_state_change";
+ };
+ };
+
pm8916_leds: qcom,leds@a100 {
compatible = "qcom,leds-qpnp";
reg = <0xa100 0x100>;
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index b8f8b68..fb26ab6 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -35,7 +35,8 @@
};
pmi632_vadc: vadc@3100 {
- compatible = "qcom,qpnp-vadc-hc";
+ compatible = "qcom,qpnp-vadc-hc",
+ "qcom,qpnp-adc-hc-pm5";
reg = <0x3100 0x100>;
#address-cells = <1>;
#size-cells = <0>;
@@ -43,8 +44,6 @@
interrupt-names = "eoc-int-en-set";
qcom,adc-vdd-reference = <1875>;
qcom,adc-full-scale-code = <0x70e4>;
- pinctrl-names = "default";
- pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
chan@0 {
label = "ref_gnd";
@@ -154,6 +153,30 @@
qcom,cal-val = <0>;
};
+ chan@2a {
+ label = "bat_therm_PU30";
+ reg = <0x2a>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <24>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
+ chan@6a {
+ label = "bat_therm_PU400";
+ reg = <0x6a>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <25>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ qcom,cal-val = <0>;
+ };
+
chan@4b {
label = "bat_id";
reg = <0x4b>;
@@ -228,7 +251,8 @@
};
pmi632_adc_tm: vadc@3500 {
- compatible = "qcom,qpnp-adc-tm-hc";
+ compatible = "qcom,qpnp-adc-tm-hc",
+ "qcom,qpnp-adc-tm-hc-pm5";
reg = <0x3500 0x100>;
#address-cells = <1>;
#size-cells = <0>;
@@ -279,21 +303,6 @@
gpio-controller;
#gpio-cells = <2>;
qcom,gpios-disallowed = <1>;
-
- quiet_therm {
- quiet_therm_default: quiet_therm_default {
- pins = "gpio3";
- bias-high-impedance;
- };
- };
-
- smb_therm {
- smb_therm_default: smb_therm_default {
- pins = "gpio4";
- bias-high-impedance;
- };
- };
-
};
pmi632_charger: qcom,qpnp-smb5 {
@@ -305,6 +314,10 @@
qcom,pmic-revid = <&pmi632_revid>;
dpdm-supply = <&qusb_phy>;
qcom,auto-recharge-soc = <98>;
+ qcom,chg-vadc = <&pmi632_vadc>;
+ qcom,flash-disable-soc = <10>;
+ qcom,sw-jeita-enable;
+ qcom,step-charging-enable;
qcom,thermal-mitigation
= <3000000 2500000 2000000 1500000
@@ -514,6 +527,16 @@
compatible = "qcom,msm-bcl-soc";
#thermal-sensor-cells = <0>;
};
+
+ pmi632_pbs_client3: qcom,pbs@7400 {
+ compatible = "qcom,qpnp-pbs";
+ reg = <0x7400 0x100>;
+ };
+
+ pmi632_sdam7: qcom,sdam@b600 {
+ compatible = "qcom,spmi-sdam";
+ reg = <0xb600 0x100>;
+ };
};
pmi632_3: qcom,pmi632@3 {
@@ -526,7 +549,7 @@
compatible = "qcom,qpnp-vibrator-ldo";
reg = <0x5700 0x100>;
qcom,vib-ldo-volt-uv = <3000000>;
- qcom,vib-overdrive-volt-uv = <3544000>;
+ qcom,disable-overdrive;
};
pmi632_pwm: qcom,pwms@b300 {
@@ -534,6 +557,36 @@
reg = <0xb300 0x500>;
reg-names = "lpg-base";
#pwm-cells = <2>;
+ nvmem-names = "ppg_sdam";
+ nvmem = <&pmi632_sdam7>;
+ qcom,pbs-client = <&pmi632_pbs_client3>;
+ qcom,lut-sdam-base = <0x80>;
+ qcom,lut-patterns = <0 0 0 14 28 42 56 70 84 100
+ 100 84 70 56 42 28 14 0 0 0>;
+ lpg@1 {
+ qcom,lpg-chan-id = <1>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <19>;
+ qcom,ramp-pattern-repeat;
+ qcom,lpg-sdam-base = <0x48>;
+ };
+ lpg@2 {
+ qcom,lpg-chan-id = <2>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <19>;
+ qcom,ramp-pattern-repeat;
+ qcom,lpg-sdam-base = <0x56>;
+ };
+ lpg@3 {
+ qcom,lpg-chan-id = <3>;
+ qcom,ramp-step-ms = <200>;
+ qcom,ramp-low-index = <0>;
+ qcom,ramp-high-index = <19>;
+ qcom,ramp-pattern-repeat;
+ qcom,lpg-sdam-base = <0x64>;
+ };
};
pmi632_rgb: qcom,leds@d000 {
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
index 8797ea8..fa93918 100644
--- a/arch/arm64/boot/dts/qcom/pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -587,13 +587,15 @@
};
};
- pmi_haptic: qcom,haptic@c000 {
+ pmi_haptic: qcom,haptics@c000 {
compatible = "qcom,qpnp-haptics";
reg = <0xc000 0x100>;
interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "hap-sc-irq", "hap-play-irq";
qcom,pmic-revid = <&pmi8950_revid>;
+ vcc_pon-supply = <&pon_perph_reg>;
+ qcom,int-pwm-freq-khz = <505>;
qcom,play-mode = "direct";
qcom,wave-play-rate-us = <5263>;
qcom,actuator-type = <0>;
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-cdp-base.dts b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp-base.dts
new file mode 100644
index 0000000..e08acea
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp-base.dts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "qcs605-lc.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. QCS605 LC CDP Base SoC";
+ compatible = "qcom,qcs605";
+ qcom,board-id = <1 4>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp-overlay.dts
new file mode 100644
index 0000000..5987fb3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp-overlay.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "qcs605-lc-cdp.dtsi"
+#include "qcs605-lc-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. QC605 LC Groot + PM8005 CDP";
+ compatible = "qcom,qcs605-cdp", "qcom,qcs605", "qcom,cdp";
+ qcom,msm-id = <347 0x0>;
+ qcom,board-id = <1 4>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-cdp.dts b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp.dts
new file mode 100644
index 0000000..753b496
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605-lc.dtsi"
+#include "qcs605-lc-cdp.dtsi"
+#include "qcs605-lc-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. QC605 LC Groot + PM8005 CDP";
+ compatible = "qcom,qcs605-cdp", "qcom,qcs605", "qcom,cdp";
+ qcom,board-id = <1 4>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc-cdp.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp.dtsi
new file mode 100644
index 0000000..e32128c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc-cdp.dtsi
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qcs605-lc-pmic-overlay.dtsi"
+
+&qupv3_se9_2uart {
+ status = "disabled";
+};
+
+&qupv3_se12_2uart {
+ status = "ok";
+};
+
+&qupv3_se8_spi {
+ status = "disabled";
+};
+
+&sdhc_1 {
+ vdd-supply = <&pm660_l19>;
+ qcom,vdd-voltage-level = <2960000 2960000>;
+ qcom,vdd-current-level = <0 570000>;
+
+ vdd-io-supply = <&pm660_l8>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <0 325000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+ status = "ok";
+};
+
+&tlmm {
+ sdc2_cd_on: cd_on {
+ mux {
+ pins = "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio116";
+ drive-strength = <2>;
+ bias-pull-up;
+ };
+ };
+
+ sdc2_cd_off: cd_off {
+ mux {
+ pins = "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio116";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+};
+
+&sdhc_2 {
+ /* VDD external regulator is enabled/disabled by pm660_l18 regulator */
+ vdd-io-supply = <&pm660_l18>;
+ qcom,vdd-io-voltage-level = <1800000 2960000>;
+ qcom,vdd-io-current-level = <0 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+ cd-gpios = <&tlmm 116 0x1>;
+
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
index 15b23de..f8dde39 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi
@@ -369,3 +369,13 @@
&int_codec {
/delete-property/ qcom,ext-disp-audio-rx;
};
+
+&bluetooth {
+ qca,bt-vdd-core-supply = <&pm660_l9>;
+ qca,bt-vdd-pa-supply = <&pm660_l3>;
+ /delete-property/ qca,bt-vdd-ldo-supply;
+};
+
+&qupv3_se6_4uart {
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index 1e1d82c..16ae8de 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -19,39 +19,39 @@
};
&pil_modem_mem {
- reg = <0 0x8b000000 0 0x3e00000>;
+ reg = <0 0x8b000000 0 0x3100000>;
};
&pil_video_mem {
- reg = <0 0x8ee00000 0 0x500000>;
+ reg = <0 0x8e100000 0 0x500000>;
};
&wlan_msa_mem {
- reg = <0 0x8f300000 0 0x100000>;
+ reg = <0 0x8e600000 0 0x100000>;
};
&pil_cdsp_mem {
- reg = <0 0x8f400000 0 0x800000>;
+ reg = <0 0x8e700000 0 0x800000>;
};
&pil_mba_mem {
- reg = <0 0x8fc00000 0 0x200000>;
+ reg = <0 0x8ef00000 0 0x200000>;
};
&pil_adsp_mem {
- reg = <0 0x8fe00000 0 0x1e00000>;
+ reg = <0 0x8f100000 0 0x1e00000>;
};
&pil_ipa_fw_mem {
- reg = <0 0x91c00000 0 0x10000>;
+ reg = <0 0x90f00000 0 0x10000>;
};
&pil_ipa_gsi_mem {
- reg = <0 0x91c10000 0 0x5000>;
+ reg = <0 0x90f10000 0 0x5000>;
};
&pil_gpu_mem {
- reg = <0 0x91c15000 0 0x2000>;
+ reg = <0 0x90f15000 0 0x2000>;
};
&adsp_mem {
@@ -66,6 +66,10 @@
status = "disabled";
};
+&dump_mem {
+ size = <0 0x800000>;
+};
+
&soc {
qcom,rmnet-ipa {
status = "disabled";
@@ -76,6 +80,12 @@
status = "disabled";
};
+&mem_dump {
+ rpmh {
+ qcom,dump-size = <0x400000>;
+ };
+};
+
&thermal_zones {
lmh-dcvs-00 {
trips {
@@ -98,4 +108,276 @@
&msm_gpu {
/delete-node/qcom,gpu-mempools;
+ /delete-node/qcom,gpu-pwrlevel-bins;
+
+ qcom,gpu-pwrlevel-bins {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible="qcom,gpu-pwrlevel-bins";
+
+ qcom,gpu-pwrlevels-0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <0>;
+
+ qcom,initial-pwrlevel = <4>;
+ qcom,ca-target-pwrlevel = <5>;
+
+ /* TURBO_L1 */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <780000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <750000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <9>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <650000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <565000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <430000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <355000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <5>;
+ qcom,bus-max = <8>;
+ };
+
+ /* LOW SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <267000000>;
+ qcom,bus-freq = <6>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* MIN SVS */
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <180000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@8 {
+ reg = <8>;
+ qcom,gpu-freq = <0>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ qcom,gpu-pwrlevels-1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <146>;
+
+ qcom,initial-pwrlevel = <3>;
+ qcom,ca-target-pwrlevel = <4>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <700000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <9>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <650000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <565000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <430000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <355000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <5>;
+ qcom,bus-max = <8>;
+ };
+
+ /* LOW SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <267000000>;
+ qcom,bus-freq = <6>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* MIN SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <180000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <0>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ qcom,gpu-pwrlevels-2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <163>;
+
+ qcom,initial-pwrlevel = <4>;
+ qcom,ca-target-pwrlevel = <5>;
+
+ /* TURBO_L1 */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <780000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <750000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <9>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <650000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <565000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <430000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <355000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <5>;
+ qcom,bus-max = <8>;
+ };
+
+ /* LOW SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <267000000>;
+ qcom,bus-freq = <6>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* MIN SVS */
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <180000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@8 {
+ reg = <8>;
+ qcom,gpu-freq = <0>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sda429.dts b/arch/arm64/boot/dts/qcom/sda429.dts
new file mode 100644
index 0000000..6a26f23
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda429.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda429.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA429 CDP";
+ compatible = "qcom,sda429-cdp", "qcom,sda429", "qcom,cdp";
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+ qcom.pmic-name = "PMI632";
+};
diff --git a/arch/arm64/boot/dts/qcom/sda439.dts b/arch/arm64/boot/dts/qcom/sda439.dts
new file mode 100644
index 0000000..a124c75
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda439.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda439.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA439";
+ compatible = "qcom,sda439";
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+ qcom.pmic-name = "PMI632";
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
index c907977..f20c2ba 100644
--- a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
@@ -14,8 +14,8 @@
/dts-v1/;
#include "sda450.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDA450 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sda670-camera-sensor-hdk.dtsi b/arch/arm64/boot/dts/qcom/sda670-camera-sensor-hdk.dtsi
new file mode 100644
index 0000000..484ed64
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-camera-sensor-hdk.dtsi
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ com,camera-flash@0 {
+ cell-index = <0>;
+ reg = <0x00 0x00>;
+ compatible = "qcom,camera-flash";
+ flash-source = <&pm660l_flash0 &pm660l_flash1>;
+ torch-source = <&pm660l_torch0 &pm660l_torch1>;
+ switch-source = <&pm660l_switch0>;
+ status = "ok";
+ };
+
+ qcom,camera-flash@1 {
+ cell-index = <1>;
+ reg = <0x01 0x00>;
+ compatible = "qcom,camera-flash";
+ flash-source = <&pm660l_flash0 &pm660l_flash1>;
+ torch-source = <&pm660l_torch0 &pm660l_torch1>;
+ switch-source = <&pm660l_switch0>;
+ status = "ok";
+ };
+
+ led_flash_front: qcom,camera-flash@2 {
+ cell-index = <2>;
+ reg = <0x02 0x00>;
+ compatible = "qcom,camera-flash";
+ flash-source = <&pm660l_flash2>;
+ torch-source = <&pm660l_torch2>;
+ switch-source = <&pm660l_switch1>;
+ status = "ok";
+ };
+
+ gpio-regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0x00 0x00>;
+ regulator-name = "actuator_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-enable-ramp-delay = <100>;
+ enable-active-high;
+ gpio = <&tlmm 27 0>;
+ };
+
+ camera_ldo: gpio-regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <0x02 0x00>;
+ regulator-name = "camera_ldo";
+ regulator-min-microvolt = <1352000>;
+ regulator-max-microvolt = <1352000>;
+ regulator-enable-ramp-delay = <233>;
+ enable-active-high;
+ gpio = <&pm660l_gpios 3 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_dvdd_en_default>;
+ vin-supply = <&pm660_s6>;
+ };
+
+ camera_rear_ldo: gpio-regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <0x01 0x00>;
+ regulator-name = "camera_rear_ldo";
+ regulator-min-microvolt = <1352000>;
+ regulator-max-microvolt = <1352000>;
+ regulator-enable-ramp-delay = <135>;
+ enable-active-high;
+ gpio = <&pm660l_gpios 4 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_rear_dvdd_en_default>;
+ vin-supply = <&pm660_s6>;
+ };
+
+ camera_vio_ldo: gpio-regulator@3 {
+ compatible = "regulator-fixed";
+ reg = <0x03 0x00>;
+ regulator-name = "camera_vio_ldo";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-enable-ramp-delay = <233>;
+ enable-active-high;
+ gpio = <&tlmm 29 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&cam_sensor_rear_vio>;
+ vin-supply = <&pm660_s4>;
+ };
+
+ camera_vana_ldo: gpio-regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <0x04 0x00>;
+ regulator-name = "camera_vana_ldo";
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ regulator-enable-ramp-delay = <233>;
+ enable-active-high;
+ gpio = <&tlmm 8 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&cam_sensor_rear_vana>;
+ vin-supply = <&pm660l_bob>;
+ };
+};
+
+&cam_cci {
+ qcom,cam-res-mgr {
+ compatible = "qcom,cam-res-mgr";
+ status = "ok";
+ };
+
+ qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ cci-master = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <2800000>;
+ rgltr-max-voltage = <2800000>;
+ rgltr-load-current = <0>;
+ };
+
+ qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ cci-master = <1>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <2800000>;
+ rgltr-max-voltage = <2800000>;
+ rgltr-load-current = <0>;
+ };
+
+ qcom,actuator@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,actuator";
+ cci-master = <1>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <2800000>;
+ rgltr-max-voltage = <2800000>;
+ rgltr-load-current = <0>;
+ };
+
+ ois_rear: qcom,ois@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,ois";
+ cci-master = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <2800000>;
+ rgltr-max-voltage = <2800000>;
+ rgltr-load-current = <0>;
+ status = "disabled";
+ };
+
+ qcom,eeprom@0 {
+ cell-index = <0>;
+ reg = <0>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&camera_vio_ldo>;
+ cam_vana-supply = <&camera_vana_ldo>;
+ cam_vdig-supply = <&camera_rear_ldo>;
+ cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+ "cam_clk", "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+ rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+ rgltr-load-current = <0 80000 105000 0 0>;
+ gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 30 0>;
+ gpio-reset = <1>;
+ gpio-req-tbl-num = <0 1>;
+ gpio-req-tbl-flags = <1 0>;
+ gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0";
+ sensor-mode = <0>;
+ cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+ clock-names = "cam_clk";
+ clock-cntl-level = "turbo";
+ clock-rates = <24000000>;
+ };
+
+ qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&camera_vio_ldo>;
+ cam_vana-supply = <&camera_vana_ldo>;
+ cam_vdig-supply = <&camera_ldo>;
+ cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_clk", "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <1352000 1800000 2850000 0 2800000>;
+ rgltr-max-voltage = <1352000 1800000 2850000 0 2800000>;
+ rgltr-load-current = <105000 0 80000 0 0>;
+ gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>;
+ gpio-reset = <1>;
+ gpio-req-tbl-num = <0 1>;
+ gpio-req-tbl-flags = <1 0>;
+ gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1";
+ sensor-position = <0>;
+ sensor-mode = <0>;
+ cci-master = <1>;
+ status = "ok";
+ clock-names = "cam_clk";
+ clock-cntl-level = "turbo";
+ clock-rates = <24000000>;
+ };
+
+ qcom,eeprom@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&camera_vio_ldo>;
+ cam_vana-supply = <&camera_vana_ldo>;
+ cam_vdig-supply = <&camera_ldo>;
+ cam_clk-supply = <&titan_top_gdsc>;
+ cam_vaf-supply = <&actuator_regulator>;
+ regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_clk", "cam_vaf";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <1352000 1800000 2850000 0 2800000>;
+ rgltr-max-voltage = <1352000 1800000 2850000 0 2800000>;
+ rgltr-load-current = <105000 0 80000 0 0>;
+ gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>;
+ gpio-reset = <1>;
+ gpio-req-tbl-num = <0 1>;
+ gpio-req-tbl-flags = <1 0>;
+ gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1";
+ sensor-mode = <0>;
+ cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ clock-cntl-level = "turbo";
+ clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@0 {
+ cell-index = <0>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x0>;
+ csiphy-sd-index = <0>;
+ sensor-position-roll = <270>;
+ sensor-position-pitch = <0>;
+ sensor-position-yaw = <180>;
+ led-flash-src = <&led_flash_rear>;
+ actuator-src = <&actuator_rear>;
+ ois-src = <&ois_rear>;
+ eeprom-src = <&eeprom_rear>;
+ cam_vio-supply = <&camera_vio_ldo>;
+ cam_vana-supply = <&camera_vana_ldo>;
+ cam_vdig-supply = <&camera_rear_ldo>;
+ cam_clk-supply = <&titan_top_gdsc>;
+ regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+ "cam_clk";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <1800000 2850000 1352000 0>;
+ rgltr-max-voltage = <1800000 2850000 1352000 0>;
+ rgltr-load-current = <0 80000 105000 0>;
+ gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 30 0>;
+ gpio-reset = <1>;
+ gpio-req-tbl-num = <0 1>;
+ gpio-req-tbl-flags = <1 0>;
+ gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0";
+ sensor-mode = <0>;
+ cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+ clock-names = "cam_clk";
+ clock-cntl-level = "turbo";
+ clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@1 {
+ cell-index = <1>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x1>;
+ csiphy-sd-index = <1>;
+ sensor-position-roll = <270>;
+ sensor-position-pitch = <0>;
+ sensor-position-yaw = <180>;
+ led-flash-src = <&led_flash_rear_aux>;
+ actuator-src = <&actuator_rear_aux>;
+ eeprom-src = <&eeprom_rear_aux>;
+ cam_vio-supply = <&camera_vio_ldo>;
+ cam_vana-supply = <&camera_vana_ldo>;
+ cam_vdig-supply = <&camera_ldo>;
+ cam_clk-supply = <&titan_top_gdsc>;
+ regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_clk";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <1352000 1800000 2850000 0>;
+ rgltr-max-voltage = <1352000 1800000 2850000 0>;
+ rgltr-load-current = <105000 0 80000 0>;
+ gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>;
+ gpio-reset = <1>;
+ gpio-req-tbl-num = <0 1>;
+ gpio-req-tbl-flags = <1 0>;
+ gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1";
+ sensor-mode = <0>;
+ cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ clock-cntl-level = "turbo";
+ clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@2 {
+ cell-index = <2>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x02>;
+ csiphy-sd-index = <2>;
+ sensor-position-roll = <270>;
+ sensor-position-pitch = <0>;
+ sensor-position-yaw = <0>;
+ eeprom-src = <&eeprom_front>;
+ actuator-src = <&actuator_front>;
+ led-flash-src = <&led_flash_front>;
+ cam_vio-supply = <&camera_vio_ldo>;
+ cam_vana-supply = <&camera_vana_ldo>;
+ cam_vdig-supply = <&camera_ldo>;
+ cam_clk-supply = <&titan_top_gdsc>;
+ regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+ "cam_clk";
+ rgltr-cntrl-support;
+ rgltr-min-voltage = <1352000 1800000 2850000 0>;
+ rgltr-max-voltage = <1352000 1800000 2850000 0>;
+ rgltr-load-current = <105000 0 80000 0>;
+ gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>;
+ gpio-reset = <1>;
+ gpio-req-tbl-num = <0 1>;
+ gpio-req-tbl-flags = <1 0>;
+ gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1";
+ sensor-mode = <0>;
+ cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ clock-cntl-level = "turbo";
+ clock-rates = <24000000>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sda670-hdk-overlay.dts b/arch/arm64/boot/dts/qcom/sda670-hdk-overlay.dts
new file mode 100644
index 0000000..a7299a4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-hdk-overlay.dts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "sda670-hdk.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA670 HDK";
+ compatible = "qcom,sda670-hdk", "qcom,sda670", "qcom,hdk";
+ qcom,board-id = <0x01001F 0x00>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda670-hdk.dts b/arch/arm64/boot/dts/qcom/sda670-hdk.dts
new file mode 100644
index 0000000..ed9eec9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-hdk.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda670.dtsi"
+#include "sda670-hdk.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDA670 HDK";
+ compatible = "qcom,sda670-hdk", "qcom,sda670", "qcom,hdk";
+ qcom,board-id = <0x01001F 0x00>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda670-hdk.dtsi b/arch/arm64/boot/dts/qcom/sda670-hdk.dtsi
new file mode 100644
index 0000000..5b1ed9c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-hdk.dtsi
@@ -0,0 +1,59 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sdm670-qrd.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sda670-camera-sensor-hdk.dtsi"
+
+&dsi_dual_nt36850_truly_cmd_display {
+ /delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_hx8399_truly_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_hx8399_truly_cmd_display {
+ qcom,dsi-display-active;
+};
+
+&ufsphy_mem {
+ compatible = "qcom,ufs-phy-qmp-v3";
+
+ vdda-phy-supply = <&pm660l_l1>; /* 0.88v */
+ vdda-pll-supply = <&pm660_l1>; /* 1.2v */
+ vdda-phy-max-microamp = <62900>;
+ vdda-pll-max-microamp = <18300>;
+
+ status = "ok";
+};
+
+&ufshc_mem {
+ vdd-hba-supply = <&ufs_phy_gdsc>;
+ vdd-hba-fixed-regulator;
+ vcc-supply = <&pm660l_l4>;
+ vcc-voltage-level = <2960000 2960000>;
+ vccq2-supply = <&pm660_l8>;
+ vcc-max-microamp = <600000>;
+ vccq2-max-microamp = <600000>;
+
+ qcom,vddp-ref-clk-supply = <&pm660_l1>;
+ qcom,vddp-ref-clk-max-microamp = <100>;
+
+ status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-sdxpoorwills.dtsi b/arch/arm64/boot/dts/qcom/sda845-sdxpoorwills.dtsi
index 0b678a8..c4b4a50 100644
--- a/arch/arm64/boot/dts/qcom/sda845-sdxpoorwills.dtsi
+++ b/arch/arm64/boot/dts/qcom/sda845-sdxpoorwills.dtsi
@@ -32,7 +32,6 @@
/* MDM PON conrol*/
pins = "gpio10";
function = "normal";
- output-low;
power-source = <0>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sda845-svr-pinctrl-overlay.dtsi b/arch/arm64/boot/dts/qcom/sda845-svr-pinctrl-overlay.dtsi
index c76ef2b..19d1370 100644
--- a/arch/arm64/boot/dts/qcom/sda845-svr-pinctrl-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sda845-svr-pinctrl-overlay.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-&cam_sensor_mclk0_active{
+&cam_sensor_mclk0_active {
/* MCLK0 */
mux {
pins = "gpio13";
@@ -20,7 +20,7 @@
config {
pins = "gpio13";
bias-disable; /* No PULL */
- drive-strength = <8>; /* 2 MA */
+ drive-strength = <8>; /* 8 MA */
};
};
@@ -34,19 +34,75 @@
config {
pins = "gpio13";
bias-pull-down; /* PULL DOWN */
- drive-strength = <8>; /* 2 MA */
+ drive-strength = <8>; /* 8 MA */
+ };
+};
+
+&cam_sensor_mclk1_active {
+ /* MCLK1 */
+ mux {
+ pins = "gpio14";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio14";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 8 MA */
+ };
+};
+
+&cam_sensor_mclk1_suspend {
+ /* MCLK1 */
+ mux {
+ pins = "gpio14";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio14";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <8>; /* 8 MA */
+ };
+};
+
+&cam_sensor_mclk2_active {
+ /* MCLK2 */
+ mux {
+ pins = "gpio15";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio15";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 8 MA */
+ };
+};
+
+&cam_sensor_mclk2_suspend {
+ /* MCLK2 */
+ mux {
+ pins = "gpio15";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio15";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <8>; /* 8 MA */
};
};
&cam_sensor_rear_active {
/* RESET, AVDD LDO */
mux {
- pins = "gpio8","gpio79";
+ pins = "gpio8", "gpio29";
function = "gpio";
};
config {
- pins = "gpio8","gpio79";
+ pins = "gpio8", "gpio29";
bias-disable; /* No PULL */
drive-strength = <2>; /* 2 MA */
};
@@ -55,43 +111,115 @@
&cam_sensor_rear_suspend {
/* RESET, AVDD LDO */
mux {
- pins = "gpio8","gpio79";
+ pins = "gpio8", "gpio29";
function = "gpio";
};
config {
- pins = "gpio8","gpio79";
+ pins = "gpio8", "gpio29";
bias-pull-down; /* PULL DOWN */
drive-strength = <2>; /* 2 MA */
output-low;
};
};
-&cam_sensor_front_active{
- /* RESET AVDD_LDO*/
+&cam_sensor_front_active {
+ /* RESET AVDD_LDO */
mux {
- pins = "gpio26", "gpio8";
+ pins = "gpio26", "gpio12";
function = "gpio";
};
config {
- pins = "gpio26", "gpio8";
+ pins = "gpio26", "gpio12";
bias-disable; /* No PULL */
drive-strength = <2>; /* 2 MA */
};
};
-&cam_sensor_front_suspend{
+&cam_sensor_front_suspend {
/* RESET */
mux {
- pins = "gpio26", "gpio8";
+ pins = "gpio26", "gpio12";
function = "gpio";
};
config {
- pins = "gpio26", "gpio8";
+ pins = "gpio26", "gpio12";
bias-pull-down; /* PULL DOWN */
drive-strength = <2>; /* 2 MA */
output-low;
};
};
+
+&cam_sensor_iris_active {
+ /* RESET AVDD_LDO */
+ mux {
+ pins = "gpio21", "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21", "gpio122";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+&cam_sensor_iris_suspend {
+ /* RESET AVDD_LDO */
+ mux {
+ pins = "gpio21", "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio21", "gpio122";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ output-low;
+ };
+};
+
+&cam_sensor_rear_vana {
+ /* AVDD_LDO */
+ mux {
+ pins = "gpio7";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio7";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+&cam_res_mgr_active {
+ /* AVDD_LDO */
+ mux {
+ pins = "gpio79";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio79";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+};
+
+&cam_res_mgr_suspend {
+ /* AVDD_LDO */
+ mux {
+ pins = "gpio79";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio79";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ output-low;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-svr.dtsi b/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
index ce62781..1062ca6 100644
--- a/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
@@ -14,9 +14,12 @@
#include "sdm845-pinctrl-overlay.dtsi"
#include "sda845-svr-pinctrl-overlay.dtsi"
#include "sdm845-camera-sensor-svr.dtsi"
-#include "smb1355.dtsi"
#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
+&qupv3_se10_i2c {
+#include "smb1355.dtsi"
+};
+
&vendor {
bluetooth: bt_wcn3990 {
compatible = "qca,wcn3990";
@@ -249,6 +252,24 @@
#cooling-cells = <2>;
};
+&pm8998_l10 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,init-voltage = <1800000>;
+};
+
+&pm8998_l15 {
+ regulator-min-microvolt = <1504000>;
+ regulator-max-microvolt = <1504000>;
+ qcom,init-voltage = <1504000>;
+};
+
+&pm8998_l16 {
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3312000>;
+ qcom,init-voltage = <3312000>;
+};
+
&ufsphy_mem {
compatible = "qcom,ufs-phy-qmp-v3";
diff --git a/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts
index 93a9ae9..5f0db81 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts
@@ -14,13 +14,9 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clock/msm-clocks-8953.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm429-cdp.dtsi"
/ {
model = "CDP";
qcom,board-id = <1 3>;
- qcom,msm-id = <354 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi b/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi
index ab874c9..6c4ea9f 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-cpu.dtsi
@@ -129,10 +129,10 @@
CPU_COST_0: core-cost0 {
busy-cost-data = <
960000 159
- 1001600 165
1305600 207
1497600 256
1708800 327
+ 1804800 343
1958400 445
>;
idle-cost-data = <
@@ -141,11 +141,11 @@
};
CLUSTER_COST_0: cluster-cost0 {
busy-cost-data = <
- 960000 52
- 1001600 53
+ 960000 53
1305600 61
1497600 71
1708800 85
+ 1804800 88
1958400 110
>;
idle-cost-data = <
@@ -156,5 +156,15 @@
};
&soc {
- /delete-node/ cpuss_dump;
+ cpuss_dump {
+ /delete-node/ qcom,l2_dump0;
+ /delete-node/ qcom,l1_i_cache0;
+ /delete-node/ qcom,l1_i_cache1;
+ /delete-node/ qcom,l1_i_cache2;
+ /delete-node/ qcom,l1_i_cache3;
+ /delete-node/ qcom,l1_d_cache0;
+ /delete-node/ qcom,l1_d_cache1;
+ /delete-node/ qcom,l1_d_cache2;
+ /delete-node/ qcom,l1_d_cache3;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm429-mtp-overlay.dts
index 3a339da..571f0fc 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm429-mtp-overlay.dts
@@ -14,13 +14,9 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clock/msm-clocks-8953.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm429-mtp.dtsi"
/ {
model = "MTP";
qcom,board-id = <8 2>;
- qcom,msm-id = <354 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm429-qrd-overlay.dts
index 8abccb7..8e12295 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm429-qrd-overlay.dts
@@ -14,13 +14,9 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clock/msm-clocks-8953.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm429-qrd.dtsi"
/ {
model = "QRD";
qcom,board-id = <0xb 3>;
- qcom,msm-id = <354 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
index 7116662..dde9c56 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
@@ -12,3 +12,7 @@
*/
#include "sdm439-qrd.dtsi"
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_hx8399c_hd_vid>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429.dtsi b/arch/arm64/boot/dts/qcom/sdm429.dtsi
index 33b352d..b52ee2d 100644
--- a/arch/arm64/boot/dts/qcom/sdm429.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429.dtsi
@@ -28,6 +28,10 @@
/delete-node/ cti@61b9000;
/delete-node/ cti@61ba000;
/delete-node/ cti@61bb000;
+ /delete-node/ jtagmm@619c000;
+ /delete-node/ jtagmm@619d000;
+ /delete-node/ jtagmm@619e000;
+ /delete-node/ jtagmm@619f000;
qcom,spm@b1d2000 {
qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>;
@@ -39,6 +43,7 @@
};
};
+ /delete-node/ qcom,msm-cpufreq;
msm_cpufreq: qcom,msm-cpufreq {
compatible = "qcom,msm-cpufreq";
clock-names =
@@ -54,35 +59,39 @@
< 1305600 >,
< 1497600 >,
< 1708800 >,
+ < 1804800 >,
< 1958400 >;
};
+ /delete-node/ devfreq-cpufreq;
devfreq-cpufreq {
cpubw-cpufreq {
target-dev = <&cpubw>;
cpu-to-dev-map =
- < 960000 2929 >,
- < 1305600 5053 >,
- < 1497600 5712 >,
- < 1708800 7031 >,
- < 1958400 7031 >;
+ < 960000 2929 >,
+ < 1305600 5126 >,
+ < 1497600 5859 >,
+ < 1708800 6445 >,
+ < 1804800 7104 >,
+ < 1958400 7104 >;
};
cci-cpufreq {
target-dev = <&cci_cache>;
cpu-to-dev-map =
- < 960000 400000 >,
+ < 960000 400000 >,
< 1305600 400000 >,
- < 1497600 533333 >,
- < 1708800 533333 >,
- < 1958400 533333 >;
+ < 1497600 400000 >,
+ < 1708800 533000 >,
+ < 1804800 576000 >,
+ < 1958400 576000 >;
};
mincpubw-cpufreq {
target-dev = <&mincpubw>;
cpu-to-dev-map =
< 1305600 2929 >,
- < 1958400 4248 >;
+ < 1804800 5859 >;
};
};
};
@@ -107,6 +116,15 @@
};
/delete-node/ cpuss0-step;
+
+ quiet-therm-step {
+ cooling-maps {
+ /delete-node/ skin_cpu4;
+ /delete-node/ skin_cpu5;
+ /delete-node/ skin_cpu6;
+ /delete-node/ skin_cpu7;
+ };
+ };
};
&clock_gcc {
@@ -128,6 +146,16 @@
};
&soc {
+ devfreq_spdm_cpu {
+ status = "disabled";
+ };
+
+ devfreq_spdm_gov {
+ status = "disabled";
+ };
+};
+
+&soc {
/delete-node/ qcom,cpu-clock-8939@b111050;
clock_cpu: qcom,cpu-clock-8939@b111050 {
compatible = "qcom,cpu-clock-sdm429";
@@ -178,3 +206,20 @@
#clock-cells = <1>;
};
};
+
+&clock_gcc_mdss {
+ compatible = "qcom,gcc-mdss-sdm429";
+ clocks = <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_src>;
+ clock-names = "pclk0_src", "byte0_src", "pclk1_src",
+ "byte1_src";
+ #clock-cells = <1>;
+};
+
+/* GPU overrides */
+&msm_gpu {
+ /* Update GPU chip ID*/
+ qcom,chipid = <0x05000400>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
index f6751d2..a6b2371 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
@@ -15,6 +15,7 @@
int_codec: sound {
qcom,model = "sdm439-snd-card-mtp";
qcom,msm-hs-micbias-type = "internal";
+ qcom,msm-micbias2-ext-cap;
asoc-codec = <&stub_codec>, <&msm_digital_codec>,
<&pmic_analog_codec>;
@@ -24,6 +25,65 @@
qcom,msm-vdd-wsa-switch-voltage = <1800000>;
qcom,msm-vdd-wsa-switch-current = <10000>;
};
+
+ clock_audio_native: audio_ext_clk_native {
+ status = "disabled";
+ compatible = "qcom,audio-ref-clk";
+ #clock-cells = <1>;
+ qcom,codec-mclk-clk-freq = <11289600>;
+ qcom,audio-ref-clk-gpio = <&tlmm 66 0>;
+ qcom,lpass-mclk-id = "pri_mclk";
+ pinctrl-names = "sleep", "active";
+ pinctrl-0 = <&cdc_mclk2_sleep>;
+ pinctrl-1 = <&cdc_mclk2_active>;
+ };
+};
+
+
+&clock_audio {
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&tasha_mclk_default>;
+ pinctrl-1 = <&tasha_mclk_default>;
+ qcom,audio-ref-clk-gpio = <&pm8953_gpios 1 0>;
+};
+
+&wcd9335 {
+ cdc-vdd-buck-supply = <&dbu1>;
+ qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-buck-current = <650000>;
+
+ cdc-buck-sido-supply = <&dbu1>;
+ qcom,cdc-buck-sido-voltage = <1800000 1800000>;
+ qcom,cdc-buck-sido-current = <150000>;
+
+ cdc-vdd-tx-h-supply = <&dbu1>;
+ qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-tx-h-current = <25000>;
+
+ cdc-vdd-rx-h-supply = <&dbu1>;
+ qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-rx-h-current = <25000>;
+
+ cdc-vdd-px-supply = <&dbu1>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+
+ cdc-vdd-mic-bias-supply = <&pm8953_l13>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+ qcom,cdc-vdd-mic-bias-current = <15000>;
+};
+
+&pm8953_gpios {
+ tasha_mclk {
+ tasha_mclk_default: tasha_mclk_default{
+ pins = "gpio1";
+ function = "func1";
+ qcom,drive-strength = <2>;
+ power-source = <0>;
+ bias-disable;
+ output-low;
+ };
+ };
};
&pm8953_1 {
@@ -76,7 +136,7 @@
qcom,cdc-vdd-pa-current = <260000>;
cdc-vdd-mic-bias-supply = <&pm8953_l13>;
- qcom,cdc-vdd-mic-bias-voltage = <3125000 3125000>;
+ qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
qcom,cdc-vdd-mic-bias-current = <5000>;
qcom,cdc-mclk-clk-rate = <9600000>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-cdp.dtsi
index 5e2c740..eae8c56 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-cdp.dtsi
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/clock/msm-clocks-8952.h>
+
&cci {
actuator0: qcom,actuator@0 {
cell-index = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-mtp.dtsi
index 5e2c740..eae8c56 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-mtp.dtsi
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/clock/msm-clocks-8952.h>
+
&cci {
actuator0: qcom,actuator@0 {
cell-index = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-qrd.dtsi
index c2c9c79..ef0e977 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-qrd.dtsi
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/clock/msm-clocks-8952.h>
+
&cci {
actuator0: qcom,actuator@0 {
cell-index = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts
index 6d6f99b..87239b9 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts
@@ -14,13 +14,9 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clock/msm-clocks-8953.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm439-cdp.dtsi"
/ {
model = "CDP";
qcom,board-id = <1 2>;
- qcom,msm-id = <353 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
index f0e550e..6ecd0dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
@@ -150,6 +150,30 @@
};
};
+&cdc_pdm_lines_2_act {
+ mux {
+ pins = "gpio70", "gpio71", "gpio72";
+ function = "cdc_pdm0";
+ };
+
+ config {
+ pins = "gpio70", "gpio71", "gpio72";
+ drive-strength = <16>;
+ };
+};
+
+&cdc_pdm_lines_act {
+ mux {
+ pins = "gpio69", "gpio73", "gpio74";
+ function = "cdc_pdm0";
+ };
+
+ config {
+ pins = "gpio69", "gpio73", "gpio74";
+ drive-strength = <16>;
+ };
+};
+
&pm8953_pwm {
status = "ok";
};
@@ -175,7 +199,10 @@
qcom,platform-reset-gpio = <&tlmm 60 0>;
lab-supply = <&lcdb_ldo_vreg>;
ibb-supply = <&lcdb_ncp_vreg>;
+};
+&mdss_dsi1 {
+ status = "disabled";
};
&dsi_hx8399c_truly_vid {
@@ -188,6 +215,19 @@
qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
};
@@ -199,4 +239,332 @@
qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 10
+ 15 01 00 00 00 00 02 35 00
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 14
+ 00 02 28 00 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings-phy-12nm = [17 0a 0f 06 03 08 06 0e];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,ulps-enabled;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ /delete-node/ qcom,mdss-dsi-display-timings;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 03
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00
+ 14 00 02 28 00 05 01 00 00 78 00
+ 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings-phy-12nm = [17 0a 0f 06 03 08 06 0e];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ /delete-node/ qcom,mdss-dsi-display-timings;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-ext-audio-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-ext-audio-mtp.dtsi
new file mode 100644
index 0000000..a74fb75
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-ext-audio-mtp.dtsi
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&int_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
+
+&cdc_pri_mi2s_gpios {
+ status = "disabled";
+};
+
+&wsa881x_analog_vi_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_clk_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_reset_gpio {
+ status = "disabled";
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&cdc_us_euro_sw {
+ status = "okay";
+};
+
+&cdc_quin_mi2s_gpios {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ status = "okay";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp-overlay.dts
new file mode 100644
index 0000000..37741b2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp-overlay.dts
@@ -0,0 +1,24 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include "sdm439-mtp.dtsi"
+#include "sdm439-external-codec.dtsi"
+
+/ {
+ model = "MTP";
+ qcom,board-id = <8 3>;
+ qcom,msm-id = <353 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp.dts b/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp.dts
new file mode 100644
index 0000000..e2a86bb
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp.dts
@@ -0,0 +1,25 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm439.dtsi"
+#include "sdm439-mtp.dtsi"
+#include "sdm439-external-codec.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM439 Audio Codec MTP";
+ compatible = "qcom,sdm439-mtp", "qcom,sdm439", "qcom,mtp";
+ qcom,board-id = <8 3>;
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-external-codec.dtsi b/arch/arm64/boot/dts/qcom/sdm439-external-codec.dtsi
new file mode 100644
index 0000000..83864ef
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-external-codec.dtsi
@@ -0,0 +1,12 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sdm439-ext-audio-mtp.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm439-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-mtp-overlay.dts
index df8a0d7..a7c5f4e 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm439-mtp-overlay.dts
@@ -14,13 +14,9 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clock/msm-clocks-8953.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm439-mtp.dtsi"
/ {
model = "MTP";
qcom,board-id = <8 1>;
- qcom,msm-id = <353 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
index c6cec53..a940515 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
@@ -175,7 +175,10 @@
qcom,platform-reset-gpio = <&tlmm 60 0>;
lab-supply = <&lcdb_ldo_vreg>;
ibb-supply = <&lcdb_ncp_vreg>;
+};
+&mdss_dsi1 {
+ status = "disabled";
};
&dsi_hx8399c_truly_vid {
@@ -188,6 +191,19 @@
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
};
&dsi_hx8399c_hd_vid {
@@ -198,4 +214,398 @@
qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 10
+ 15 01 00 00 00 00 02 35 00
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 14
+ 00 02 28 00 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings-phy-12nm = [17 0a 0f 06 03 08 06 0e];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,ulps-enabled;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ /delete-node/ qcom,mdss-dsi-display-timings;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <120>;
+ qcom,mdss-dsi-h-back-porch = <60>;
+ qcom,mdss-dsi-h-pulse-width = <12>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-v-back-porch = <2>;
+ qcom,mdss-dsi-v-front-porch = <12>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-on-command =
+ [15 01 00 00 10 00 02 ff 20
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 55
+ 15 01 00 00 00 00 02 02 45
+ 15 01 00 00 00 00 02 03 55
+ 15 01 00 00 00 00 02 05 50
+ 15 01 00 00 00 00 02 06 a8
+ 15 01 00 00 00 00 02 07 ad
+ 15 01 00 00 00 00 02 08 0c
+ 15 01 00 00 00 00 02 0b aa
+ 15 01 00 00 00 00 02 0c aa
+ 15 01 00 00 00 00 02 0e b0
+ 15 01 00 00 00 00 02 0f b3
+ 15 01 00 00 00 00 02 11 28
+ 15 01 00 00 00 00 02 12 10
+ 15 01 00 00 00 00 02 13 01
+ 15 01 00 00 00 00 02 14 4a
+ 15 01 00 00 00 00 02 15 12
+ 15 01 00 00 00 00 02 16 12
+ 15 01 00 00 00 00 02 30 01
+ 15 01 00 00 00 00 02 72 11
+ 15 01 00 00 00 00 02 58 82
+ 15 01 00 00 00 00 02 59 00
+ 15 01 00 00 00 00 02 5a 02
+ 15 01 00 00 00 00 02 5b 00
+ 15 01 00 00 00 00 02 5c 82
+ 15 01 00 00 00 00 02 5d 80
+ 15 01 00 00 00 00 02 5e 02
+ 15 01 00 00 00 00 02 5f 00
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 00 01
+ 15 01 00 00 00 00 02 01 0b
+ 15 01 00 00 00 00 02 02 0c
+ 15 01 00 00 00 00 02 03 89
+ 15 01 00 00 00 00 02 04 8a
+ 15 01 00 00 00 00 02 05 0f
+ 15 01 00 00 00 00 02 06 10
+ 15 01 00 00 00 00 02 07 10
+ 15 01 00 00 00 00 02 08 1c
+ 15 01 00 00 00 00 02 09 00
+ 15 01 00 00 00 00 02 0a 00
+ 15 01 00 00 00 00 02 0b 00
+ 15 01 00 00 00 00 02 0c 00
+ 15 01 00 00 00 00 02 0d 13
+ 15 01 00 00 00 00 02 0e 15
+ 15 01 00 00 00 00 02 0f 17
+ 15 01 00 00 00 00 02 10 01
+ 15 01 00 00 00 00 02 11 0b
+ 15 01 00 00 00 00 02 12 0c
+ 15 01 00 00 00 00 02 13 89
+ 15 01 00 00 00 00 02 14 8a
+ 15 01 00 00 00 00 02 15 0f
+ 15 01 00 00 00 00 02 16 10
+ 15 01 00 00 00 00 02 17 10
+ 15 01 00 00 00 00 02 18 1c
+ 15 01 00 00 00 00 02 19 00
+ 15 01 00 00 00 00 02 1a 00
+ 15 01 00 00 00 00 02 1b 00
+ 15 01 00 00 00 00 02 1c 00
+ 15 01 00 00 00 00 02 1d 13
+ 15 01 00 00 00 00 02 1e 15
+ 15 01 00 00 00 00 02 1f 17
+ 15 01 00 00 00 00 02 20 00
+ 15 01 00 00 00 00 02 21 01
+ 15 01 00 00 00 00 02 22 00
+ 15 01 00 00 00 00 02 23 40
+ 15 01 00 00 00 00 02 24 40
+ 15 01 00 00 00 00 02 25 6d
+ 15 01 00 00 00 00 02 26 40
+ 15 01 00 00 00 00 02 27 40
+ 15 01 00 00 00 00 02 29 d8
+ 15 01 00 00 00 00 02 2a 2a
+ 15 01 00 00 00 00 02 4b 03
+ 15 01 00 00 00 00 02 4c 11
+ 15 01 00 00 00 00 02 4d 10
+ 15 01 00 00 00 00 02 4e 01
+ 15 01 00 00 00 00 02 4f 01
+ 15 01 00 00 00 00 02 50 10
+ 15 01 00 00 00 00 02 51 00
+ 15 01 00 00 00 00 02 52 80
+ 15 01 00 00 00 00 02 53 00
+ 15 01 00 00 00 00 02 54 07
+ 15 01 00 00 00 00 02 55 25
+ 15 01 00 00 00 00 02 56 00
+ 15 01 00 00 00 00 02 58 07
+ 15 01 00 00 00 00 02 5b 43
+ 15 01 00 00 00 00 02 5c 00
+ 15 01 00 00 00 00 02 5f 73
+ 15 01 00 00 00 00 02 60 73
+ 15 01 00 00 00 00 02 63 22
+ 15 01 00 00 00 00 02 64 00
+ 15 01 00 00 00 00 02 67 08
+ 15 01 00 00 00 00 02 68 04
+ 15 01 00 00 00 00 02 7a 80
+ 15 01 00 00 00 00 02 7b 91
+ 15 01 00 00 00 00 02 7c d8
+ 15 01 00 00 00 00 02 7d 60
+ 15 01 00 00 00 00 02 93 06
+ 15 01 00 00 00 00 02 94 06
+ 15 01 00 00 00 00 02 8a 00
+ 15 01 00 00 00 00 02 9b 0f
+ 15 01 00 00 00 00 02 b3 c0
+ 15 01 00 00 00 00 02 b4 00
+ 15 01 00 00 00 00 02 b5 00
+ 15 01 00 00 00 00 02 b6 21
+ 15 01 00 00 00 00 02 b7 22
+ 15 01 00 00 00 00 02 b8 07
+ 15 01 00 00 00 00 02 b9 07
+ 15 01 00 00 00 00 02 ba 22
+ 15 01 00 00 00 00 02 bd 20
+ 15 01 00 00 00 00 02 be 07
+ 15 01 00 00 00 00 02 bf 07
+ 15 01 00 00 00 00 02 c1 6d
+ 15 01 00 00 00 00 02 c4 24
+ 15 01 00 00 00 00 02 e3 00
+ 15 01 00 00 00 00 02 ec 00
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 bb 03
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00
+ 14 00 02 28 00 05 01 00 00 78 00
+ 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-timings-phy-12nm = [17 0a 0f 06 03 08 06 0e];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ /delete-node/ qcom,mdss-dsi-display-timings;
+};
+
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+ smb_en {
+ smb_en_default: smb_en_default {
+ pins = "gpio2";
+ function = "func1";
+ output-enable;
+ };
+ };
+
+ pmi632_sense {
+ /* GPIO 7 and 8 are external-sense pins for PMI632 */
+ pmi632_sense_default: pmi632_sense_default {
+ pins = "gpio7", "gpio8";
+ bias-high-impedance; /* disable the GPIO */
+ bias-disable; /* no-pull */
+ };
+ };
+};
+
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio61";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio61";
+ drive-strength = <2>;
+ bias-pull-up;
+ input-enable;
+ };
+ };
+};
+
+&smb1355_0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <61 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
+};
+
+&smb1355_1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <61 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi
index 452f0c7..615489e 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pm8953.dtsi
@@ -126,7 +126,22 @@
&mdss_dsi {
vdda-supply = <&pm8953_l23>;
- vddio-supply = <&pm8953_l6>;
+ vddio-supply = <&pm8953_l5>;
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda";
+ qcom,supply-min-voltage = <800000>;
+ qcom,supply-max-voltage = <800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <20>;
+ };
+ };
};
&usb_otg {
@@ -256,6 +271,21 @@
};
};
};
+
+ pa-therm0 {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pm8953_adc_tm 0x36>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ };
};
&pm8953_vadc {
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
index 300c83a..2bfab8f 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
@@ -43,6 +43,27 @@
qcom,battery-data = <&mtp_batterydata>;
};
+&pmi632_vadc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
+};
+
+&pmi632_gpios {
+ quiet_therm {
+ quiet_therm_default: quiet_therm_default {
+ pins = "gpio3";
+ bias-high-impedance;
+ };
+ };
+
+ smb_therm {
+ smb_therm_default: smb_therm_default {
+ pins = "gpio4";
+ bias-high-impedance;
+ };
+ };
+};
+
&pm8953_typec {
status = "disabled";
};
@@ -105,4 +126,98 @@
};
};
};
+
+ quiet-therm-step {
+ polling-delay-passive = <1000>;
+ polling-delay = <0>;
+ thermal-sensors = <&pmi632_adc_tm 0x53>;
+ thermal-governor = "step_wise";
+
+ trips {
+ batt_trip1: batt-trip1 {
+ temperature = <41000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ cpus_trip: cpus-trip {
+ temperature = <44000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ batt_trip2: batt-trip2 {
+ temperature = <45000>;
+ hysteresis = <4000>;
+ type = "passive";
+ };
+ gpu_trip: gpu-trip {
+ temperature = <50000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ skin_cpu0 {
+ trip = <&cpus_trip>;
+ /* throttle from fmax to 1094400KHz */
+ cooling-device = <&CPU0 THERMAL_NO_LIMIT 5>;
+ };
+ skin_cpu1 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU1 THERMAL_NO_LIMIT 5>;
+ };
+ skin_cpu2 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU2 THERMAL_NO_LIMIT 5>;
+ };
+ skin_cpu3 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU3 THERMAL_NO_LIMIT 5>;
+ };
+ skin_cpu4 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU4 THERMAL_NO_LIMIT 3>;
+ };
+ skin_cpu5 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU5 THERMAL_NO_LIMIT 3>;
+ };
+ skin_cpu6 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU6 THERMAL_NO_LIMIT 3>;
+ };
+ skin_cpu7 {
+ trip = <&cpus_trip>;
+ cooling-device = <&CPU7 THERMAL_NO_LIMIT 3>;
+ };
+ skin_gpu {
+ trip = <&gpu_trip>;
+ /* throttle from fmax to 375000000Hz */
+ cooling-device = <&msm_gpu THERMAL_NO_LIMIT 2>;
+ };
+ battery_lvl1 {
+ trip = <&batt_trip1>;
+ cooling-device = <&pmi632_charger 4 4>;
+ };
+ battery_lvl2 {
+ trip = <&batt_trip2>;
+ cooling-device = <&pmi632_charger 5 5>;
+ };
+ };
+ };
+
+ quiet-therm-adc {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&pmi632_adc_tm 0x53>;
+ thermal-governor = "user_space";
+
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts
index ac059c4d..46a7856 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts
@@ -14,13 +14,9 @@
/dts-v1/;
/plugin/;
- #include <dt-bindings/gpio/gpio.h>
- #include <dt-bindings/clock/msm-clocks-8953.h>
- #include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm439-qrd.dtsi"
/ {
model = "QRD";
qcom,board-id = <0xb 2>;
- qcom,msm-id = <353 0x0>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dts b/arch/arm64/boot/dts/qcom/sdm439-qrd.dts
index b8a9f2b..4a2fbff 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd.dts
@@ -22,10 +22,3 @@
qcom,board-id = <0xb 2>;
qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
};
-
-&pmi632_vadc {
- chan@4a {
- qcom,scale-function = <22>;
- };
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
index 6d19786..ac31fc8 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
@@ -51,6 +51,11 @@
qcom,msm-mbhc-hphl-swh = <1>;
qcom,msm-mbhc-gnd-swh = <0>;
qcom,msm-hs-micbias-type = "external";
+ /delete-property/ qcom,quin-mi2s-gpios;
+};
+
+&cdc_quin_mi2s_gpios {
+ status = "disabled";
};
&wsa881x_i2c_f {
@@ -136,6 +141,24 @@
gpio-key,wakeup;
};
};
+
+ fpc1020 {
+ compatible = "fpc,fpc1020";
+ interrupt-parent = <&tlmm>;
+ interrupts = <48 0>;
+ fpc,gpio_rst = <&tlmm 124 0x0>;
+ fpc,gpio_irq = <&tlmm 48 0>;
+ vcc_spi-supply = <&pm8953_l5>;
+ vdd_io-supply = <&pm8953_l5>;
+ vdd_ana-supply = <&pm8953_l5>;
+ fpc,enable-on-boot;
+ pinctrl-names = "fpc1020_reset_reset",
+ "fpc1020_reset_active",
+ "fpc1020_irq_active";
+ pinctrl-0 = <&fpc_reset_low>;
+ pinctrl-1 = <&fpc_reset_high>;
+ pinctrl-2 = <&fpc_int_low>;
+ };
};
&tlmm {
@@ -283,4 +306,112 @@
qcom,mdss-dsi-panel-timings-phy-12nm = [18 0a 10 06 03 08 06 0e];
qcom,mdss-dsi-t-clk-post = <0x02>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_hx8399c_hd_vid {
+ /delete-property/ qcom,mdss-dsi-panel-timings;
+ qcom,mdss-dsi-panel-timings-phy-12nm = [08 06 0a 02 00 04 02 08];
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+ qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+ qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+ qcom,mdss-dsi-panel-status-read-length = <4>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update =
+ "dfps_immediate_porch_mode_vfp";
+};
+
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_vadc {
+ chan@4a {
+ qcom,scale-function = <22>;
+ };
+};
+
+&pmi632_gpios {
+ smb_en {
+ smb_en_default: smb_en_default {
+ pins = "gpio2";
+ function = "func1";
+ output-enable;
+ };
+ };
+
+ pmi632_sense {
+ /* GPIO 7 and 8 are external-sense pins for PMI632 */
+ pmi632_sense_default: pmi632_sense_default {
+ pins = "gpio7", "gpio8";
+ bias-high-impedance; /* disable the GPIO */
+ bias-disable; /* no-pull */
+ };
+ };
+};
+
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio61";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio61";
+ drive-strength = <2>;
+ bias-pull-up;
+ input-enable;
+ };
+ };
+};
+
+&smb1355_0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <61 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
+};
+
+&smb1355_1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <61 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-rcm-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-rcm-overlay.dts
new file mode 100644
index 0000000..be0de06
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-rcm-overlay.dts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "sdm439-rcm.dtsi"
+
+/ {
+ model = "RCM";
+ qcom,board-id = <21 1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-rcm.dts b/arch/arm64/boot/dts/qcom/sdm439-rcm.dts
new file mode 100644
index 0000000..71d02a0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-rcm.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sdm439.dtsi"
+#include "sdm439-rcm.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM439 RCM";
+ compatible = "qcom,sdm439-cdp", "qcom,sdm439", "qcom,cdp";
+ qcom,board-id = <21 1>;
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-rcm.dtsi b/arch/arm64/boot/dts/qcom/sdm439-rcm.dtsi
new file mode 100644
index 0000000..4ba4c00
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm439-rcm.dtsi
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm439-cdp.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
index f325925..414e8fe 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
@@ -67,7 +67,7 @@
pm8953_cx_cdev: regulator-cx-cdev {
compatible = "qcom,regulator-cooling-device";
regulator-cdev-supply = <&pm8953_s2_floor_level>;
- regulator-levels = <RPM_SMD_REGULATOR_LEVEL_NOM
+ regulator-levels = <RPM_SMD_REGULATOR_LEVEL_NOM_PLUS
RPM_SMD_REGULATOR_LEVEL_RETENTION>;
#cooling-cells = <2>;
};
@@ -378,12 +378,12 @@
reg = <0x2000 0x100>;
regulator-name = "pm8953_s5";
regulator-min-microvolt = <490000>;
- regulator-max-microvolt = <910000>;
+ regulator-max-microvolt = <960000>;
pm8953_s5_limit: avs-limit-regulator {
regulator-name = "pm8953_s5_avs_limit";
regulator-min-microvolt = <490000>;
- regulator-max-microvolt = <910000>;
+ regulator-max-microvolt = <960000>;
};
};
};
@@ -421,7 +421,7 @@
regulator-max-microvolt = <5>;
qcom,cpr-fuse-corners = <3>;
- qcom,cpr-voltage-ceiling = <760000 795000 910000>;
+ qcom,cpr-voltage-ceiling = <810000 845000 960000>;
qcom,cpr-voltage-floor = <700000 700000 790000>;
vdd-apc-supply = <&pm8953_s5>;
mem-acc-supply = <&apc_mem_acc_vreg>;
@@ -468,5 +468,33 @@
qcom,cpr-quot-adjust-scaling-factor-max = <0 1400 1400>;
qcom,cpr-voltage-scaling-factor-max = <0 2000 2000>;
qcom,cpr-scaled-init-voltage-as-ceiling;
+
+ qcom,cpr-fuse-version-map =
+ /* <Speed-bin pvs-version cpr-rev ... ... ...> */
+ <(-1) (-1) ( 0) (-1) (-1) (-1)>,
+ <(-1) (-1) ( 1) (-1) (-1) (-1)>,
+ <(-1) (-1) (-1) (-1) (-1) (-1)>;
+
+ qcom,cpr-quotient-adjustment =
+ <66 77 66>, /* SVSP/NOM/TUR:30/35/30 mV */
+ <(-74) (-57) (-30)>, /* SVSP/NOM/TUR:-34/-26/-14 mV */
+ <0 0 0>;
+
+ qcom,cpr-floor-to-ceiling-max-range =
+ <50000 50000 50000 65000 65000>,
+ <50000 50000 50000 65000 65000>,
+ <50000 50000 50000 65000 65000>;
+
+ qcom,cpr-voltage-ceiling-override =
+ <(-1) (-1) 810000 845000 885000 960000 960000>;
+
+ qcom,cpr-enable;
+ };
+
+ dbu1: dbu1 {
+ compatible = "regulator-fixed";
+ regulator-name = "dbu1";
+ startup-delay-us = <0>;
+ enable-active-high;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm439.dtsi b/arch/arm64/boot/dts/qcom/sdm439.dtsi
index 6cfd2d7..1448a65 100644
--- a/arch/arm64/boot/dts/qcom/sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439.dtsi
@@ -14,6 +14,7 @@
#include "msm8937.dtsi"
#include "sdm439-pm8953.dtsi"
#include "sdm439-pmi632.dtsi"
+#include "sdm439-audio.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM439";
@@ -23,15 +24,19 @@
&soc {
qcom,csid@1b30000 {
+ qcom,csi-vdd-voltage = <800000>;
qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
};
qcom,csid@1b30400 {
+ qcom,csi-vdd-voltage = <800000>;
qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
};
qcom,csid@1b30800 {
+ qcom,csi-vdd-voltage = <800000>;
qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
};
+ /delete-node/ qcom,msm-cpufreq;
msm_cpufreq: qcom,msm-cpufreq {
compatible = "qcom,msm-cpufreq";
clock-names =
@@ -45,33 +50,91 @@
qcom,governor-per-policy;
qcom,cpufreq-table-0 =
+ < 960000 >,
< 1305600 >,
< 1497600 >,
< 1708800 >,
+ < 1804800 >,
< 1958400 >;
qcom,cpufreq-table-4 =
< 768000 >,
- < 1001600 >,
+ < 998400 >,
< 1171200 >,
< 1305600 >,
< 1459200 >;
};
+ /delete-node/ qcom,cpubw;
+ cpubw: qcom,cpubw {
+ compatible = "qcom,devbw";
+ governor = "cpufreq";
+ qcom,src-dst-ports = <1 512>;
+ qcom,active-only;
+ qcom,bw-tbl =
+ < 769 /* 100.8 MHz */ >,
+ < 1611 /* 211.2 MHz */ >, /*Low SVS*/
+ < 2124 /* 278.4 MHz */ >,
+ < 2929 /* 384 MHz */ >, /* SVS */
+ < 3221 /* 422.4 MHz */ >,
+ < 4248 /* 556.8 MHz */ >,
+ < 5126 /* 662.4 MHz */ >, /* SVS+ */
+ < 5859 /* 748.8 MHz */ >, /* NOM */
+ < 6152 /* 806.4 MHz */ >, /* NOM+ */
+ < 6445 /* 844.8 MHz */ >,
+ < 7104 /* 931.2 MHz */ >; /* TURBO */
+ };
+
+ /delete-node/ qcom,mincpubw;
+ mincpubw: qcom,mincpubw {
+ compatible = "qcom,devbw";
+ governor = "cpufreq";
+ qcom,src-dst-ports = <1 512>;
+ qcom,active-only;
+ qcom,bw-tbl =
+ < 769 /* 100.8 MHz */ >,
+ < 1611 /* 211.2 MHz */ >, /*Low SVS*/
+ < 2124 /* 278.4 MHz */ >,
+ < 2929 /* 384 MHz */ >, /* SVS */
+ < 3221 /* 422.4 MHz */ >,
+ < 4248 /* 556.8 MHz */ >,
+ < 5126 /* 662.4 MHz */ >, /* SVS+ */
+ < 5859 /* 748.8 MHz */ >, /* NOM */
+ < 6152 /* 806.4 MHz */ >, /* NOM+ */
+ < 6445 /* 844.8 MHz */ >,
+ < 7104 /* 931.2 MHz */ >; /* TURBO */
+ };
+
+ /delete-node/ qcom,cci;
+ cci_cache: qcom,cci {
+ compatible = "devfreq-simple-dev";
+ clock-names = "devfreq_clk";
+ clocks = <&clock_cpu clk_cci_clk>;
+ governor = "cpufreq";
+ freq-tbl-khz =
+ < 400000 >,
+ < 400000 >,
+ < 400000 >,
+ < 533000 >,
+ < 576000 >;
+ };
+
+ /delete-node/ devfreq-cpufreq;
devfreq-cpufreq {
cpubw-cpufreq {
target-dev = <&cpubw>;
cpu-to-dev-map-0 =
- < 1305600 2929 >,
- < 1497600 5053 >,
- < 1708800 5712 >,
- < 1958400 7031 >;
+ < 1305600 5126 >,
+ < 1497600 5859 >,
+ < 1708800 6445 >,
+ < 1804800 7104 >,
+ < 1958400 7104 >;
cpu-to-dev-map-4 =
< 768000 2929 >,
- < 1001600 4101 >,
- < 1171200 5053 >,
+ < 998400 5126 >,
+ < 1171200 5859 >,
< 1305600 6152 >,
- < 1459200 7031 >;
+ < 1459200 7104 >;
};
@@ -80,80 +143,84 @@
cpu-to-dev-map-0 =
< 1305600 400000 >,
< 1497600 400000 >,
- < 1708800 533333 >,
- < 1958400 533333 >;
+ < 1708800 533000 >,
+ < 1804800 576000 >,
+ < 1958400 576000 >;
cpu-to-dev-map-4 =
< 768000 400000 >,
- < 1001600 400000 >,
- < 1171200 533333 >,
- < 1305600 533333 >,
- < 1459200 533333 >;
+ < 998400 400000 >,
+ < 1171200 400000 >,
+ < 1305600 533000 >,
+ < 1459200 576000 >;
};
mincpubw-cpufreq {
target-dev = <&mincpubw>;
cpu-to-dev-map-0 =
< 1305600 2929 >,
- < 1958400 4248 >;
+ < 1804800 5859 >;
cpu-to-dev-map-4 =
< 1171200 2929 >,
- < 1459200 4248 >;
+ < 1459200 5859 >;
};
};
};
-&energy_costs {
- compatible = "sched-energy";
+/{
+ /delete-node/ energy-costs;
+ energy_costs: energy-costs {
+ compatible = "sched-energy";
- CPU_COST_0: core-cost0 {
- busy-cost-data = <
- 800000 137
- 1001600 165
- 1305600 207
- 1497600 256
- 1708800 327
- 1958400 445
- >;
- idle-cost-data = <
- 100 80 60 40
- >;
- };
- CPU_COST_1: core-cost1 {
- busy-cost-data = <
- 768000 43
- 1001600 56
- 1171200 71
- 1305600 89
- 1459200 120
- >;
- idle-cost-data = <
- 40 20 10 8
- >;
- };
- CLUSTER_COST_0: cluster-cost0 {
+ CPU_COST_0: core-cost0 {
busy-cost-data = <
- 800000 49
- 1001600 53
- 1305600 61
- 1497600 71
- 1708800 85
- 1958400 110
- >;
- idle-cost-data = <
- 4 3 2 1
- >;
- };
- CLUSTER_COST_1: cluster-cost1 {
+ 800000 137
+ 1305600 207
+ 1497600 256
+ 1708800 327
+ 1804800 343
+ 1958400 445
+ >;
+ idle-cost-data = <
+ 100 80 60 40
+ >;
+ };
+ CPU_COST_1: core-cost1 {
busy-cost-data = <
- 768000 8
- 1001600 10
- 1171200 13
- 1305600 15
- 1459200 20
- >;
- idle-cost-data = <
- 4 3 2 1
- >;
+ 768000 43
+ 998400 56
+ 1171200 71
+ 1305600 89
+ 1459200 120
+ >;
+ idle-cost-data = <
+ 40 20 10 8
+ >;
+ };
+ CLUSTER_COST_0: cluster-cost0 {
+ busy-cost-data = <
+ 800000 49
+ 1305600 61
+ 1497600 71
+ 1708800 85
+ 1804800 88
+ 1958400 110
+ >;
+ idle-cost-data = <
+ 4 3 2 1
+ >;
+ };
+ CLUSTER_COST_1: cluster-cost1 {
+ busy-cost-data = <
+ 768000 8
+ 998400 10
+ 1171200 13
+ 1305600 15
+ 1459200 20
+ >;
+ idle-cost-data = <
+ 4 3 2 1
+ >;
+ };
};
};
@@ -213,6 +280,7 @@
qcom,speed0-bin-v0-c1 =
< 0 0>,
+ < 960000000 1>,
< 1305600000 1>,
< 1497600000 2>,
< 1708800000 3>,
@@ -233,6 +301,7 @@
qcom,speed1-bin-v0-c1 =
< 0 0>,
+ < 960000000 1>,
< 1305600000 1>,
< 1497600000 2>,
< 1708800000 3>,
@@ -258,3 +327,315 @@
vdd_hf_dig-supply = <&pm8953_s2_level_ao>;
vdd_hf_pll-supply = <&pm8953_l7_ao>;
};
+
+&soc {
+ devfreq_spdm_cpu {
+ status = "disabled";
+ };
+
+ devfreq_spdm_gov {
+ status = "disabled";
+ };
+};
+
+&clock_gcc_mdss {
+ compatible = "qcom,gcc-mdss-sdm439";
+ clocks = <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_src>;
+ clock-names = "pclk0_src", "byte0_src", "pclk1_src",
+ "byte1_src";
+ #clock-cells = <1>;
+};
+
+&mdss_dsi0_pll {
+ compatible = "qcom,mdss_dsi_pll_sdm439";
+ reg = <0x001a94400 0x400>,
+ <0x0184d074 0x8>;
+ reg-names = "pll_base", "gdsc_base";
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
+ qcom,ssc-frequency-hz = <31500>;
+ qcom,ssc-ppm = <5000>;
+};
+
+&mdss_dsi1_pll {
+ compatible = "qcom,mdss_dsi_pll_sdm439";
+ reg = <0x001a96400 0x400>,
+ <0x0184d074 0x8>;
+ reg-names = "pll_base", "gdsc_base";
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
+ qcom,ssc-frequency-hz = <31500>;
+ qcom,ssc-ppm = <5000>;
+};
+
+&mdss_dsi {
+ ranges = <0x1a94000 0x1a94000 0x300
+ 0x1a94400 0x1a94400 0x400
+ 0x193e000 0x193e000 0x30
+ 0x1a96000 0x1a96000 0x300
+ 0x1a96400 0x1a96400 0x400
+ 0x193e000 0x193e000 0x30>;
+};
+
+&mdss_dsi0 {
+ reg = <0x1a94000 0x300>,
+ <0x1a94400 0x400>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+ /delete-property/ qcom,platform-strength-ctrl;
+ /delete-property/ qcom,platform-bist-ctrl;
+ /delete-property/ qcom,platform-regulator-settings;
+ /delete-property/ qcom,platform-lane-config;
+};
+
+&mdss_dsi1 {
+ reg = <0x1a96000 0x300>,
+ <0x1a96400 0x400>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+ /delete-property/ qcom,platform-strength-ctrl;
+ /delete-property/ qcom,platform-bist-ctrl;
+ /delete-property/ qcom,platform-regulator-settings;
+ /delete-property/ qcom,platform-lane-config;
+};
+
+/* GPU Overrides*/
+&gpubw {
+ /delete-property/qcom,bw-tbl;
+ qcom,bw-tbl =
+ < 0 >, /* off */
+ < 769 >, /* 1. DDR:100.80 MHz BIMC: 50.40 MHz */
+ < 1611 >, /* 2. DDR:211.20 MHz BIMC: 105.60 MHz */
+ < 2273 >, /* 3. DDR:297.60 MHz BIMC: 148.80 MHz */
+ < 2929 >, /* 4. DDR:384.00 MHz BIMC: 192.00 MHz */
+ < 4248 >, /* 5. DDR:556.80 MHz BIMC: 278.40 MHz */
+ < 5346 >, /* 6. DDR:662.40 MHz BIMC: 331.20 MHz */
+ < 5712 >, /* 7. DDR:748.80 MHz BIMC: 374.40 MHz */
+ < 6150 >, /* 8. DDR:796.80 MHz BIMC: 398.40 MHz */
+ < 7105 >; /* 9. DDR:931.20 MHz BIMC: 465.60 MHz */
+};
+
+&msm_gpu {
+ /delete-property/qcom,msm-bus,num-cases;
+ qcom,msm-bus,num-cases = <10>;
+ /delete-property/qcom,msm-bus,vectors-KBps;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>, /* off */
+ <26 512 0 806400>, /* 1. 100.80 MHz */
+ <26 512 0 1689600>, /* 2. 211.20 MHz */
+ <26 512 0 2380800>, /* 3. 297.60 MHz */
+ <26 512 0 3072000>, /* 4. 384.00 MHz */
+ <26 512 0 4454400>, /* 5. 556.80 MHz */
+ <26 512 0 5299200>, /* 6. 662.40 MHz */
+ <26 512 0 5990400>, /* 7. 748.80 MHz */
+ <26 512 0 6374400>, /* 8. 796.80 MHz */
+ <26 512 0 7449600>; /* 9. 931.20 MHz */
+
+ qcom,gpu-speed-bin-vectors =
+ <0x6004 0x00c00000 22>,
+ <0x6008 0x00000600 7>;
+
+ /delete-node/qcom,gpu-pwrlevels;
+ qcom,gpu-pwrlevel-bins {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible="qcom,gpu-pwrlevel-bins";
+
+ qcom,gpu-pwrlevels-0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <0>;
+
+ qcom,initial-pwrlevel = <3>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <650000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <9>;
+ qcom,bus-max = <9>;
+ };
+
+ /* NOM+ */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <560000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <9>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <510000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <8>;
+ };
+
+ /* SVS+ */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ qcom,gpu-pwrlevels-1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <4>;
+
+ qcom,initial-pwrlevel = <2>;
+
+ /* NOM+ */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <560000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <9>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <510000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <8>;
+ };
+
+ /* SVS+ */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ qcom,gpu-pwrlevels-2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <5>;
+
+ qcom,initial-pwrlevel = <1>;
+
+ /* NOM */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <510000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <8>;
+ };
+
+ /* SVS+ */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+
+ qcom,gpu-pwrlevels-3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <10>;
+
+ qcom,initial-pwrlevel = <0>;
+
+ /* SVS */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <8>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+ };
+};
+
+&mdss_mdp {
+ qcom,vbif-settings = <0xd0 0x20>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts b/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts
index e12ad51..97c9389 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts
@@ -18,7 +18,6 @@
/ {
model = "CDP S2";
- compatible = "qcom,cdp";
qcom,board-id = <1 2>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts b/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts
index ae522a5..3dd7200 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts
@@ -18,7 +18,6 @@
/ {
model = "MTP S3";
- compatible = "qcom,mtp";
qcom,board-id = <8 3>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
index b9aadc1..b73b49a 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
@@ -14,8 +14,8 @@
/dts-v1/;
#include "sdm450.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM450 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
index 64d9e64..07d2e08 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
@@ -46,3 +46,69 @@
qcom,mdss-dsi-bl-pmic-bank-select = <0>;
qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
};
+
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+ smb_en {
+ smb_en_default: smb_en_default {
+ pins = "gpio2";
+ function = "func1";
+ output-enable;
+ };
+ };
+
+ pmi632_sense {
+ /* GPIO 7 and 8 are external-sense pins for PMI632 */
+ pmi632_sense_default: pmi632_sense_default {
+ pins = "gpio7", "gpio8";
+ bias-high-impedance; /* disable the GPIO */
+ bias-disable; /* no-pull */
+ };
+ };
+};
+
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio59";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio59";
+ drive-strength = <2>;
+ bias-pull-up;
+ input-enable;
+ };
+ };
+};
+
+&smb1355_0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
+};
+
+&smb1355_1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index 5c127bc..6e39327 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -41,6 +41,27 @@
};
};
+&pmi632_vadc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
+};
+
+&pmi632_gpios {
+ quiet_therm {
+ quiet_therm_default: quiet_therm_default {
+ pins = "gpio3";
+ bias-high-impedance;
+ };
+ };
+
+ smb_therm {
+ smb_therm_default: smb_therm_default {
+ pins = "gpio4";
+ bias-high-impedance;
+ };
+ };
+};
+
&pmi632_qg {
qcom,battery-data = <&mtp_batterydata>;
};
@@ -351,3 +372,24 @@
};
};
};
+
+&tlmm {
+ pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio61";
+ };
+ config {
+ pins = "gpio61";
+ };
+ };
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio61";
+ };
+ config {
+ pins = "gpio61";
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts
index 558c3c6..e8dca18 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts
@@ -18,7 +18,6 @@
/ {
model = "QRD SKU4";
- compatible = "qcom,qrd";
qcom,board-id = <0xb 1>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
index 386bd71..a2bd5cd 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
@@ -177,3 +177,68 @@
};
};
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+ smb_en {
+ smb_en_default: smb_en_default {
+ pins = "gpio2";
+ function = "func1";
+ output-enable;
+ };
+ };
+
+ pmi632_sense {
+ /* GPIO 7 and 8 are external-sense pins for PMI632 */
+ pmi632_sense_default: pmi632_sense_default {
+ pins = "gpio7", "gpio8";
+ bias-high-impedance; /* disable the GPIO */
+ bias-disable; /* no-pull */
+ };
+ };
+};
+
+&tlmm {
+ smb_int_default: smb_int_default {
+ mux {
+ pins = "gpio59";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio59";
+ drive-strength = <2>;
+ bias-pull-up;
+ input-enable;
+ };
+ };
+};
+
+&smb1355_0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
+};
+
+&smb1355_1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb_int_default
+ &smb_en_default &pmi632_sense_default>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ status ="ok";
+ /delete-property/ io-channels;
+ /delete-property/ io-channels-names;
+ qcom,parallel-mode = <1>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
index 2669d1f..9d6543f 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
@@ -24,33 +24,3 @@
qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
};
-
-&soc {
- gpio_keys {
- /delete-node/home;
- };
-};
-
-&tlmm {
- tlmm_gpio_key {
- gpio_key_active: gpio_key_active {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
-
- gpio_key_suspend: gpio_key_suspend {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
- };
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi
new file mode 100644
index 0000000..62eeb65
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi
@@ -0,0 +1,154 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+&soc {
+
+ /delete-node/ etm@61bc000;
+ /delete-node/ etm@61bd000;
+ /delete-node/ etm@61be000;
+ /delete-node/ etm@61bf000;
+ /delete-node/ cti@61b8000;
+ /delete-node/ cti@61b9000;
+ /delete-node/ cti@61ba000;
+ /delete-node/ cti@61bb000;
+
+ etm4: etm@61b3000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61b3000 0x1000>;
+ cpu = <&CPU4>;
+ coresight-name = "coresight-etm4";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm4_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm4>;
+ };
+ };
+ };
+
+ etm5: etm@61b7000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61b7000 0x1000>;
+ cpu = <&CPU5>;
+ coresight-name = "coresight-etm5";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm5_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm5>;
+ };
+ };
+ };
+
+ etm6: etm@61bb000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61bb000 0x1000>;
+ cpu = <&CPU6>;
+ coresight-name = "coresight-etm6";
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm6_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm6>;
+ };
+ };
+ };
+
+ etm7: etm@61bf000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb959>;
+
+ reg = <0x61bf000 0x1000>;
+ coresight-name = "coresight-etm7";
+ cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+
+ port {
+ etm7_out_funnel_apss0: endpoint {
+ remote-endpoint = <&funnel_apss0_in_etm7>;
+ };
+ };
+ };
+
+ cti_cpu4: cti@61b1000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b1000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu4";
+ cpu = <&CPU4>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu5: cti@61b5000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b5000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu5";
+ cpu = <&CPU5>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu6: cti@61b9000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61b9000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu6";
+ cpu = <&CPU6>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_cpu7: cti@61bd000{
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+
+ reg = <0x61bd000 0x1000>;
+ reg-names = "cti-base";
+ coresight-name = "coresight-cti-cpu7";
+ cpu = <&CPU7>;
+
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi b/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi
index de1bd1f..c37750a 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi
@@ -258,7 +258,7 @@
busy-cost-data = <
633600 722
902400 1287
- 1036800 1739
+ 1094400 1739
1401600 2819
1555200 3532
1804800 5038
@@ -287,7 +287,7 @@
busy-cost-data = <
633600 68
902400 103
- 1036800 132
+ 1094400 132
1401600 193
1555200 233
1804800 292
diff --git a/arch/arm64/boot/dts/qcom/sdm632-ext-audio-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm632-ext-audio-mtp.dtsi
new file mode 100644
index 0000000..b34e3d8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-ext-audio-mtp.dtsi
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&int_codec {
+ status = "disabled";
+};
+
+&pmic_analog_codec {
+ status = "disabled";
+};
+
+&wsa881x_i2c_f {
+ status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+ status = "disabled";
+};
+
+&cdc_pri_mi2s_gpios {
+ status = "disabled";
+};
+
+&wsa881x_analog_vi_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_clk_gpio {
+ status = "disabled";
+};
+
+&wsa881x_analog_reset_gpio {
+ status = "disabled";
+};
+
+&cdc_comp_gpios {
+ status = "disabled";
+};
+
+&slim_msm {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+
+ dc-vdd-buck-supply = <&dbu3>;
+ qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-buck-current = <650000>;
+
+ cdc-buck-sido-supply = <&dbu3>;
+ qcom,cdc-buck-sido-voltage = <1800000 1800000>;
+ qcom,cdc-buck-sido-current = <150000>;
+
+ cdc-vdd-tx-h-supply = <&dbu3>;
+ qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-tx-h-current = <25000>;
+
+ cdc-vdd-rx-h-supply = <&dbu3>;
+ qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-rx-h-current = <25000>;
+
+ cdc-vdd-px-supply = <&dbu3>;
+ qcom,cdc-vdd-px-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-px-current = <10000>;
+};
+
+&cdc_us_euro_sw {
+ status = "okay";
+};
+
+&cdc_quin_mi2s_gpios {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&ext_codec {
+ qcom,model = "msm8953-tasha-snd-card";
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3-overlay.dts b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3-overlay.dts
index eb5afbd..c3e0ba9 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3-overlay.dts
@@ -18,7 +18,6 @@
/ {
model = "Ext Codec CDP S3";
- compatible = "qcom,cdp";
qcom,board-id = <1 3>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts
index 17ae9d1..60b149d 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts
@@ -24,34 +24,3 @@
qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
};
-
-&soc {
- gpio_keys {
- /delete-node/home;
- };
-};
-
-&tlmm {
- tlmm_gpio_key {
- gpio_key_active: gpio_key_active {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
-
- gpio_key_suspend: gpio_key_suspend {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
- };
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4-overlay.dts b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4-overlay.dts
index 5656fd0..63b51ab 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4-overlay.dts
@@ -18,7 +18,6 @@
/ {
model = "Ext Codec MTP S4";
- compatible = "qcom,mtp";
qcom,board-id = <8 4>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4.dtsi b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4.dtsi
index d8326ff..fd1eb3d 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4.dtsi
@@ -12,4 +12,5 @@
*/
#include "sdm450-pmi632-mtp-s3.dtsi"
+#include "sdm632-ext-audio-mtp.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
index 3662cf3..1dd1163 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
@@ -14,8 +14,8 @@
/dts-v1/;
#include "sdm632.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM632 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts
index 4d68901..e0e6b4b 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts
@@ -25,34 +25,3 @@
qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
};
-
-&soc {
- gpio_keys {
- /delete-node/home;
- };
-};
-
-&tlmm {
- tlmm_gpio_key {
- gpio_key_active: gpio_key_active {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
-
- gpio_key_suspend: gpio_key_suspend {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
- };
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts
index 6ca2940..413e85f 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts
@@ -25,34 +25,3 @@
qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
};
-
-&soc {
- gpio_keys {
- /delete-node/home;
- };
-};
-
-&tlmm {
- tlmm_gpio_key {
- gpio_key_active: gpio_key_active {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
-
- gpio_key_suspend: gpio_key_suspend {
- mux {
- pins = "gpio85", "gpio86", "gpio87";
- };
-
- config {
- pins = "gpio85", "gpio86", "gpio87";
- };
- };
- };
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts
index d2a9cf1..aea6bff 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts
@@ -14,8 +14,8 @@
/dts-v1/;
#include "sdm632.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
#include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
#include "sdm632-pm8004.dtsi"
/ {
diff --git a/arch/arm64/boot/dts/qcom/sdm632-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm632-qrd-overlay.dts
index e6217e5..4b7f6b2 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-qrd-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-qrd-overlay.dts
@@ -18,7 +18,6 @@
/ {
model = "QRD";
- compatible = "qcom,qrd";
qcom,board-id = <0xb 3>;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm632-qrd.dtsi
index 09077c42..cefc078 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-qrd.dtsi
@@ -12,3 +12,9 @@
*/
#include "sdm450-qrd-sku4.dtsi"
+
+&ssphy {
+ fpc-redrive-supply = <&pm8953_l6>;
+ qcom,redrive-voltage-level = <0 1800000 1900000>;
+ qcom,redrive-load = <105000>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi b/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi
index 14ba3b4..aa20680 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi
@@ -13,3 +13,34 @@
#include "sdm450-pmi632-cdp-s2.dtsi"
+&soc {
+ gpio_keys {
+ home {
+ status = "disabled";
+ };
+ };
+};
+
+&tlmm {
+ tlmm_gpio_key {
+ gpio_key_active {
+ mux {
+ pins = "gpio85", "gpio86", "gpio87";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio87";
+ };
+ };
+
+ gpio_key_suspend {
+ mux {
+ pins = "gpio85", "gpio86", "gpio87";
+ };
+
+ config {
+ pins = "gpio85", "gpio86", "gpio87";
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm632-regulator.dtsi
index b5373a2..33ac930 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-regulator.dtsi
@@ -114,6 +114,8 @@
qcom,cpr-count-repeat = <14>;
qcom,cpr-down-error-step-limit = <1>;
qcom,cpr-up-error-step-limit = <1>;
+ qcom,cpr-reset-step-quot-loop-en;
+ qcom,cpr-thread-has-always-vote-en;
qcom,apm-ctrl = <&apc_apm>;
qcom,apm-threshold-voltage = <875000>;
@@ -131,10 +133,13 @@
"APCS_ALIAS0_APM_CTLER_STATUS",
"APCS0_CPR_CORE_ADJ_MODE_REG";
+ qcom,cpr-enable;
+ qcom,cpr-hw-closed-loop;
+
thread@0 {
qcom,cpr-thread-id = <0>;
qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <2>;
+ qcom,cpr-consecutive-down = <0>;
qcom,cpr-up-threshold = <2>;
qcom,cpr-down-threshold = <1>;
@@ -143,10 +148,10 @@
regulator-min-microvolt = <1>;
regulator-max-microvolt = <7>;
- qcom,cpr-fuse-corners = <5>;
+ qcom,cpr-fuse-corners = <4>;
qcom,cpr-fuse-combos = <64>;
qcom,cpr-corners = <7>;
- qcom,cpr-corner-fmax-map = <1 2 3 4 7>;
+ qcom,cpr-corner-fmax-map = <1 3 4 7>;
qcom,cpr-voltage-ceiling =
<720000 790000 865000 865000 920000
@@ -156,6 +161,10 @@
<500000 500000 500000 500000 500000
500000 500000>;
+ qcom,cpr-floor-to-ceiling-max-range =
+ <50000 50000 50000 50000 50000
+ 50000 50000>;
+
qcom,mem-acc-voltage = <1 1 2 2 2 2 3>;
qcom,corner-frequencies =
@@ -171,44 +180,55 @@
<3600 3600 3830 2430 2520 2700 1790 1760
1970 1880 2110 2010 2510 4900 4370 4780>,
<3600 3600 3830 2430 2520 2700 1790 1760
- 1970 1880 2110 2010 2510 4900 4370 4780>,
- <3600 3600 3830 2430 2520 2700 1790 1760
1970 1880 2110 2010 2510 4900 4370 4780>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ < 0 0 0 10000>;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ <(-10000) 0 0 10000>;
};
};
thread@1 {
qcom,cpr-thread-id = <1>;
qcom,cpr-consecutive-up = <0>;
- qcom,cpr-consecutive-down = <2>;
+ qcom,cpr-consecutive-down = <0>;
qcom,cpr-up-threshold = <2>;
qcom,cpr-down-threshold = <1>;
apc1_perfcl_vreg: regulator {
regulator-name = "apc1_perfcl_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <5>;
+ regulator-max-microvolt = <7>;
- qcom,cpr-fuse-corners = <3>;
+ qcom,cpr-fuse-corners = <4>;
qcom,cpr-fuse-combos = <64>;
- qcom,cpr-corners = <5>;
- qcom,cpr-corner-fmax-map = <1 2 5>;
+ qcom,cpr-corners = <7>;
+ qcom,cpr-corner-fmax-map = <1 3 4 7>;
qcom,cpr-voltage-ceiling =
- <865000 865000 920000 990000 1065000>;
+ <720000 790000 865000 865000 920000
+ 990000 1065000>;
qcom,cpr-voltage-floor =
- <500000 500000 500000 500000 500000>;
+ <500000 500000 500000 500000 500000
+ 500000 500000>;
- qcom,mem-acc-voltage = <2 2 2 2 3>;
+ qcom,cpr-floor-to-ceiling-max-range =
+ <50000 50000 50000 50000 50000
+ 50000 50000>;
+
+ qcom,mem-acc-voltage = <1 1 2 2 2 2 3>;
qcom,corner-frequencies =
- <1094400000 1401600000 1555200000
- 1804800000 2016000000>;
+ <633600000 902400000 1094400000
+ 1401600000 1555200000 1804800000
+ 2016000000>;
qcom,cpr-ro-scaling-factor =
<3600 3600 3830 2430 2520 2700 1790 1760
@@ -216,11 +236,175 @@
<3600 3600 3830 2430 2520 2700 1790 1760
1970 1880 2110 2010 2510 4900 4370 4780>,
<3600 3600 3830 2430 2520 2700 1790 1760
+ 1970 1880 2110 2010 2510 4900 4370 4780>,
+ <3600 3600 3830 2430 2520 2700 1790 1760
1970 1880 2110 2010 2510 4900 4370 4780>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-open-loop-voltage-fuse-adjustment =
+ /* Speed bin 0; CPR rev 0..7 */
+ < 30000 0 10000 20000>,
+ < 30000 0 10000 20000>,
+ < 0 0 10000 20000>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 1; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 2; CPR rev 0..7 */
+ < 30000 0 10000 20000>,
+ < 30000 0 10000 20000>,
+ < 0 0 10000 20000>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 3; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 4; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 5; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 6; CPR rev 0..7 */
+ < 30000 0 10000 20000>,
+ < 30000 0 10000 20000>,
+ < 0 0 10000 20000>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 7; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>;
+
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ /* Speed bin 0; CPR rev 0..7 */
+ < 30000 0 0 0>,
+ < 30000 0 0 0>,
+ <(-10000) 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 1; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 2; CPR rev 0..7 */
+ < 30000 0 0 0>,
+ < 30000 0 0 0>,
+ <(-10000) 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 3; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 4; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 5; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 6; CPR rev 0..7 */
+ < 30000 0 0 0>,
+ < 30000 0 0 0>,
+ <(-10000) 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+
+ /* Speed bin 7; CPR rev 0..7 */
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>,
+ < 0 0 0 0>;
};
};
};
@@ -267,4 +451,11 @@
mem-acc-supply = <&gfx_mem_acc>;
qcom,mem-acc-corner-map = <1 1 1 2 2 2 2>;
};
+
+ dbu3: dbu3 {
+ compatible = "regulator-fixed";
+ regulator-name = "dbu3";
+ startup-delay-us = <0>;
+ enable-active-high;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm632.dtsi b/arch/arm64/boot/dts/qcom/sdm632.dtsi
index 62689f5..ee2476e 100644
--- a/arch/arm64/boot/dts/qcom/sdm632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632.dtsi
@@ -38,8 +38,29 @@
compatible = "qcom,cc-debug-sdm632";
};
+&soc {
+ devfreq_spdm_cpu {
+ status = "disabled";
+ };
+
+ devfreq_spdm_gov {
+ status = "disabled";
+ };
+};
+
&clock_gcc_gfx {
compatible = "qcom,gcc-gfx-sdm632";
+ qcom,gfxfreq-corner =
+ < 0 0 >,
+ < 133330000 1 >, /* Min SVS */
+ < 216000000 2 >, /* Low SVS */
+ < 320000000 3 >, /* SVS */
+ < 400000000 4 >, /* SVS Plus */
+ < 510000000 5 >, /* NOM */
+ < 560000000 6 >, /* Nom Plus */
+ < 650000000 7 >, /* Turbo */
+ < 700000000 7 >, /* Turbo */
+ < 725000000 7 >; /* Turbo */
};
&thermal_zones {
@@ -824,10 +845,12 @@
< 1804800000 7>;
qcom,speed0-bin-v0-c1 =
< 0 0>,
- < 1094400000 1>,
- < 1401600000 2>,
- < 1555200000 3>,
- < 1804800000 4>;
+ < 633600000 1>,
+ < 902400000 2>,
+ < 1094400000 3>,
+ < 1401600000 4>,
+ < 1555200000 5>,
+ < 1804800000 6>;
qcom,speed0-bin-v0-cci =
< 0 0>,
< 307200000 1>,
@@ -848,10 +871,12 @@
< 1804800000 7>;
qcom,speed6-bin-v0-c1 =
< 0 0>,
- < 1094400000 1>,
- < 1401600000 2>,
- < 1555200000 3>,
- < 1804800000 4>;
+ < 633600000 1>,
+ < 902400000 2>,
+ < 1094400000 3>,
+ < 1401600000 4>,
+ < 1555200000 5>,
+ < 1804800000 6>;
qcom,speed6-bin-v0-cci =
< 0 0>,
< 307200000 1>,
@@ -872,11 +897,13 @@
< 1804800000 7>;
qcom,speed2-bin-v0-c1 =
< 0 0>,
- < 1094400000 1>,
- < 1401600000 2>,
- < 1555200000 3>,
- < 1804800000 4>,
- < 2016000000 5>;
+ < 633600000 1>,
+ < 902400000 2>,
+ < 1094400000 3>,
+ < 1401600000 4>,
+ < 1555200000 5>,
+ < 1804800000 6>,
+ < 2016000000 7>;
qcom,speed2-bin-v0-cci =
< 0 0>,
< 307200000 1>,
@@ -914,6 +941,8 @@
< 1804800 >;
qcom,cpufreq-table-4 =
+ < 633600 >,
+ < 902400 >,
< 1094400 >,
< 1401600 >,
< 1555200 >,
@@ -961,6 +990,8 @@
< 1536000 768000>, /* NOM+ */
< 1670400 787200>; /* TURBO */
cpu-to-dev-map-4 =
+ < 633600 307200>, /* SVS */
+ < 902400 403200>,
< 1094400 499200>, /* SVS */
< 1401600 691200>, /* NOM */
< 1555200 768000>, /* NOM+ */
@@ -983,3 +1014,98 @@
/delete-node/ case-therm-step;
};
+#include "sdm632-coresight.dtsi"
+
+/* GPU Overrides*/
+&msm_gpu {
+
+ qcom,ca-target-pwrlevel = <4>;
+ qcom,initial-pwrlevel = <5>;
+ /delete-node/qcom,gpu-pwrlevels;
+
+ /* Power levels */
+ qcom,gpu-pwrlevels {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "qcom,gpu-pwrlevels";
+ /* TURBO LD0 */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <725000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <10>;
+ };
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <650000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <10>;
+ };
+
+ /* NOM+ */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <560000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <10>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <510000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <10>;
+ };
+
+ /* SVS+ */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <400000000>;
+ qcom,bus-freq = <7>;
+ qcom,bus-min = <5>;
+ qcom,bus-max = <8>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <320000000>;
+ qcom,bus-freq = <4>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <6>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <216000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <4>;
+ };
+
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <133300000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <1>;
+ qcom,bus-max = <4>;
+ };
+ /* XO */
+ qcom,gpu-pwrlevel@8 {
+ reg = <8>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi
index c8f7ac0..78047bd 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+
&soc {
led_flash_rear: qcom,camera-flash@0 {
cell-index = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
index 9402294..348ba6f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
@@ -411,6 +411,7 @@
qcom,cpas-hw-ver = <0x170110>; /* Titan v170 v1.1.0 */
nvmem-cells = <&minor_rev>;
nvmem-cell-names = "minor_rev";
+ camnoc-axi-min-ib-bw = <3000000000>;
regulator-names = "camss-vdd";
camss-vdd-supply = <&titan_top_gdsc>;
clock-names = "gcc_ahb_clk",
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index bd88087..95fb25a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -38,6 +38,8 @@
qcom,vddp-ref-clk-supply = <&pm660_l1>;
qcom,vddp-ref-clk-max-microamp = <100>;
+ force-ufshc-probe;
+
status = "ok";
};
@@ -143,7 +145,7 @@
cam_snapshot {
label = "cam_snapshot";
- gpios = <&tlmm 91 0>;
+ gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <766>;
gpio-key,wakeup;
@@ -153,7 +155,7 @@
cam_focus {
label = "cam_focus";
- gpios = <&tlmm 92 0>;
+ gpios = <&tlmm 92 GPIO_ACTIVE_HIGH>;
linux,input-type = <1>;
linux,code = <528>;
gpio-key,wakeup;
@@ -296,6 +298,17 @@
qcom,platform-te-gpio = <&tlmm 10 0>;
};
+&dsi_hx8399_truly_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+};
+
&dsi_dual_nt35597_truly_video_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index cc55127..7162257 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -14,7 +14,10 @@
#include "sdm670-pmic-overlay.dtsi"
#include "sdm670-sde-display.dtsi"
#include "sdm670-camera-sensor-mtp.dtsi"
+
+&qupv3_se10_i2c {
#include "smb1355.dtsi"
+};
&ufsphy_mem {
compatible = "qcom,ufs-phy-qmp-v3";
@@ -202,7 +205,7 @@
cam_snapshot {
label = "cam_snapshot";
- gpios = <&tlmm 91 0>;
+ gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <766>;
gpio-key,wakeup;
@@ -212,7 +215,7 @@
cam_focus {
label = "cam_focus";
- gpios = <&tlmm 92 0>;
+ gpios = <&tlmm 92 GPIO_ACTIVE_HIGH>;
linux,input-type = <1>;
linux,code = <528>;
gpio-key,wakeup;
@@ -355,6 +358,17 @@
qcom,platform-te-gpio = <&tlmm 10 0>;
};
+&dsi_hx8399_truly_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+};
+
&dsi_dual_nt35597_truly_video_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
index bdcd039..8ed821a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
@@ -134,7 +134,9 @@
#size-cells = <0>;
qcom,psci-mode-shift = <0>;
qcom,psci-mode-mask = <0xf>;
- qcom,disable-prediction;
+ qcom,ref-stddev = <100>;
+ qcom,tmr-add = <100>;
+ qcom,ref-premature-cnt = <3>;
qcom,cpu = <&CPU6 &CPU7>;
qcom,pm-cpu-level@0 { /* C1 */
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
index 27be1fd..f63d442 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
&pm660_0{
pm660_charger: qcom,qpnp-smb2 {
compatible = "qcom,qpnp-smb2";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
index 36d485e..67b5ebe 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,7 +19,7 @@
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku1.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku1.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku1.dtsi
new file mode 100644
index 0000000..2c1cde6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku1.dtsi
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm670-qrd.dtsi"
+#include "sdm670-audio-overlay.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
index 73d1909..d5edb36 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
@@ -13,13 +13,7 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/clock/qcom,gcc-sdm845.h>
-#include <dt-bindings/clock/qcom,camcc-sdm845.h>
-#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
-#include <dt-bindings/clock/qcom,rpmh.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
-
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku2.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD SKU2";
@@ -30,22 +24,3 @@
<0x0001001b 0x0102001a 0x0 0x0>,
<0x0001001b 0x0201011a 0x0 0x0>;
};
-
-&dsi_dual_nt36850_truly_cmd_display {
- /delete-property/ qcom,dsi-display-active;
-};
-
-&dsi_hx8399_truly_cmd {
- qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
- qcom,mdss-dsi-bl-min-level = <1>;
- qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,panel-mode-gpio = <&tlmm 76 0>;
- qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
- qcom,platform-reset-gpio = <&tlmm 75 0>;
- qcom,platform-te-gpio = <&tlmm 10 0>;
-};
-
-&dsi_hx8399_truly_cmd_display {
- qcom,dsi-display-active;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
index 680bc17..9f871c5 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,7 +14,7 @@
/dts-v1/;
#include "sdm670.dtsi"
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku2.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD SKU2";
@@ -24,22 +24,3 @@
<0x0001001b 0x0102001a 0x0 0x0>,
<0x0001001b 0x0201011a 0x0 0x0>;
};
-
-&dsi_dual_nt36850_truly_cmd_display {
- /delete-property/ qcom,dsi-display-active;
-};
-
-&dsi_hx8399_truly_cmd {
- qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
- qcom,mdss-dsi-bl-min-level = <1>;
- qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,panel-mode-gpio = <&tlmm 76 0>;
- qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
- qcom,platform-reset-gpio = <&tlmm 75 0>;
- qcom,platform-te-gpio = <&tlmm 10 0>;
-};
-
-&dsi_hx8399_truly_cmd_display {
- qcom,dsi-display-active;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dtsi
new file mode 100644
index 0000000..cdb652e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dtsi
@@ -0,0 +1,32 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sdm670-qrd.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+&dsi_dual_nt36850_truly_cmd_display {
+ /delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_hx8399_truly_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-mode-gpio = <&tlmm 76 0>;
+ qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+ qcom,platform-reset-gpio = <&tlmm 75 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_hx8399_truly_cmd_display {
+ qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
index c22afa4..318939f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,7 +14,7 @@
/dts-v1/;
#include "sdm670.dtsi"
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku1.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index 43f1465..5ff2c32 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -10,13 +10,16 @@
* GNU General Public License for more details.
*/
+#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/gpio/gpio.h>
#include "sdm670-camera-sensor-qrd.dtsi"
#include "sdm670-pmic-overlay.dtsi"
-#include "sdm670-audio-overlay.dtsi"
-#include "smb1355.dtsi"
#include "sdm670-sde-display.dtsi"
+&qupv3_se10_i2c {
+#include "smb1355.dtsi"
+};
+
&qupv3_se9_2uart {
status = "disabled";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index 48deca6..395e88c 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -468,7 +468,7 @@
dsi_hx8399_truly_cmd_display: qcom,dsi-display@16 {
compatible = "qcom,dsi-display";
- label = "dsi_hx8399_truly_cmd_display";
+ label = "dsi_hx8399_truly_fhd_video_display";
qcom,display-type = "primary";
qcom,dsi-ctrl = <&mdss_dsi0>;
@@ -858,6 +858,7 @@
qcom,mdss-dsi-panel-status-value = <0x9c>;
qcom,mdss-dsi-panel-on-check-value = <0x9c>;
qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-cmds-only-by-right;
qcom,mdss-dsi-display-timings {
timing@0{
qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08
diff --git a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
index a0b4a22..f5e9489 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
@@ -129,15 +129,19 @@
/* Clocks */
clock-names = "core_clk", "iface_clk", "bus_clk",
- "core0_clk", "core0_bus_clk";
+ "core0_clk", "core0_bus_clk", "core1_clk",
+ "core1_bus_clk";
clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
<&clock_videocc VIDEO_CC_VENUS_AHB_CLK>,
<&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>,
<&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>,
- <&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>;
+ <&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_AXI_CLK>;
qcom,proxy-clock-names = "core_clk", "iface_clk",
- "bus_clk", "core0_clk", "core0_bus_clk";
- qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0>;
+ "bus_clk", "core0_clk", "core0_bus_clk",
+ "core1_clk", "core1_bus_clk";
+ qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0 0x4 0x4>;
qcom,allowed-clock-rates = <100000000 200000000 330000000
364700000>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 3ca33b2..c962b42 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -730,6 +730,7 @@
<0x17a60000 0x100000>; /* GICR * 8 */
interrupts = <1 9 4>;
interrupt-parent = <&intc>;
+ ignored-save-restore-irqs = <38>;
};
pdc: interrupt-controller@b220000{
@@ -1096,7 +1097,7 @@
clock_debug: qcom,cc-debug {
compatible = "qcom,debugcc-sdm845";
- qcom,cc-count = <5>;
+ qcom,cc-count = <6>;
qcom,gcc = <&clock_gcc>;
qcom,videocc = <&clock_videocc>;
qcom,camcc = <&clock_camcc>;
@@ -1389,7 +1390,7 @@
};
};
- mem_dump {
+ mem_dump: mem_dump {
compatible = "qcom,mem-dump";
memory-region = <&dump_mem>;
@@ -1866,6 +1867,7 @@
ufs-qcom-crypto = <&ufs_ice>;
lanes-per-direction = <1>;
+ spm-level = <5>;
dev-ref-clk-freq = <0>; /* 19.2 MHz */
clock-names =
@@ -1879,7 +1881,7 @@
"rx_lane0_sync_clk";
clocks =
<&clock_gcc GCC_UFS_PHY_AXI_HW_CTL_CLK>,
- <&clock_gcc GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK>,
+ <&clock_gcc UFS_PHY_AXI_UFS_VOTE_CLK>,
<&clock_gcc GCC_UFS_PHY_AHB_CLK>,
<&clock_gcc GCC_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK>,
<&clock_gcc GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK>,
@@ -2216,6 +2218,7 @@
status = "ok";
memory-region = <&pil_modem_mem>;
qcom,mem-protect-id = <0xF>;
+ qcom,complete-ramdump;
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
@@ -2382,7 +2385,7 @@
clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>,
<&clock_gcc GCC_SDCC1_APPS_CLK>,
<&clock_gcc GCC_SDCC1_ICE_CORE_CLK>,
- <&clock_gcc GCC_AGGRE_UFS_PHY_AXI_CLK>;
+ <&clock_gcc UFS_PHY_AXI_EMMC_VOTE_CLK>;
clock-names = "iface_clk", "core_clk", "ice_core_clk",
"bus_aggr_clk";
diff --git a/arch/arm64/boot/dts/qcom/sdm710-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm710-qrd-overlay.dts
index 803616d..08c3433 100644
--- a/arch/arm64/boot/dts/qcom/sdm710-qrd-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm710-qrd-overlay.dts
@@ -13,13 +13,7 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/clock/qcom,gcc-sdm845.h>
-#include <dt-bindings/clock/qcom,camcc-sdm845.h>
-#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
-#include <dt-bindings/clock/qcom,rpmh.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
-
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku1.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM710 PM660 + PM660L QRD";
diff --git a/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2-overlay.dts b/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2-overlay.dts
index ab3ce4d..91891ba 100644
--- a/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2-overlay.dts
@@ -13,13 +13,7 @@
/dts-v1/;
/plugin/;
-#include <dt-bindings/clock/qcom,gcc-sdm845.h>
-#include <dt-bindings/clock/qcom,camcc-sdm845.h>
-#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
-#include <dt-bindings/clock/qcom,rpmh.h>
-#include <dt-bindings/interrupt-controller/arm-gic.h>
-
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku2.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM710 PM660 + PM660L QRD SKU2";
@@ -30,22 +24,3 @@
<0x0001001b 0x0102001a 0x0 0x0>,
<0x0001001b 0x0201011a 0x0 0x0>;
};
-
-&dsi_dual_nt36850_truly_cmd_display {
- /delete-property/ qcom,dsi-display-active;
-};
-
-&dsi_hx8399_truly_cmd {
- qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
- qcom,mdss-dsi-bl-min-level = <1>;
- qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,panel-mode-gpio = <&tlmm 76 0>;
- qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
- qcom,platform-reset-gpio = <&tlmm 75 0>;
- qcom,platform-te-gpio = <&tlmm 10 0>;
-};
-
-&dsi_hx8399_truly_cmd_display {
- qcom,dsi-display-active;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2.dts b/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2.dts
index 76b2862..f674893 100644
--- a/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm710-qrd-sku2.dts
@@ -14,7 +14,7 @@
/dts-v1/;
#include "sdm710.dtsi"
-#include "sdm670-qrd.dtsi"
+#include "sdm670-qrd-sku2.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM710 PM660 + PM660L QRD SKU2";
@@ -24,22 +24,3 @@
<0x0001001b 0x0102001a 0x0 0x0>,
<0x0001001b 0x0201011a 0x0 0x0>;
};
-
-&dsi_dual_nt36850_truly_cmd_display {
- /delete-property/ qcom,dsi-display-active;
-};
-
-&dsi_hx8399_truly_cmd {
- qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
- qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
- qcom,mdss-dsi-bl-min-level = <1>;
- qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,panel-mode-gpio = <&tlmm 76 0>;
- qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
- qcom,platform-reset-gpio = <&tlmm 75 0>;
- qcom,platform-te-gpio = <&tlmm 10 0>;
-};
-
-&dsi_hx8399_truly_cmd_display {
- qcom,dsi-display-active;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm710-qrd.dts b/arch/arm64/boot/dts/qcom/sdm710-qrd.dts
index e3cb7cc..4eb414f 100644
--- a/arch/arm64/boot/dts/qcom/sdm710-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/sdm710-qrd.dts
@@ -15,6 +15,7 @@
#include "sdm710.dtsi"
#include "sdm670-qrd.dtsi"
+#include "sdm670-audio-overlay.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM710 PM660 + PM660L QRD";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi
index 9903d19..0c37bf1 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-670-usb-common.dtsi
@@ -36,6 +36,7 @@
qcom,dwc-usb3-msm-tx-fifo-size = <21288>;
qcom,num-gsi-evt-buffs = <0x3>;
qcom,use-pdc-interrupts;
+ qcom,pm-qos-latency = <44>;
extcon = <0>, <0>, <&eud>, <0>, <0>;
clocks = <&clock_gcc GCC_USB30_PRIM_MASTER_CLK>,
diff --git a/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi
index a5c6d84..944c1dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,15 +37,6 @@
qcom,auxpcm-audio-intf;
qcom,msm-mi2s-master = <1>, <1>, <1>, <1>;
- reg = <0x1711a000 0x4>,
- <0x1711b000 0x4>,
- <0x1711c000 0x4>,
- <0x1711d000 0x4>;
- reg-names = "lpaif_pri_mode_muxsel",
- "lpaif_sec_mode_muxsel",
- "lpaif_tert_mode_muxsel",
- "lpaif_quat_mode_muxsel";
-
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
<&afe>, <&lsm>, <&routing>, <&compr>,
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-svr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-svr.dtsi
index d387f93..aa068e5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-svr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-svr.dtsi
@@ -220,7 +220,7 @@
&cam_sensor_rear2_suspend>;
gpios = <&tlmm 15 0>,
<&tlmm 9 0>,
- <&tlmm 8 0>;
+ <&tlmm 7 0>;
gpio-reset = <1>;
gpio-vana = <2>;
gpio-req-tbl-num = <0 1 2>;
@@ -261,7 +261,7 @@
&cam_sensor_front_suspend>;
gpios = <&tlmm 14 0>,
<&tlmm 28 0>,
- <&tlmm 8 0>;
+ <&tlmm 7 0>;
gpio-reset = <1>;
gpio-vana = <2>;
gpio-req-tbl-num = <0 1 2>;
@@ -441,37 +441,39 @@
sensor-position-roll = <270>;
sensor-position-pitch = <0>;
sensor-position-yaw = <0>;
- led-flash-src = <&led_flash_iris>;
- cam_vio-supply = <&pm8998_lvs1>;
- cam_vana-supply = <&pmi8998_bob>;
- cam_vdig-supply = <&camera_ldo>;
+ cam_vio-supply = <&pm8998_l9>;
+ cam_vana-supply = <&pm8998_l16>;
+ cam_vdig-supply = <&pm8998_l10>;
cam_clk-supply = <&titan_top_gdsc>;
regulator-names = "cam_vio", "cam_vana", "cam_vdig",
"cam_clk";
rgltr-cntrl-support;
- rgltr-min-voltage = <0 3312000 1050000 0>;
- rgltr-max-voltage = <0 3600000 1050000 0>;
+ rgltr-min-voltage = <1800000 3312000 1800000 0>;
+ rgltr-max-voltage = <1800000 3312000 1800000 0>;
rgltr-load-current = <0 80000 105000 0>;
gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
- pinctrl-0 = <&cam_sensor_mclk3_active
+ pinctrl-0 = <&cam_sensor_mclk2_active
&cam_sensor_iris_active>;
- pinctrl-1 = <&cam_sensor_mclk3_suspend
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
&cam_sensor_iris_suspend>;
- gpios = <&tlmm 16 0>,
- <&tlmm 9 0>,
- <&tlmm 8 0>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 21 0>,
+ <&tlmm 122 0>,
+ <&tlmm 59 0>;
gpio-reset = <1>;
gpio-vana = <2>;
- gpio-req-tbl-num = <0 1 2>;
- gpio-req-tbl-flags = <1 0 0>;
+ gpio-vdig = <3>;
+ gpio-req-tbl-num = <0 1 2 3>;
+ gpio-req-tbl-flags = <1 0 0 0>;
gpio-req-tbl-label = "CAMIF_MCLK3",
"CAM_RESET3",
- "CAM_VANA1";
+ "CAM_VANA3",
+ "CAM_VDIG3";
sensor-mode = <0>;
cci-master = <1>;
status = "ok";
- clocks = <&clock_camcc CAM_CC_MCLK3_CLK>;
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
clock-names = "cam_clk";
clock-cntl-level = "turbo";
clock-rates = <24000000>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index fd6a0c7..2e2de74 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -381,6 +381,7 @@
interrupt-names = "cpas_camnoc";
interrupts = <0 459 0>;
qcom,cpas-hw-ver = <0x170100>; /* Titan v170 v1.0.0 */
+ camnoc-axi-min-ib-bw = <3000000000>;
regulator-names = "camss-vdd";
camss-vdd-supply = <&titan_top_gdsc>;
clock-names = "gcc_ahb_clk",
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 349c4c0..812a313 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -14,7 +14,10 @@
#include "sdm845-pmic-overlay.dtsi"
#include "sdm845-pinctrl-overlay.dtsi"
#include "sdm845-camera-sensor-mtp.dtsi"
+
+&qupv3_se10_i2c {
#include "smb1355.dtsi"
+};
&vendor {
bluetooth: bt_wcn3990 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index 6034b6d..f5a979c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -13,9 +13,12 @@
#include "sdm845-pmic-overlay.dtsi"
#include "sdm845-pinctrl-overlay.dtsi"
#include "sdm845-camera-sensor-qrd.dtsi"
-#include "smb1355.dtsi"
#include <dt-bindings/gpio/gpio.h>
+&qupv3_se10_i2c {
+#include "smb1355.dtsi"
+};
+
&vendor {
bluetooth: bt_wcn3990 {
compatible = "qca,wcn3990";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index a5c6ab5..b2b0000 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -18,7 +18,10 @@
#include "sdm845-pmic-overlay.dtsi"
#include "sdm845-pinctrl-overlay.dtsi"
+
+&qupv3_se10_i2c {
#include "smb1355.dtsi"
+};
&vendor {
bluetooth: bt_wcn3990 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index bfcebf6..6132722 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -258,33 +258,13 @@
/* data and reg bus scale settings */
qcom,sde-data-bus {
- qcom,msm-bus,name = "mdss_sde_mnoc";
+ qcom,msm-bus,name = "mdss_sde";
qcom,msm-bus,num-cases = <3>;
qcom,msm-bus,num-paths = <2>;
qcom,msm-bus,vectors-KBps =
- <22 773 0 0>, <23 773 0 0>,
- <22 773 0 6400000>, <23 773 0 6400000>,
- <22 773 0 6400000>, <23 773 0 6400000>;
- };
-
- qcom,sde-llcc-bus {
- qcom,msm-bus,name = "mdss_sde_llcc";
- qcom,msm-bus,num-cases = <3>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps =
- <132 770 0 0>,
- <132 770 0 6400000>,
- <132 770 0 6400000>;
- };
-
- qcom,sde-ebi-bus {
- qcom,msm-bus,name = "mdss_sde_ebi";
- qcom,msm-bus,num-cases = <3>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps =
- <129 512 0 0>,
- <129 512 0 6400000>,
- <129 512 0 6400000>;
+ <22 512 0 0>, <23 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>;
};
qcom,sde-reg-bus {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
index 85419c8..97cb981 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
@@ -302,6 +302,7 @@
interrupt-names = "cpas_camnoc";
interrupts = <0 459 0>;
qcom,cpas-hw-ver = <0x170110>; /* Titan v170 v1.1.0 */
+ camnoc-axi-min-ib-bw = <3000000000>;
regulator-names = "camss-vdd";
camss-vdd-supply = <&titan_top_gdsc>;
clock-names = "gcc_ahb_clk",
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 575cf12..229d06b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -51,7 +51,7 @@
compatible = "qcom,memshare-peripheral";
qcom,peripheral-size = <0x500000>;
qcom,client-id = <1>;
- qcom,allocate-boot-time;
+ qcom,allocate-on-request;
label = "modem";
};
};
@@ -469,6 +469,8 @@
2764800 30000
2784000 35000
2803200 40000
+ 2841600 50000
+ 2956800 60000
>;
idle-cost-data = <
100 80 60 40
@@ -535,6 +537,8 @@
2764800 160
2784000 165
2803200 170
+ 2841600 180
+ 2956800 190
>;
idle-cost-data = <
4 3 2 1
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 22b4b90..6c71212 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -776,6 +776,7 @@
<0x17a60000 0x100000>; /* GICR * 8 */
interrupts = <1 9 4>;
interrupt-parent = <&intc>;
+ ignored-save-restore-irqs = <38>;
};
pdc: interrupt-controller@b220000{
@@ -1276,7 +1277,7 @@
clock_debug: qcom,cc-debug@100000 {
compatible = "qcom,debugcc-sdm845";
- qcom,cc-count = <5>;
+ qcom,cc-count = <6>;
qcom,gcc = <&clock_gcc>;
qcom,videocc = <&clock_videocc>;
qcom,camcc = <&clock_camcc>;
diff --git a/arch/arm64/boot/dts/qcom/smb1355.dtsi b/arch/arm64/boot/dts/qcom/smb1355.dtsi
index 3412b25d..5939440 100644
--- a/arch/arm64/boot/dts/qcom/smb1355.dtsi
+++ b/arch/arm64/boot/dts/qcom/smb1355.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,98 +12,96 @@
#include <dt-bindings/interrupt-controller/irq.h>
-&qupv3_se10_i2c {
- smb1355_0: qcom,smb1355@8 {
- compatible = "qcom,i2c-pmic";
- reg = <0x8>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupt-parent = <&spmi_bus>;
- interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
- interrupt_names = "smb1355_0";
- interrupt-controller;
- #interrupt-cells = <3>;
- qcom,periph-map = <0x10 0x12 0x13 0x16>;
+smb1355_0: qcom,smb1355@8 {
+ compatible = "qcom,i2c-pmic";
+ reg = <0x8>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
+ interrupt_names = "smb1355_0";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ qcom,periph-map = <0x10 0x12 0x13 0x16>;
- smb1355_revid_0: qcom,revid@100 {
- compatible = "qcom,qpnp-revid";
- reg = <0x100 0x100>;
- };
-
- smb1355_charger_0: qcom,smb1355-charger@1000 {
- compatible = "qcom,smb1355";
- qcom,pmic-revid = <&smb1355_revid_0>;
- reg = <0x1000 0x700>;
- #address-cells = <1>;
- #size-cells = <1>;
- interrupt-parent = <&smb1355_0>;
- status = "disabled";
-
- io-channels = <&pmi8998_rradc 2>,
- <&pmi8998_rradc 12>;
- io-channel-names = "charger_temp",
- "charger_temp_max";
-
- qcom,chgr@1000 {
- reg = <0x1000 0x100>;
- interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "chg-state-change";
- };
-
- qcom,chgr-misc@1600 {
- reg = <0x1600 0x100>;
- interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
- <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "wdog-bark",
- "temperature-change";
- };
- };
+ smb1355_revid_0: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
};
- smb1355_1: qcom,smb1355@c {
- compatible = "qcom,i2c-pmic";
- reg = <0xc>;
+ smb1355_charger_0: qcom,smb1355-charger@1000 {
+ compatible = "qcom,smb1355";
+ qcom,pmic-revid = <&smb1355_revid_0>;
+ reg = <0x1000 0x700>;
#address-cells = <1>;
- #size-cells = <0>;
- interrupt-parent = <&spmi_bus>;
- interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
- interrupt_names = "smb1355_1";
- interrupt-controller;
- #interrupt-cells = <3>;
- qcom,periph-map = <0x10 0x12 0x13 0x16>;
+ #size-cells = <1>;
+ interrupt-parent = <&smb1355_0>;
+ status = "disabled";
- smb1355_revid_1: qcom,revid@100 {
- compatible = "qcom,qpnp-revid";
- reg = <0x100 0x100>;
+ io-channels = <&pmi8998_rradc 2>,
+ <&pmi8998_rradc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "chg-state-change";
};
- smb1355_charger_1: qcom,smb1355-charger@1000 {
- compatible = "qcom,smb1355";
- qcom,pmic-revid = <&smb1355_revid_1>;
- reg = <0x1000 0x700>;
- #address-cells = <1>;
- #size-cells = <1>;
- interrupt-parent = <&smb1355_1>;
- status = "disabled";
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "wdog-bark",
+ "temperature-change";
+ };
+ };
+};
- io-channels = <&pmi8998_rradc 2>,
- <&pmi8998_rradc 12>;
- io-channel-names = "charger_temp",
- "charger_temp_max";
+smb1355_1: qcom,smb1355@c {
+ compatible = "qcom,i2c-pmic";
+ reg = <0xc>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
+ interrupt_names = "smb1355_1";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ qcom,periph-map = <0x10 0x12 0x13 0x16>;
- qcom,chgr@1000 {
- reg = <0x1000 0x100>;
- interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "chg-state-change";
- };
+ smb1355_revid_1: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
- qcom,chgr-misc@1600 {
- reg = <0x1600 0x100>;
- interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
- <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "wdog-bark",
- "temperature-change";
- };
+ smb1355_charger_1: qcom,smb1355-charger@1000 {
+ compatible = "qcom,smb1355";
+ qcom,pmic-revid = <&smb1355_revid_1>;
+ reg = <0x1000 0x700>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&smb1355_1>;
+ status = "disabled";
+
+ io-channels = <&pmi8998_rradc 2>,
+ <&pmi8998_rradc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+
+ qcom,chgr@1000 {
+ reg = <0x1000 0x100>;
+ interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "chg-state-change";
+ };
+
+ qcom,chgr-misc@1600 {
+ reg = <0x1600 0x100>;
+ interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "wdog-bark",
+ "temperature-change";
};
};
};
diff --git a/arch/arm64/boot/dts/qcom/smb1390.dtsi b/arch/arm64/boot/dts/qcom/smb1390.dtsi
new file mode 100644
index 0000000..92ac103
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/smb1390.dtsi
@@ -0,0 +1,61 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+smb1390: qcom,smb1390@10 {
+ compatible = "qcom,i2c-pmic";
+ reg = <0x10>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "smb1390";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ qcom,periph-map = <0x10>;
+
+ smb1390_revid: qcom,revid {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100>;
+ };
+
+ smb1390_charger: qcom,charge_pump {
+ compatible = "qcom,smb1390-charger";
+ qcom,pmic-revid = <&smb1390_revid>;
+ interrupt-parent = <&smb1390>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&smb1390_die_temp_default>;
+ qcom,smb-vadc = <&pm8998_vadc>;
+ qcom,channel-num = <0x14>;
+ status = "disabled";
+
+ qcom,core {
+ interrupts = <0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x4 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x5 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x6 IRQ_TYPE_EDGE_RISING>,
+ <0x10 0x7 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "switcher-off-window",
+ "switcher-off-fault",
+ "tsd-fault",
+ "irev-fault",
+ "vph-ov-hard",
+ "vph-ov-soft",
+ "ilim",
+ "temp-alarm";
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp-overlay.dts
new file mode 100644
index 0000000..c3aa763
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp-overlay.dts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sxr1120-lc-cdp.dtsi"
+#include "qcs605-lc-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 CDP";
+ compatible = "qcom,sxr1120-cdp", "qcom,sxr1120", "qcom,cdp";
+ qcom,msm-id = <370 0x0>;
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp.dts
new file mode 100644
index 0000000..6237873
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sxr1120-lc.dtsi"
+#include "sxr1120-lc-cdp.dtsi"
+#include "qcs605-lc-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 CDP";
+ compatible = "qcom,sxr1120-cdp", "qcom,sxr1120", "qcom,cdp";
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp.dtsi b/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp.dtsi
new file mode 100644
index 0000000..d4d42c5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-cdp.dtsi
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qcs605-lc-cdp.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-cdp-overlay.dts
new file mode 100644
index 0000000..e90f3b4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-cdp-overlay.dts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sxr1120-lc-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 Ext. Audio Codec CDP";
+ compatible = "qcom,sxr1120-cdp", "qcom,sxr1120", "qcom,cdp";
+ qcom,msm-id = <370 0x0>;
+ qcom,board-id = <1 1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-cdp.dts
new file mode 100644
index 0000000..76c424d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-cdp.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sxr1120-lc.dtsi"
+#include "sxr1120-lc-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 Ext. Audio Codec CDP";
+ compatible = "qcom,sxr1120-cdp", "qcom,sxr1120", "qcom,cdp";
+ qcom,board-id = <1 1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-mtp-overlay.dts
new file mode 100644
index 0000000..946298f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-mtp-overlay.dts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sxr1120-lc-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 Ext. Audio Codec MTP";
+ compatible = "qcom,sxr1120-mtp", "qcom,sxr1120", "qcom,mtp";
+ qcom,msm-id = <370 0x0>;
+ qcom,board-id = <8 1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-mtp.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-mtp.dts
new file mode 100644
index 0000000..e53bbe3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-external-codec-mtp.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sxr1120-lc.dtsi"
+#include "sxr1120-lc-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 Ext. Audio Codec MTP";
+ compatible = "qcom,sxr1120-mtp", "qcom,sxr1120", "qcom,mtp";
+ qcom,board-id = <8 1>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp-overlay.dts
new file mode 100644
index 0000000..8af46ef
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp-overlay.dts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sxr1120-lc-mtp.dtsi"
+#include "qcs605-lc-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 MTP";
+ compatible = "qcom,sxr1120-mtp", "qcom,sxr1120", "qcom,mtp";
+ qcom,msm-id = <370 0x0>;
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp.dts
new file mode 100644
index 0000000..ffcdeda
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sxr1120-lc.dtsi"
+#include "sxr1120-lc-mtp.dtsi"
+#include "qcs605-lc-audio-overlay.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC Groot+PM8005 MTP";
+ compatible = "qcom,sxr1120-mtp", "qcom,sxr1120", "qcom,mtp";
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp.dtsi b/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp.dtsi
new file mode 100644
index 0000000..270aa0e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc-mtp.dtsi
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qcs605-lc-mtp.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc.dts b/arch/arm64/boot/dts/qcom/sxr1120-lc.dts
new file mode 100644
index 0000000..5967388
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc.dts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sxr1120-lc.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 LC SoC";
+ compatible = "qcom,sxr1120";
+ qcom,board-id = <0 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sxr1120-lc.dtsi b/arch/arm64/boot/dts/qcom/sxr1120-lc.dtsi
new file mode 100644
index 0000000..1413e2c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sxr1120-lc.dtsi
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qcs605-lc.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SXR1120 SoC";
+ compatible = "qcom,sxr1120";
+ qcom,msm-id = <370 0x0>;
+};
diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
index 9217da9..53d03cb 100644
--- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi
@@ -36,9 +36,8 @@
enable-method = "psci";
};
- L2_CA57: cache-controller@0 {
+ L2_CA57: cache-controller-0 {
compatible = "cache";
- reg = <0>;
power-domains = <&sysc R8A7796_PD_CA57_SCU>;
cache-unified;
cache-level = <2>;
diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig
index df62bd1..4d0cde9 100644
--- a/arch/arm64/configs/msm8937-perf_defconfig
+++ b/arch/arm64/configs/msm8937-perf_defconfig
@@ -7,6 +7,9 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_NOCB_CPU=y
@@ -63,8 +66,8 @@
CONFIG_HZ_100=y
CONFIG_CMA=y
CONFIG_ZSMALLOC=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -100,6 +103,7 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
@@ -111,6 +115,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -144,6 +149,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -205,8 +211,6 @@
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
-CONFIG_NET_SCH_MULTIQ=y
-CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
CONFIG_CLS_U32_MARK=y
@@ -218,13 +222,12 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_NET_ACT_GACT=y
-CONFIG_NET_ACT_MIRRED=y
-CONFIG_NET_ACT_SKBEDIT=y
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -242,7 +245,8 @@
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
-CONFIG_MEMORY_STATE_TIME=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_FPR_FPC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -254,10 +258,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -266,6 +268,14 @@
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
@@ -279,6 +289,21 @@
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_USB_USBNET=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
@@ -286,6 +311,10 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
@@ -318,12 +347,12 @@
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -345,6 +374,7 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_QTI_BCL_PMIC5=y
CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -391,11 +421,9 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
-CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=y
CONFIG_QCOM_KGSL=y
@@ -406,7 +434,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -422,12 +450,9 @@
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
-CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
@@ -441,14 +466,10 @@
CONFIG_USB_STORAGE_ONETOUCH=y
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_DWC3=y
-CONFIG_USB_DWC3_MSM=y
CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -457,7 +478,6 @@
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
-CONFIG_USB_CONFIGFS_QCRNDIS=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
@@ -475,11 +495,12 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -541,6 +562,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -549,6 +571,7 @@
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
CONFIG_MSM_PM=y
+CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
@@ -562,6 +585,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -571,10 +595,10 @@
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -582,8 +606,6 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig
index 9d35d7d..99b4a5a 100644
--- a/arch/arm64/configs/msm8937_defconfig
+++ b/arch/arm64/configs/msm8937_defconfig
@@ -69,8 +69,8 @@
CONFIG_CMA=y
CONFIG_CMA_DEBUGFS=y
CONFIG_ZSMALLOC=y
+CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -107,6 +107,7 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
@@ -118,6 +119,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -151,6 +153,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -213,8 +216,6 @@
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_PRIO=y
-CONFIG_NET_SCH_MULTIQ=y
-CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
CONFIG_CLS_U32_MARK=y
@@ -226,14 +227,13 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_NET_ACT_GACT=y
-CONFIG_NET_ACT_MIRRED=y
-CONFIG_NET_ACT_SKBEDIT=y
CONFIG_DNS_RESOLVER=y
CONFIG_RMNET_DATA=y
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
@@ -252,7 +252,7 @@
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
+CONFIG_FPR_FPC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -264,10 +264,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -276,6 +274,14 @@
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_CADENCE is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
@@ -289,6 +295,21 @@
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_USB_USBNET=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
@@ -296,6 +317,10 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
@@ -330,12 +355,12 @@
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -357,6 +382,7 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_QTI_BCL_PMIC5=y
CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -403,11 +429,9 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
-CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=y
CONFIG_QCOM_KGSL=y
@@ -419,7 +443,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -435,12 +459,9 @@
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
-CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
@@ -454,14 +475,10 @@
CONFIG_USB_STORAGE_ONETOUCH=y
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
-CONFIG_USB_DWC3=y
-CONFIG_USB_DWC3_MSM=y
CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
@@ -470,7 +487,6 @@
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
-CONFIG_USB_CONFIGFS_QCRNDIS=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
@@ -488,12 +504,13 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -563,6 +580,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_QMI_INTERFACE=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL=y
CONFIG_MSM_PIL_SSR_GENERIC=y
CONFIG_MSM_PIL_MSS_QDSP6V5=y
@@ -585,6 +603,7 @@
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -594,10 +613,10 @@
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -606,8 +625,6 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 46b34b3..9ac93aa 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -68,6 +68,7 @@
CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_HARDEN_BRANCH_PREDICTOR=y
+CONFIG_PSCI_BP_HARDENING=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -103,6 +104,7 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
@@ -114,6 +116,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -147,6 +150,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -223,8 +227,12 @@
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
@@ -241,7 +249,6 @@
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -253,10 +260,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -338,12 +343,11 @@
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
-CONFIG_POWER_RESET_SYSCON=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -365,7 +369,9 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_QTI_BCL_PMIC5=y
CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_CPR=y
@@ -381,6 +387,7 @@
CONFIG_REGULATOR_STUB=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_V4L_PLATFORM_DRIVERS=y
@@ -411,10 +418,12 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_SW=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=y
CONFIG_QCOM_KGSL=y
@@ -425,7 +434,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -492,11 +501,12 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -536,6 +546,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
@@ -574,12 +585,14 @@
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_BIG_CLUSTER_MIN_FREQ_ADJUST=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -598,8 +611,6 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index dc58fc3..63dce91 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -71,6 +71,7 @@
CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_HARDEN_BRANCH_PREDICTOR=y
+CONFIG_PSCI_BP_HARDENING=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -107,6 +108,7 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
@@ -118,6 +120,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -151,6 +154,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -229,8 +233,12 @@
CONFIG_RMNET_DATA_FC=y
CONFIG_RMNET_DATA_DEBUG_PKT=y
CONFIG_BT=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_LE is not set
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
@@ -247,7 +255,6 @@
CONFIG_HDCP_QSEECOM=y
CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
-CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -259,10 +266,8 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
-CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
-CONFIG_DM_DEBUG=y
CONFIG_DM_CRYPT=y
CONFIG_DM_REQ_CRYPT=y
CONFIG_DM_UEVENT=y
@@ -347,12 +352,11 @@
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
-CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
-CONFIG_POWER_RESET_SYSCON=y
CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMB5=y
CONFIG_QPNP_SMBCHARGER=y
@@ -374,7 +378,9 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_QTI_BCL_PMIC5=y
CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_CPR=y
@@ -390,6 +396,7 @@
CONFIG_REGULATOR_STUB=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_V4L_PLATFORM_DRIVERS=y
@@ -420,10 +427,12 @@
CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
-CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_3X_V4L2=y
CONFIG_MSM_VIDC_3X_GOVERNORS=y
CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_SW=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=y
CONFIG_QCOM_KGSL=y
@@ -435,7 +444,7 @@
CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -502,12 +511,13 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
CONFIG_MMC_RING_BUFFER=y
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
-CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
@@ -547,6 +557,7 @@
CONFIG_MSM_RMNET_BAM=y
CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_TIMER_LEAP=y
CONFIG_MAILBOX=y
CONFIG_ARM_SMMU=y
CONFIG_QCOM_LAZY_MAPPING=y
@@ -594,12 +605,14 @@
CONFIG_WCNSS_CORE=y
CONFIG_WCNSS_CORE_PRONTO=y
CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_BIG_CLUSTER_MIN_FREQ_ADJUST=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_PWM_QTI_LPG=y
@@ -619,8 +632,6 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_ECRYPT_FS=y
-CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_SDCARD_FS=y
# CONFIG_NETWORK_FILESYSTEMS is not set
CONFIG_NLS_CODEPAGE_437=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index f82678e..956bcc5 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -67,6 +67,7 @@
CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_HARDEN_BRANCH_PREDICTOR=y
+CONFIG_PSCI_BP_HARDENING=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -102,9 +103,11 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -113,6 +116,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -146,6 +150,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -229,12 +234,15 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_NFC_NQ=y
CONFIG_IPC_ROUTER=y
CONFIG_IPC_ROUTER_SECURITY=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_AQT_REGMAP=y
CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
@@ -283,6 +291,7 @@
CONFIG_PPPOPNS=y
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
+CONFIG_USB_LAN78XX=y
CONFIG_USB_USBNET=y
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
@@ -296,7 +305,6 @@
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
# CONFIG_SERIO_SERPORT is not set
-# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVMEM is not set
# CONFIG_DEVKMEM is not set
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index ee3418b..f6895a5 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -72,6 +72,7 @@
CONFIG_PROCESS_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_HARDEN_BRANCH_PREDICTOR=y
+CONFIG_PSCI_BP_HARDENING=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -107,9 +108,11 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -118,6 +121,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -151,6 +155,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -237,6 +242,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
@@ -290,6 +297,7 @@
CONFIG_PPPOPNS=y
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
+CONFIG_USB_LAN78XX=y
CONFIG_USB_USBNET=y
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CLD_LL_CORE=y
@@ -304,7 +312,6 @@
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
# CONFIG_SERIO_SERPORT is not set
-# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_MSM_GENI=y
CONFIG_SERIAL_MSM_GENI_CONSOLE=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index f5dc2a8..e35e571 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -65,6 +65,7 @@
CONFIG_BALANCE_ANON_FILE_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_HARDEN_BRANCH_PREDICTOR=y
+CONFIG_PSCI_BP_HARDENING=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -82,6 +83,7 @@
# CONFIG_PM_WAKELOCKS_GC is not set
CONFIG_CPU_IDLE=y
CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
@@ -101,9 +103,11 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -112,6 +116,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -145,6 +150,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -228,6 +234,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
CONFIG_NFC_NQ=y
@@ -545,6 +553,7 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
@@ -565,13 +574,14 @@
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_F2FS_FS=y
CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_FS_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -603,6 +613,7 @@
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_DUMMY=y
+CONFIG_PFK=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_HARDENED_USERCOPY=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 8dc560d..ab983e0 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -69,6 +69,7 @@
CONFIG_BALANCE_ANON_FILE_RECLAIM=y
CONFIG_SECCOMP=y
CONFIG_HARDEN_BRANCH_PREDICTOR=y
+CONFIG_PSCI_BP_HARDENING=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
@@ -85,6 +86,7 @@
CONFIG_PM_DEBUG=y
CONFIG_CPU_IDLE=y
CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
@@ -104,9 +106,11 @@
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+CONFIG_NET_IPVTI=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_IPCOMP=y
+CONFIG_INET_UDP_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
@@ -115,6 +119,7 @@
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_VTI=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
@@ -148,6 +153,7 @@
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
@@ -233,6 +239,8 @@
CONFIG_BT=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_CERTIFICATION_ONUS=y
+CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_INTERNAL_REGDB=y
# CONFIG_CFG80211_CRDA_SUPPORT is not set
CONFIG_RFKILL=y
@@ -462,7 +470,6 @@
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_KRYO3XX_ARM64=y
-CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_CE=y
CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_UE=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
@@ -564,6 +571,7 @@
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_MSM_REMOTEQDSS=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_BIMC_BWMON=y
@@ -586,13 +594,14 @@
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_F2FS_FS=y
CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_FS_ENCRYPTION=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V2=y
@@ -668,6 +677,7 @@
CONFIG_CORESIGHT_TGU=y
CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_DUMMY=y
+CONFIG_PFK=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_HARDENED_USERCOPY=y
diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig
index 2cf32e9..2b10984 100644
--- a/arch/arm64/crypto/Kconfig
+++ b/arch/arm64/crypto/Kconfig
@@ -53,4 +53,11 @@
tristate "CRC32 and CRC32C using optional ARMv8 instructions"
depends on ARM64
select CRYPTO_HASH
+
+config CRYPTO_SPECK_NEON
+ tristate "NEON accelerated Speck cipher algorithms"
+ depends on KERNEL_MODE_NEON
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_GF128MUL
+ select CRYPTO_SPECK
endif
diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile
index abb79b3..9908765 100644
--- a/arch/arm64/crypto/Makefile
+++ b/arch/arm64/crypto/Makefile
@@ -18,7 +18,8 @@
ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o
obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o
-CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto
+aes-ce-cipher-y := aes-ce-cipher-glue.o aes-ce-cipher-core.o
+CFLAGS_aes-ce-cipher-core.o += -march=armv8-a+crypto -Wa,-march=armv8-a+crypto $(DISABLE_LTO)
obj-$(CONFIG_CRYPTO_AES_ARM64_CE_CCM) += aes-ce-ccm.o
aes-ce-ccm-y := aes-ce-ccm-glue.o aes-ce-ccm-core.o
@@ -29,6 +30,9 @@
obj-$(CONFIG_CRYPTO_AES_ARM64_NEON_BLK) += aes-neon-blk.o
aes-neon-blk-y := aes-glue-neon.o aes-neon.o
+obj-$(CONFIG_CRYPTO_SPECK_NEON) += speck-neon.o
+speck-neon-y := speck-neon-core.o speck-neon-glue.o
+
AFLAGS_aes-ce.o := -DINTERLEAVE=4
AFLAGS_aes-neon.o := -DINTERLEAVE=4
diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher-core.c
similarity index 77%
rename from arch/arm64/crypto/aes-ce-cipher.c
rename to arch/arm64/crypto/aes-ce-cipher-core.c
index 50d9fe1..9f41917 100644
--- a/arch/arm64/crypto/aes-ce-cipher.c
+++ b/arch/arm64/crypto/aes-ce-cipher-core.c
@@ -1,5 +1,5 @@
/*
- * aes-ce-cipher.c - core AES cipher using ARMv8 Crypto Extensions
+ * aes-ce-cipher-core.c - core AES cipher using ARMv8 Crypto Extensions
*
* Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org>
*
@@ -10,16 +10,10 @@
#include <asm/neon.h>
#include <crypto/aes.h>
-#include <linux/cpufeature.h>
#include <linux/crypto.h>
-#include <linux/module.h>
#include "aes-ce-setkey.h"
-MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions");
-MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
-MODULE_LICENSE("GPL v2");
-
struct aes_block {
u8 b[AES_BLOCK_SIZE];
};
@@ -36,7 +30,7 @@
return 6 + ctx->key_length / 4;
}
-static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
{
struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
struct aes_block *out = (struct aes_block *)dst;
@@ -81,7 +75,7 @@
kernel_neon_end();
}
-static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
{
struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
struct aes_block *out = (struct aes_block *)dst;
@@ -223,48 +217,3 @@
return 0;
}
EXPORT_SYMBOL(ce_aes_expandkey);
-
-int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
- unsigned int key_len)
-{
- struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
- int ret;
-
- ret = ce_aes_expandkey(ctx, in_key, key_len);
- if (!ret)
- return 0;
-
- tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
- return -EINVAL;
-}
-EXPORT_SYMBOL(ce_aes_setkey);
-
-static struct crypto_alg aes_alg = {
- .cra_name = "aes",
- .cra_driver_name = "aes-ce",
- .cra_priority = 250,
- .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct crypto_aes_ctx),
- .cra_module = THIS_MODULE,
- .cra_cipher = {
- .cia_min_keysize = AES_MIN_KEY_SIZE,
- .cia_max_keysize = AES_MAX_KEY_SIZE,
- .cia_setkey = ce_aes_setkey,
- .cia_encrypt = aes_cipher_encrypt,
- .cia_decrypt = aes_cipher_decrypt
- }
-};
-
-static int __init aes_mod_init(void)
-{
- return crypto_register_alg(&aes_alg);
-}
-
-static void __exit aes_mod_exit(void)
-{
- crypto_unregister_alg(&aes_alg);
-}
-
-module_cpu_feature_match(AES, aes_mod_init);
-module_exit(aes_mod_exit);
diff --git a/arch/arm64/crypto/aes-ce-cipher-glue.c b/arch/arm64/crypto/aes-ce-cipher-glue.c
new file mode 100644
index 0000000..442949e
--- /dev/null
+++ b/arch/arm64/crypto/aes-ce-cipher-glue.c
@@ -0,0 +1,83 @@
+/*
+ * aes-ce-cipher.c - core AES cipher using ARMv8 Crypto Extensions
+ *
+ * Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
+ * 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 <crypto/aes.h>
+#include <linux/cpufeature.h>
+#include <linux/crypto.h>
+#include <linux/module.h>
+
+#include "aes-ce-setkey.h"
+
+MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions");
+MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
+MODULE_LICENSE("GPL v2");
+
+extern void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]);
+extern void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]);
+
+#ifdef CONFIG_CFI_CLANG
+static inline void __cfi_aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+{
+ aes_cipher_encrypt(tfm, dst, src);
+}
+
+static inline void __cfi_aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
+{
+ aes_cipher_decrypt(tfm, dst, src);
+}
+
+#define aes_cipher_encrypt __cfi_aes_cipher_encrypt
+#define aes_cipher_decrypt __cfi_aes_cipher_decrypt
+#endif
+
+int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
+ unsigned int key_len)
+{
+ struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+ int ret;
+
+ ret = ce_aes_expandkey(ctx, in_key, key_len);
+ if (!ret)
+ return 0;
+
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ce_aes_setkey);
+
+static struct crypto_alg aes_alg = {
+ .cra_name = "aes",
+ .cra_driver_name = "aes-ce",
+ .cra_priority = 250,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto_aes_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = ce_aes_setkey,
+ .cia_encrypt = aes_cipher_encrypt,
+ .cia_decrypt = aes_cipher_decrypt
+ }
+};
+
+static int __init aes_mod_init(void)
+{
+ return crypto_register_alg(&aes_alg);
+}
+
+static void __exit aes_mod_exit(void)
+{
+ crypto_unregister_alg(&aes_alg);
+}
+
+module_cpu_feature_match(AES, aes_mod_init);
+module_exit(aes_mod_exit);
diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c
index ea319c0..f08ecf4 100644
--- a/arch/arm64/crypto/sha1-ce-glue.c
+++ b/arch/arm64/crypto/sha1-ce-glue.c
@@ -28,6 +28,14 @@
asmlinkage void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src,
int blocks);
+#ifdef CONFIG_CFI_CLANG
+static inline void __cfi_sha1_ce_transform(struct sha1_state *sst,
+ u8 const *src, int blocks)
+{
+ sha1_ce_transform((struct sha1_ce_state *)sst, src, blocks);
+}
+#define sha1_ce_transform __cfi_sha1_ce_transform
+#endif
const u32 sha1_ce_offsetof_count = offsetof(struct sha1_ce_state, sst.count);
const u32 sha1_ce_offsetof_finalize = offsetof(struct sha1_ce_state, finalize);
diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c
index 0ed9486..c243221 100644
--- a/arch/arm64/crypto/sha2-ce-glue.c
+++ b/arch/arm64/crypto/sha2-ce-glue.c
@@ -28,6 +28,14 @@
asmlinkage void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src,
int blocks);
+#ifdef CONFIG_CFI_CLANG
+static inline void __cfi_sha2_ce_transform(struct sha256_state *sst,
+ u8 const *src, int blocks)
+{
+ sha2_ce_transform((struct sha256_ce_state *)sst, src, blocks);
+}
+#define sha2_ce_transform __cfi_sha2_ce_transform
+#endif
const u32 sha256_ce_offsetof_count = offsetof(struct sha256_ce_state,
sst.count);
diff --git a/arch/arm64/crypto/sha256-core.S b/arch/arm64/crypto/sha256-core.S
new file mode 100644
index 0000000..3ce82cc
--- /dev/null
+++ b/arch/arm64/crypto/sha256-core.S
@@ -0,0 +1,2061 @@
+// Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License"). You may not use
+// this file except in compliance with the License. You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+
+// ====================================================================
+// Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+// project. The module is, however, dual licensed under OpenSSL and
+// CRYPTOGAMS licenses depending on where you obtain it. For further
+// details see http://www.openssl.org/~appro/cryptogams/.
+//
+// Permission to use under GPLv2 terms is granted.
+// ====================================================================
+//
+// SHA256/512 for ARMv8.
+//
+// Performance in cycles per processed byte and improvement coefficient
+// over code generated with "default" compiler:
+//
+// SHA256-hw SHA256(*) SHA512
+// Apple A7 1.97 10.5 (+33%) 6.73 (-1%(**))
+// Cortex-A53 2.38 15.5 (+115%) 10.0 (+150%(***))
+// Cortex-A57 2.31 11.6 (+86%) 7.51 (+260%(***))
+// Denver 2.01 10.5 (+26%) 6.70 (+8%)
+// X-Gene 20.0 (+100%) 12.8 (+300%(***))
+// Mongoose 2.36 13.0 (+50%) 8.36 (+33%)
+//
+// (*) Software SHA256 results are of lesser relevance, presented
+// mostly for informational purposes.
+// (**) The result is a trade-off: it's possible to improve it by
+// 10% (or by 1 cycle per round), but at the cost of 20% loss
+// on Cortex-A53 (or by 4 cycles per round).
+// (***) Super-impressive coefficients over gcc-generated code are
+// indication of some compiler "pathology", most notably code
+// generated with -mgeneral-regs-only is significanty faster
+// and the gap is only 40-90%.
+//
+// October 2016.
+//
+// Originally it was reckoned that it makes no sense to implement NEON
+// version of SHA256 for 64-bit processors. This is because performance
+// improvement on most wide-spread Cortex-A5x processors was observed
+// to be marginal, same on Cortex-A53 and ~10% on A57. But then it was
+// observed that 32-bit NEON SHA256 performs significantly better than
+// 64-bit scalar version on *some* of the more recent processors. As
+// result 64-bit NEON version of SHA256 was added to provide best
+// all-round performance. For example it executes ~30% faster on X-Gene
+// and Mongoose. [For reference, NEON version of SHA512 is bound to
+// deliver much less improvement, likely *negative* on Cortex-A5x.
+// Which is why NEON support is limited to SHA256.]
+
+#ifndef __KERNEL__
+# include "arm_arch.h"
+#endif
+
+.text
+
+.extern OPENSSL_armcap_P
+.globl sha256_block_data_order
+.type sha256_block_data_order,%function
+.align 6
+sha256_block_data_order:
+#ifndef __KERNEL__
+# ifdef __ILP32__
+ ldrsw x16,.LOPENSSL_armcap_P
+# else
+ ldr x16,.LOPENSSL_armcap_P
+# endif
+ adr x17,.LOPENSSL_armcap_P
+ add x16,x16,x17
+ ldr w16,[x16]
+ tst w16,#ARMV8_SHA256
+ b.ne .Lv8_entry
+ tst w16,#ARMV7_NEON
+ b.ne .Lneon_entry
+#endif
+ stp x29,x30,[sp,#-128]!
+ add x29,sp,#0
+
+ stp x19,x20,[sp,#16]
+ stp x21,x22,[sp,#32]
+ stp x23,x24,[sp,#48]
+ stp x25,x26,[sp,#64]
+ stp x27,x28,[sp,#80]
+ sub sp,sp,#4*4
+
+ ldp w20,w21,[x0] // load context
+ ldp w22,w23,[x0,#2*4]
+ ldp w24,w25,[x0,#4*4]
+ add x2,x1,x2,lsl#6 // end of input
+ ldp w26,w27,[x0,#6*4]
+ adr x30,.LK256
+ stp x0,x2,[x29,#96]
+
+.Loop:
+ ldp w3,w4,[x1],#2*4
+ ldr w19,[x30],#4 // *K++
+ eor w28,w21,w22 // magic seed
+ str x1,[x29,#112]
+#ifndef __AARCH64EB__
+ rev w3,w3 // 0
+#endif
+ ror w16,w24,#6
+ add w27,w27,w19 // h+=K[i]
+ eor w6,w24,w24,ror#14
+ and w17,w25,w24
+ bic w19,w26,w24
+ add w27,w27,w3 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w20,w21 // a^b, b^c in next round
+ eor w16,w16,w6,ror#11 // Sigma1(e)
+ ror w6,w20,#2
+ add w27,w27,w17 // h+=Ch(e,f,g)
+ eor w17,w20,w20,ror#9
+ add w27,w27,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w23,w23,w27 // d+=h
+ eor w28,w28,w21 // Maj(a,b,c)
+ eor w17,w6,w17,ror#13 // Sigma0(a)
+ add w27,w27,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w27,w27,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w4,w4 // 1
+#endif
+ ldp w5,w6,[x1],#2*4
+ add w27,w27,w17 // h+=Sigma0(a)
+ ror w16,w23,#6
+ add w26,w26,w28 // h+=K[i]
+ eor w7,w23,w23,ror#14
+ and w17,w24,w23
+ bic w28,w25,w23
+ add w26,w26,w4 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w27,w20 // a^b, b^c in next round
+ eor w16,w16,w7,ror#11 // Sigma1(e)
+ ror w7,w27,#2
+ add w26,w26,w17 // h+=Ch(e,f,g)
+ eor w17,w27,w27,ror#9
+ add w26,w26,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w22,w22,w26 // d+=h
+ eor w19,w19,w20 // Maj(a,b,c)
+ eor w17,w7,w17,ror#13 // Sigma0(a)
+ add w26,w26,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w26,w26,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w5,w5 // 2
+#endif
+ add w26,w26,w17 // h+=Sigma0(a)
+ ror w16,w22,#6
+ add w25,w25,w19 // h+=K[i]
+ eor w8,w22,w22,ror#14
+ and w17,w23,w22
+ bic w19,w24,w22
+ add w25,w25,w5 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w26,w27 // a^b, b^c in next round
+ eor w16,w16,w8,ror#11 // Sigma1(e)
+ ror w8,w26,#2
+ add w25,w25,w17 // h+=Ch(e,f,g)
+ eor w17,w26,w26,ror#9
+ add w25,w25,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w21,w21,w25 // d+=h
+ eor w28,w28,w27 // Maj(a,b,c)
+ eor w17,w8,w17,ror#13 // Sigma0(a)
+ add w25,w25,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w25,w25,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w6,w6 // 3
+#endif
+ ldp w7,w8,[x1],#2*4
+ add w25,w25,w17 // h+=Sigma0(a)
+ ror w16,w21,#6
+ add w24,w24,w28 // h+=K[i]
+ eor w9,w21,w21,ror#14
+ and w17,w22,w21
+ bic w28,w23,w21
+ add w24,w24,w6 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w25,w26 // a^b, b^c in next round
+ eor w16,w16,w9,ror#11 // Sigma1(e)
+ ror w9,w25,#2
+ add w24,w24,w17 // h+=Ch(e,f,g)
+ eor w17,w25,w25,ror#9
+ add w24,w24,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w20,w20,w24 // d+=h
+ eor w19,w19,w26 // Maj(a,b,c)
+ eor w17,w9,w17,ror#13 // Sigma0(a)
+ add w24,w24,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w24,w24,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w7,w7 // 4
+#endif
+ add w24,w24,w17 // h+=Sigma0(a)
+ ror w16,w20,#6
+ add w23,w23,w19 // h+=K[i]
+ eor w10,w20,w20,ror#14
+ and w17,w21,w20
+ bic w19,w22,w20
+ add w23,w23,w7 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w24,w25 // a^b, b^c in next round
+ eor w16,w16,w10,ror#11 // Sigma1(e)
+ ror w10,w24,#2
+ add w23,w23,w17 // h+=Ch(e,f,g)
+ eor w17,w24,w24,ror#9
+ add w23,w23,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w27,w27,w23 // d+=h
+ eor w28,w28,w25 // Maj(a,b,c)
+ eor w17,w10,w17,ror#13 // Sigma0(a)
+ add w23,w23,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w23,w23,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w8,w8 // 5
+#endif
+ ldp w9,w10,[x1],#2*4
+ add w23,w23,w17 // h+=Sigma0(a)
+ ror w16,w27,#6
+ add w22,w22,w28 // h+=K[i]
+ eor w11,w27,w27,ror#14
+ and w17,w20,w27
+ bic w28,w21,w27
+ add w22,w22,w8 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w23,w24 // a^b, b^c in next round
+ eor w16,w16,w11,ror#11 // Sigma1(e)
+ ror w11,w23,#2
+ add w22,w22,w17 // h+=Ch(e,f,g)
+ eor w17,w23,w23,ror#9
+ add w22,w22,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w26,w26,w22 // d+=h
+ eor w19,w19,w24 // Maj(a,b,c)
+ eor w17,w11,w17,ror#13 // Sigma0(a)
+ add w22,w22,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w22,w22,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w9,w9 // 6
+#endif
+ add w22,w22,w17 // h+=Sigma0(a)
+ ror w16,w26,#6
+ add w21,w21,w19 // h+=K[i]
+ eor w12,w26,w26,ror#14
+ and w17,w27,w26
+ bic w19,w20,w26
+ add w21,w21,w9 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w22,w23 // a^b, b^c in next round
+ eor w16,w16,w12,ror#11 // Sigma1(e)
+ ror w12,w22,#2
+ add w21,w21,w17 // h+=Ch(e,f,g)
+ eor w17,w22,w22,ror#9
+ add w21,w21,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w25,w25,w21 // d+=h
+ eor w28,w28,w23 // Maj(a,b,c)
+ eor w17,w12,w17,ror#13 // Sigma0(a)
+ add w21,w21,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w21,w21,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w10,w10 // 7
+#endif
+ ldp w11,w12,[x1],#2*4
+ add w21,w21,w17 // h+=Sigma0(a)
+ ror w16,w25,#6
+ add w20,w20,w28 // h+=K[i]
+ eor w13,w25,w25,ror#14
+ and w17,w26,w25
+ bic w28,w27,w25
+ add w20,w20,w10 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w21,w22 // a^b, b^c in next round
+ eor w16,w16,w13,ror#11 // Sigma1(e)
+ ror w13,w21,#2
+ add w20,w20,w17 // h+=Ch(e,f,g)
+ eor w17,w21,w21,ror#9
+ add w20,w20,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w24,w24,w20 // d+=h
+ eor w19,w19,w22 // Maj(a,b,c)
+ eor w17,w13,w17,ror#13 // Sigma0(a)
+ add w20,w20,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w20,w20,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w11,w11 // 8
+#endif
+ add w20,w20,w17 // h+=Sigma0(a)
+ ror w16,w24,#6
+ add w27,w27,w19 // h+=K[i]
+ eor w14,w24,w24,ror#14
+ and w17,w25,w24
+ bic w19,w26,w24
+ add w27,w27,w11 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w20,w21 // a^b, b^c in next round
+ eor w16,w16,w14,ror#11 // Sigma1(e)
+ ror w14,w20,#2
+ add w27,w27,w17 // h+=Ch(e,f,g)
+ eor w17,w20,w20,ror#9
+ add w27,w27,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w23,w23,w27 // d+=h
+ eor w28,w28,w21 // Maj(a,b,c)
+ eor w17,w14,w17,ror#13 // Sigma0(a)
+ add w27,w27,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w27,w27,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w12,w12 // 9
+#endif
+ ldp w13,w14,[x1],#2*4
+ add w27,w27,w17 // h+=Sigma0(a)
+ ror w16,w23,#6
+ add w26,w26,w28 // h+=K[i]
+ eor w15,w23,w23,ror#14
+ and w17,w24,w23
+ bic w28,w25,w23
+ add w26,w26,w12 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w27,w20 // a^b, b^c in next round
+ eor w16,w16,w15,ror#11 // Sigma1(e)
+ ror w15,w27,#2
+ add w26,w26,w17 // h+=Ch(e,f,g)
+ eor w17,w27,w27,ror#9
+ add w26,w26,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w22,w22,w26 // d+=h
+ eor w19,w19,w20 // Maj(a,b,c)
+ eor w17,w15,w17,ror#13 // Sigma0(a)
+ add w26,w26,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w26,w26,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w13,w13 // 10
+#endif
+ add w26,w26,w17 // h+=Sigma0(a)
+ ror w16,w22,#6
+ add w25,w25,w19 // h+=K[i]
+ eor w0,w22,w22,ror#14
+ and w17,w23,w22
+ bic w19,w24,w22
+ add w25,w25,w13 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w26,w27 // a^b, b^c in next round
+ eor w16,w16,w0,ror#11 // Sigma1(e)
+ ror w0,w26,#2
+ add w25,w25,w17 // h+=Ch(e,f,g)
+ eor w17,w26,w26,ror#9
+ add w25,w25,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w21,w21,w25 // d+=h
+ eor w28,w28,w27 // Maj(a,b,c)
+ eor w17,w0,w17,ror#13 // Sigma0(a)
+ add w25,w25,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w25,w25,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w14,w14 // 11
+#endif
+ ldp w15,w0,[x1],#2*4
+ add w25,w25,w17 // h+=Sigma0(a)
+ str w6,[sp,#12]
+ ror w16,w21,#6
+ add w24,w24,w28 // h+=K[i]
+ eor w6,w21,w21,ror#14
+ and w17,w22,w21
+ bic w28,w23,w21
+ add w24,w24,w14 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w25,w26 // a^b, b^c in next round
+ eor w16,w16,w6,ror#11 // Sigma1(e)
+ ror w6,w25,#2
+ add w24,w24,w17 // h+=Ch(e,f,g)
+ eor w17,w25,w25,ror#9
+ add w24,w24,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w20,w20,w24 // d+=h
+ eor w19,w19,w26 // Maj(a,b,c)
+ eor w17,w6,w17,ror#13 // Sigma0(a)
+ add w24,w24,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w24,w24,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w15,w15 // 12
+#endif
+ add w24,w24,w17 // h+=Sigma0(a)
+ str w7,[sp,#0]
+ ror w16,w20,#6
+ add w23,w23,w19 // h+=K[i]
+ eor w7,w20,w20,ror#14
+ and w17,w21,w20
+ bic w19,w22,w20
+ add w23,w23,w15 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w24,w25 // a^b, b^c in next round
+ eor w16,w16,w7,ror#11 // Sigma1(e)
+ ror w7,w24,#2
+ add w23,w23,w17 // h+=Ch(e,f,g)
+ eor w17,w24,w24,ror#9
+ add w23,w23,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w27,w27,w23 // d+=h
+ eor w28,w28,w25 // Maj(a,b,c)
+ eor w17,w7,w17,ror#13 // Sigma0(a)
+ add w23,w23,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w23,w23,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w0,w0 // 13
+#endif
+ ldp w1,w2,[x1]
+ add w23,w23,w17 // h+=Sigma0(a)
+ str w8,[sp,#4]
+ ror w16,w27,#6
+ add w22,w22,w28 // h+=K[i]
+ eor w8,w27,w27,ror#14
+ and w17,w20,w27
+ bic w28,w21,w27
+ add w22,w22,w0 // h+=X[i]
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w23,w24 // a^b, b^c in next round
+ eor w16,w16,w8,ror#11 // Sigma1(e)
+ ror w8,w23,#2
+ add w22,w22,w17 // h+=Ch(e,f,g)
+ eor w17,w23,w23,ror#9
+ add w22,w22,w16 // h+=Sigma1(e)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ add w26,w26,w22 // d+=h
+ eor w19,w19,w24 // Maj(a,b,c)
+ eor w17,w8,w17,ror#13 // Sigma0(a)
+ add w22,w22,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ //add w22,w22,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w1,w1 // 14
+#endif
+ ldr w6,[sp,#12]
+ add w22,w22,w17 // h+=Sigma0(a)
+ str w9,[sp,#8]
+ ror w16,w26,#6
+ add w21,w21,w19 // h+=K[i]
+ eor w9,w26,w26,ror#14
+ and w17,w27,w26
+ bic w19,w20,w26
+ add w21,w21,w1 // h+=X[i]
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w22,w23 // a^b, b^c in next round
+ eor w16,w16,w9,ror#11 // Sigma1(e)
+ ror w9,w22,#2
+ add w21,w21,w17 // h+=Ch(e,f,g)
+ eor w17,w22,w22,ror#9
+ add w21,w21,w16 // h+=Sigma1(e)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ add w25,w25,w21 // d+=h
+ eor w28,w28,w23 // Maj(a,b,c)
+ eor w17,w9,w17,ror#13 // Sigma0(a)
+ add w21,w21,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ //add w21,w21,w17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev w2,w2 // 15
+#endif
+ ldr w7,[sp,#0]
+ add w21,w21,w17 // h+=Sigma0(a)
+ str w10,[sp,#12]
+ ror w16,w25,#6
+ add w20,w20,w28 // h+=K[i]
+ ror w9,w4,#7
+ and w17,w26,w25
+ ror w8,w1,#17
+ bic w28,w27,w25
+ ror w10,w21,#2
+ add w20,w20,w2 // h+=X[i]
+ eor w16,w16,w25,ror#11
+ eor w9,w9,w4,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w21,w22 // a^b, b^c in next round
+ eor w16,w16,w25,ror#25 // Sigma1(e)
+ eor w10,w10,w21,ror#13
+ add w20,w20,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w8,w8,w1,ror#19
+ eor w9,w9,w4,lsr#3 // sigma0(X[i+1])
+ add w20,w20,w16 // h+=Sigma1(e)
+ eor w19,w19,w22 // Maj(a,b,c)
+ eor w17,w10,w21,ror#22 // Sigma0(a)
+ eor w8,w8,w1,lsr#10 // sigma1(X[i+14])
+ add w3,w3,w12
+ add w24,w24,w20 // d+=h
+ add w20,w20,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w3,w3,w9
+ add w20,w20,w17 // h+=Sigma0(a)
+ add w3,w3,w8
+.Loop_16_xx:
+ ldr w8,[sp,#4]
+ str w11,[sp,#0]
+ ror w16,w24,#6
+ add w27,w27,w19 // h+=K[i]
+ ror w10,w5,#7
+ and w17,w25,w24
+ ror w9,w2,#17
+ bic w19,w26,w24
+ ror w11,w20,#2
+ add w27,w27,w3 // h+=X[i]
+ eor w16,w16,w24,ror#11
+ eor w10,w10,w5,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w20,w21 // a^b, b^c in next round
+ eor w16,w16,w24,ror#25 // Sigma1(e)
+ eor w11,w11,w20,ror#13
+ add w27,w27,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w9,w9,w2,ror#19
+ eor w10,w10,w5,lsr#3 // sigma0(X[i+1])
+ add w27,w27,w16 // h+=Sigma1(e)
+ eor w28,w28,w21 // Maj(a,b,c)
+ eor w17,w11,w20,ror#22 // Sigma0(a)
+ eor w9,w9,w2,lsr#10 // sigma1(X[i+14])
+ add w4,w4,w13
+ add w23,w23,w27 // d+=h
+ add w27,w27,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w4,w4,w10
+ add w27,w27,w17 // h+=Sigma0(a)
+ add w4,w4,w9
+ ldr w9,[sp,#8]
+ str w12,[sp,#4]
+ ror w16,w23,#6
+ add w26,w26,w28 // h+=K[i]
+ ror w11,w6,#7
+ and w17,w24,w23
+ ror w10,w3,#17
+ bic w28,w25,w23
+ ror w12,w27,#2
+ add w26,w26,w4 // h+=X[i]
+ eor w16,w16,w23,ror#11
+ eor w11,w11,w6,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w27,w20 // a^b, b^c in next round
+ eor w16,w16,w23,ror#25 // Sigma1(e)
+ eor w12,w12,w27,ror#13
+ add w26,w26,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w10,w10,w3,ror#19
+ eor w11,w11,w6,lsr#3 // sigma0(X[i+1])
+ add w26,w26,w16 // h+=Sigma1(e)
+ eor w19,w19,w20 // Maj(a,b,c)
+ eor w17,w12,w27,ror#22 // Sigma0(a)
+ eor w10,w10,w3,lsr#10 // sigma1(X[i+14])
+ add w5,w5,w14
+ add w22,w22,w26 // d+=h
+ add w26,w26,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w5,w5,w11
+ add w26,w26,w17 // h+=Sigma0(a)
+ add w5,w5,w10
+ ldr w10,[sp,#12]
+ str w13,[sp,#8]
+ ror w16,w22,#6
+ add w25,w25,w19 // h+=K[i]
+ ror w12,w7,#7
+ and w17,w23,w22
+ ror w11,w4,#17
+ bic w19,w24,w22
+ ror w13,w26,#2
+ add w25,w25,w5 // h+=X[i]
+ eor w16,w16,w22,ror#11
+ eor w12,w12,w7,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w26,w27 // a^b, b^c in next round
+ eor w16,w16,w22,ror#25 // Sigma1(e)
+ eor w13,w13,w26,ror#13
+ add w25,w25,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w11,w11,w4,ror#19
+ eor w12,w12,w7,lsr#3 // sigma0(X[i+1])
+ add w25,w25,w16 // h+=Sigma1(e)
+ eor w28,w28,w27 // Maj(a,b,c)
+ eor w17,w13,w26,ror#22 // Sigma0(a)
+ eor w11,w11,w4,lsr#10 // sigma1(X[i+14])
+ add w6,w6,w15
+ add w21,w21,w25 // d+=h
+ add w25,w25,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w6,w6,w12
+ add w25,w25,w17 // h+=Sigma0(a)
+ add w6,w6,w11
+ ldr w11,[sp,#0]
+ str w14,[sp,#12]
+ ror w16,w21,#6
+ add w24,w24,w28 // h+=K[i]
+ ror w13,w8,#7
+ and w17,w22,w21
+ ror w12,w5,#17
+ bic w28,w23,w21
+ ror w14,w25,#2
+ add w24,w24,w6 // h+=X[i]
+ eor w16,w16,w21,ror#11
+ eor w13,w13,w8,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w25,w26 // a^b, b^c in next round
+ eor w16,w16,w21,ror#25 // Sigma1(e)
+ eor w14,w14,w25,ror#13
+ add w24,w24,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w12,w12,w5,ror#19
+ eor w13,w13,w8,lsr#3 // sigma0(X[i+1])
+ add w24,w24,w16 // h+=Sigma1(e)
+ eor w19,w19,w26 // Maj(a,b,c)
+ eor w17,w14,w25,ror#22 // Sigma0(a)
+ eor w12,w12,w5,lsr#10 // sigma1(X[i+14])
+ add w7,w7,w0
+ add w20,w20,w24 // d+=h
+ add w24,w24,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w7,w7,w13
+ add w24,w24,w17 // h+=Sigma0(a)
+ add w7,w7,w12
+ ldr w12,[sp,#4]
+ str w15,[sp,#0]
+ ror w16,w20,#6
+ add w23,w23,w19 // h+=K[i]
+ ror w14,w9,#7
+ and w17,w21,w20
+ ror w13,w6,#17
+ bic w19,w22,w20
+ ror w15,w24,#2
+ add w23,w23,w7 // h+=X[i]
+ eor w16,w16,w20,ror#11
+ eor w14,w14,w9,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w24,w25 // a^b, b^c in next round
+ eor w16,w16,w20,ror#25 // Sigma1(e)
+ eor w15,w15,w24,ror#13
+ add w23,w23,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w13,w13,w6,ror#19
+ eor w14,w14,w9,lsr#3 // sigma0(X[i+1])
+ add w23,w23,w16 // h+=Sigma1(e)
+ eor w28,w28,w25 // Maj(a,b,c)
+ eor w17,w15,w24,ror#22 // Sigma0(a)
+ eor w13,w13,w6,lsr#10 // sigma1(X[i+14])
+ add w8,w8,w1
+ add w27,w27,w23 // d+=h
+ add w23,w23,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w8,w8,w14
+ add w23,w23,w17 // h+=Sigma0(a)
+ add w8,w8,w13
+ ldr w13,[sp,#8]
+ str w0,[sp,#4]
+ ror w16,w27,#6
+ add w22,w22,w28 // h+=K[i]
+ ror w15,w10,#7
+ and w17,w20,w27
+ ror w14,w7,#17
+ bic w28,w21,w27
+ ror w0,w23,#2
+ add w22,w22,w8 // h+=X[i]
+ eor w16,w16,w27,ror#11
+ eor w15,w15,w10,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w23,w24 // a^b, b^c in next round
+ eor w16,w16,w27,ror#25 // Sigma1(e)
+ eor w0,w0,w23,ror#13
+ add w22,w22,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w14,w14,w7,ror#19
+ eor w15,w15,w10,lsr#3 // sigma0(X[i+1])
+ add w22,w22,w16 // h+=Sigma1(e)
+ eor w19,w19,w24 // Maj(a,b,c)
+ eor w17,w0,w23,ror#22 // Sigma0(a)
+ eor w14,w14,w7,lsr#10 // sigma1(X[i+14])
+ add w9,w9,w2
+ add w26,w26,w22 // d+=h
+ add w22,w22,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w9,w9,w15
+ add w22,w22,w17 // h+=Sigma0(a)
+ add w9,w9,w14
+ ldr w14,[sp,#12]
+ str w1,[sp,#8]
+ ror w16,w26,#6
+ add w21,w21,w19 // h+=K[i]
+ ror w0,w11,#7
+ and w17,w27,w26
+ ror w15,w8,#17
+ bic w19,w20,w26
+ ror w1,w22,#2
+ add w21,w21,w9 // h+=X[i]
+ eor w16,w16,w26,ror#11
+ eor w0,w0,w11,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w22,w23 // a^b, b^c in next round
+ eor w16,w16,w26,ror#25 // Sigma1(e)
+ eor w1,w1,w22,ror#13
+ add w21,w21,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w15,w15,w8,ror#19
+ eor w0,w0,w11,lsr#3 // sigma0(X[i+1])
+ add w21,w21,w16 // h+=Sigma1(e)
+ eor w28,w28,w23 // Maj(a,b,c)
+ eor w17,w1,w22,ror#22 // Sigma0(a)
+ eor w15,w15,w8,lsr#10 // sigma1(X[i+14])
+ add w10,w10,w3
+ add w25,w25,w21 // d+=h
+ add w21,w21,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w10,w10,w0
+ add w21,w21,w17 // h+=Sigma0(a)
+ add w10,w10,w15
+ ldr w15,[sp,#0]
+ str w2,[sp,#12]
+ ror w16,w25,#6
+ add w20,w20,w28 // h+=K[i]
+ ror w1,w12,#7
+ and w17,w26,w25
+ ror w0,w9,#17
+ bic w28,w27,w25
+ ror w2,w21,#2
+ add w20,w20,w10 // h+=X[i]
+ eor w16,w16,w25,ror#11
+ eor w1,w1,w12,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w21,w22 // a^b, b^c in next round
+ eor w16,w16,w25,ror#25 // Sigma1(e)
+ eor w2,w2,w21,ror#13
+ add w20,w20,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w0,w0,w9,ror#19
+ eor w1,w1,w12,lsr#3 // sigma0(X[i+1])
+ add w20,w20,w16 // h+=Sigma1(e)
+ eor w19,w19,w22 // Maj(a,b,c)
+ eor w17,w2,w21,ror#22 // Sigma0(a)
+ eor w0,w0,w9,lsr#10 // sigma1(X[i+14])
+ add w11,w11,w4
+ add w24,w24,w20 // d+=h
+ add w20,w20,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w11,w11,w1
+ add w20,w20,w17 // h+=Sigma0(a)
+ add w11,w11,w0
+ ldr w0,[sp,#4]
+ str w3,[sp,#0]
+ ror w16,w24,#6
+ add w27,w27,w19 // h+=K[i]
+ ror w2,w13,#7
+ and w17,w25,w24
+ ror w1,w10,#17
+ bic w19,w26,w24
+ ror w3,w20,#2
+ add w27,w27,w11 // h+=X[i]
+ eor w16,w16,w24,ror#11
+ eor w2,w2,w13,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w20,w21 // a^b, b^c in next round
+ eor w16,w16,w24,ror#25 // Sigma1(e)
+ eor w3,w3,w20,ror#13
+ add w27,w27,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w1,w1,w10,ror#19
+ eor w2,w2,w13,lsr#3 // sigma0(X[i+1])
+ add w27,w27,w16 // h+=Sigma1(e)
+ eor w28,w28,w21 // Maj(a,b,c)
+ eor w17,w3,w20,ror#22 // Sigma0(a)
+ eor w1,w1,w10,lsr#10 // sigma1(X[i+14])
+ add w12,w12,w5
+ add w23,w23,w27 // d+=h
+ add w27,w27,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w12,w12,w2
+ add w27,w27,w17 // h+=Sigma0(a)
+ add w12,w12,w1
+ ldr w1,[sp,#8]
+ str w4,[sp,#4]
+ ror w16,w23,#6
+ add w26,w26,w28 // h+=K[i]
+ ror w3,w14,#7
+ and w17,w24,w23
+ ror w2,w11,#17
+ bic w28,w25,w23
+ ror w4,w27,#2
+ add w26,w26,w12 // h+=X[i]
+ eor w16,w16,w23,ror#11
+ eor w3,w3,w14,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w27,w20 // a^b, b^c in next round
+ eor w16,w16,w23,ror#25 // Sigma1(e)
+ eor w4,w4,w27,ror#13
+ add w26,w26,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w2,w2,w11,ror#19
+ eor w3,w3,w14,lsr#3 // sigma0(X[i+1])
+ add w26,w26,w16 // h+=Sigma1(e)
+ eor w19,w19,w20 // Maj(a,b,c)
+ eor w17,w4,w27,ror#22 // Sigma0(a)
+ eor w2,w2,w11,lsr#10 // sigma1(X[i+14])
+ add w13,w13,w6
+ add w22,w22,w26 // d+=h
+ add w26,w26,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w13,w13,w3
+ add w26,w26,w17 // h+=Sigma0(a)
+ add w13,w13,w2
+ ldr w2,[sp,#12]
+ str w5,[sp,#8]
+ ror w16,w22,#6
+ add w25,w25,w19 // h+=K[i]
+ ror w4,w15,#7
+ and w17,w23,w22
+ ror w3,w12,#17
+ bic w19,w24,w22
+ ror w5,w26,#2
+ add w25,w25,w13 // h+=X[i]
+ eor w16,w16,w22,ror#11
+ eor w4,w4,w15,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w26,w27 // a^b, b^c in next round
+ eor w16,w16,w22,ror#25 // Sigma1(e)
+ eor w5,w5,w26,ror#13
+ add w25,w25,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w3,w3,w12,ror#19
+ eor w4,w4,w15,lsr#3 // sigma0(X[i+1])
+ add w25,w25,w16 // h+=Sigma1(e)
+ eor w28,w28,w27 // Maj(a,b,c)
+ eor w17,w5,w26,ror#22 // Sigma0(a)
+ eor w3,w3,w12,lsr#10 // sigma1(X[i+14])
+ add w14,w14,w7
+ add w21,w21,w25 // d+=h
+ add w25,w25,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w14,w14,w4
+ add w25,w25,w17 // h+=Sigma0(a)
+ add w14,w14,w3
+ ldr w3,[sp,#0]
+ str w6,[sp,#12]
+ ror w16,w21,#6
+ add w24,w24,w28 // h+=K[i]
+ ror w5,w0,#7
+ and w17,w22,w21
+ ror w4,w13,#17
+ bic w28,w23,w21
+ ror w6,w25,#2
+ add w24,w24,w14 // h+=X[i]
+ eor w16,w16,w21,ror#11
+ eor w5,w5,w0,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w25,w26 // a^b, b^c in next round
+ eor w16,w16,w21,ror#25 // Sigma1(e)
+ eor w6,w6,w25,ror#13
+ add w24,w24,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w4,w4,w13,ror#19
+ eor w5,w5,w0,lsr#3 // sigma0(X[i+1])
+ add w24,w24,w16 // h+=Sigma1(e)
+ eor w19,w19,w26 // Maj(a,b,c)
+ eor w17,w6,w25,ror#22 // Sigma0(a)
+ eor w4,w4,w13,lsr#10 // sigma1(X[i+14])
+ add w15,w15,w8
+ add w20,w20,w24 // d+=h
+ add w24,w24,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w15,w15,w5
+ add w24,w24,w17 // h+=Sigma0(a)
+ add w15,w15,w4
+ ldr w4,[sp,#4]
+ str w7,[sp,#0]
+ ror w16,w20,#6
+ add w23,w23,w19 // h+=K[i]
+ ror w6,w1,#7
+ and w17,w21,w20
+ ror w5,w14,#17
+ bic w19,w22,w20
+ ror w7,w24,#2
+ add w23,w23,w15 // h+=X[i]
+ eor w16,w16,w20,ror#11
+ eor w6,w6,w1,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w24,w25 // a^b, b^c in next round
+ eor w16,w16,w20,ror#25 // Sigma1(e)
+ eor w7,w7,w24,ror#13
+ add w23,w23,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w5,w5,w14,ror#19
+ eor w6,w6,w1,lsr#3 // sigma0(X[i+1])
+ add w23,w23,w16 // h+=Sigma1(e)
+ eor w28,w28,w25 // Maj(a,b,c)
+ eor w17,w7,w24,ror#22 // Sigma0(a)
+ eor w5,w5,w14,lsr#10 // sigma1(X[i+14])
+ add w0,w0,w9
+ add w27,w27,w23 // d+=h
+ add w23,w23,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w0,w0,w6
+ add w23,w23,w17 // h+=Sigma0(a)
+ add w0,w0,w5
+ ldr w5,[sp,#8]
+ str w8,[sp,#4]
+ ror w16,w27,#6
+ add w22,w22,w28 // h+=K[i]
+ ror w7,w2,#7
+ and w17,w20,w27
+ ror w6,w15,#17
+ bic w28,w21,w27
+ ror w8,w23,#2
+ add w22,w22,w0 // h+=X[i]
+ eor w16,w16,w27,ror#11
+ eor w7,w7,w2,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w23,w24 // a^b, b^c in next round
+ eor w16,w16,w27,ror#25 // Sigma1(e)
+ eor w8,w8,w23,ror#13
+ add w22,w22,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w6,w6,w15,ror#19
+ eor w7,w7,w2,lsr#3 // sigma0(X[i+1])
+ add w22,w22,w16 // h+=Sigma1(e)
+ eor w19,w19,w24 // Maj(a,b,c)
+ eor w17,w8,w23,ror#22 // Sigma0(a)
+ eor w6,w6,w15,lsr#10 // sigma1(X[i+14])
+ add w1,w1,w10
+ add w26,w26,w22 // d+=h
+ add w22,w22,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w1,w1,w7
+ add w22,w22,w17 // h+=Sigma0(a)
+ add w1,w1,w6
+ ldr w6,[sp,#12]
+ str w9,[sp,#8]
+ ror w16,w26,#6
+ add w21,w21,w19 // h+=K[i]
+ ror w8,w3,#7
+ and w17,w27,w26
+ ror w7,w0,#17
+ bic w19,w20,w26
+ ror w9,w22,#2
+ add w21,w21,w1 // h+=X[i]
+ eor w16,w16,w26,ror#11
+ eor w8,w8,w3,ror#18
+ orr w17,w17,w19 // Ch(e,f,g)
+ eor w19,w22,w23 // a^b, b^c in next round
+ eor w16,w16,w26,ror#25 // Sigma1(e)
+ eor w9,w9,w22,ror#13
+ add w21,w21,w17 // h+=Ch(e,f,g)
+ and w28,w28,w19 // (b^c)&=(a^b)
+ eor w7,w7,w0,ror#19
+ eor w8,w8,w3,lsr#3 // sigma0(X[i+1])
+ add w21,w21,w16 // h+=Sigma1(e)
+ eor w28,w28,w23 // Maj(a,b,c)
+ eor w17,w9,w22,ror#22 // Sigma0(a)
+ eor w7,w7,w0,lsr#10 // sigma1(X[i+14])
+ add w2,w2,w11
+ add w25,w25,w21 // d+=h
+ add w21,w21,w28 // h+=Maj(a,b,c)
+ ldr w28,[x30],#4 // *K++, w19 in next round
+ add w2,w2,w8
+ add w21,w21,w17 // h+=Sigma0(a)
+ add w2,w2,w7
+ ldr w7,[sp,#0]
+ str w10,[sp,#12]
+ ror w16,w25,#6
+ add w20,w20,w28 // h+=K[i]
+ ror w9,w4,#7
+ and w17,w26,w25
+ ror w8,w1,#17
+ bic w28,w27,w25
+ ror w10,w21,#2
+ add w20,w20,w2 // h+=X[i]
+ eor w16,w16,w25,ror#11
+ eor w9,w9,w4,ror#18
+ orr w17,w17,w28 // Ch(e,f,g)
+ eor w28,w21,w22 // a^b, b^c in next round
+ eor w16,w16,w25,ror#25 // Sigma1(e)
+ eor w10,w10,w21,ror#13
+ add w20,w20,w17 // h+=Ch(e,f,g)
+ and w19,w19,w28 // (b^c)&=(a^b)
+ eor w8,w8,w1,ror#19
+ eor w9,w9,w4,lsr#3 // sigma0(X[i+1])
+ add w20,w20,w16 // h+=Sigma1(e)
+ eor w19,w19,w22 // Maj(a,b,c)
+ eor w17,w10,w21,ror#22 // Sigma0(a)
+ eor w8,w8,w1,lsr#10 // sigma1(X[i+14])
+ add w3,w3,w12
+ add w24,w24,w20 // d+=h
+ add w20,w20,w19 // h+=Maj(a,b,c)
+ ldr w19,[x30],#4 // *K++, w28 in next round
+ add w3,w3,w9
+ add w20,w20,w17 // h+=Sigma0(a)
+ add w3,w3,w8
+ cbnz w19,.Loop_16_xx
+
+ ldp x0,x2,[x29,#96]
+ ldr x1,[x29,#112]
+ sub x30,x30,#260 // rewind
+
+ ldp w3,w4,[x0]
+ ldp w5,w6,[x0,#2*4]
+ add x1,x1,#14*4 // advance input pointer
+ ldp w7,w8,[x0,#4*4]
+ add w20,w20,w3
+ ldp w9,w10,[x0,#6*4]
+ add w21,w21,w4
+ add w22,w22,w5
+ add w23,w23,w6
+ stp w20,w21,[x0]
+ add w24,w24,w7
+ add w25,w25,w8
+ stp w22,w23,[x0,#2*4]
+ add w26,w26,w9
+ add w27,w27,w10
+ cmp x1,x2
+ stp w24,w25,[x0,#4*4]
+ stp w26,w27,[x0,#6*4]
+ b.ne .Loop
+
+ ldp x19,x20,[x29,#16]
+ add sp,sp,#4*4
+ ldp x21,x22,[x29,#32]
+ ldp x23,x24,[x29,#48]
+ ldp x25,x26,[x29,#64]
+ ldp x27,x28,[x29,#80]
+ ldp x29,x30,[sp],#128
+ ret
+.size sha256_block_data_order,.-sha256_block_data_order
+
+.align 6
+.type .LK256,%object
+.LK256:
+ .long 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
+ .long 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
+ .long 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
+ .long 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
+ .long 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
+ .long 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
+ .long 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
+ .long 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
+ .long 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
+ .long 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
+ .long 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
+ .long 0xd192e819,0xd6990624,0xf40e3585,0x106aa070
+ .long 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
+ .long 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
+ .long 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
+ .long 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+ .long 0 //terminator
+.size .LK256,.-.LK256
+#ifndef __KERNEL__
+.align 3
+.LOPENSSL_armcap_P:
+# ifdef __ILP32__
+ .long OPENSSL_armcap_P-.
+# else
+ .quad OPENSSL_armcap_P-.
+# endif
+#endif
+.asciz "SHA256 block transform for ARMv8, CRYPTOGAMS by <appro@openssl.org>"
+.align 2
+#ifndef __KERNEL__
+.type sha256_block_armv8,%function
+.align 6
+sha256_block_armv8:
+.Lv8_entry:
+ stp x29,x30,[sp,#-16]!
+ add x29,sp,#0
+
+ ld1 {v0.4s,v1.4s},[x0]
+ adr x3,.LK256
+
+.Loop_hw:
+ ld1 {v4.16b-v7.16b},[x1],#64
+ sub x2,x2,#1
+ ld1 {v16.4s},[x3],#16
+ rev32 v4.16b,v4.16b
+ rev32 v5.16b,v5.16b
+ rev32 v6.16b,v6.16b
+ rev32 v7.16b,v7.16b
+ orr v18.16b,v0.16b,v0.16b // offload
+ orr v19.16b,v1.16b,v1.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v4.4s
+ .inst 0x5e2828a4 //sha256su0 v4.16b,v5.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+ .inst 0x5e0760c4 //sha256su1 v4.16b,v6.16b,v7.16b
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v5.4s
+ .inst 0x5e2828c5 //sha256su0 v5.16b,v6.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+ .inst 0x5e0460e5 //sha256su1 v5.16b,v7.16b,v4.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v6.4s
+ .inst 0x5e2828e6 //sha256su0 v6.16b,v7.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+ .inst 0x5e056086 //sha256su1 v6.16b,v4.16b,v5.16b
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v7.4s
+ .inst 0x5e282887 //sha256su0 v7.16b,v4.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+ .inst 0x5e0660a7 //sha256su1 v7.16b,v5.16b,v6.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v4.4s
+ .inst 0x5e2828a4 //sha256su0 v4.16b,v5.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+ .inst 0x5e0760c4 //sha256su1 v4.16b,v6.16b,v7.16b
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v5.4s
+ .inst 0x5e2828c5 //sha256su0 v5.16b,v6.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+ .inst 0x5e0460e5 //sha256su1 v5.16b,v7.16b,v4.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v6.4s
+ .inst 0x5e2828e6 //sha256su0 v6.16b,v7.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+ .inst 0x5e056086 //sha256su1 v6.16b,v4.16b,v5.16b
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v7.4s
+ .inst 0x5e282887 //sha256su0 v7.16b,v4.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+ .inst 0x5e0660a7 //sha256su1 v7.16b,v5.16b,v6.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v4.4s
+ .inst 0x5e2828a4 //sha256su0 v4.16b,v5.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+ .inst 0x5e0760c4 //sha256su1 v4.16b,v6.16b,v7.16b
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v5.4s
+ .inst 0x5e2828c5 //sha256su0 v5.16b,v6.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+ .inst 0x5e0460e5 //sha256su1 v5.16b,v7.16b,v4.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v6.4s
+ .inst 0x5e2828e6 //sha256su0 v6.16b,v7.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+ .inst 0x5e056086 //sha256su1 v6.16b,v4.16b,v5.16b
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v7.4s
+ .inst 0x5e282887 //sha256su0 v7.16b,v4.16b
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+ .inst 0x5e0660a7 //sha256su1 v7.16b,v5.16b,v6.16b
+ ld1 {v17.4s},[x3],#16
+ add v16.4s,v16.4s,v4.4s
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+
+ ld1 {v16.4s},[x3],#16
+ add v17.4s,v17.4s,v5.4s
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+
+ ld1 {v17.4s},[x3]
+ add v16.4s,v16.4s,v6.4s
+ sub x3,x3,#64*4-16 // rewind
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e104020 //sha256h v0.16b,v1.16b,v16.4s
+ .inst 0x5e105041 //sha256h2 v1.16b,v2.16b,v16.4s
+
+ add v17.4s,v17.4s,v7.4s
+ orr v2.16b,v0.16b,v0.16b
+ .inst 0x5e114020 //sha256h v0.16b,v1.16b,v17.4s
+ .inst 0x5e115041 //sha256h2 v1.16b,v2.16b,v17.4s
+
+ add v0.4s,v0.4s,v18.4s
+ add v1.4s,v1.4s,v19.4s
+
+ cbnz x2,.Loop_hw
+
+ st1 {v0.4s,v1.4s},[x0]
+
+ ldr x29,[sp],#16
+ ret
+.size sha256_block_armv8,.-sha256_block_armv8
+#endif
+#ifdef __KERNEL__
+.globl sha256_block_neon
+#endif
+.type sha256_block_neon,%function
+.align 4
+sha256_block_neon:
+.Lneon_entry:
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ sub sp,sp,#16*4
+
+ adr x16,.LK256
+ add x2,x1,x2,lsl#6 // len to point at the end of inp
+
+ ld1 {v0.16b},[x1], #16
+ ld1 {v1.16b},[x1], #16
+ ld1 {v2.16b},[x1], #16
+ ld1 {v3.16b},[x1], #16
+ ld1 {v4.4s},[x16], #16
+ ld1 {v5.4s},[x16], #16
+ ld1 {v6.4s},[x16], #16
+ ld1 {v7.4s},[x16], #16
+ rev32 v0.16b,v0.16b // yes, even on
+ rev32 v1.16b,v1.16b // big-endian
+ rev32 v2.16b,v2.16b
+ rev32 v3.16b,v3.16b
+ mov x17,sp
+ add v4.4s,v4.4s,v0.4s
+ add v5.4s,v5.4s,v1.4s
+ add v6.4s,v6.4s,v2.4s
+ st1 {v4.4s-v5.4s},[x17], #32
+ add v7.4s,v7.4s,v3.4s
+ st1 {v6.4s-v7.4s},[x17]
+ sub x17,x17,#32
+
+ ldp w3,w4,[x0]
+ ldp w5,w6,[x0,#8]
+ ldp w7,w8,[x0,#16]
+ ldp w9,w10,[x0,#24]
+ ldr w12,[sp,#0]
+ mov w13,wzr
+ eor w14,w4,w5
+ mov w15,wzr
+ b .L_00_48
+
+.align 4
+.L_00_48:
+ ext v4.16b,v0.16b,v1.16b,#4
+ add w10,w10,w12
+ add w3,w3,w15
+ and w12,w8,w7
+ bic w15,w9,w7
+ ext v7.16b,v2.16b,v3.16b,#4
+ eor w11,w7,w7,ror#5
+ add w3,w3,w13
+ mov d19,v3.d[1]
+ orr w12,w12,w15
+ eor w11,w11,w7,ror#19
+ ushr v6.4s,v4.4s,#7
+ eor w15,w3,w3,ror#11
+ ushr v5.4s,v4.4s,#3
+ add w10,w10,w12
+ add v0.4s,v0.4s,v7.4s
+ ror w11,w11,#6
+ sli v6.4s,v4.4s,#25
+ eor w13,w3,w4
+ eor w15,w15,w3,ror#20
+ ushr v7.4s,v4.4s,#18
+ add w10,w10,w11
+ ldr w12,[sp,#4]
+ and w14,w14,w13
+ eor v5.16b,v5.16b,v6.16b
+ ror w15,w15,#2
+ add w6,w6,w10
+ sli v7.4s,v4.4s,#14
+ eor w14,w14,w4
+ ushr v16.4s,v19.4s,#17
+ add w9,w9,w12
+ add w10,w10,w15
+ and w12,w7,w6
+ eor v5.16b,v5.16b,v7.16b
+ bic w15,w8,w6
+ eor w11,w6,w6,ror#5
+ sli v16.4s,v19.4s,#15
+ add w10,w10,w14
+ orr w12,w12,w15
+ ushr v17.4s,v19.4s,#10
+ eor w11,w11,w6,ror#19
+ eor w15,w10,w10,ror#11
+ ushr v7.4s,v19.4s,#19
+ add w9,w9,w12
+ ror w11,w11,#6
+ add v0.4s,v0.4s,v5.4s
+ eor w14,w10,w3
+ eor w15,w15,w10,ror#20
+ sli v7.4s,v19.4s,#13
+ add w9,w9,w11
+ ldr w12,[sp,#8]
+ and w13,w13,w14
+ eor v17.16b,v17.16b,v16.16b
+ ror w15,w15,#2
+ add w5,w5,w9
+ eor w13,w13,w3
+ eor v17.16b,v17.16b,v7.16b
+ add w8,w8,w12
+ add w9,w9,w15
+ and w12,w6,w5
+ add v0.4s,v0.4s,v17.4s
+ bic w15,w7,w5
+ eor w11,w5,w5,ror#5
+ add w9,w9,w13
+ ushr v18.4s,v0.4s,#17
+ orr w12,w12,w15
+ ushr v19.4s,v0.4s,#10
+ eor w11,w11,w5,ror#19
+ eor w15,w9,w9,ror#11
+ sli v18.4s,v0.4s,#15
+ add w8,w8,w12
+ ushr v17.4s,v0.4s,#19
+ ror w11,w11,#6
+ eor w13,w9,w10
+ eor v19.16b,v19.16b,v18.16b
+ eor w15,w15,w9,ror#20
+ add w8,w8,w11
+ sli v17.4s,v0.4s,#13
+ ldr w12,[sp,#12]
+ and w14,w14,w13
+ ror w15,w15,#2
+ ld1 {v4.4s},[x16], #16
+ add w4,w4,w8
+ eor v19.16b,v19.16b,v17.16b
+ eor w14,w14,w10
+ eor v17.16b,v17.16b,v17.16b
+ add w7,w7,w12
+ add w8,w8,w15
+ and w12,w5,w4
+ mov v17.d[1],v19.d[0]
+ bic w15,w6,w4
+ eor w11,w4,w4,ror#5
+ add w8,w8,w14
+ add v0.4s,v0.4s,v17.4s
+ orr w12,w12,w15
+ eor w11,w11,w4,ror#19
+ eor w15,w8,w8,ror#11
+ add v4.4s,v4.4s,v0.4s
+ add w7,w7,w12
+ ror w11,w11,#6
+ eor w14,w8,w9
+ eor w15,w15,w8,ror#20
+ add w7,w7,w11
+ ldr w12,[sp,#16]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w3,w3,w7
+ eor w13,w13,w9
+ st1 {v4.4s},[x17], #16
+ ext v4.16b,v1.16b,v2.16b,#4
+ add w6,w6,w12
+ add w7,w7,w15
+ and w12,w4,w3
+ bic w15,w5,w3
+ ext v7.16b,v3.16b,v0.16b,#4
+ eor w11,w3,w3,ror#5
+ add w7,w7,w13
+ mov d19,v0.d[1]
+ orr w12,w12,w15
+ eor w11,w11,w3,ror#19
+ ushr v6.4s,v4.4s,#7
+ eor w15,w7,w7,ror#11
+ ushr v5.4s,v4.4s,#3
+ add w6,w6,w12
+ add v1.4s,v1.4s,v7.4s
+ ror w11,w11,#6
+ sli v6.4s,v4.4s,#25
+ eor w13,w7,w8
+ eor w15,w15,w7,ror#20
+ ushr v7.4s,v4.4s,#18
+ add w6,w6,w11
+ ldr w12,[sp,#20]
+ and w14,w14,w13
+ eor v5.16b,v5.16b,v6.16b
+ ror w15,w15,#2
+ add w10,w10,w6
+ sli v7.4s,v4.4s,#14
+ eor w14,w14,w8
+ ushr v16.4s,v19.4s,#17
+ add w5,w5,w12
+ add w6,w6,w15
+ and w12,w3,w10
+ eor v5.16b,v5.16b,v7.16b
+ bic w15,w4,w10
+ eor w11,w10,w10,ror#5
+ sli v16.4s,v19.4s,#15
+ add w6,w6,w14
+ orr w12,w12,w15
+ ushr v17.4s,v19.4s,#10
+ eor w11,w11,w10,ror#19
+ eor w15,w6,w6,ror#11
+ ushr v7.4s,v19.4s,#19
+ add w5,w5,w12
+ ror w11,w11,#6
+ add v1.4s,v1.4s,v5.4s
+ eor w14,w6,w7
+ eor w15,w15,w6,ror#20
+ sli v7.4s,v19.4s,#13
+ add w5,w5,w11
+ ldr w12,[sp,#24]
+ and w13,w13,w14
+ eor v17.16b,v17.16b,v16.16b
+ ror w15,w15,#2
+ add w9,w9,w5
+ eor w13,w13,w7
+ eor v17.16b,v17.16b,v7.16b
+ add w4,w4,w12
+ add w5,w5,w15
+ and w12,w10,w9
+ add v1.4s,v1.4s,v17.4s
+ bic w15,w3,w9
+ eor w11,w9,w9,ror#5
+ add w5,w5,w13
+ ushr v18.4s,v1.4s,#17
+ orr w12,w12,w15
+ ushr v19.4s,v1.4s,#10
+ eor w11,w11,w9,ror#19
+ eor w15,w5,w5,ror#11
+ sli v18.4s,v1.4s,#15
+ add w4,w4,w12
+ ushr v17.4s,v1.4s,#19
+ ror w11,w11,#6
+ eor w13,w5,w6
+ eor v19.16b,v19.16b,v18.16b
+ eor w15,w15,w5,ror#20
+ add w4,w4,w11
+ sli v17.4s,v1.4s,#13
+ ldr w12,[sp,#28]
+ and w14,w14,w13
+ ror w15,w15,#2
+ ld1 {v4.4s},[x16], #16
+ add w8,w8,w4
+ eor v19.16b,v19.16b,v17.16b
+ eor w14,w14,w6
+ eor v17.16b,v17.16b,v17.16b
+ add w3,w3,w12
+ add w4,w4,w15
+ and w12,w9,w8
+ mov v17.d[1],v19.d[0]
+ bic w15,w10,w8
+ eor w11,w8,w8,ror#5
+ add w4,w4,w14
+ add v1.4s,v1.4s,v17.4s
+ orr w12,w12,w15
+ eor w11,w11,w8,ror#19
+ eor w15,w4,w4,ror#11
+ add v4.4s,v4.4s,v1.4s
+ add w3,w3,w12
+ ror w11,w11,#6
+ eor w14,w4,w5
+ eor w15,w15,w4,ror#20
+ add w3,w3,w11
+ ldr w12,[sp,#32]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w7,w7,w3
+ eor w13,w13,w5
+ st1 {v4.4s},[x17], #16
+ ext v4.16b,v2.16b,v3.16b,#4
+ add w10,w10,w12
+ add w3,w3,w15
+ and w12,w8,w7
+ bic w15,w9,w7
+ ext v7.16b,v0.16b,v1.16b,#4
+ eor w11,w7,w7,ror#5
+ add w3,w3,w13
+ mov d19,v1.d[1]
+ orr w12,w12,w15
+ eor w11,w11,w7,ror#19
+ ushr v6.4s,v4.4s,#7
+ eor w15,w3,w3,ror#11
+ ushr v5.4s,v4.4s,#3
+ add w10,w10,w12
+ add v2.4s,v2.4s,v7.4s
+ ror w11,w11,#6
+ sli v6.4s,v4.4s,#25
+ eor w13,w3,w4
+ eor w15,w15,w3,ror#20
+ ushr v7.4s,v4.4s,#18
+ add w10,w10,w11
+ ldr w12,[sp,#36]
+ and w14,w14,w13
+ eor v5.16b,v5.16b,v6.16b
+ ror w15,w15,#2
+ add w6,w6,w10
+ sli v7.4s,v4.4s,#14
+ eor w14,w14,w4
+ ushr v16.4s,v19.4s,#17
+ add w9,w9,w12
+ add w10,w10,w15
+ and w12,w7,w6
+ eor v5.16b,v5.16b,v7.16b
+ bic w15,w8,w6
+ eor w11,w6,w6,ror#5
+ sli v16.4s,v19.4s,#15
+ add w10,w10,w14
+ orr w12,w12,w15
+ ushr v17.4s,v19.4s,#10
+ eor w11,w11,w6,ror#19
+ eor w15,w10,w10,ror#11
+ ushr v7.4s,v19.4s,#19
+ add w9,w9,w12
+ ror w11,w11,#6
+ add v2.4s,v2.4s,v5.4s
+ eor w14,w10,w3
+ eor w15,w15,w10,ror#20
+ sli v7.4s,v19.4s,#13
+ add w9,w9,w11
+ ldr w12,[sp,#40]
+ and w13,w13,w14
+ eor v17.16b,v17.16b,v16.16b
+ ror w15,w15,#2
+ add w5,w5,w9
+ eor w13,w13,w3
+ eor v17.16b,v17.16b,v7.16b
+ add w8,w8,w12
+ add w9,w9,w15
+ and w12,w6,w5
+ add v2.4s,v2.4s,v17.4s
+ bic w15,w7,w5
+ eor w11,w5,w5,ror#5
+ add w9,w9,w13
+ ushr v18.4s,v2.4s,#17
+ orr w12,w12,w15
+ ushr v19.4s,v2.4s,#10
+ eor w11,w11,w5,ror#19
+ eor w15,w9,w9,ror#11
+ sli v18.4s,v2.4s,#15
+ add w8,w8,w12
+ ushr v17.4s,v2.4s,#19
+ ror w11,w11,#6
+ eor w13,w9,w10
+ eor v19.16b,v19.16b,v18.16b
+ eor w15,w15,w9,ror#20
+ add w8,w8,w11
+ sli v17.4s,v2.4s,#13
+ ldr w12,[sp,#44]
+ and w14,w14,w13
+ ror w15,w15,#2
+ ld1 {v4.4s},[x16], #16
+ add w4,w4,w8
+ eor v19.16b,v19.16b,v17.16b
+ eor w14,w14,w10
+ eor v17.16b,v17.16b,v17.16b
+ add w7,w7,w12
+ add w8,w8,w15
+ and w12,w5,w4
+ mov v17.d[1],v19.d[0]
+ bic w15,w6,w4
+ eor w11,w4,w4,ror#5
+ add w8,w8,w14
+ add v2.4s,v2.4s,v17.4s
+ orr w12,w12,w15
+ eor w11,w11,w4,ror#19
+ eor w15,w8,w8,ror#11
+ add v4.4s,v4.4s,v2.4s
+ add w7,w7,w12
+ ror w11,w11,#6
+ eor w14,w8,w9
+ eor w15,w15,w8,ror#20
+ add w7,w7,w11
+ ldr w12,[sp,#48]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w3,w3,w7
+ eor w13,w13,w9
+ st1 {v4.4s},[x17], #16
+ ext v4.16b,v3.16b,v0.16b,#4
+ add w6,w6,w12
+ add w7,w7,w15
+ and w12,w4,w3
+ bic w15,w5,w3
+ ext v7.16b,v1.16b,v2.16b,#4
+ eor w11,w3,w3,ror#5
+ add w7,w7,w13
+ mov d19,v2.d[1]
+ orr w12,w12,w15
+ eor w11,w11,w3,ror#19
+ ushr v6.4s,v4.4s,#7
+ eor w15,w7,w7,ror#11
+ ushr v5.4s,v4.4s,#3
+ add w6,w6,w12
+ add v3.4s,v3.4s,v7.4s
+ ror w11,w11,#6
+ sli v6.4s,v4.4s,#25
+ eor w13,w7,w8
+ eor w15,w15,w7,ror#20
+ ushr v7.4s,v4.4s,#18
+ add w6,w6,w11
+ ldr w12,[sp,#52]
+ and w14,w14,w13
+ eor v5.16b,v5.16b,v6.16b
+ ror w15,w15,#2
+ add w10,w10,w6
+ sli v7.4s,v4.4s,#14
+ eor w14,w14,w8
+ ushr v16.4s,v19.4s,#17
+ add w5,w5,w12
+ add w6,w6,w15
+ and w12,w3,w10
+ eor v5.16b,v5.16b,v7.16b
+ bic w15,w4,w10
+ eor w11,w10,w10,ror#5
+ sli v16.4s,v19.4s,#15
+ add w6,w6,w14
+ orr w12,w12,w15
+ ushr v17.4s,v19.4s,#10
+ eor w11,w11,w10,ror#19
+ eor w15,w6,w6,ror#11
+ ushr v7.4s,v19.4s,#19
+ add w5,w5,w12
+ ror w11,w11,#6
+ add v3.4s,v3.4s,v5.4s
+ eor w14,w6,w7
+ eor w15,w15,w6,ror#20
+ sli v7.4s,v19.4s,#13
+ add w5,w5,w11
+ ldr w12,[sp,#56]
+ and w13,w13,w14
+ eor v17.16b,v17.16b,v16.16b
+ ror w15,w15,#2
+ add w9,w9,w5
+ eor w13,w13,w7
+ eor v17.16b,v17.16b,v7.16b
+ add w4,w4,w12
+ add w5,w5,w15
+ and w12,w10,w9
+ add v3.4s,v3.4s,v17.4s
+ bic w15,w3,w9
+ eor w11,w9,w9,ror#5
+ add w5,w5,w13
+ ushr v18.4s,v3.4s,#17
+ orr w12,w12,w15
+ ushr v19.4s,v3.4s,#10
+ eor w11,w11,w9,ror#19
+ eor w15,w5,w5,ror#11
+ sli v18.4s,v3.4s,#15
+ add w4,w4,w12
+ ushr v17.4s,v3.4s,#19
+ ror w11,w11,#6
+ eor w13,w5,w6
+ eor v19.16b,v19.16b,v18.16b
+ eor w15,w15,w5,ror#20
+ add w4,w4,w11
+ sli v17.4s,v3.4s,#13
+ ldr w12,[sp,#60]
+ and w14,w14,w13
+ ror w15,w15,#2
+ ld1 {v4.4s},[x16], #16
+ add w8,w8,w4
+ eor v19.16b,v19.16b,v17.16b
+ eor w14,w14,w6
+ eor v17.16b,v17.16b,v17.16b
+ add w3,w3,w12
+ add w4,w4,w15
+ and w12,w9,w8
+ mov v17.d[1],v19.d[0]
+ bic w15,w10,w8
+ eor w11,w8,w8,ror#5
+ add w4,w4,w14
+ add v3.4s,v3.4s,v17.4s
+ orr w12,w12,w15
+ eor w11,w11,w8,ror#19
+ eor w15,w4,w4,ror#11
+ add v4.4s,v4.4s,v3.4s
+ add w3,w3,w12
+ ror w11,w11,#6
+ eor w14,w4,w5
+ eor w15,w15,w4,ror#20
+ add w3,w3,w11
+ ldr w12,[x16]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w7,w7,w3
+ eor w13,w13,w5
+ st1 {v4.4s},[x17], #16
+ cmp w12,#0 // check for K256 terminator
+ ldr w12,[sp,#0]
+ sub x17,x17,#64
+ bne .L_00_48
+
+ sub x16,x16,#256 // rewind x16
+ cmp x1,x2
+ mov x17, #64
+ csel x17, x17, xzr, eq
+ sub x1,x1,x17 // avoid SEGV
+ mov x17,sp
+ add w10,w10,w12
+ add w3,w3,w15
+ and w12,w8,w7
+ ld1 {v0.16b},[x1],#16
+ bic w15,w9,w7
+ eor w11,w7,w7,ror#5
+ ld1 {v4.4s},[x16],#16
+ add w3,w3,w13
+ orr w12,w12,w15
+ eor w11,w11,w7,ror#19
+ eor w15,w3,w3,ror#11
+ rev32 v0.16b,v0.16b
+ add w10,w10,w12
+ ror w11,w11,#6
+ eor w13,w3,w4
+ eor w15,w15,w3,ror#20
+ add v4.4s,v4.4s,v0.4s
+ add w10,w10,w11
+ ldr w12,[sp,#4]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w6,w6,w10
+ eor w14,w14,w4
+ add w9,w9,w12
+ add w10,w10,w15
+ and w12,w7,w6
+ bic w15,w8,w6
+ eor w11,w6,w6,ror#5
+ add w10,w10,w14
+ orr w12,w12,w15
+ eor w11,w11,w6,ror#19
+ eor w15,w10,w10,ror#11
+ add w9,w9,w12
+ ror w11,w11,#6
+ eor w14,w10,w3
+ eor w15,w15,w10,ror#20
+ add w9,w9,w11
+ ldr w12,[sp,#8]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w5,w5,w9
+ eor w13,w13,w3
+ add w8,w8,w12
+ add w9,w9,w15
+ and w12,w6,w5
+ bic w15,w7,w5
+ eor w11,w5,w5,ror#5
+ add w9,w9,w13
+ orr w12,w12,w15
+ eor w11,w11,w5,ror#19
+ eor w15,w9,w9,ror#11
+ add w8,w8,w12
+ ror w11,w11,#6
+ eor w13,w9,w10
+ eor w15,w15,w9,ror#20
+ add w8,w8,w11
+ ldr w12,[sp,#12]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w4,w4,w8
+ eor w14,w14,w10
+ add w7,w7,w12
+ add w8,w8,w15
+ and w12,w5,w4
+ bic w15,w6,w4
+ eor w11,w4,w4,ror#5
+ add w8,w8,w14
+ orr w12,w12,w15
+ eor w11,w11,w4,ror#19
+ eor w15,w8,w8,ror#11
+ add w7,w7,w12
+ ror w11,w11,#6
+ eor w14,w8,w9
+ eor w15,w15,w8,ror#20
+ add w7,w7,w11
+ ldr w12,[sp,#16]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w3,w3,w7
+ eor w13,w13,w9
+ st1 {v4.4s},[x17], #16
+ add w6,w6,w12
+ add w7,w7,w15
+ and w12,w4,w3
+ ld1 {v1.16b},[x1],#16
+ bic w15,w5,w3
+ eor w11,w3,w3,ror#5
+ ld1 {v4.4s},[x16],#16
+ add w7,w7,w13
+ orr w12,w12,w15
+ eor w11,w11,w3,ror#19
+ eor w15,w7,w7,ror#11
+ rev32 v1.16b,v1.16b
+ add w6,w6,w12
+ ror w11,w11,#6
+ eor w13,w7,w8
+ eor w15,w15,w7,ror#20
+ add v4.4s,v4.4s,v1.4s
+ add w6,w6,w11
+ ldr w12,[sp,#20]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w10,w10,w6
+ eor w14,w14,w8
+ add w5,w5,w12
+ add w6,w6,w15
+ and w12,w3,w10
+ bic w15,w4,w10
+ eor w11,w10,w10,ror#5
+ add w6,w6,w14
+ orr w12,w12,w15
+ eor w11,w11,w10,ror#19
+ eor w15,w6,w6,ror#11
+ add w5,w5,w12
+ ror w11,w11,#6
+ eor w14,w6,w7
+ eor w15,w15,w6,ror#20
+ add w5,w5,w11
+ ldr w12,[sp,#24]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w9,w9,w5
+ eor w13,w13,w7
+ add w4,w4,w12
+ add w5,w5,w15
+ and w12,w10,w9
+ bic w15,w3,w9
+ eor w11,w9,w9,ror#5
+ add w5,w5,w13
+ orr w12,w12,w15
+ eor w11,w11,w9,ror#19
+ eor w15,w5,w5,ror#11
+ add w4,w4,w12
+ ror w11,w11,#6
+ eor w13,w5,w6
+ eor w15,w15,w5,ror#20
+ add w4,w4,w11
+ ldr w12,[sp,#28]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w8,w8,w4
+ eor w14,w14,w6
+ add w3,w3,w12
+ add w4,w4,w15
+ and w12,w9,w8
+ bic w15,w10,w8
+ eor w11,w8,w8,ror#5
+ add w4,w4,w14
+ orr w12,w12,w15
+ eor w11,w11,w8,ror#19
+ eor w15,w4,w4,ror#11
+ add w3,w3,w12
+ ror w11,w11,#6
+ eor w14,w4,w5
+ eor w15,w15,w4,ror#20
+ add w3,w3,w11
+ ldr w12,[sp,#32]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w7,w7,w3
+ eor w13,w13,w5
+ st1 {v4.4s},[x17], #16
+ add w10,w10,w12
+ add w3,w3,w15
+ and w12,w8,w7
+ ld1 {v2.16b},[x1],#16
+ bic w15,w9,w7
+ eor w11,w7,w7,ror#5
+ ld1 {v4.4s},[x16],#16
+ add w3,w3,w13
+ orr w12,w12,w15
+ eor w11,w11,w7,ror#19
+ eor w15,w3,w3,ror#11
+ rev32 v2.16b,v2.16b
+ add w10,w10,w12
+ ror w11,w11,#6
+ eor w13,w3,w4
+ eor w15,w15,w3,ror#20
+ add v4.4s,v4.4s,v2.4s
+ add w10,w10,w11
+ ldr w12,[sp,#36]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w6,w6,w10
+ eor w14,w14,w4
+ add w9,w9,w12
+ add w10,w10,w15
+ and w12,w7,w6
+ bic w15,w8,w6
+ eor w11,w6,w6,ror#5
+ add w10,w10,w14
+ orr w12,w12,w15
+ eor w11,w11,w6,ror#19
+ eor w15,w10,w10,ror#11
+ add w9,w9,w12
+ ror w11,w11,#6
+ eor w14,w10,w3
+ eor w15,w15,w10,ror#20
+ add w9,w9,w11
+ ldr w12,[sp,#40]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w5,w5,w9
+ eor w13,w13,w3
+ add w8,w8,w12
+ add w9,w9,w15
+ and w12,w6,w5
+ bic w15,w7,w5
+ eor w11,w5,w5,ror#5
+ add w9,w9,w13
+ orr w12,w12,w15
+ eor w11,w11,w5,ror#19
+ eor w15,w9,w9,ror#11
+ add w8,w8,w12
+ ror w11,w11,#6
+ eor w13,w9,w10
+ eor w15,w15,w9,ror#20
+ add w8,w8,w11
+ ldr w12,[sp,#44]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w4,w4,w8
+ eor w14,w14,w10
+ add w7,w7,w12
+ add w8,w8,w15
+ and w12,w5,w4
+ bic w15,w6,w4
+ eor w11,w4,w4,ror#5
+ add w8,w8,w14
+ orr w12,w12,w15
+ eor w11,w11,w4,ror#19
+ eor w15,w8,w8,ror#11
+ add w7,w7,w12
+ ror w11,w11,#6
+ eor w14,w8,w9
+ eor w15,w15,w8,ror#20
+ add w7,w7,w11
+ ldr w12,[sp,#48]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w3,w3,w7
+ eor w13,w13,w9
+ st1 {v4.4s},[x17], #16
+ add w6,w6,w12
+ add w7,w7,w15
+ and w12,w4,w3
+ ld1 {v3.16b},[x1],#16
+ bic w15,w5,w3
+ eor w11,w3,w3,ror#5
+ ld1 {v4.4s},[x16],#16
+ add w7,w7,w13
+ orr w12,w12,w15
+ eor w11,w11,w3,ror#19
+ eor w15,w7,w7,ror#11
+ rev32 v3.16b,v3.16b
+ add w6,w6,w12
+ ror w11,w11,#6
+ eor w13,w7,w8
+ eor w15,w15,w7,ror#20
+ add v4.4s,v4.4s,v3.4s
+ add w6,w6,w11
+ ldr w12,[sp,#52]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w10,w10,w6
+ eor w14,w14,w8
+ add w5,w5,w12
+ add w6,w6,w15
+ and w12,w3,w10
+ bic w15,w4,w10
+ eor w11,w10,w10,ror#5
+ add w6,w6,w14
+ orr w12,w12,w15
+ eor w11,w11,w10,ror#19
+ eor w15,w6,w6,ror#11
+ add w5,w5,w12
+ ror w11,w11,#6
+ eor w14,w6,w7
+ eor w15,w15,w6,ror#20
+ add w5,w5,w11
+ ldr w12,[sp,#56]
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w9,w9,w5
+ eor w13,w13,w7
+ add w4,w4,w12
+ add w5,w5,w15
+ and w12,w10,w9
+ bic w15,w3,w9
+ eor w11,w9,w9,ror#5
+ add w5,w5,w13
+ orr w12,w12,w15
+ eor w11,w11,w9,ror#19
+ eor w15,w5,w5,ror#11
+ add w4,w4,w12
+ ror w11,w11,#6
+ eor w13,w5,w6
+ eor w15,w15,w5,ror#20
+ add w4,w4,w11
+ ldr w12,[sp,#60]
+ and w14,w14,w13
+ ror w15,w15,#2
+ add w8,w8,w4
+ eor w14,w14,w6
+ add w3,w3,w12
+ add w4,w4,w15
+ and w12,w9,w8
+ bic w15,w10,w8
+ eor w11,w8,w8,ror#5
+ add w4,w4,w14
+ orr w12,w12,w15
+ eor w11,w11,w8,ror#19
+ eor w15,w4,w4,ror#11
+ add w3,w3,w12
+ ror w11,w11,#6
+ eor w14,w4,w5
+ eor w15,w15,w4,ror#20
+ add w3,w3,w11
+ and w13,w13,w14
+ ror w15,w15,#2
+ add w7,w7,w3
+ eor w13,w13,w5
+ st1 {v4.4s},[x17], #16
+ add w3,w3,w15 // h+=Sigma0(a) from the past
+ ldp w11,w12,[x0,#0]
+ add w3,w3,w13 // h+=Maj(a,b,c) from the past
+ ldp w13,w14,[x0,#8]
+ add w3,w3,w11 // accumulate
+ add w4,w4,w12
+ ldp w11,w12,[x0,#16]
+ add w5,w5,w13
+ add w6,w6,w14
+ ldp w13,w14,[x0,#24]
+ add w7,w7,w11
+ add w8,w8,w12
+ ldr w12,[sp,#0]
+ stp w3,w4,[x0,#0]
+ add w9,w9,w13
+ mov w13,wzr
+ stp w5,w6,[x0,#8]
+ add w10,w10,w14
+ stp w7,w8,[x0,#16]
+ eor w14,w4,w5
+ stp w9,w10,[x0,#24]
+ mov w15,wzr
+ mov x17,sp
+ b.ne .L_00_48
+
+ ldr x29,[x29]
+ add sp,sp,#16*4+16
+ ret
+.size sha256_block_neon,.-sha256_block_neon
+#ifndef __KERNEL__
+.comm OPENSSL_armcap_P,4,4
+#endif
diff --git a/arch/arm64/crypto/sha512-core.S b/arch/arm64/crypto/sha512-core.S
new file mode 100644
index 0000000..bd0f59f
--- /dev/null
+++ b/arch/arm64/crypto/sha512-core.S
@@ -0,0 +1,1085 @@
+// Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+//
+// Licensed under the OpenSSL license (the "License"). You may not use
+// this file except in compliance with the License. You can obtain a copy
+// in the file LICENSE in the source distribution or at
+// https://www.openssl.org/source/license.html
+
+// ====================================================================
+// Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+// project. The module is, however, dual licensed under OpenSSL and
+// CRYPTOGAMS licenses depending on where you obtain it. For further
+// details see http://www.openssl.org/~appro/cryptogams/.
+//
+// Permission to use under GPLv2 terms is granted.
+// ====================================================================
+//
+// SHA256/512 for ARMv8.
+//
+// Performance in cycles per processed byte and improvement coefficient
+// over code generated with "default" compiler:
+//
+// SHA256-hw SHA256(*) SHA512
+// Apple A7 1.97 10.5 (+33%) 6.73 (-1%(**))
+// Cortex-A53 2.38 15.5 (+115%) 10.0 (+150%(***))
+// Cortex-A57 2.31 11.6 (+86%) 7.51 (+260%(***))
+// Denver 2.01 10.5 (+26%) 6.70 (+8%)
+// X-Gene 20.0 (+100%) 12.8 (+300%(***))
+// Mongoose 2.36 13.0 (+50%) 8.36 (+33%)
+//
+// (*) Software SHA256 results are of lesser relevance, presented
+// mostly for informational purposes.
+// (**) The result is a trade-off: it's possible to improve it by
+// 10% (or by 1 cycle per round), but at the cost of 20% loss
+// on Cortex-A53 (or by 4 cycles per round).
+// (***) Super-impressive coefficients over gcc-generated code are
+// indication of some compiler "pathology", most notably code
+// generated with -mgeneral-regs-only is significanty faster
+// and the gap is only 40-90%.
+//
+// October 2016.
+//
+// Originally it was reckoned that it makes no sense to implement NEON
+// version of SHA256 for 64-bit processors. This is because performance
+// improvement on most wide-spread Cortex-A5x processors was observed
+// to be marginal, same on Cortex-A53 and ~10% on A57. But then it was
+// observed that 32-bit NEON SHA256 performs significantly better than
+// 64-bit scalar version on *some* of the more recent processors. As
+// result 64-bit NEON version of SHA256 was added to provide best
+// all-round performance. For example it executes ~30% faster on X-Gene
+// and Mongoose. [For reference, NEON version of SHA512 is bound to
+// deliver much less improvement, likely *negative* on Cortex-A5x.
+// Which is why NEON support is limited to SHA256.]
+
+#ifndef __KERNEL__
+# include "arm_arch.h"
+#endif
+
+.text
+
+.extern OPENSSL_armcap_P
+.globl sha512_block_data_order
+.type sha512_block_data_order,%function
+.align 6
+sha512_block_data_order:
+ stp x29,x30,[sp,#-128]!
+ add x29,sp,#0
+
+ stp x19,x20,[sp,#16]
+ stp x21,x22,[sp,#32]
+ stp x23,x24,[sp,#48]
+ stp x25,x26,[sp,#64]
+ stp x27,x28,[sp,#80]
+ sub sp,sp,#4*8
+
+ ldp x20,x21,[x0] // load context
+ ldp x22,x23,[x0,#2*8]
+ ldp x24,x25,[x0,#4*8]
+ add x2,x1,x2,lsl#7 // end of input
+ ldp x26,x27,[x0,#6*8]
+ adr x30,.LK512
+ stp x0,x2,[x29,#96]
+
+.Loop:
+ ldp x3,x4,[x1],#2*8
+ ldr x19,[x30],#8 // *K++
+ eor x28,x21,x22 // magic seed
+ str x1,[x29,#112]
+#ifndef __AARCH64EB__
+ rev x3,x3 // 0
+#endif
+ ror x16,x24,#14
+ add x27,x27,x19 // h+=K[i]
+ eor x6,x24,x24,ror#23
+ and x17,x25,x24
+ bic x19,x26,x24
+ add x27,x27,x3 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x20,x21 // a^b, b^c in next round
+ eor x16,x16,x6,ror#18 // Sigma1(e)
+ ror x6,x20,#28
+ add x27,x27,x17 // h+=Ch(e,f,g)
+ eor x17,x20,x20,ror#5
+ add x27,x27,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x23,x23,x27 // d+=h
+ eor x28,x28,x21 // Maj(a,b,c)
+ eor x17,x6,x17,ror#34 // Sigma0(a)
+ add x27,x27,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x27,x27,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x4,x4 // 1
+#endif
+ ldp x5,x6,[x1],#2*8
+ add x27,x27,x17 // h+=Sigma0(a)
+ ror x16,x23,#14
+ add x26,x26,x28 // h+=K[i]
+ eor x7,x23,x23,ror#23
+ and x17,x24,x23
+ bic x28,x25,x23
+ add x26,x26,x4 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x27,x20 // a^b, b^c in next round
+ eor x16,x16,x7,ror#18 // Sigma1(e)
+ ror x7,x27,#28
+ add x26,x26,x17 // h+=Ch(e,f,g)
+ eor x17,x27,x27,ror#5
+ add x26,x26,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x22,x22,x26 // d+=h
+ eor x19,x19,x20 // Maj(a,b,c)
+ eor x17,x7,x17,ror#34 // Sigma0(a)
+ add x26,x26,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x26,x26,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x5,x5 // 2
+#endif
+ add x26,x26,x17 // h+=Sigma0(a)
+ ror x16,x22,#14
+ add x25,x25,x19 // h+=K[i]
+ eor x8,x22,x22,ror#23
+ and x17,x23,x22
+ bic x19,x24,x22
+ add x25,x25,x5 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x26,x27 // a^b, b^c in next round
+ eor x16,x16,x8,ror#18 // Sigma1(e)
+ ror x8,x26,#28
+ add x25,x25,x17 // h+=Ch(e,f,g)
+ eor x17,x26,x26,ror#5
+ add x25,x25,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x21,x21,x25 // d+=h
+ eor x28,x28,x27 // Maj(a,b,c)
+ eor x17,x8,x17,ror#34 // Sigma0(a)
+ add x25,x25,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x25,x25,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x6,x6 // 3
+#endif
+ ldp x7,x8,[x1],#2*8
+ add x25,x25,x17 // h+=Sigma0(a)
+ ror x16,x21,#14
+ add x24,x24,x28 // h+=K[i]
+ eor x9,x21,x21,ror#23
+ and x17,x22,x21
+ bic x28,x23,x21
+ add x24,x24,x6 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x25,x26 // a^b, b^c in next round
+ eor x16,x16,x9,ror#18 // Sigma1(e)
+ ror x9,x25,#28
+ add x24,x24,x17 // h+=Ch(e,f,g)
+ eor x17,x25,x25,ror#5
+ add x24,x24,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x20,x20,x24 // d+=h
+ eor x19,x19,x26 // Maj(a,b,c)
+ eor x17,x9,x17,ror#34 // Sigma0(a)
+ add x24,x24,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x24,x24,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x7,x7 // 4
+#endif
+ add x24,x24,x17 // h+=Sigma0(a)
+ ror x16,x20,#14
+ add x23,x23,x19 // h+=K[i]
+ eor x10,x20,x20,ror#23
+ and x17,x21,x20
+ bic x19,x22,x20
+ add x23,x23,x7 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x24,x25 // a^b, b^c in next round
+ eor x16,x16,x10,ror#18 // Sigma1(e)
+ ror x10,x24,#28
+ add x23,x23,x17 // h+=Ch(e,f,g)
+ eor x17,x24,x24,ror#5
+ add x23,x23,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x27,x27,x23 // d+=h
+ eor x28,x28,x25 // Maj(a,b,c)
+ eor x17,x10,x17,ror#34 // Sigma0(a)
+ add x23,x23,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x23,x23,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x8,x8 // 5
+#endif
+ ldp x9,x10,[x1],#2*8
+ add x23,x23,x17 // h+=Sigma0(a)
+ ror x16,x27,#14
+ add x22,x22,x28 // h+=K[i]
+ eor x11,x27,x27,ror#23
+ and x17,x20,x27
+ bic x28,x21,x27
+ add x22,x22,x8 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x23,x24 // a^b, b^c in next round
+ eor x16,x16,x11,ror#18 // Sigma1(e)
+ ror x11,x23,#28
+ add x22,x22,x17 // h+=Ch(e,f,g)
+ eor x17,x23,x23,ror#5
+ add x22,x22,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x26,x26,x22 // d+=h
+ eor x19,x19,x24 // Maj(a,b,c)
+ eor x17,x11,x17,ror#34 // Sigma0(a)
+ add x22,x22,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x22,x22,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x9,x9 // 6
+#endif
+ add x22,x22,x17 // h+=Sigma0(a)
+ ror x16,x26,#14
+ add x21,x21,x19 // h+=K[i]
+ eor x12,x26,x26,ror#23
+ and x17,x27,x26
+ bic x19,x20,x26
+ add x21,x21,x9 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x22,x23 // a^b, b^c in next round
+ eor x16,x16,x12,ror#18 // Sigma1(e)
+ ror x12,x22,#28
+ add x21,x21,x17 // h+=Ch(e,f,g)
+ eor x17,x22,x22,ror#5
+ add x21,x21,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x25,x25,x21 // d+=h
+ eor x28,x28,x23 // Maj(a,b,c)
+ eor x17,x12,x17,ror#34 // Sigma0(a)
+ add x21,x21,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x21,x21,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x10,x10 // 7
+#endif
+ ldp x11,x12,[x1],#2*8
+ add x21,x21,x17 // h+=Sigma0(a)
+ ror x16,x25,#14
+ add x20,x20,x28 // h+=K[i]
+ eor x13,x25,x25,ror#23
+ and x17,x26,x25
+ bic x28,x27,x25
+ add x20,x20,x10 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x21,x22 // a^b, b^c in next round
+ eor x16,x16,x13,ror#18 // Sigma1(e)
+ ror x13,x21,#28
+ add x20,x20,x17 // h+=Ch(e,f,g)
+ eor x17,x21,x21,ror#5
+ add x20,x20,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x24,x24,x20 // d+=h
+ eor x19,x19,x22 // Maj(a,b,c)
+ eor x17,x13,x17,ror#34 // Sigma0(a)
+ add x20,x20,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x20,x20,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x11,x11 // 8
+#endif
+ add x20,x20,x17 // h+=Sigma0(a)
+ ror x16,x24,#14
+ add x27,x27,x19 // h+=K[i]
+ eor x14,x24,x24,ror#23
+ and x17,x25,x24
+ bic x19,x26,x24
+ add x27,x27,x11 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x20,x21 // a^b, b^c in next round
+ eor x16,x16,x14,ror#18 // Sigma1(e)
+ ror x14,x20,#28
+ add x27,x27,x17 // h+=Ch(e,f,g)
+ eor x17,x20,x20,ror#5
+ add x27,x27,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x23,x23,x27 // d+=h
+ eor x28,x28,x21 // Maj(a,b,c)
+ eor x17,x14,x17,ror#34 // Sigma0(a)
+ add x27,x27,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x27,x27,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x12,x12 // 9
+#endif
+ ldp x13,x14,[x1],#2*8
+ add x27,x27,x17 // h+=Sigma0(a)
+ ror x16,x23,#14
+ add x26,x26,x28 // h+=K[i]
+ eor x15,x23,x23,ror#23
+ and x17,x24,x23
+ bic x28,x25,x23
+ add x26,x26,x12 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x27,x20 // a^b, b^c in next round
+ eor x16,x16,x15,ror#18 // Sigma1(e)
+ ror x15,x27,#28
+ add x26,x26,x17 // h+=Ch(e,f,g)
+ eor x17,x27,x27,ror#5
+ add x26,x26,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x22,x22,x26 // d+=h
+ eor x19,x19,x20 // Maj(a,b,c)
+ eor x17,x15,x17,ror#34 // Sigma0(a)
+ add x26,x26,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x26,x26,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x13,x13 // 10
+#endif
+ add x26,x26,x17 // h+=Sigma0(a)
+ ror x16,x22,#14
+ add x25,x25,x19 // h+=K[i]
+ eor x0,x22,x22,ror#23
+ and x17,x23,x22
+ bic x19,x24,x22
+ add x25,x25,x13 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x26,x27 // a^b, b^c in next round
+ eor x16,x16,x0,ror#18 // Sigma1(e)
+ ror x0,x26,#28
+ add x25,x25,x17 // h+=Ch(e,f,g)
+ eor x17,x26,x26,ror#5
+ add x25,x25,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x21,x21,x25 // d+=h
+ eor x28,x28,x27 // Maj(a,b,c)
+ eor x17,x0,x17,ror#34 // Sigma0(a)
+ add x25,x25,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x25,x25,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x14,x14 // 11
+#endif
+ ldp x15,x0,[x1],#2*8
+ add x25,x25,x17 // h+=Sigma0(a)
+ str x6,[sp,#24]
+ ror x16,x21,#14
+ add x24,x24,x28 // h+=K[i]
+ eor x6,x21,x21,ror#23
+ and x17,x22,x21
+ bic x28,x23,x21
+ add x24,x24,x14 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x25,x26 // a^b, b^c in next round
+ eor x16,x16,x6,ror#18 // Sigma1(e)
+ ror x6,x25,#28
+ add x24,x24,x17 // h+=Ch(e,f,g)
+ eor x17,x25,x25,ror#5
+ add x24,x24,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x20,x20,x24 // d+=h
+ eor x19,x19,x26 // Maj(a,b,c)
+ eor x17,x6,x17,ror#34 // Sigma0(a)
+ add x24,x24,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x24,x24,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x15,x15 // 12
+#endif
+ add x24,x24,x17 // h+=Sigma0(a)
+ str x7,[sp,#0]
+ ror x16,x20,#14
+ add x23,x23,x19 // h+=K[i]
+ eor x7,x20,x20,ror#23
+ and x17,x21,x20
+ bic x19,x22,x20
+ add x23,x23,x15 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x24,x25 // a^b, b^c in next round
+ eor x16,x16,x7,ror#18 // Sigma1(e)
+ ror x7,x24,#28
+ add x23,x23,x17 // h+=Ch(e,f,g)
+ eor x17,x24,x24,ror#5
+ add x23,x23,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x27,x27,x23 // d+=h
+ eor x28,x28,x25 // Maj(a,b,c)
+ eor x17,x7,x17,ror#34 // Sigma0(a)
+ add x23,x23,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x23,x23,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x0,x0 // 13
+#endif
+ ldp x1,x2,[x1]
+ add x23,x23,x17 // h+=Sigma0(a)
+ str x8,[sp,#8]
+ ror x16,x27,#14
+ add x22,x22,x28 // h+=K[i]
+ eor x8,x27,x27,ror#23
+ and x17,x20,x27
+ bic x28,x21,x27
+ add x22,x22,x0 // h+=X[i]
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x23,x24 // a^b, b^c in next round
+ eor x16,x16,x8,ror#18 // Sigma1(e)
+ ror x8,x23,#28
+ add x22,x22,x17 // h+=Ch(e,f,g)
+ eor x17,x23,x23,ror#5
+ add x22,x22,x16 // h+=Sigma1(e)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ add x26,x26,x22 // d+=h
+ eor x19,x19,x24 // Maj(a,b,c)
+ eor x17,x8,x17,ror#34 // Sigma0(a)
+ add x22,x22,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ //add x22,x22,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x1,x1 // 14
+#endif
+ ldr x6,[sp,#24]
+ add x22,x22,x17 // h+=Sigma0(a)
+ str x9,[sp,#16]
+ ror x16,x26,#14
+ add x21,x21,x19 // h+=K[i]
+ eor x9,x26,x26,ror#23
+ and x17,x27,x26
+ bic x19,x20,x26
+ add x21,x21,x1 // h+=X[i]
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x22,x23 // a^b, b^c in next round
+ eor x16,x16,x9,ror#18 // Sigma1(e)
+ ror x9,x22,#28
+ add x21,x21,x17 // h+=Ch(e,f,g)
+ eor x17,x22,x22,ror#5
+ add x21,x21,x16 // h+=Sigma1(e)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ add x25,x25,x21 // d+=h
+ eor x28,x28,x23 // Maj(a,b,c)
+ eor x17,x9,x17,ror#34 // Sigma0(a)
+ add x21,x21,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ //add x21,x21,x17 // h+=Sigma0(a)
+#ifndef __AARCH64EB__
+ rev x2,x2 // 15
+#endif
+ ldr x7,[sp,#0]
+ add x21,x21,x17 // h+=Sigma0(a)
+ str x10,[sp,#24]
+ ror x16,x25,#14
+ add x20,x20,x28 // h+=K[i]
+ ror x9,x4,#1
+ and x17,x26,x25
+ ror x8,x1,#19
+ bic x28,x27,x25
+ ror x10,x21,#28
+ add x20,x20,x2 // h+=X[i]
+ eor x16,x16,x25,ror#18
+ eor x9,x9,x4,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x21,x22 // a^b, b^c in next round
+ eor x16,x16,x25,ror#41 // Sigma1(e)
+ eor x10,x10,x21,ror#34
+ add x20,x20,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x8,x8,x1,ror#61
+ eor x9,x9,x4,lsr#7 // sigma0(X[i+1])
+ add x20,x20,x16 // h+=Sigma1(e)
+ eor x19,x19,x22 // Maj(a,b,c)
+ eor x17,x10,x21,ror#39 // Sigma0(a)
+ eor x8,x8,x1,lsr#6 // sigma1(X[i+14])
+ add x3,x3,x12
+ add x24,x24,x20 // d+=h
+ add x20,x20,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x3,x3,x9
+ add x20,x20,x17 // h+=Sigma0(a)
+ add x3,x3,x8
+.Loop_16_xx:
+ ldr x8,[sp,#8]
+ str x11,[sp,#0]
+ ror x16,x24,#14
+ add x27,x27,x19 // h+=K[i]
+ ror x10,x5,#1
+ and x17,x25,x24
+ ror x9,x2,#19
+ bic x19,x26,x24
+ ror x11,x20,#28
+ add x27,x27,x3 // h+=X[i]
+ eor x16,x16,x24,ror#18
+ eor x10,x10,x5,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x20,x21 // a^b, b^c in next round
+ eor x16,x16,x24,ror#41 // Sigma1(e)
+ eor x11,x11,x20,ror#34
+ add x27,x27,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x9,x9,x2,ror#61
+ eor x10,x10,x5,lsr#7 // sigma0(X[i+1])
+ add x27,x27,x16 // h+=Sigma1(e)
+ eor x28,x28,x21 // Maj(a,b,c)
+ eor x17,x11,x20,ror#39 // Sigma0(a)
+ eor x9,x9,x2,lsr#6 // sigma1(X[i+14])
+ add x4,x4,x13
+ add x23,x23,x27 // d+=h
+ add x27,x27,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x4,x4,x10
+ add x27,x27,x17 // h+=Sigma0(a)
+ add x4,x4,x9
+ ldr x9,[sp,#16]
+ str x12,[sp,#8]
+ ror x16,x23,#14
+ add x26,x26,x28 // h+=K[i]
+ ror x11,x6,#1
+ and x17,x24,x23
+ ror x10,x3,#19
+ bic x28,x25,x23
+ ror x12,x27,#28
+ add x26,x26,x4 // h+=X[i]
+ eor x16,x16,x23,ror#18
+ eor x11,x11,x6,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x27,x20 // a^b, b^c in next round
+ eor x16,x16,x23,ror#41 // Sigma1(e)
+ eor x12,x12,x27,ror#34
+ add x26,x26,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x10,x10,x3,ror#61
+ eor x11,x11,x6,lsr#7 // sigma0(X[i+1])
+ add x26,x26,x16 // h+=Sigma1(e)
+ eor x19,x19,x20 // Maj(a,b,c)
+ eor x17,x12,x27,ror#39 // Sigma0(a)
+ eor x10,x10,x3,lsr#6 // sigma1(X[i+14])
+ add x5,x5,x14
+ add x22,x22,x26 // d+=h
+ add x26,x26,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x5,x5,x11
+ add x26,x26,x17 // h+=Sigma0(a)
+ add x5,x5,x10
+ ldr x10,[sp,#24]
+ str x13,[sp,#16]
+ ror x16,x22,#14
+ add x25,x25,x19 // h+=K[i]
+ ror x12,x7,#1
+ and x17,x23,x22
+ ror x11,x4,#19
+ bic x19,x24,x22
+ ror x13,x26,#28
+ add x25,x25,x5 // h+=X[i]
+ eor x16,x16,x22,ror#18
+ eor x12,x12,x7,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x26,x27 // a^b, b^c in next round
+ eor x16,x16,x22,ror#41 // Sigma1(e)
+ eor x13,x13,x26,ror#34
+ add x25,x25,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x11,x11,x4,ror#61
+ eor x12,x12,x7,lsr#7 // sigma0(X[i+1])
+ add x25,x25,x16 // h+=Sigma1(e)
+ eor x28,x28,x27 // Maj(a,b,c)
+ eor x17,x13,x26,ror#39 // Sigma0(a)
+ eor x11,x11,x4,lsr#6 // sigma1(X[i+14])
+ add x6,x6,x15
+ add x21,x21,x25 // d+=h
+ add x25,x25,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x6,x6,x12
+ add x25,x25,x17 // h+=Sigma0(a)
+ add x6,x6,x11
+ ldr x11,[sp,#0]
+ str x14,[sp,#24]
+ ror x16,x21,#14
+ add x24,x24,x28 // h+=K[i]
+ ror x13,x8,#1
+ and x17,x22,x21
+ ror x12,x5,#19
+ bic x28,x23,x21
+ ror x14,x25,#28
+ add x24,x24,x6 // h+=X[i]
+ eor x16,x16,x21,ror#18
+ eor x13,x13,x8,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x25,x26 // a^b, b^c in next round
+ eor x16,x16,x21,ror#41 // Sigma1(e)
+ eor x14,x14,x25,ror#34
+ add x24,x24,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x12,x12,x5,ror#61
+ eor x13,x13,x8,lsr#7 // sigma0(X[i+1])
+ add x24,x24,x16 // h+=Sigma1(e)
+ eor x19,x19,x26 // Maj(a,b,c)
+ eor x17,x14,x25,ror#39 // Sigma0(a)
+ eor x12,x12,x5,lsr#6 // sigma1(X[i+14])
+ add x7,x7,x0
+ add x20,x20,x24 // d+=h
+ add x24,x24,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x7,x7,x13
+ add x24,x24,x17 // h+=Sigma0(a)
+ add x7,x7,x12
+ ldr x12,[sp,#8]
+ str x15,[sp,#0]
+ ror x16,x20,#14
+ add x23,x23,x19 // h+=K[i]
+ ror x14,x9,#1
+ and x17,x21,x20
+ ror x13,x6,#19
+ bic x19,x22,x20
+ ror x15,x24,#28
+ add x23,x23,x7 // h+=X[i]
+ eor x16,x16,x20,ror#18
+ eor x14,x14,x9,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x24,x25 // a^b, b^c in next round
+ eor x16,x16,x20,ror#41 // Sigma1(e)
+ eor x15,x15,x24,ror#34
+ add x23,x23,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x13,x13,x6,ror#61
+ eor x14,x14,x9,lsr#7 // sigma0(X[i+1])
+ add x23,x23,x16 // h+=Sigma1(e)
+ eor x28,x28,x25 // Maj(a,b,c)
+ eor x17,x15,x24,ror#39 // Sigma0(a)
+ eor x13,x13,x6,lsr#6 // sigma1(X[i+14])
+ add x8,x8,x1
+ add x27,x27,x23 // d+=h
+ add x23,x23,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x8,x8,x14
+ add x23,x23,x17 // h+=Sigma0(a)
+ add x8,x8,x13
+ ldr x13,[sp,#16]
+ str x0,[sp,#8]
+ ror x16,x27,#14
+ add x22,x22,x28 // h+=K[i]
+ ror x15,x10,#1
+ and x17,x20,x27
+ ror x14,x7,#19
+ bic x28,x21,x27
+ ror x0,x23,#28
+ add x22,x22,x8 // h+=X[i]
+ eor x16,x16,x27,ror#18
+ eor x15,x15,x10,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x23,x24 // a^b, b^c in next round
+ eor x16,x16,x27,ror#41 // Sigma1(e)
+ eor x0,x0,x23,ror#34
+ add x22,x22,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x14,x14,x7,ror#61
+ eor x15,x15,x10,lsr#7 // sigma0(X[i+1])
+ add x22,x22,x16 // h+=Sigma1(e)
+ eor x19,x19,x24 // Maj(a,b,c)
+ eor x17,x0,x23,ror#39 // Sigma0(a)
+ eor x14,x14,x7,lsr#6 // sigma1(X[i+14])
+ add x9,x9,x2
+ add x26,x26,x22 // d+=h
+ add x22,x22,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x9,x9,x15
+ add x22,x22,x17 // h+=Sigma0(a)
+ add x9,x9,x14
+ ldr x14,[sp,#24]
+ str x1,[sp,#16]
+ ror x16,x26,#14
+ add x21,x21,x19 // h+=K[i]
+ ror x0,x11,#1
+ and x17,x27,x26
+ ror x15,x8,#19
+ bic x19,x20,x26
+ ror x1,x22,#28
+ add x21,x21,x9 // h+=X[i]
+ eor x16,x16,x26,ror#18
+ eor x0,x0,x11,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x22,x23 // a^b, b^c in next round
+ eor x16,x16,x26,ror#41 // Sigma1(e)
+ eor x1,x1,x22,ror#34
+ add x21,x21,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x15,x15,x8,ror#61
+ eor x0,x0,x11,lsr#7 // sigma0(X[i+1])
+ add x21,x21,x16 // h+=Sigma1(e)
+ eor x28,x28,x23 // Maj(a,b,c)
+ eor x17,x1,x22,ror#39 // Sigma0(a)
+ eor x15,x15,x8,lsr#6 // sigma1(X[i+14])
+ add x10,x10,x3
+ add x25,x25,x21 // d+=h
+ add x21,x21,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x10,x10,x0
+ add x21,x21,x17 // h+=Sigma0(a)
+ add x10,x10,x15
+ ldr x15,[sp,#0]
+ str x2,[sp,#24]
+ ror x16,x25,#14
+ add x20,x20,x28 // h+=K[i]
+ ror x1,x12,#1
+ and x17,x26,x25
+ ror x0,x9,#19
+ bic x28,x27,x25
+ ror x2,x21,#28
+ add x20,x20,x10 // h+=X[i]
+ eor x16,x16,x25,ror#18
+ eor x1,x1,x12,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x21,x22 // a^b, b^c in next round
+ eor x16,x16,x25,ror#41 // Sigma1(e)
+ eor x2,x2,x21,ror#34
+ add x20,x20,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x0,x0,x9,ror#61
+ eor x1,x1,x12,lsr#7 // sigma0(X[i+1])
+ add x20,x20,x16 // h+=Sigma1(e)
+ eor x19,x19,x22 // Maj(a,b,c)
+ eor x17,x2,x21,ror#39 // Sigma0(a)
+ eor x0,x0,x9,lsr#6 // sigma1(X[i+14])
+ add x11,x11,x4
+ add x24,x24,x20 // d+=h
+ add x20,x20,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x11,x11,x1
+ add x20,x20,x17 // h+=Sigma0(a)
+ add x11,x11,x0
+ ldr x0,[sp,#8]
+ str x3,[sp,#0]
+ ror x16,x24,#14
+ add x27,x27,x19 // h+=K[i]
+ ror x2,x13,#1
+ and x17,x25,x24
+ ror x1,x10,#19
+ bic x19,x26,x24
+ ror x3,x20,#28
+ add x27,x27,x11 // h+=X[i]
+ eor x16,x16,x24,ror#18
+ eor x2,x2,x13,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x20,x21 // a^b, b^c in next round
+ eor x16,x16,x24,ror#41 // Sigma1(e)
+ eor x3,x3,x20,ror#34
+ add x27,x27,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x1,x1,x10,ror#61
+ eor x2,x2,x13,lsr#7 // sigma0(X[i+1])
+ add x27,x27,x16 // h+=Sigma1(e)
+ eor x28,x28,x21 // Maj(a,b,c)
+ eor x17,x3,x20,ror#39 // Sigma0(a)
+ eor x1,x1,x10,lsr#6 // sigma1(X[i+14])
+ add x12,x12,x5
+ add x23,x23,x27 // d+=h
+ add x27,x27,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x12,x12,x2
+ add x27,x27,x17 // h+=Sigma0(a)
+ add x12,x12,x1
+ ldr x1,[sp,#16]
+ str x4,[sp,#8]
+ ror x16,x23,#14
+ add x26,x26,x28 // h+=K[i]
+ ror x3,x14,#1
+ and x17,x24,x23
+ ror x2,x11,#19
+ bic x28,x25,x23
+ ror x4,x27,#28
+ add x26,x26,x12 // h+=X[i]
+ eor x16,x16,x23,ror#18
+ eor x3,x3,x14,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x27,x20 // a^b, b^c in next round
+ eor x16,x16,x23,ror#41 // Sigma1(e)
+ eor x4,x4,x27,ror#34
+ add x26,x26,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x2,x2,x11,ror#61
+ eor x3,x3,x14,lsr#7 // sigma0(X[i+1])
+ add x26,x26,x16 // h+=Sigma1(e)
+ eor x19,x19,x20 // Maj(a,b,c)
+ eor x17,x4,x27,ror#39 // Sigma0(a)
+ eor x2,x2,x11,lsr#6 // sigma1(X[i+14])
+ add x13,x13,x6
+ add x22,x22,x26 // d+=h
+ add x26,x26,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x13,x13,x3
+ add x26,x26,x17 // h+=Sigma0(a)
+ add x13,x13,x2
+ ldr x2,[sp,#24]
+ str x5,[sp,#16]
+ ror x16,x22,#14
+ add x25,x25,x19 // h+=K[i]
+ ror x4,x15,#1
+ and x17,x23,x22
+ ror x3,x12,#19
+ bic x19,x24,x22
+ ror x5,x26,#28
+ add x25,x25,x13 // h+=X[i]
+ eor x16,x16,x22,ror#18
+ eor x4,x4,x15,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x26,x27 // a^b, b^c in next round
+ eor x16,x16,x22,ror#41 // Sigma1(e)
+ eor x5,x5,x26,ror#34
+ add x25,x25,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x3,x3,x12,ror#61
+ eor x4,x4,x15,lsr#7 // sigma0(X[i+1])
+ add x25,x25,x16 // h+=Sigma1(e)
+ eor x28,x28,x27 // Maj(a,b,c)
+ eor x17,x5,x26,ror#39 // Sigma0(a)
+ eor x3,x3,x12,lsr#6 // sigma1(X[i+14])
+ add x14,x14,x7
+ add x21,x21,x25 // d+=h
+ add x25,x25,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x14,x14,x4
+ add x25,x25,x17 // h+=Sigma0(a)
+ add x14,x14,x3
+ ldr x3,[sp,#0]
+ str x6,[sp,#24]
+ ror x16,x21,#14
+ add x24,x24,x28 // h+=K[i]
+ ror x5,x0,#1
+ and x17,x22,x21
+ ror x4,x13,#19
+ bic x28,x23,x21
+ ror x6,x25,#28
+ add x24,x24,x14 // h+=X[i]
+ eor x16,x16,x21,ror#18
+ eor x5,x5,x0,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x25,x26 // a^b, b^c in next round
+ eor x16,x16,x21,ror#41 // Sigma1(e)
+ eor x6,x6,x25,ror#34
+ add x24,x24,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x4,x4,x13,ror#61
+ eor x5,x5,x0,lsr#7 // sigma0(X[i+1])
+ add x24,x24,x16 // h+=Sigma1(e)
+ eor x19,x19,x26 // Maj(a,b,c)
+ eor x17,x6,x25,ror#39 // Sigma0(a)
+ eor x4,x4,x13,lsr#6 // sigma1(X[i+14])
+ add x15,x15,x8
+ add x20,x20,x24 // d+=h
+ add x24,x24,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x15,x15,x5
+ add x24,x24,x17 // h+=Sigma0(a)
+ add x15,x15,x4
+ ldr x4,[sp,#8]
+ str x7,[sp,#0]
+ ror x16,x20,#14
+ add x23,x23,x19 // h+=K[i]
+ ror x6,x1,#1
+ and x17,x21,x20
+ ror x5,x14,#19
+ bic x19,x22,x20
+ ror x7,x24,#28
+ add x23,x23,x15 // h+=X[i]
+ eor x16,x16,x20,ror#18
+ eor x6,x6,x1,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x24,x25 // a^b, b^c in next round
+ eor x16,x16,x20,ror#41 // Sigma1(e)
+ eor x7,x7,x24,ror#34
+ add x23,x23,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x5,x5,x14,ror#61
+ eor x6,x6,x1,lsr#7 // sigma0(X[i+1])
+ add x23,x23,x16 // h+=Sigma1(e)
+ eor x28,x28,x25 // Maj(a,b,c)
+ eor x17,x7,x24,ror#39 // Sigma0(a)
+ eor x5,x5,x14,lsr#6 // sigma1(X[i+14])
+ add x0,x0,x9
+ add x27,x27,x23 // d+=h
+ add x23,x23,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x0,x0,x6
+ add x23,x23,x17 // h+=Sigma0(a)
+ add x0,x0,x5
+ ldr x5,[sp,#16]
+ str x8,[sp,#8]
+ ror x16,x27,#14
+ add x22,x22,x28 // h+=K[i]
+ ror x7,x2,#1
+ and x17,x20,x27
+ ror x6,x15,#19
+ bic x28,x21,x27
+ ror x8,x23,#28
+ add x22,x22,x0 // h+=X[i]
+ eor x16,x16,x27,ror#18
+ eor x7,x7,x2,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x23,x24 // a^b, b^c in next round
+ eor x16,x16,x27,ror#41 // Sigma1(e)
+ eor x8,x8,x23,ror#34
+ add x22,x22,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x6,x6,x15,ror#61
+ eor x7,x7,x2,lsr#7 // sigma0(X[i+1])
+ add x22,x22,x16 // h+=Sigma1(e)
+ eor x19,x19,x24 // Maj(a,b,c)
+ eor x17,x8,x23,ror#39 // Sigma0(a)
+ eor x6,x6,x15,lsr#6 // sigma1(X[i+14])
+ add x1,x1,x10
+ add x26,x26,x22 // d+=h
+ add x22,x22,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x1,x1,x7
+ add x22,x22,x17 // h+=Sigma0(a)
+ add x1,x1,x6
+ ldr x6,[sp,#24]
+ str x9,[sp,#16]
+ ror x16,x26,#14
+ add x21,x21,x19 // h+=K[i]
+ ror x8,x3,#1
+ and x17,x27,x26
+ ror x7,x0,#19
+ bic x19,x20,x26
+ ror x9,x22,#28
+ add x21,x21,x1 // h+=X[i]
+ eor x16,x16,x26,ror#18
+ eor x8,x8,x3,ror#8
+ orr x17,x17,x19 // Ch(e,f,g)
+ eor x19,x22,x23 // a^b, b^c in next round
+ eor x16,x16,x26,ror#41 // Sigma1(e)
+ eor x9,x9,x22,ror#34
+ add x21,x21,x17 // h+=Ch(e,f,g)
+ and x28,x28,x19 // (b^c)&=(a^b)
+ eor x7,x7,x0,ror#61
+ eor x8,x8,x3,lsr#7 // sigma0(X[i+1])
+ add x21,x21,x16 // h+=Sigma1(e)
+ eor x28,x28,x23 // Maj(a,b,c)
+ eor x17,x9,x22,ror#39 // Sigma0(a)
+ eor x7,x7,x0,lsr#6 // sigma1(X[i+14])
+ add x2,x2,x11
+ add x25,x25,x21 // d+=h
+ add x21,x21,x28 // h+=Maj(a,b,c)
+ ldr x28,[x30],#8 // *K++, x19 in next round
+ add x2,x2,x8
+ add x21,x21,x17 // h+=Sigma0(a)
+ add x2,x2,x7
+ ldr x7,[sp,#0]
+ str x10,[sp,#24]
+ ror x16,x25,#14
+ add x20,x20,x28 // h+=K[i]
+ ror x9,x4,#1
+ and x17,x26,x25
+ ror x8,x1,#19
+ bic x28,x27,x25
+ ror x10,x21,#28
+ add x20,x20,x2 // h+=X[i]
+ eor x16,x16,x25,ror#18
+ eor x9,x9,x4,ror#8
+ orr x17,x17,x28 // Ch(e,f,g)
+ eor x28,x21,x22 // a^b, b^c in next round
+ eor x16,x16,x25,ror#41 // Sigma1(e)
+ eor x10,x10,x21,ror#34
+ add x20,x20,x17 // h+=Ch(e,f,g)
+ and x19,x19,x28 // (b^c)&=(a^b)
+ eor x8,x8,x1,ror#61
+ eor x9,x9,x4,lsr#7 // sigma0(X[i+1])
+ add x20,x20,x16 // h+=Sigma1(e)
+ eor x19,x19,x22 // Maj(a,b,c)
+ eor x17,x10,x21,ror#39 // Sigma0(a)
+ eor x8,x8,x1,lsr#6 // sigma1(X[i+14])
+ add x3,x3,x12
+ add x24,x24,x20 // d+=h
+ add x20,x20,x19 // h+=Maj(a,b,c)
+ ldr x19,[x30],#8 // *K++, x28 in next round
+ add x3,x3,x9
+ add x20,x20,x17 // h+=Sigma0(a)
+ add x3,x3,x8
+ cbnz x19,.Loop_16_xx
+
+ ldp x0,x2,[x29,#96]
+ ldr x1,[x29,#112]
+ sub x30,x30,#648 // rewind
+
+ ldp x3,x4,[x0]
+ ldp x5,x6,[x0,#2*8]
+ add x1,x1,#14*8 // advance input pointer
+ ldp x7,x8,[x0,#4*8]
+ add x20,x20,x3
+ ldp x9,x10,[x0,#6*8]
+ add x21,x21,x4
+ add x22,x22,x5
+ add x23,x23,x6
+ stp x20,x21,[x0]
+ add x24,x24,x7
+ add x25,x25,x8
+ stp x22,x23,[x0,#2*8]
+ add x26,x26,x9
+ add x27,x27,x10
+ cmp x1,x2
+ stp x24,x25,[x0,#4*8]
+ stp x26,x27,[x0,#6*8]
+ b.ne .Loop
+
+ ldp x19,x20,[x29,#16]
+ add sp,sp,#4*8
+ ldp x21,x22,[x29,#32]
+ ldp x23,x24,[x29,#48]
+ ldp x25,x26,[x29,#64]
+ ldp x27,x28,[x29,#80]
+ ldp x29,x30,[sp],#128
+ ret
+.size sha512_block_data_order,.-sha512_block_data_order
+
+.align 6
+.type .LK512,%object
+.LK512:
+ .quad 0x428a2f98d728ae22,0x7137449123ef65cd
+ .quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
+ .quad 0x3956c25bf348b538,0x59f111f1b605d019
+ .quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118
+ .quad 0xd807aa98a3030242,0x12835b0145706fbe
+ .quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
+ .quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1
+ .quad 0x9bdc06a725c71235,0xc19bf174cf692694
+ .quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3
+ .quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
+ .quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483
+ .quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5
+ .quad 0x983e5152ee66dfab,0xa831c66d2db43210
+ .quad 0xb00327c898fb213f,0xbf597fc7beef0ee4
+ .quad 0xc6e00bf33da88fc2,0xd5a79147930aa725
+ .quad 0x06ca6351e003826f,0x142929670a0e6e70
+ .quad 0x27b70a8546d22ffc,0x2e1b21385c26c926
+ .quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df
+ .quad 0x650a73548baf63de,0x766a0abb3c77b2a8
+ .quad 0x81c2c92e47edaee6,0x92722c851482353b
+ .quad 0xa2bfe8a14cf10364,0xa81a664bbc423001
+ .quad 0xc24b8b70d0f89791,0xc76c51a30654be30
+ .quad 0xd192e819d6ef5218,0xd69906245565a910
+ .quad 0xf40e35855771202a,0x106aa07032bbd1b8
+ .quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53
+ .quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
+ .quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
+ .quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
+ .quad 0x748f82ee5defb2fc,0x78a5636f43172f60
+ .quad 0x84c87814a1f0ab72,0x8cc702081a6439ec
+ .quad 0x90befffa23631e28,0xa4506cebde82bde9
+ .quad 0xbef9a3f7b2c67915,0xc67178f2e372532b
+ .quad 0xca273eceea26619c,0xd186b8c721c0c207
+ .quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
+ .quad 0x06f067aa72176fba,0x0a637dc5a2c898a6
+ .quad 0x113f9804bef90dae,0x1b710b35131c471b
+ .quad 0x28db77f523047d84,0x32caab7b40c72493
+ .quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
+ .quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a
+ .quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817
+ .quad 0 // terminator
+.size .LK512,.-.LK512
+#ifndef __KERNEL__
+.align 3
+.LOPENSSL_armcap_P:
+# ifdef __ILP32__
+ .long OPENSSL_armcap_P-.
+# else
+ .quad OPENSSL_armcap_P-.
+# endif
+#endif
+.asciz "SHA512 block transform for ARMv8, CRYPTOGAMS by <appro@openssl.org>"
+.align 2
+#ifndef __KERNEL__
+.comm OPENSSL_armcap_P,4,4
+#endif
diff --git a/arch/arm64/crypto/speck-neon-core.S b/arch/arm64/crypto/speck-neon-core.S
new file mode 100644
index 0000000..b144634
--- /dev/null
+++ b/arch/arm64/crypto/speck-neon-core.S
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM64 NEON-accelerated implementation of Speck128-XTS and Speck64-XTS
+ *
+ * Copyright (c) 2018 Google, Inc
+ *
+ * Author: Eric Biggers <ebiggers@google.com>
+ */
+
+#include <linux/linkage.h>
+
+ .text
+
+ // arguments
+ ROUND_KEYS .req x0 // const {u64,u32} *round_keys
+ NROUNDS .req w1 // int nrounds
+ NROUNDS_X .req x1
+ DST .req x2 // void *dst
+ SRC .req x3 // const void *src
+ NBYTES .req w4 // unsigned int nbytes
+ TWEAK .req x5 // void *tweak
+
+ // registers which hold the data being encrypted/decrypted
+ // (underscores avoid a naming collision with ARM64 registers x0-x3)
+ X_0 .req v0
+ Y_0 .req v1
+ X_1 .req v2
+ Y_1 .req v3
+ X_2 .req v4
+ Y_2 .req v5
+ X_3 .req v6
+ Y_3 .req v7
+
+ // the round key, duplicated in all lanes
+ ROUND_KEY .req v8
+
+ // index vector for tbl-based 8-bit rotates
+ ROTATE_TABLE .req v9
+ ROTATE_TABLE_Q .req q9
+
+ // temporary registers
+ TMP0 .req v10
+ TMP1 .req v11
+ TMP2 .req v12
+ TMP3 .req v13
+
+ // multiplication table for updating XTS tweaks
+ GFMUL_TABLE .req v14
+ GFMUL_TABLE_Q .req q14
+
+ // next XTS tweak value(s)
+ TWEAKV_NEXT .req v15
+
+ // XTS tweaks for the blocks currently being encrypted/decrypted
+ TWEAKV0 .req v16
+ TWEAKV1 .req v17
+ TWEAKV2 .req v18
+ TWEAKV3 .req v19
+ TWEAKV4 .req v20
+ TWEAKV5 .req v21
+ TWEAKV6 .req v22
+ TWEAKV7 .req v23
+
+ .align 4
+.Lror64_8_table:
+ .octa 0x080f0e0d0c0b0a090007060504030201
+.Lror32_8_table:
+ .octa 0x0c0f0e0d080b0a090407060500030201
+.Lrol64_8_table:
+ .octa 0x0e0d0c0b0a09080f0605040302010007
+.Lrol32_8_table:
+ .octa 0x0e0d0c0f0a09080b0605040702010003
+.Lgf128mul_table:
+ .octa 0x00000000000000870000000000000001
+.Lgf64mul_table:
+ .octa 0x0000000000000000000000002d361b00
+
+/*
+ * _speck_round_128bytes() - Speck encryption round on 128 bytes at a time
+ *
+ * Do one Speck encryption round on the 128 bytes (8 blocks for Speck128, 16 for
+ * Speck64) stored in X0-X3 and Y0-Y3, using the round key stored in all lanes
+ * of ROUND_KEY. 'n' is the lane size: 64 for Speck128, or 32 for Speck64.
+ * 'lanes' is the lane specifier: "2d" for Speck128 or "4s" for Speck64.
+ */
+.macro _speck_round_128bytes n, lanes
+
+ // x = ror(x, 8)
+ tbl X_0.16b, {X_0.16b}, ROTATE_TABLE.16b
+ tbl X_1.16b, {X_1.16b}, ROTATE_TABLE.16b
+ tbl X_2.16b, {X_2.16b}, ROTATE_TABLE.16b
+ tbl X_3.16b, {X_3.16b}, ROTATE_TABLE.16b
+
+ // x += y
+ add X_0.\lanes, X_0.\lanes, Y_0.\lanes
+ add X_1.\lanes, X_1.\lanes, Y_1.\lanes
+ add X_2.\lanes, X_2.\lanes, Y_2.\lanes
+ add X_3.\lanes, X_3.\lanes, Y_3.\lanes
+
+ // x ^= k
+ eor X_0.16b, X_0.16b, ROUND_KEY.16b
+ eor X_1.16b, X_1.16b, ROUND_KEY.16b
+ eor X_2.16b, X_2.16b, ROUND_KEY.16b
+ eor X_3.16b, X_3.16b, ROUND_KEY.16b
+
+ // y = rol(y, 3)
+ shl TMP0.\lanes, Y_0.\lanes, #3
+ shl TMP1.\lanes, Y_1.\lanes, #3
+ shl TMP2.\lanes, Y_2.\lanes, #3
+ shl TMP3.\lanes, Y_3.\lanes, #3
+ sri TMP0.\lanes, Y_0.\lanes, #(\n - 3)
+ sri TMP1.\lanes, Y_1.\lanes, #(\n - 3)
+ sri TMP2.\lanes, Y_2.\lanes, #(\n - 3)
+ sri TMP3.\lanes, Y_3.\lanes, #(\n - 3)
+
+ // y ^= x
+ eor Y_0.16b, TMP0.16b, X_0.16b
+ eor Y_1.16b, TMP1.16b, X_1.16b
+ eor Y_2.16b, TMP2.16b, X_2.16b
+ eor Y_3.16b, TMP3.16b, X_3.16b
+.endm
+
+/*
+ * _speck_unround_128bytes() - Speck decryption round on 128 bytes at a time
+ *
+ * This is the inverse of _speck_round_128bytes().
+ */
+.macro _speck_unround_128bytes n, lanes
+
+ // y ^= x
+ eor TMP0.16b, Y_0.16b, X_0.16b
+ eor TMP1.16b, Y_1.16b, X_1.16b
+ eor TMP2.16b, Y_2.16b, X_2.16b
+ eor TMP3.16b, Y_3.16b, X_3.16b
+
+ // y = ror(y, 3)
+ ushr Y_0.\lanes, TMP0.\lanes, #3
+ ushr Y_1.\lanes, TMP1.\lanes, #3
+ ushr Y_2.\lanes, TMP2.\lanes, #3
+ ushr Y_3.\lanes, TMP3.\lanes, #3
+ sli Y_0.\lanes, TMP0.\lanes, #(\n - 3)
+ sli Y_1.\lanes, TMP1.\lanes, #(\n - 3)
+ sli Y_2.\lanes, TMP2.\lanes, #(\n - 3)
+ sli Y_3.\lanes, TMP3.\lanes, #(\n - 3)
+
+ // x ^= k
+ eor X_0.16b, X_0.16b, ROUND_KEY.16b
+ eor X_1.16b, X_1.16b, ROUND_KEY.16b
+ eor X_2.16b, X_2.16b, ROUND_KEY.16b
+ eor X_3.16b, X_3.16b, ROUND_KEY.16b
+
+ // x -= y
+ sub X_0.\lanes, X_0.\lanes, Y_0.\lanes
+ sub X_1.\lanes, X_1.\lanes, Y_1.\lanes
+ sub X_2.\lanes, X_2.\lanes, Y_2.\lanes
+ sub X_3.\lanes, X_3.\lanes, Y_3.\lanes
+
+ // x = rol(x, 8)
+ tbl X_0.16b, {X_0.16b}, ROTATE_TABLE.16b
+ tbl X_1.16b, {X_1.16b}, ROTATE_TABLE.16b
+ tbl X_2.16b, {X_2.16b}, ROTATE_TABLE.16b
+ tbl X_3.16b, {X_3.16b}, ROTATE_TABLE.16b
+.endm
+
+.macro _next_xts_tweak next, cur, tmp, n
+.if \n == 64
+ /*
+ * Calculate the next tweak by multiplying the current one by x,
+ * modulo p(x) = x^128 + x^7 + x^2 + x + 1.
+ */
+ sshr \tmp\().2d, \cur\().2d, #63
+ and \tmp\().16b, \tmp\().16b, GFMUL_TABLE.16b
+ shl \next\().2d, \cur\().2d, #1
+ ext \tmp\().16b, \tmp\().16b, \tmp\().16b, #8
+ eor \next\().16b, \next\().16b, \tmp\().16b
+.else
+ /*
+ * Calculate the next two tweaks by multiplying the current ones by x^2,
+ * modulo p(x) = x^64 + x^4 + x^3 + x + 1.
+ */
+ ushr \tmp\().2d, \cur\().2d, #62
+ shl \next\().2d, \cur\().2d, #2
+ tbl \tmp\().16b, {GFMUL_TABLE.16b}, \tmp\().16b
+ eor \next\().16b, \next\().16b, \tmp\().16b
+.endif
+.endm
+
+/*
+ * _speck_xts_crypt() - Speck-XTS encryption/decryption
+ *
+ * Encrypt or decrypt NBYTES bytes of data from the SRC buffer to the DST buffer
+ * using Speck-XTS, specifically the variant with a block size of '2n' and round
+ * count given by NROUNDS. The expanded round keys are given in ROUND_KEYS, and
+ * the current XTS tweak value is given in TWEAK. It's assumed that NBYTES is a
+ * nonzero multiple of 128.
+ */
+.macro _speck_xts_crypt n, lanes, decrypting
+
+ /*
+ * If decrypting, modify the ROUND_KEYS parameter to point to the last
+ * round key rather than the first, since for decryption the round keys
+ * are used in reverse order.
+ */
+.if \decrypting
+ mov NROUNDS, NROUNDS /* zero the high 32 bits */
+.if \n == 64
+ add ROUND_KEYS, ROUND_KEYS, NROUNDS_X, lsl #3
+ sub ROUND_KEYS, ROUND_KEYS, #8
+.else
+ add ROUND_KEYS, ROUND_KEYS, NROUNDS_X, lsl #2
+ sub ROUND_KEYS, ROUND_KEYS, #4
+.endif
+.endif
+
+ // Load the index vector for tbl-based 8-bit rotates
+.if \decrypting
+ ldr ROTATE_TABLE_Q, .Lrol\n\()_8_table
+.else
+ ldr ROTATE_TABLE_Q, .Lror\n\()_8_table
+.endif
+
+ // One-time XTS preparation
+.if \n == 64
+ // Load first tweak
+ ld1 {TWEAKV0.16b}, [TWEAK]
+
+ // Load GF(2^128) multiplication table
+ ldr GFMUL_TABLE_Q, .Lgf128mul_table
+.else
+ // Load first tweak
+ ld1 {TWEAKV0.8b}, [TWEAK]
+
+ // Load GF(2^64) multiplication table
+ ldr GFMUL_TABLE_Q, .Lgf64mul_table
+
+ // Calculate second tweak, packing it together with the first
+ ushr TMP0.2d, TWEAKV0.2d, #63
+ shl TMP1.2d, TWEAKV0.2d, #1
+ tbl TMP0.8b, {GFMUL_TABLE.16b}, TMP0.8b
+ eor TMP0.8b, TMP0.8b, TMP1.8b
+ mov TWEAKV0.d[1], TMP0.d[0]
+.endif
+
+.Lnext_128bytes_\@:
+
+ // Calculate XTS tweaks for next 128 bytes
+ _next_xts_tweak TWEAKV1, TWEAKV0, TMP0, \n
+ _next_xts_tweak TWEAKV2, TWEAKV1, TMP0, \n
+ _next_xts_tweak TWEAKV3, TWEAKV2, TMP0, \n
+ _next_xts_tweak TWEAKV4, TWEAKV3, TMP0, \n
+ _next_xts_tweak TWEAKV5, TWEAKV4, TMP0, \n
+ _next_xts_tweak TWEAKV6, TWEAKV5, TMP0, \n
+ _next_xts_tweak TWEAKV7, TWEAKV6, TMP0, \n
+ _next_xts_tweak TWEAKV_NEXT, TWEAKV7, TMP0, \n
+
+ // Load the next source blocks into {X,Y}[0-3]
+ ld1 {X_0.16b-Y_1.16b}, [SRC], #64
+ ld1 {X_2.16b-Y_3.16b}, [SRC], #64
+
+ // XOR the source blocks with their XTS tweaks
+ eor TMP0.16b, X_0.16b, TWEAKV0.16b
+ eor Y_0.16b, Y_0.16b, TWEAKV1.16b
+ eor TMP1.16b, X_1.16b, TWEAKV2.16b
+ eor Y_1.16b, Y_1.16b, TWEAKV3.16b
+ eor TMP2.16b, X_2.16b, TWEAKV4.16b
+ eor Y_2.16b, Y_2.16b, TWEAKV5.16b
+ eor TMP3.16b, X_3.16b, TWEAKV6.16b
+ eor Y_3.16b, Y_3.16b, TWEAKV7.16b
+
+ /*
+ * De-interleave the 'x' and 'y' elements of each block, i.e. make it so
+ * that the X[0-3] registers contain only the second halves of blocks,
+ * and the Y[0-3] registers contain only the first halves of blocks.
+ * (Speck uses the order (y, x) rather than the more intuitive (x, y).)
+ */
+ uzp2 X_0.\lanes, TMP0.\lanes, Y_0.\lanes
+ uzp1 Y_0.\lanes, TMP0.\lanes, Y_0.\lanes
+ uzp2 X_1.\lanes, TMP1.\lanes, Y_1.\lanes
+ uzp1 Y_1.\lanes, TMP1.\lanes, Y_1.\lanes
+ uzp2 X_2.\lanes, TMP2.\lanes, Y_2.\lanes
+ uzp1 Y_2.\lanes, TMP2.\lanes, Y_2.\lanes
+ uzp2 X_3.\lanes, TMP3.\lanes, Y_3.\lanes
+ uzp1 Y_3.\lanes, TMP3.\lanes, Y_3.\lanes
+
+ // Do the cipher rounds
+ mov x6, ROUND_KEYS
+ mov w7, NROUNDS
+.Lnext_round_\@:
+.if \decrypting
+ ld1r {ROUND_KEY.\lanes}, [x6]
+ sub x6, x6, #( \n / 8 )
+ _speck_unround_128bytes \n, \lanes
+.else
+ ld1r {ROUND_KEY.\lanes}, [x6], #( \n / 8 )
+ _speck_round_128bytes \n, \lanes
+.endif
+ subs w7, w7, #1
+ bne .Lnext_round_\@
+
+ // Re-interleave the 'x' and 'y' elements of each block
+ zip1 TMP0.\lanes, Y_0.\lanes, X_0.\lanes
+ zip2 Y_0.\lanes, Y_0.\lanes, X_0.\lanes
+ zip1 TMP1.\lanes, Y_1.\lanes, X_1.\lanes
+ zip2 Y_1.\lanes, Y_1.\lanes, X_1.\lanes
+ zip1 TMP2.\lanes, Y_2.\lanes, X_2.\lanes
+ zip2 Y_2.\lanes, Y_2.\lanes, X_2.\lanes
+ zip1 TMP3.\lanes, Y_3.\lanes, X_3.\lanes
+ zip2 Y_3.\lanes, Y_3.\lanes, X_3.\lanes
+
+ // XOR the encrypted/decrypted blocks with the tweaks calculated earlier
+ eor X_0.16b, TMP0.16b, TWEAKV0.16b
+ eor Y_0.16b, Y_0.16b, TWEAKV1.16b
+ eor X_1.16b, TMP1.16b, TWEAKV2.16b
+ eor Y_1.16b, Y_1.16b, TWEAKV3.16b
+ eor X_2.16b, TMP2.16b, TWEAKV4.16b
+ eor Y_2.16b, Y_2.16b, TWEAKV5.16b
+ eor X_3.16b, TMP3.16b, TWEAKV6.16b
+ eor Y_3.16b, Y_3.16b, TWEAKV7.16b
+ mov TWEAKV0.16b, TWEAKV_NEXT.16b
+
+ // Store the ciphertext in the destination buffer
+ st1 {X_0.16b-Y_1.16b}, [DST], #64
+ st1 {X_2.16b-Y_3.16b}, [DST], #64
+
+ // Continue if there are more 128-byte chunks remaining
+ subs NBYTES, NBYTES, #128
+ bne .Lnext_128bytes_\@
+
+ // Store the next tweak and return
+.if \n == 64
+ st1 {TWEAKV_NEXT.16b}, [TWEAK]
+.else
+ st1 {TWEAKV_NEXT.8b}, [TWEAK]
+.endif
+ ret
+.endm
+
+ENTRY(speck128_xts_encrypt_neon)
+ _speck_xts_crypt n=64, lanes=2d, decrypting=0
+ENDPROC(speck128_xts_encrypt_neon)
+
+ENTRY(speck128_xts_decrypt_neon)
+ _speck_xts_crypt n=64, lanes=2d, decrypting=1
+ENDPROC(speck128_xts_decrypt_neon)
+
+ENTRY(speck64_xts_encrypt_neon)
+ _speck_xts_crypt n=32, lanes=4s, decrypting=0
+ENDPROC(speck64_xts_encrypt_neon)
+
+ENTRY(speck64_xts_decrypt_neon)
+ _speck_xts_crypt n=32, lanes=4s, decrypting=1
+ENDPROC(speck64_xts_decrypt_neon)
diff --git a/arch/arm64/crypto/speck-neon-glue.c b/arch/arm64/crypto/speck-neon-glue.c
new file mode 100644
index 0000000..c7d1856
--- /dev/null
+++ b/arch/arm64/crypto/speck-neon-glue.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NEON-accelerated implementation of Speck128-XTS and Speck64-XTS
+ * (64-bit version; based on the 32-bit version)
+ *
+ * Copyright (c) 2018 Google, Inc
+ */
+
+#include <asm/hwcap.h>
+#include <asm/neon.h>
+#include <asm/simd.h>
+#include <crypto/algapi.h>
+#include <crypto/gf128mul.h>
+#include <crypto/speck.h>
+#include <crypto/xts.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+/* The assembly functions only handle multiples of 128 bytes */
+#define SPECK_NEON_CHUNK_SIZE 128
+
+/* Speck128 */
+
+struct speck128_xts_tfm_ctx {
+ struct speck128_tfm_ctx main_key;
+ struct speck128_tfm_ctx tweak_key;
+};
+
+asmlinkage void speck128_xts_encrypt_neon(const u64 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+asmlinkage void speck128_xts_decrypt_neon(const u64 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+typedef void (*speck128_crypt_one_t)(const struct speck128_tfm_ctx *,
+ u8 *, const u8 *);
+typedef void (*speck128_xts_crypt_many_t)(const u64 *, int, void *,
+ const void *, unsigned int, void *);
+
+static __always_inline int
+__speck128_xts_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes,
+ speck128_crypt_one_t crypt_one,
+ speck128_xts_crypt_many_t crypt_many)
+{
+ struct crypto_blkcipher *tfm = desc->tfm;
+ const struct speck128_xts_tfm_ctx *ctx = crypto_blkcipher_ctx(tfm);
+ struct blkcipher_walk walk;
+ le128 tweak;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt_block(desc, &walk, SPECK_NEON_CHUNK_SIZE);
+
+ crypto_speck128_encrypt(&ctx->tweak_key, (u8 *)&tweak, walk.iv);
+
+ while (walk.nbytes > 0) {
+ unsigned int nbytes = walk.nbytes;
+ u8 *dst = walk.dst.virt.addr;
+ const u8 *src = walk.src.virt.addr;
+
+ if (nbytes >= SPECK_NEON_CHUNK_SIZE && may_use_simd()) {
+ unsigned int count;
+
+ count = round_down(nbytes, SPECK_NEON_CHUNK_SIZE);
+ kernel_neon_begin();
+ (*crypt_many)(ctx->main_key.round_keys,
+ ctx->main_key.nrounds,
+ dst, src, count, &tweak);
+ kernel_neon_end();
+ dst += count;
+ src += count;
+ nbytes -= count;
+ }
+
+ /* Handle any remainder with generic code */
+ while (nbytes >= sizeof(tweak)) {
+ le128_xor((le128 *)dst, (const le128 *)src, &tweak);
+ (*crypt_one)(&ctx->main_key, dst, dst);
+ le128_xor((le128 *)dst, (const le128 *)dst, &tweak);
+ gf128mul_x_ble((be128 *)&tweak, (const be128 *)&tweak);
+
+ dst += sizeof(tweak);
+ src += sizeof(tweak);
+ nbytes -= sizeof(tweak);
+ }
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ return err;
+}
+
+static int speck128_xts_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck128_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck128_encrypt,
+ speck128_xts_encrypt_neon);
+}
+
+static int speck128_xts_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck128_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck128_decrypt,
+ speck128_xts_decrypt_neon);
+}
+
+static int speck128_xts_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct speck128_xts_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+ int err;
+
+ if (keylen % 2)
+ return -EINVAL;
+
+ keylen /= 2;
+
+ err = crypto_speck128_setkey(&ctx->main_key, key, keylen);
+ if (err)
+ return err;
+
+ return crypto_speck128_setkey(&ctx->tweak_key, key + keylen, keylen);
+}
+
+/* Speck64 */
+
+struct speck64_xts_tfm_ctx {
+ struct speck64_tfm_ctx main_key;
+ struct speck64_tfm_ctx tweak_key;
+};
+
+asmlinkage void speck64_xts_encrypt_neon(const u32 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+asmlinkage void speck64_xts_decrypt_neon(const u32 *round_keys, int nrounds,
+ void *dst, const void *src,
+ unsigned int nbytes, void *tweak);
+
+typedef void (*speck64_crypt_one_t)(const struct speck64_tfm_ctx *,
+ u8 *, const u8 *);
+typedef void (*speck64_xts_crypt_many_t)(const u32 *, int, void *,
+ const void *, unsigned int, void *);
+
+static __always_inline int
+__speck64_xts_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes,
+ speck64_crypt_one_t crypt_one,
+ speck64_xts_crypt_many_t crypt_many)
+{
+ struct crypto_blkcipher *tfm = desc->tfm;
+ const struct speck64_xts_tfm_ctx *ctx = crypto_blkcipher_ctx(tfm);
+ struct blkcipher_walk walk;
+ __le64 tweak;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt_block(desc, &walk, SPECK_NEON_CHUNK_SIZE);
+
+ crypto_speck64_encrypt(&ctx->tweak_key, (u8 *)&tweak, walk.iv);
+
+ while (walk.nbytes > 0) {
+ unsigned int nbytes = walk.nbytes;
+ u8 *dst = walk.dst.virt.addr;
+ const u8 *src = walk.src.virt.addr;
+
+ if (nbytes >= SPECK_NEON_CHUNK_SIZE && may_use_simd()) {
+ unsigned int count;
+
+ count = round_down(nbytes, SPECK_NEON_CHUNK_SIZE);
+ kernel_neon_begin();
+ (*crypt_many)(ctx->main_key.round_keys,
+ ctx->main_key.nrounds,
+ dst, src, count, &tweak);
+ kernel_neon_end();
+ dst += count;
+ src += count;
+ nbytes -= count;
+ }
+
+ /* Handle any remainder with generic code */
+ while (nbytes >= sizeof(tweak)) {
+ *(__le64 *)dst = *(__le64 *)src ^ tweak;
+ (*crypt_one)(&ctx->main_key, dst, dst);
+ *(__le64 *)dst ^= tweak;
+ tweak = cpu_to_le64((le64_to_cpu(tweak) << 1) ^
+ ((tweak & cpu_to_le64(1ULL << 63)) ?
+ 0x1B : 0));
+ dst += sizeof(tweak);
+ src += sizeof(tweak);
+ nbytes -= sizeof(tweak);
+ }
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ return err;
+}
+
+static int speck64_xts_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck64_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck64_encrypt,
+ speck64_xts_encrypt_neon);
+}
+
+static int speck64_xts_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ return __speck64_xts_crypt(desc, dst, src, nbytes,
+ crypto_speck64_decrypt,
+ speck64_xts_decrypt_neon);
+}
+
+static int speck64_xts_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct speck64_xts_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
+ int err;
+
+ if (keylen % 2)
+ return -EINVAL;
+
+ keylen /= 2;
+
+ err = crypto_speck64_setkey(&ctx->main_key, key, keylen);
+ if (err)
+ return err;
+
+ return crypto_speck64_setkey(&ctx->tweak_key, key + keylen, keylen);
+}
+
+static struct crypto_alg speck_algs[] = {
+ {
+ .cra_name = "xts(speck128)",
+ .cra_driver_name = "xts-speck128-neon",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_blocksize = SPECK128_BLOCK_SIZE,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_ctxsize = sizeof(struct speck128_xts_tfm_ctx),
+ .cra_alignmask = 7,
+ .cra_module = THIS_MODULE,
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = 2 * SPECK128_128_KEY_SIZE,
+ .max_keysize = 2 * SPECK128_256_KEY_SIZE,
+ .ivsize = SPECK128_BLOCK_SIZE,
+ .setkey = speck128_xts_setkey,
+ .encrypt = speck128_xts_encrypt,
+ .decrypt = speck128_xts_decrypt,
+ }
+ }
+ }, {
+ .cra_name = "xts(speck64)",
+ .cra_driver_name = "xts-speck64-neon",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_blocksize = SPECK64_BLOCK_SIZE,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_ctxsize = sizeof(struct speck64_xts_tfm_ctx),
+ .cra_alignmask = 7,
+ .cra_module = THIS_MODULE,
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = 2 * SPECK64_96_KEY_SIZE,
+ .max_keysize = 2 * SPECK64_128_KEY_SIZE,
+ .ivsize = SPECK64_BLOCK_SIZE,
+ .setkey = speck64_xts_setkey,
+ .encrypt = speck64_xts_encrypt,
+ .decrypt = speck64_xts_decrypt,
+ }
+ }
+ }
+};
+
+static int __init speck_neon_module_init(void)
+{
+ if (!(elf_hwcap & HWCAP_ASIMD))
+ return -ENODEV;
+ return crypto_register_algs(speck_algs, ARRAY_SIZE(speck_algs));
+}
+
+static void __exit speck_neon_module_exit(void)
+{
+ crypto_unregister_algs(speck_algs, ARRAY_SIZE(speck_algs));
+}
+
+module_init(speck_neon_module_init);
+module_exit(speck_neon_module_exit);
+
+MODULE_DESCRIPTION("Speck block cipher (NEON-accelerated)");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
+MODULE_ALIAS_CRYPTO("xts(speck128)");
+MODULE_ALIAS_CRYPTO("xts-speck128-neon");
+MODULE_ALIAS_CRYPTO("xts(speck64)");
+MODULE_ALIAS_CRYPTO("xts-speck64-neon");
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index f1ace59..f8b5c48 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -84,14 +84,20 @@
#define read_gicreg(r) \
({ \
u64 reg; \
- asm volatile("mrs_s %0, " __stringify(r) : "=r" (reg)); \
+ asm volatile(DEFINE_MRS_S \
+ "mrs_s %0, " __stringify(r) "\n" \
+ UNDEFINE_MRS_S \
+ : "=r" (reg)); \
reg; \
})
#define write_gicreg(v,r) \
do { \
u64 __val = (v); \
- asm volatile("msr_s " __stringify(r) ", %0" : : "r" (__val));\
+ asm volatile(DEFINE_MSR_S \
+ "msr_s " __stringify(r) ", %0\n" \
+ UNDEFINE_MSR_S \
+ : : "r" (__val)); \
} while (0)
/*
@@ -103,13 +109,19 @@
static inline void gic_write_eoir(u32 irq)
{
- asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" ((u64)irq));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_EOIR1_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)irq));
isb();
}
static inline void gic_write_dir(u32 irq)
{
- asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" ((u64)irq));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_DIR_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)irq));
isb();
}
@@ -117,7 +129,10 @@
{
u64 irqstat;
- asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+ asm volatile(DEFINE_MRS_S
+ "mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n"
+ UNDEFINE_MRS_S
+ : "=r" (irqstat));
dsb(sy);
return irqstat;
}
@@ -136,7 +151,9 @@
asm volatile(
"nop;nop;nop;nop\n\t"
"nop;nop;nop;nop\n\t"
+ DEFINE_MRS_S
"mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n\t"
+ UNDEFINE_MRS_S
"nop;nop;nop;nop"
: "=r" (irqstat));
mb();
@@ -146,26 +163,38 @@
static inline void gic_write_pmr(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_PMR_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
/* As per the architecture specification */
mb();
}
static inline void gic_write_ctlr(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_CTLR_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
isb();
}
static inline void gic_write_grpen1(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_GRPEN1_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
isb();
}
static inline void gic_write_sgi1r(u64 val)
{
- asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_SGI1R_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" (val));
/* As per the architecture specification */
mb();
}
@@ -174,22 +203,32 @@
{
u64 val;
- asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
+ asm volatile(DEFINE_MRS_S
+ "mrs_s %0, " __stringify(ICC_SRE_EL1) "\n"
+ UNDEFINE_MRS_S
+ : "=r" (val));
return val;
}
static inline void gic_write_sre(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" ((u64)val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_SRE_EL1) ", %0\n"
+ UNDEFINE_MSR_S
+ : : "r" ((u64)val));
isb();
}
static inline void gic_write_bpr1(u32 val)
{
- asm volatile("msr_s " __stringify(ICC_BPR1_EL1) ", %0" : : "r" (val));
+ asm volatile(DEFINE_MSR_S
+ "msr_s " __stringify(ICC_BPR1_EL1) ", %x0\n"
+ UNDEFINE_MSR_S
+ : : "rZ" (val));
}
#define gic_read_typer(c) readq_relaxed_no_log(c)
+#define gic_read_irouter(c) readq_relaxed_no_log(c)
#define gic_write_irouter(v, c) writeq_relaxed_no_log(v, c)
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..f95235a 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -148,8 +148,17 @@
static inline u64 arch_counter_get_cntvct(void)
{
+ u64 cval;
isb();
- return arch_timer_reg_read_stable(cntvct_el0);
+#if IS_ENABLED(CONFIG_MSM_TIMER_LEAP)
+#define L32_BITS 0x00000000FFFFFFFF
+ do {
+ cval = arch_timer_reg_read_stable(cntvct_el0);
+ } while ((cval & L32_BITS) == L32_BITS);
+#else
+ cval = arch_timer_reg_read_stable(cntvct_el0);
+#endif
+ return cval;
}
static inline int arch_timer_arch_init(void)
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 88a4d1e..c7749d8 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -109,6 +109,24 @@
.endm
/*
+ * Value prediction barrier
+ */
+ .macro csdb
+ hint #20
+ .endm
+
+/*
+ * Sanitise a 64-bit bounded index wrt speculation, returning zero if out
+ * of bounds.
+ */
+ .macro mask_nospec64, idx, limit, tmp
+ sub \tmp, \idx, \limit
+ bic \tmp, \tmp, \idx
+ and \idx, \idx, \tmp, asr #63
+ csdb
+ .endm
+
+/*
* NOP sequence
*/
.macro nops, num
@@ -492,4 +510,8 @@
.Ldone\@:
.endm
+ .macro pte_to_phys, phys, pte
+ and \phys, \pte, #(((1 << (48 - PAGE_SHIFT)) - 1) << PAGE_SHIFT)
+ .endm
+
#endif /* __ASM_ASSEMBLER_H */
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 0fe7e43..0b0755c 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -31,6 +31,8 @@
#define dmb(opt) asm volatile("dmb " #opt : : : "memory")
#define dsb(opt) asm volatile("dsb " #opt : : : "memory")
+#define csdb() asm volatile("hint #20" : : : "memory")
+
#define mb() dsb(sy)
#define rmb() dsb(ld)
#define wmb() dsb(st)
@@ -38,6 +40,27 @@
#define dma_rmb() dmb(oshld)
#define dma_wmb() dmb(oshst)
+/*
+ * Generate a mask for array_index__nospec() that is ~0UL when 0 <= idx < sz
+ * and 0 otherwise.
+ */
+#define array_index_mask_nospec array_index_mask_nospec
+static inline unsigned long array_index_mask_nospec(unsigned long idx,
+ unsigned long sz)
+{
+ unsigned long mask;
+
+ asm volatile(
+ " cmp %1, %2\n"
+ " sbc %0, xzr, xzr\n"
+ : "=r" (mask)
+ : "r" (idx), "Ir" (sz)
+ : "cc");
+
+ csdb();
+ return mask;
+}
+
#define __smp_mb() dmb(ish)
#define __smp_rmb() dmb(ishld)
#define __smp_wmb() dmb(ishst)
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 847bfe6..9a6b81a 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -92,6 +92,7 @@
#define CAVIUM_CPU_PART_THUNDERX 0x0A1
#define CAVIUM_CPU_PART_THUNDERX_81XX 0x0A2
+#define CAVIUM_CPU_PART_THUNDERX2 0x0AF
#define BRCM_CPU_PART_VULCAN 0x516
@@ -107,6 +108,8 @@
#define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX)
#define MIDR_KRYO2XX_GOLD \
MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO2XX_GOLD)
+#define MIDR_CAVIUM_THUNDERX2 MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX2)
+#define MIDR_BRCM_VULCAN MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_VULCAN)
#ifndef __ASSEMBLY__
diff --git a/arch/arm64/include/asm/dma-contiguous.h b/arch/arm64/include/asm/dma-contiguous.h
index f7e2c32..50f70a8 100644
--- a/arch/arm64/include/asm/dma-contiguous.h
+++ b/arch/arm64/include/asm/dma-contiguous.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013,2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013,2017-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,7 +15,6 @@
#define _ASM_DMA_CONTIGUOUS_H
#ifdef __KERNEL__
-#ifdef CONFIG_DMA_CMA
#include <linux/types.h>
@@ -23,5 +22,3 @@
#endif
#endif
-
-#endif
diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h
index 85c4a89..a891bb6 100644
--- a/arch/arm64/include/asm/futex.h
+++ b/arch/arm64/include/asm/futex.h
@@ -48,20 +48,10 @@
} while (0)
static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret, tmp;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
-
pagefault_disable();
switch (op) {
@@ -91,30 +81,24 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
static inline int
-futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *_uaddr,
u32 oldval, u32 newval)
{
int ret = 0;
u32 val, tmp;
+ u32 __user *uaddr;
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+ if (!access_ok(VERIFY_WRITE, _uaddr, sizeof(u32)))
return -EFAULT;
+ uaddr = __uaccess_mask_ptr(_uaddr);
uaccess_enable();
asm volatile("// futex_atomic_cmpxchg_inatomic\n"
" prfm pstl1strm, %2\n"
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 5c0c57e..388a0ca 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -167,9 +167,9 @@
#define readq_relaxed_no_log(c) ({ u64 __v = le64_to_cpu((__force __le64)__raw_readq_no_log(c)); __v; })
#define writeb_relaxed_no_log(v, c) ((void)__raw_writeb_no_log((v), (c)))
-#define writew_relaxed_no_log(v, c) ((void)__raw_writew_no_log((__force u16)cpu_to_le32(v), (c)))
+#define writew_relaxed_no_log(v, c) ((void)__raw_writew_no_log((__force u16)cpu_to_le16(v), (c)))
#define writel_relaxed_no_log(v, c) ((void)__raw_writel_no_log((__force u32)cpu_to_le32(v), (c)))
-#define writeq_relaxed_no_log(v, c) ((void)__raw_writeq_no_log((__force u64)cpu_to_le32(v), (c)))
+#define writeq_relaxed_no_log(v, c) ((void)__raw_writeq_no_log((__force u64)cpu_to_le64(v), (c)))
/*
* I/O memory access primitives. Reads are ordered relative to any
diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
index 77a27af..7803343 100644
--- a/arch/arm64/include/asm/kernel-pgtable.h
+++ b/arch/arm64/include/asm/kernel-pgtable.h
@@ -78,16 +78,8 @@
/*
* Initial memory map attributes.
*/
-#define _SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
-#define _SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
-
-#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
-#define SWAPPER_PTE_FLAGS (_SWAPPER_PTE_FLAGS | PTE_NG)
-#define SWAPPER_PMD_FLAGS (_SWAPPER_PMD_FLAGS | PMD_SECT_NG)
-#else
-#define SWAPPER_PTE_FLAGS _SWAPPER_PTE_FLAGS
-#define SWAPPER_PMD_FLAGS _SWAPPER_PMD_FLAGS
-#endif
+#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
+#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_MM_MMUFLAGS (PMD_ATTRINDX(MT_NORMAL) | SWAPPER_PMD_FLAGS)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e505038..0a33ea3 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -73,6 +73,9 @@
/* Timer */
struct arch_timer_kvm timer;
+
+ /* Mandated version of PSCI */
+ u32 psci_version;
};
#define KVM_NR_MEM_OBJS 40
@@ -393,4 +396,9 @@
"PARange is %d bits, unsupported configuration!", parange);
}
+static inline bool kvm_arm_harden_branch_predictor(void)
+{
+ return cpus_have_cap(ARM64_HARDEN_BRANCH_PREDICTOR);
+}
+
#endif /* __ARM64_KVM_HOST_H__ */
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index b18e852..36d6758 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -29,7 +29,9 @@
({ \
u64 reg; \
asm volatile(ALTERNATIVE("mrs %0, " __stringify(r##nvh),\
- "mrs_s %0, " __stringify(r##vh),\
+ DEFINE_MRS_S \
+ "mrs_s %0, " __stringify(r##vh) "\n"\
+ UNDEFINE_MRS_S, \
ARM64_HAS_VIRT_HOST_EXTN) \
: "=r" (reg)); \
reg; \
@@ -39,7 +41,9 @@
do { \
u64 __val = (u64)(v); \
asm volatile(ALTERNATIVE("msr " __stringify(r##nvh) ", %x0",\
- "msr_s " __stringify(r##vh) ", %x0",\
+ DEFINE_MSR_S \
+ "msr_s " __stringify(r##vh) ", %x0\n"\
+ UNDEFINE_MSR_S, \
ARM64_HAS_VIRT_HOST_EXTN) \
: : "rZ" (__val)); \
} while (0)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 35ea9c1..24a8369 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -313,6 +313,22 @@
return (cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8;
}
+/*
+ * We are not in the kvm->srcu critical section most of the time, so we take
+ * the SRCU read lock here. Since we copy the data from the user page, we
+ * can immediately drop the lock again.
+ */
+static inline int kvm_read_guest_lock(struct kvm *kvm,
+ gpa_t gpa, void *data, unsigned long len)
+{
+ int srcu_idx = srcu_read_lock(&kvm->srcu);
+ int ret = kvm_read_guest(kvm, gpa, data, len);
+
+ srcu_read_unlock(&kvm->srcu, srcu_idx);
+
+ return ret;
+}
+
#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR
#include <asm/mmu.h>
@@ -325,7 +341,7 @@
vect = __bp_harden_hyp_vecs_start +
data->hyp_vectors_slot * SZ_2K;
- if (!has_vhe())
+ if (!cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN))
vect = lm_alias(vect);
}
diff --git a/arch/arm64/include/asm/kvm_psci.h b/arch/arm64/include/asm/kvm_psci.h
deleted file mode 100644
index bc39e55..0000000
--- a/arch/arm64/include/asm/kvm_psci.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2012,2013 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __ARM64_KVM_PSCI_H__
-#define __ARM64_KVM_PSCI_H__
-
-#define KVM_ARM_PSCI_0_1 1
-#define KVM_ARM_PSCI_0_2 2
-
-int kvm_psci_version(struct kvm_vcpu *vcpu);
-int kvm_psci_call(struct kvm_vcpu *vcpu);
-
-#endif /* __ARM64_KVM_PSCI_H__ */
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 8fe5ffc..708028e 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -61,12 +61,12 @@
* KIMAGE_VADDR - the virtual address of the start of the kernel image
* VA_BITS - the maximum number of bits for virtual addresses.
* VA_START - the first kernel virtual address.
- * TASK_SIZE - the maximum size of a user space task.
- * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
*/
#define VA_BITS (CONFIG_ARM64_VA_BITS)
-#define VA_START (UL(0xffffffffffffffff) << VA_BITS)
-#define PAGE_OFFSET (UL(0xffffffffffffffff) << (VA_BITS - 1))
+#define VA_START (UL(0xffffffffffffffff) - \
+ (UL(1) << VA_BITS) + 1)
+#define PAGE_OFFSET (UL(0xffffffffffffffff) - \
+ (UL(1) << (VA_BITS - 1)) + 1)
#define KIMAGE_VADDR (MODULES_END)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE)
@@ -75,19 +75,6 @@
#define PCI_IO_END (VMEMMAP_START - SZ_2M)
#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
#define FIXADDR_TOP (PCI_IO_START - SZ_2M)
-#define TASK_SIZE_64 (UL(1) << VA_BITS)
-
-#ifdef CONFIG_COMPAT
-#define TASK_SIZE_32 UL(0x100000000)
-#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
- TASK_SIZE_32 : TASK_SIZE_64)
-#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \
- TASK_SIZE_32 : TASK_SIZE_64)
-#else
-#define TASK_SIZE TASK_SIZE_64
-#endif /* CONFIG_COMPAT */
-
-#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4))
#define KERNEL_START _text
#define KERNEL_END _end
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index f543df3..24c780d 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -21,7 +21,7 @@
#ifndef __ASSEMBLY__
-#include <asm/percpu.h>
+#include <linux/percpu.h>
typedef struct {
atomic64_t id;
@@ -55,7 +55,7 @@
static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void)
{
- return raw_cpu_ptr(&bp_hardening_data);
+ return this_cpu_ptr(&bp_hardening_data);
}
static inline void arm64_apply_bp_hardening(void)
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index a0fac5c..f2ea8a2 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -134,7 +134,7 @@
* Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD,
* avoiding the possibility of conflicting TLB entries being allocated.
*/
-static inline void cpu_replace_ttbr1(pgd_t *pgd)
+static inline void __nocfi cpu_replace_ttbr1(pgd_t *pgd)
{
typedef void (ttbr_replace_func)(phys_addr_t);
extern ttbr_replace_func idmap_cpu_replace_ttbr1;
diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h
index 84b5283..f705d96 100644
--- a/arch/arm64/include/asm/pgtable-prot.h
+++ b/arch/arm64/include/asm/pgtable-prot.h
@@ -37,13 +37,11 @@
#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
#define _PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
-#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
-#define PROT_DEFAULT (_PROT_DEFAULT | PTE_NG)
-#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_SECT_NG)
-#else
-#define PROT_DEFAULT _PROT_DEFAULT
-#define PROT_SECT_DEFAULT _PROT_SECT_DEFAULT
-#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */
+#define PTE_MAYBE_NG (arm64_kernel_unmapped_at_el0() ? PTE_NG : 0)
+#define PMD_MAYBE_NG (arm64_kernel_unmapped_at_el0() ? PMD_SECT_NG : 0)
+
+#define PROT_DEFAULT (_PROT_DEFAULT | PTE_MAYBE_NG)
+#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_MAYBE_NG)
#define PROT_DEVICE_nGnRnE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
@@ -55,22 +53,22 @@
#define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
#define PROT_SECT_NORMAL_EXEC (PROT_SECT_DEFAULT | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
-#define _PAGE_DEFAULT (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL))
-#define _HYP_PAGE_DEFAULT (_PAGE_DEFAULT & ~PTE_NG)
+#define _PAGE_DEFAULT (_PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL))
+#define _HYP_PAGE_DEFAULT _PAGE_DEFAULT
-#define PAGE_KERNEL __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE)
-#define PAGE_KERNEL_RO __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_RDONLY)
-#define PAGE_KERNEL_ROX __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_RDONLY)
-#define PAGE_KERNEL_EXEC __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE)
-#define PAGE_KERNEL_EXEC_CONT __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_CONT)
+#define PAGE_KERNEL __pgprot(PROT_NORMAL)
+#define PAGE_KERNEL_RO __pgprot((PROT_NORMAL & ~PTE_WRITE) | PTE_RDONLY)
+#define PAGE_KERNEL_ROX __pgprot((PROT_NORMAL & ~(PTE_WRITE | PTE_PXN)) | PTE_RDONLY)
+#define PAGE_KERNEL_EXEC __pgprot(PROT_NORMAL & ~PTE_PXN)
+#define PAGE_KERNEL_EXEC_CONT __pgprot((PROT_NORMAL & ~PTE_PXN) | PTE_CONT)
#define PAGE_HYP __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP | PTE_HYP_XN)
#define PAGE_HYP_EXEC __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP | PTE_RDONLY)
#define PAGE_HYP_RO __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP | PTE_RDONLY | PTE_HYP_XN)
#define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP)
-#define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY)
-#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN)
+#define PAGE_S2 __pgprot(_PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY)
+#define PAGE_S2_DEVICE __pgprot(_PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN)
#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_NG | PTE_PXN | PTE_UXN)
#define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index a4af9f0..d8039a1 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -19,6 +19,13 @@
#ifndef __ASM_PROCESSOR_H
#define __ASM_PROCESSOR_H
+#define TASK_SIZE_64 (UL(1) << VA_BITS)
+
+#define KERNEL_DS UL(-1)
+#define USER_DS (TASK_SIZE_64 - 1)
+
+#ifndef __ASSEMBLY__
+
/*
* Default implementation of macro that returns current
* instruction pointer ("program counter").
@@ -37,6 +44,22 @@
#include <asm/ptrace.h>
#include <asm/types.h>
+/*
+ * TASK_SIZE - the maximum size of a user space task.
+ * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
+ */
+#ifdef CONFIG_COMPAT
+#define TASK_SIZE_32 UL(0x100000000)
+#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
+ TASK_SIZE_32 : TASK_SIZE_64)
+#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \
+ TASK_SIZE_32 : TASK_SIZE_64)
+#else
+#define TASK_SIZE TASK_SIZE_64
+#endif /* CONFIG_COMPAT */
+
+#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4))
+
#define STACK_TOP_MAX TASK_SIZE_64
#ifdef CONFIG_COMPAT
#define AARCH32_VECTORS_BASE 0xffff0000
@@ -195,4 +218,5 @@
int cpu_enable_uao(void *__unused);
int cpu_enable_cache_maint_trap(void *__unused);
+#endif /* __ASSEMBLY__ */
#endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 88bbe36..8546afc 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -246,20 +246,39 @@
#include <linux/types.h>
-asm(
-" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
-" .equ .L__reg_num_x\\num, \\num\n"
-" .endr\n"
+#define __DEFINE_MRS_MSR_S_REGNUM \
+" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" \
+" .equ .L__reg_num_x\\num, \\num\n" \
+" .endr\n" \
" .equ .L__reg_num_xzr, 31\n"
-"\n"
-" .macro mrs_s, rt, sreg\n"
-" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n"
+
+#define DEFINE_MRS_S \
+ __DEFINE_MRS_MSR_S_REGNUM \
+" .macro mrs_s, rt, sreg\n" \
+" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n" \
" .endm\n"
-"\n"
-" .macro msr_s, sreg, rt\n"
-" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n"
+
+#define DEFINE_MSR_S \
+ __DEFINE_MRS_MSR_S_REGNUM \
+" .macro msr_s, sreg, rt\n" \
+" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n" \
" .endm\n"
-);
+
+#define UNDEFINE_MRS_S \
+" .purgem mrs_s\n"
+
+#define UNDEFINE_MSR_S \
+" .purgem msr_s\n"
+
+#define __mrs_s(r, v) \
+ DEFINE_MRS_S \
+" mrs_s %0, " __stringify(r) "\n" \
+ UNDEFINE_MRS_S : "=r" (v)
+
+#define __msr_s(r, v) \
+ DEFINE_MSR_S \
+" msr_s " __stringify(r) ", %x0\n" \
+ UNDEFINE_MSR_S : : "rZ" (v)
/*
* Unlike read_cpuid, calls to read_sysreg are never expected to be
@@ -285,15 +304,15 @@
* For registers without architectural names, or simply unsupported by
* GAS.
*/
-#define read_sysreg_s(r) ({ \
- u64 __val; \
- asm volatile("mrs_s %0, " __stringify(r) : "=r" (__val)); \
- __val; \
+#define read_sysreg_s(r) ({ \
+ u64 __val; \
+ asm volatile(__mrs_s(r, __val)); \
+ __val; \
})
-#define write_sysreg_s(v, r) do { \
- u64 __val = (u64)v; \
- asm volatile("msr_s " __stringify(r) ", %x0" : : "rZ" (__val)); \
+#define write_sysreg_s(v, r) do { \
+ u64 __val = (u64)(v); \
+ asm volatile(__msr_s(r, __val)); \
} while (0)
static inline void config_sctlr_el1(u32 clear, u32 set)
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 9311547..de21caa 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -35,6 +35,7 @@
#include <asm/cpufeature.h>
#include <asm/kernel-pgtable.h>
+#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/errno.h>
#include <asm/memory.h>
@@ -65,10 +66,7 @@
extern int fixup_exception(struct pt_regs *regs);
-#define KERNEL_DS (-1UL)
#define get_ds() (KERNEL_DS)
-
-#define USER_DS TASK_SIZE_64
#define get_fs() (current_thread_info()->addr_limit)
static inline void set_fs(mm_segment_t fs)
@@ -76,6 +74,13 @@
current_thread_info()->addr_limit = fs;
/*
+ * Prevent a mispredicted conditional call to set_fs from forwarding
+ * the wrong address limit to access_ok under speculation.
+ */
+ dsb(nsh);
+ isb();
+
+ /*
* Enable/disable UAO so that copy_to_user() etc can access
* kernel memory with the unprivileged instructions.
*/
@@ -93,22 +98,32 @@
* Returns 1 if the range is valid, 0 otherwise.
*
* This is equivalent to the following test:
- * (u65)addr + (u65)size <= current->addr_limit
- *
- * This needs 65-bit arithmetic.
+ * (u65)addr + (u65)size <= (u65)current->addr_limit + 1
*/
-#define __range_ok(addr, size) \
-({ \
- unsigned long __addr = (unsigned long __force)(addr); \
- unsigned long flag, roksum; \
- __chk_user_ptr(addr); \
- asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls" \
- : "=&r" (flag), "=&r" (roksum) \
- : "1" (__addr), "Ir" (size), \
- "r" (current_thread_info()->addr_limit) \
- : "cc"); \
- flag; \
-})
+static inline unsigned long __range_ok(unsigned long addr, unsigned long size)
+{
+ unsigned long limit = current_thread_info()->addr_limit;
+
+ __chk_user_ptr(addr);
+ asm volatile(
+ // A + B <= C + 1 for all A,B,C, in four easy steps:
+ // 1: X = A + B; X' = X % 2^64
+ " adds %0, %0, %2\n"
+ // 2: Set C = 0 if X > 2^64, to guarantee X' > C in step 4
+ " csel %1, xzr, %1, hi\n"
+ // 3: Set X' = ~0 if X >= 2^64. For X == 2^64, this decrements X'
+ // to compensate for the carry flag being set in step 4. For
+ // X > 2^64, X' merely has to remain nonzero, which it does.
+ " csinv %0, %0, xzr, cc\n"
+ // 4: For X < 2^64, this gives us X' - C - 1 <= 0, where the -1
+ // comes from the carry in being clear. Otherwise, we are
+ // testing X' - C == 0, subject to the previous adjustments.
+ " sbcs xzr, %0, %1\n"
+ " cset %0, ls\n"
+ : "+r" (addr), "+r" (limit) : "Ir" (size) : "cc");
+
+ return addr;
+}
/*
* When dealing with data aborts, watchpoints, or instruction traps we may end
@@ -117,7 +132,7 @@
*/
#define untagged_addr(addr) sign_extend64(addr, 55)
-#define access_ok(type, addr, size) __range_ok(addr, size)
+#define access_ok(type, addr, size) __range_ok((unsigned long)(addr), size)
#define user_addr_max get_fs
#define _ASM_EXTABLE(from, to) \
@@ -236,6 +251,26 @@
}
/*
+ * Sanitise a uaccess pointer such that it becomes NULL if above the
+ * current addr_limit.
+ */
+#define uaccess_mask_ptr(ptr) (__typeof__(ptr))__uaccess_mask_ptr(ptr)
+static inline void __user *__uaccess_mask_ptr(const void __user *ptr)
+{
+ void __user *safe_ptr;
+
+ asm volatile(
+ " bics xzr, %1, %2\n"
+ " csel %0, %1, xzr, eq\n"
+ : "=&r" (safe_ptr)
+ : "r" (ptr), "r" (current_thread_info()->addr_limit)
+ : "cc");
+
+ csdb();
+ return safe_ptr;
+}
+
+/*
* The "__xxx" versions of the user access functions do not verify the address
* space - it must have been done previously with a separate "access_ok()"
* call.
@@ -277,7 +312,7 @@
(err), ARM64_HAS_UAO); \
break; \
case 8: \
- __get_user_asm("ldr", "ldtr", "%", __gu_val, (ptr), \
+ __get_user_asm("ldr", "ldtr", "%x", __gu_val, (ptr), \
(err), ARM64_HAS_UAO); \
break; \
default: \
@@ -287,29 +322,34 @@
(x) = (__force __typeof__(*(ptr)))__gu_val; \
} while (0)
-#define __get_user(x, ptr) \
+#define __get_user_check(x, ptr, err) \
({ \
- int __gu_err = 0; \
- __get_user_err((x), (ptr), __gu_err); \
- __gu_err; \
+ __typeof__(*(ptr)) __user *__p = (ptr); \
+ might_fault(); \
+ if (access_ok(VERIFY_READ, __p, sizeof(*__p))) { \
+ __p = uaccess_mask_ptr(__p); \
+ __get_user_err((x), __p, (err)); \
+ } else { \
+ (x) = 0; (err) = -EFAULT; \
+ } \
})
#define __get_user_error(x, ptr, err) \
({ \
- __get_user_err((x), (ptr), (err)); \
+ __get_user_check((x), (ptr), (err)); \
(void)0; \
})
+#define __get_user(x, ptr) \
+({ \
+ int __gu_err = 0; \
+ __get_user_check((x), (ptr), __gu_err); \
+ __gu_err; \
+})
+
#define __get_user_unaligned __get_user
-#define get_user(x, ptr) \
-({ \
- __typeof__(*(ptr)) __user *__p = (ptr); \
- might_fault(); \
- access_ok(VERIFY_READ, __p, sizeof(*__p)) ? \
- __get_user((x), __p) : \
- ((x) = 0, -EFAULT); \
-})
+#define get_user __get_user
#define __put_user_asm(instr, alt_instr, reg, x, addr, err, feature) \
asm volatile( \
@@ -344,7 +384,7 @@
(err), ARM64_HAS_UAO); \
break; \
case 8: \
- __put_user_asm("str", "sttr", "%", __pu_val, (ptr), \
+ __put_user_asm("str", "sttr", "%x", __pu_val, (ptr), \
(err), ARM64_HAS_UAO); \
break; \
default: \
@@ -353,47 +393,51 @@
uaccess_disable_not_uao(); \
} while (0)
-#define __put_user(x, ptr) \
+#define __put_user_check(x, ptr, err) \
({ \
- int __pu_err = 0; \
- __put_user_err((x), (ptr), __pu_err); \
- __pu_err; \
+ __typeof__(*(ptr)) __user *__p = (ptr); \
+ might_fault(); \
+ if (access_ok(VERIFY_WRITE, __p, sizeof(*__p))) { \
+ __p = uaccess_mask_ptr(__p); \
+ __put_user_err((x), __p, (err)); \
+ } else { \
+ (err) = -EFAULT; \
+ } \
})
#define __put_user_error(x, ptr, err) \
({ \
- __put_user_err((x), (ptr), (err)); \
+ __put_user_check((x), (ptr), (err)); \
(void)0; \
})
+#define __put_user(x, ptr) \
+({ \
+ int __pu_err = 0; \
+ __put_user_check((x), (ptr), __pu_err); \
+ __pu_err; \
+})
+
#define __put_user_unaligned __put_user
-#define put_user(x, ptr) \
-({ \
- __typeof__(*(ptr)) __user *__p = (ptr); \
- might_fault(); \
- access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ? \
- __put_user((x), __p) : \
- -EFAULT; \
-})
+#define put_user __put_user
extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);
extern unsigned long __must_check __arch_copy_to_user(void __user *to, const void *from, unsigned long n);
-extern unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n);
-extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
+extern unsigned long __must_check __arch_copy_in_user(void __user *to, const void __user *from, unsigned long n);
static inline unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n)
{
kasan_check_write(to, n);
check_object_size(to, n, false);
- return __arch_copy_from_user(to, from, n);
+ return __arch_copy_from_user(to, __uaccess_mask_ptr(from), n);
}
static inline unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n)
{
kasan_check_read(from, n);
check_object_size(from, n, true);
- return __arch_copy_to_user(to, from, n);
+ return __arch_copy_to_user(__uaccess_mask_ptr(to), from, n);
}
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
@@ -421,22 +465,25 @@
return n;
}
-static inline unsigned long __must_check copy_in_user(void __user *to, const void __user *from, unsigned long n)
+static inline unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n) && access_ok(VERIFY_WRITE, to, n))
- n = __copy_in_user(to, from, n);
+ n = __arch_copy_in_user(__uaccess_mask_ptr(to), __uaccess_mask_ptr(from), n);
return n;
}
+#define copy_in_user __copy_in_user
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
-static inline unsigned long __must_check clear_user(void __user *to, unsigned long n)
+extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n);
+static inline unsigned long __must_check __clear_user(void __user *to, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
- n = __clear_user(to, n);
+ n = __arch_clear_user(__uaccess_mask_ptr(to), n);
return n;
}
+#define clear_user __clear_user
extern long strncpy_from_user(char *dest, const char __user *src, long count);
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 3051f86..702de7a 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -195,6 +195,12 @@
#define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2)
#define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2)
+/* KVM-as-firmware specific pseudo-registers */
+#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+ KVM_REG_ARM_FW | ((r) & 0xffff))
+#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+
/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
index dd918d0..ca1cf2d 100644
--- a/arch/arm64/kernel/arm64ksyms.c
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -38,8 +38,8 @@
/* user mem (segment) */
EXPORT_SYMBOL(__arch_copy_from_user);
EXPORT_SYMBOL(__arch_copy_to_user);
-EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(__copy_in_user);
+EXPORT_SYMBOL(__arch_clear_user);
+EXPORT_SYMBOL(__arch_copy_in_user);
/* physical memory */
EXPORT_SYMBOL(memstart_addr);
diff --git a/arch/arm64/kernel/bpi.S b/arch/arm64/kernel/bpi.S
index dec95bd..746a203 100644
--- a/arch/arm64/kernel/bpi.S
+++ b/arch/arm64/kernel/bpi.S
@@ -17,6 +17,7 @@
*/
#include <linux/linkage.h>
+#include <linux/arm-smccc.h>
.macro ventry target
.rept 31
@@ -53,6 +54,7 @@
vectors __kvm_hyp_vector
.endr
ENTRY(__bp_harden_hyp_vecs_end)
+
ENTRY(__psci_hyp_bp_inval_start)
sub sp, sp, #(8 * 18)
stp x16, x17, [sp, #(16 * 0)]
@@ -77,3 +79,23 @@
ldp x0, x1, [sp, #(16 * 8)]
add sp, sp, #(8 * 18)
ENTRY(__psci_hyp_bp_inval_end)
+
+.macro smccc_workaround_1 inst
+ sub sp, sp, #(8 * 4)
+ stp x2, x3, [sp, #(8 * 0)]
+ stp x0, x1, [sp, #(8 * 2)]
+ mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1
+ \inst #0
+ ldp x2, x3, [sp, #(8 * 0)]
+ ldp x0, x1, [sp, #(8 * 2)]
+ add sp, sp, #(8 * 4)
+.endm
+
+ENTRY(__smccc_workaround_1_smc_start)
+ smccc_workaround_1 smc
+ENTRY(__smccc_workaround_1_smc_end)
+
+ENTRY(__smccc_workaround_1_hvc_start)
+ smccc_workaround_1 hvc
+ENTRY(__smccc_workaround_1_hvc_end)
+.#endif
diff --git a/arch/arm64/kernel/cpu-reset.S b/arch/arm64/kernel/cpu-reset.S
index 65f42d2..f736a6f 100644
--- a/arch/arm64/kernel/cpu-reset.S
+++ b/arch/arm64/kernel/cpu-reset.S
@@ -16,7 +16,7 @@
#include <asm/virt.h>
.text
-.pushsection .idmap.text, "ax"
+.pushsection .idmap.text, "awx"
/*
* __cpu_soft_restart(el2_switch, entry, arg0, arg1, arg2) - Helper for
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 7c8e185..49e548f 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -54,11 +54,15 @@
#ifdef CONFIG_KVM
extern char __psci_hyp_bp_inval_start[], __psci_hyp_bp_inval_end[];
+extern char __smccc_workaround_1_smc_start[];
+extern char __smccc_workaround_1_smc_end[];
+extern char __smccc_workaround_1_hvc_start[];
+extern char __smccc_workaround_1_hvc_end[];
static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start,
const char *hyp_vecs_end)
{
- void *dst = lm_alias(__bp_harden_hyp_vecs_start + slot * SZ_2K);
+ void *dst = __bp_harden_hyp_vecs_start + slot * SZ_2K;
int i;
for (i = 0; i < SZ_2K; i += 0x80)
@@ -98,6 +102,10 @@
#else
#define __psci_hyp_bp_inval_start NULL
#define __psci_hyp_bp_inval_end NULL
+#define __smccc_workaround_1_smc_start NULL
+#define __smccc_workaround_1_smc_end NULL
+#define __smccc_workaround_1_hvc_start NULL
+#define __smccc_workaround_1_hvc_end NULL
static void __install_bp_hardening_cb(bp_hardening_cb_t fn,
const char *hyp_vecs_start,
@@ -124,8 +132,11 @@
__install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end);
}
+#include <uapi/linux/psci.h>
+#include <linux/arm-smccc.h>
#include <linux/psci.h>
+#ifdef CONFIG_PSCI_BP_HARDENING
static int enable_psci_bp_hardening(void *data)
{
const struct arm64_cpu_capabilities *entry = data;
@@ -135,6 +146,59 @@
(bp_hardening_cb_t)psci_ops.get_version,
__psci_hyp_bp_inval_start,
__psci_hyp_bp_inval_end);
+ return 0;
+}
+#endif
+
+static void call_smc_arch_workaround_1(void)
+{
+ arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
+}
+
+static void call_hvc_arch_workaround_1(void)
+{
+ arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL);
+}
+
+static int enable_smccc_arch_workaround_1(void *data)
+{
+ const struct arm64_cpu_capabilities *entry = data;
+ bp_hardening_cb_t cb;
+ void *smccc_start, *smccc_end;
+ struct arm_smccc_res res;
+
+ if (!entry->matches(entry, SCOPE_LOCAL_CPU))
+ return 0;
+
+ if (psci_ops.smccc_version == SMCCC_VERSION_1_0)
+ return 0;
+
+ switch (psci_ops.conduit) {
+ case PSCI_CONDUIT_HVC:
+ arm_smccc_1_1_hvc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+ ARM_SMCCC_ARCH_WORKAROUND_1, &res);
+ if (res.a0)
+ return 0;
+ cb = call_hvc_arch_workaround_1;
+ smccc_start = __smccc_workaround_1_hvc_start;
+ smccc_end = __smccc_workaround_1_hvc_end;
+ break;
+
+ case PSCI_CONDUIT_SMC:
+ arm_smccc_1_1_smc(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+ ARM_SMCCC_ARCH_WORKAROUND_1, &res);
+ if (res.a0)
+ return 0;
+ cb = call_smc_arch_workaround_1;
+ smccc_start = __smccc_workaround_1_smc_start;
+ smccc_end = __smccc_workaround_1_smc_end;
+ break;
+
+ default:
+ return 0;
+ }
+
+ install_bp_hardening_cb(entry, cb, smccc_start, smccc_end);
return 0;
}
@@ -238,32 +302,50 @@
{
.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
- .enable = enable_psci_bp_hardening,
+ .enable = enable_smccc_arch_workaround_1,
},
{
.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
- .enable = enable_psci_bp_hardening,
+ .enable = enable_smccc_arch_workaround_1,
},
{
.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
MIDR_ALL_VERSIONS(MIDR_CORTEX_A73),
- .enable = enable_psci_bp_hardening,
+ .enable = enable_smccc_arch_workaround_1,
},
{
.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
MIDR_ALL_VERSIONS(MIDR_CORTEX_A75),
- .enable = enable_psci_bp_hardening,
+ .enable = enable_smccc_arch_workaround_1,
},
{
.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
MIDR_ALL_VERSIONS(MIDR_KRYO3G),
+#ifdef CONFIG_PSCI_BP_HARDENING
.enable = enable_psci_bp_hardening,
+#else
+ .enable = enable_smccc_arch_workaround_1,
+#endif
},
{
.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
MIDR_ALL_VERSIONS(MIDR_KRYO2XX_GOLD),
+#ifdef CONFIG_PSCI_BP_HARDENING
.enable = enable_psci_bp_hardening,
+#else
+ .enable = enable_smccc_arch_workaround_1,
+#endif
+ },
+ {
+ .capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+ MIDR_ALL_VERSIONS(MIDR_BRCM_VULCAN),
+ .enable = enable_smccc_arch_workaround_1,
+ },
+ {
+ .capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+ MIDR_ALL_VERSIONS(MIDR_CAVIUM_THUNDERX2),
+ .enable = enable_smccc_arch_workaround_1,
},
#endif
{
@@ -279,15 +361,18 @@
{
const struct arm64_cpu_capabilities *caps = arm64_errata;
- for (; caps->matches; caps++)
- if (!cpus_have_cap(caps->capability) &&
- caps->matches(caps, SCOPE_LOCAL_CPU)) {
+ for (; caps->matches; caps++) {
+ if (cpus_have_cap(caps->capability)) {
+ if (caps->enable)
+ caps->enable((void *)caps);
+ } else if (caps->matches(caps, SCOPE_LOCAL_CPU)) {
pr_crit("CPU%d: Requires work around for %s, not detected"
" at boot time\n",
smp_processor_id(),
caps->desc ? : "an erratum");
cpu_die_early();
}
+ }
}
void update_cpu_errata_workarounds(void)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 5cf4c64..675bf45 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -95,12 +95,13 @@
};
static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_CSV3_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_CSV2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 24, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_GIC_SHIFT, 4, 0),
S_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI),
S_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI),
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_CSV3_SHIFT, 4, 0),
/* Linux doesn't care about the EL3 */
ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, ID_AA64PFR0_EL3_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL2_SHIFT, 4, 0),
@@ -753,12 +754,23 @@
static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
int __unused)
{
+ char const *str = "command line option";
u64 pfr0 = read_system_reg(SYS_ID_AA64PFR0_EL1);
- /* Forced on command line? */
+ /*
+ * For reasons that aren't entirely clear, enabling KPTI on Cavium
+ * ThunderX leads to apparent I-cache corruption of kernel text, which
+ * ends as well as you might imagine. Don't even try.
+ */
+ if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_27456)) {
+ str = "ARM64_WORKAROUND_CAVIUM_27456";
+ __kpti_forced = -1;
+ }
+
+ /* Forced? */
if (__kpti_forced) {
- pr_info_once("kernel page table isolation forced %s by command line option\n",
- __kpti_forced > 0 ? "ON" : "OFF");
+ pr_info_once("kernel page table isolation forced %s by %s\n",
+ __kpti_forced > 0 ? "ON" : "OFF", str);
return __kpti_forced > 0;
}
@@ -766,11 +778,42 @@
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE))
return true;
+ /* Don't force KPTI for CPUs that are not vulnerable */
+ switch (read_cpuid_id() & MIDR_CPU_MODEL_MASK) {
+ case MIDR_CAVIUM_THUNDERX2:
+ case MIDR_BRCM_VULCAN:
+ return false;
+ }
+
/* Defer to CPU feature registers */
return !cpuid_feature_extract_unsigned_field(pfr0,
ID_AA64PFR0_CSV3_SHIFT);
}
+static int __nocfi kpti_install_ng_mappings(void *__unused)
+{
+ typedef void (kpti_remap_fn)(int, int, phys_addr_t);
+ extern kpti_remap_fn idmap_kpti_install_ng_mappings;
+ kpti_remap_fn *remap_fn;
+
+ static bool kpti_applied = false;
+ int cpu = smp_processor_id();
+
+ if (kpti_applied)
+ return 0;
+
+ remap_fn = (void *)__pa_symbol(idmap_kpti_install_ng_mappings);
+
+ cpu_install_idmap();
+ remap_fn(cpu, num_online_cpus(), __pa_symbol(swapper_pg_dir));
+ cpu_uninstall_idmap();
+
+ if (!cpu)
+ kpti_applied = true;
+
+ return 0;
+}
+
static int __init parse_kpti(char *str)
{
bool enabled;
@@ -874,6 +917,7 @@
.capability = ARM64_UNMAP_KERNEL_AT_EL0,
.def_scope = SCOPE_SYSTEM,
.matches = unmap_kernel_at_el0,
+ .enable = kpti_install_ng_mappings,
},
#endif
{},
@@ -969,6 +1013,25 @@
cap_set_elf_hwcap(hwcaps);
}
+/*
+ * Check if the current CPU has a given feature capability.
+ * Should be called from non-preemptible context.
+ */
+static bool __this_cpu_has_cap(const struct arm64_cpu_capabilities *cap_array,
+ unsigned int cap)
+{
+ const struct arm64_cpu_capabilities *caps;
+
+ if (WARN_ON(preemptible()))
+ return false;
+
+ for (caps = cap_array; caps->matches; caps++)
+ if (caps->capability == cap &&
+ caps->matches(caps, SCOPE_LOCAL_CPU))
+ return true;
+ return false;
+}
+
void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
const char *info)
{
@@ -1037,8 +1100,9 @@
}
static void
-verify_local_cpu_features(const struct arm64_cpu_capabilities *caps)
+verify_local_cpu_features(const struct arm64_cpu_capabilities *caps_list)
{
+ const struct arm64_cpu_capabilities *caps = caps_list;
for (; caps->matches; caps++) {
if (!cpus_have_cap(caps->capability))
continue;
@@ -1046,7 +1110,7 @@
* If the new CPU misses an advertised feature, we cannot proceed
* further, park the cpu.
*/
- if (!caps->matches(caps, SCOPE_LOCAL_CPU)) {
+ if (!__this_cpu_has_cap(caps_list, caps->capability)) {
pr_crit("CPU%d: missing feature: %s\n",
smp_processor_id(), caps->desc);
cpu_die_early();
@@ -1099,22 +1163,12 @@
enable_cpu_capabilities(arm64_features);
}
-/*
- * Check if the current CPU has a given feature capability.
- * Should be called from non-preemptible context.
- */
+extern const struct arm64_cpu_capabilities arm64_errata[];
+
bool this_cpu_has_cap(unsigned int cap)
{
- const struct arm64_cpu_capabilities *caps;
-
- if (WARN_ON(preemptible()))
- return false;
-
- for (caps = arm64_features; caps->desc; caps++)
- if (caps->capability == cap && caps->matches)
- return caps->matches(caps, SCOPE_LOCAL_CPU);
-
- return false;
+ return (__this_cpu_has_cap(arm64_features, cap) ||
+ __this_cpu_has_cap(arm64_errata, cap));
}
void __init setup_cpu_features(void)
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index f8ba35d..7613ed1 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -30,11 +30,13 @@
#include <asm/irq.h>
#include <asm/memory.h>
#include <asm/mmu.h>
+#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/uaccess.h>
#include <asm/asm-uaccess.h>
#include <asm/unistd.h>
+#include <asm/kernel-pgtable.h>
/*
* Context tracking subsystem. Used to instrument transitions
@@ -125,10 +127,10 @@
.else
add x21, sp, #S_FRAME_SIZE
get_thread_info tsk
- /* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */
+ /* Save the task's original addr_limit and set USER_DS */
ldr x20, [tsk, #TSK_TI_ADDR_LIMIT]
str x20, [sp, #S_ORIG_ADDR_LIMIT]
- mov x20, #TASK_SIZE_64
+ mov x20, #USER_DS
str x20, [tsk, #TSK_TI_ADDR_LIMIT]
/* No need to reset PSTATE.UAO, hardware's already set it to 0 for us */
.endif /* \el == 0 */
@@ -668,8 +670,7 @@
* Instruction abort handling
*/
mrs x26, far_el1
- // enable interrupts before calling the main handler
- enable_dbg_and_irq
+ msr daifclr, #(8 | 4 | 1)
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
@@ -704,8 +705,10 @@
* Stack or PC alignment exception handling
*/
mrs x26, far_el1
- // enable interrupts before calling the main handler
- enable_dbg_and_irq
+ enable_dbg
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
ct_user_exit
mov x0, x26
mov x1, x25
@@ -764,6 +767,11 @@
#endif
ct_user_exit
+#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR
+ tbz x22, #55, 1f
+ bl do_el0_irq_bp_hardening
+1:
+#endif
irq_handler
#ifdef CONFIG_TRACE_IRQFLAGS
@@ -876,6 +884,7 @@
b.ne __sys_trace
cmp scno, sc_nr // check upper syscall limit
b.hs ni_sys
+ mask_nospec64 scno, sc_nr, x19 // enforce bounds for syscall number
ldr x16, [stbl, scno, lsl #3] // address in the syscall table
blr x16 // call sys_* routine
b ret_fast_syscall
@@ -953,16 +962,9 @@
orr \tmp, \tmp, #USER_ASID_FLAG
msr ttbr1_el1, \tmp
/*
- * We avoid running the post_ttbr_update_workaround here because the
- * user and kernel ASIDs don't have conflicting mappings, so any
- * "blessing" as described in:
- *
- * http://lkml.kernel.org/r/56BB848A.6060603@caviumnetworks.com
- *
- * will not hurt correctness. Whilst this may partially defeat the
- * point of using split ASIDs in the first place, it avoids
- * the hit of invalidating the entire I-cache on every return to
- * userspace.
+ * We avoid running the post_ttbr_update_workaround here because
+ * it's only needed by Cavium ThunderX, which requires KPTI to be
+ * disabled.
*/
.endm
@@ -972,6 +974,11 @@
.if \regsize == 64
msr tpidrro_el0, x30 // Restored in kernel_ventry
.endif
+ /*
+ * Defend against branch aliasing attacks by pushing a dummy
+ * entry onto the return stack and using a RET instruction to
+ * enter the full-fat kernel vectors.
+ */
bl 2f
b .
2:
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index c186586..d479185 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -474,7 +474,7 @@
* end early head section, begin head code that is also used for
* hotplug and needs to have the same protections as the text region
*/
- .section ".idmap.text","ax"
+ .section ".idmap.text","awx"
ENTRY(kimage_vaddr)
.quad _text - TEXT_OFFSET
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index c9a2ab4..f035ff6 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -32,11 +32,16 @@
void *module_alloc(unsigned long size)
{
+ gfp_t gfp_mask = GFP_KERNEL;
void *p;
+ /* Silence the initial allocation */
+ if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS))
+ gfp_mask |= __GFP_NOWARN;
+
p = __vmalloc_node_range(size, MODULE_ALIGN, module_alloc_base,
module_alloc_base + MODULES_VSIZE,
- GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
+ gfp_mask, PAGE_KERNEL_EXEC, 0,
NUMA_NO_NODE, __builtin_return_address(0));
if (!p && IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
index f7c9781..eacb5c6 100644
--- a/arch/arm64/kernel/module.lds
+++ b/arch/arm64/kernel/module.lds
@@ -1,4 +1,4 @@
SECTIONS {
- .plt (NOLOAD) : { BYTE(0) }
- .init.plt (NOLOAD) : { BYTE(0) }
+ .plt : { BYTE(0) }
+ .init.plt : { BYTE(0) }
}
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index 409abc4..1b3eb67 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -175,8 +175,10 @@
return NULL;
root_ops = kzalloc_node(sizeof(*root_ops), GFP_KERNEL, node);
- if (!root_ops)
+ if (!root_ops) {
+ kfree(ri);
return NULL;
+ }
ri->cfg = pci_acpi_setup_ecam_mapping(root);
if (!ri->cfg) {
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 52710f1..e0b731e 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -869,15 +869,23 @@
{
unsigned long config_base = 0;
- if (is_kernel_in_hyp_mode() &&
- attr->exclude_kernel != attr->exclude_hv)
- return -EINVAL;
+ /*
+ * If we're running in hyp mode, then we *are* the hypervisor.
+ * Therefore we ignore exclude_hv in this configuration, since
+ * there's no hypervisor to sample anyway. This is consistent
+ * with other architectures (x86 and Power).
+ */
+ if (is_kernel_in_hyp_mode()) {
+ if (!attr->exclude_kernel)
+ config_base |= ARMV8_PMU_INCLUDE_EL2;
+ } else {
+ if (attr->exclude_kernel)
+ config_base |= ARMV8_PMU_EXCLUDE_EL1;
+ if (!attr->exclude_hv)
+ config_base |= ARMV8_PMU_INCLUDE_EL2;
+ }
if (attr->exclude_user)
config_base |= ARMV8_PMU_EXCLUDE_EL0;
- if (!is_kernel_in_hyp_mode() && attr->exclude_kernel)
- config_base |= ARMV8_PMU_EXCLUDE_EL1;
- if (!attr->exclude_hv)
- config_base |= ARMV8_PMU_INCLUDE_EL2;
/*
* Install the filter into config_base as this is used to
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index ee0ea17..08ca9dc 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -213,7 +213,7 @@
for (j = 0; j < 8; j++) {
u32 data;
if (probe_kernel_address(p, data)) {
- printk(" ********");
+ pr_cont(" ********");
} else {
pr_cont(" %08x", data);
}
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
index df67652..5a4fbcc 100644
--- a/arch/arm64/kernel/sleep.S
+++ b/arch/arm64/kernel/sleep.S
@@ -95,7 +95,7 @@
ret
ENDPROC(__cpu_suspend_enter)
- .pushsection ".idmap.text", "ax"
+ .pushsection ".idmap.text", "awx"
ENTRY(cpu_resume)
bl el2_setup // if in EL2 drop to EL1 cleanly
bl __cpu_setup
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 623dd48..69d3266 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -602,16 +602,6 @@
void (*__smp_cross_call)(const struct cpumask *, unsigned int);
DEFINE_PER_CPU(bool, pending_ipi);
-void smp_cross_call_common(const struct cpumask *cpumask, unsigned int func)
-{
- unsigned int cpu;
-
- for_each_cpu(cpu, cpumask)
- per_cpu(pending_ipi, cpu) = true;
-
- __smp_cross_call(cpumask, func);
-}
-
/*
* Enumerate the possible CPU set from the device tree and build the
* cpu logical map array containing MPIDR values related to logical
@@ -779,6 +769,17 @@
__smp_cross_call(target, ipinr);
}
+static void smp_cross_call_common(const struct cpumask *cpumask,
+ unsigned int func)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, cpumask)
+ per_cpu(pending_ipi, cpu) = true;
+
+ smp_cross_call(cpumask, func);
+}
+
void show_ipi_list(struct seq_file *p, int prec)
{
unsigned int cpu, i;
@@ -825,7 +826,8 @@
void arch_irq_work_raise(void)
{
if (__smp_cross_call)
- smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK);
+ smp_cross_call_common(cpumask_of(smp_processor_id()),
+ IPI_IRQ_WORK);
}
#endif
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index fcd2b33..bfaead1 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -53,7 +53,7 @@
"Error"
};
-int show_unhandled_signals = 1;
+int show_unhandled_signals = 0;
/*
* Dump out the contents of some kernel memory nicely...
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index 62c84f7..88fef38 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -14,6 +14,7 @@
ccflags-y := -shared -fno-common -fno-builtin
ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+ccflags-y += $(DISABLE_LTO)
# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S
index c39872a..2bd7426 100644
--- a/arch/arm64/kernel/vdso/gettimeofday.S
+++ b/arch/arm64/kernel/vdso/gettimeofday.S
@@ -66,7 +66,16 @@
.macro get_clock_shifted_nsec res, cycle_last, mult
/* Read the virtual counter. */
isb
+#if IS_ENABLED(CONFIG_MSM_TIMER_LEAP)
+#define LEAST_32BITS 0x00000000FFFFFFFF
+9999:
+ mrs x_tmp, cntvct_el0
+ and \res, x_tmp, #LEAST_32BITS
+ eor \res, \res, #LEAST_32BITS
+ cbz \res, 9999b
+#else
mrs x_tmp, cntvct_el0
+#endif
/* Calculate cycle delta and convert to ns. */
sub \res, x_tmp, \cycle_last
/* We can only guarantee 56 bits of precision. */
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 34d3ed6..402a621 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -60,7 +60,7 @@
#define TRAMP_TEXT \
. = ALIGN(PAGE_SIZE); \
VMLINUX_SYMBOL(__entry_tramp_text_start) = .; \
- *(.entry.tramp.text) \
+ KEEP(*(.entry.tramp.text)) \
. = ALIGN(PAGE_SIZE); \
VMLINUX_SYMBOL(__entry_tramp_text_end) = .;
#else
@@ -179,11 +179,11 @@
. = ALIGN(4);
.altinstructions : {
__alt_instructions = .;
- *(.altinstructions)
+ KEEP(*(.altinstructions))
__alt_instructions_end = .;
}
.altinstr_replacement : {
- *(.altinstr_replacement)
+ KEEP(*(.altinstr_replacement))
}
.rela : ALIGN(8) {
*(.rela .rela*)
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 3f9e157..d3e0a2f 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <kvm/arm_psci.h>
#include <asm/cputype.h>
#include <asm/uaccess.h>
#include <asm/kvm.h>
@@ -205,7 +206,7 @@
unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
{
return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu)
- + NUM_TIMER_REGS;
+ + kvm_arm_get_fw_num_regs(vcpu) + NUM_TIMER_REGS;
}
/**
@@ -225,6 +226,11 @@
uindices++;
}
+ ret = kvm_arm_copy_fw_reg_indices(vcpu, uindices);
+ if (ret)
+ return ret;
+ uindices += kvm_arm_get_fw_num_regs(vcpu);
+
ret = copy_timer_indices(vcpu, uindices);
if (ret)
return ret;
@@ -243,6 +249,9 @@
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
return get_core_reg(vcpu, reg);
+ if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
+ return kvm_arm_get_fw_reg(vcpu, reg);
+
if (is_timer_reg(reg->id))
return get_timer_reg(vcpu, reg);
@@ -259,6 +268,9 @@
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
return set_core_reg(vcpu, reg);
+ if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
+ return kvm_arm_set_fw_reg(vcpu, reg);
+
if (is_timer_reg(reg->id))
return set_timer_reg(vcpu, reg);
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 2e6e9e9..efe43c5 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -22,12 +22,15 @@
#include <linux/kvm.h>
#include <linux/kvm_host.h>
+#include <kvm/arm_psci.h>
+
#include <asm/esr.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
-#include <asm/kvm_psci.h>
+#include <asm/debug-monitors.h>
+#include <asm/traps.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -42,7 +45,7 @@
kvm_vcpu_hvc_get_imm(vcpu));
vcpu->stat.hvc_exit_stat++;
- ret = kvm_psci_call(vcpu);
+ ret = kvm_hvc_call_handler(vcpu);
if (ret < 0) {
vcpu_set_reg(vcpu, 0, ~0UL);
return 1;
@@ -53,7 +56,16 @@
static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
+ /*
+ * "If an SMC instruction executed at Non-secure EL1 is
+ * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
+ * Trap exception, not a Secure Monitor Call exception [...]"
+ *
+ * We need to advance the PC after the trap, as it would
+ * otherwise return to the same address...
+ */
vcpu_set_reg(vcpu, 0, ~0UL);
+ kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
return 1;
}
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
index 48b0354..c470a7c 100644
--- a/arch/arm64/kvm/hyp/Makefile
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -4,6 +4,10 @@
ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ifeq ($(cc-name),clang)
+ccflags-y += -fno-jump-tables
+endif
+
KVM=../../../../virt/kvm
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v2-sr.o
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
index 4e92399..4e9d50c 100644
--- a/arch/arm64/kvm/hyp/hyp-entry.S
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/arm-smccc.h>
#include <linux/linkage.h>
#include <asm/alternative.h>
@@ -79,10 +80,11 @@
lsr x0, x1, #ESR_ELx_EC_SHIFT
cmp x0, #ESR_ELx_EC_HVC64
+ ccmp x0, #ESR_ELx_EC_HVC32, #4, ne
b.ne el1_trap
- mrs x1, vttbr_el2 // If vttbr is valid, the 64bit guest
- cbnz x1, el1_trap // called HVC
+ mrs x1, vttbr_el2 // If vttbr is valid, the guest
+ cbnz x1, el1_hvc_guest // called HVC
/* Here, we're pretty sure the host called HVC. */
ldp x0, x1, [sp], #16
@@ -101,6 +103,20 @@
2: eret
+el1_hvc_guest:
+ /*
+ * Fastest possible path for ARM_SMCCC_ARCH_WORKAROUND_1.
+ * The workaround has already been applied on the host,
+ * so let's quickly get back to the guest. We don't bother
+ * restoring x1, as it can be clobbered anyway.
+ */
+ ldr x1, [sp] // Guest's x0
+ eor w1, w1, #ARM_SMCCC_ARCH_WORKAROUND_1
+ cbnz w1, el1_trap
+ mov x0, x1
+ add sp, sp, #16
+ eret
+
el1_trap:
/*
* x0: ESR_EC
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 3eab6ac..849ee8a 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -19,6 +19,8 @@
#include <linux/jump_label.h>
#include <uapi/linux/psci.h>
+#include <kvm/arm_psci.h>
+
#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_hyp.h>
@@ -311,14 +313,16 @@
if (exit_code == ARM_EXCEPTION_TRAP &&
(kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_HVC64 ||
- kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_HVC32) &&
- vcpu_get_reg(vcpu, 0) == PSCI_0_2_FN_PSCI_VERSION) {
- u64 val = PSCI_RET_NOT_SUPPORTED;
- if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features))
- val = 2;
+ kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_HVC32)) {
+ u32 val = vcpu_get_reg(vcpu, 0);
- vcpu_set_reg(vcpu, 0, val);
- goto again;
+ if (val == PSCI_0_2_FN_PSCI_VERSION) {
+ val = kvm_psci_version(vcpu, kern_hyp_va(vcpu->kvm));
+ if (unlikely(val == KVM_ARM_PSCI_0_1))
+ val = PSCI_RET_NOT_SUPPORTED;
+ vcpu_set_reg(vcpu, 0, val);
+ goto again;
+ }
}
if (static_branch_unlikely(&vgic_v2_cpuif_trap) &&
@@ -417,6 +421,7 @@
vcpu = (struct kvm_vcpu *)read_sysreg(tpidr_el2);
host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+ __timer_save_state(vcpu);
__deactivate_traps(vcpu);
__deactivate_vm(vcpu);
__sysreg_restore_host_state(host_ctxt);
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
index 07c7ad9..b581e16 100644
--- a/arch/arm64/lib/clear_user.S
+++ b/arch/arm64/lib/clear_user.S
@@ -21,7 +21,7 @@
.text
-/* Prototype: int __clear_user(void *addr, size_t sz)
+/* Prototype: int __arch_clear_user(void *addr, size_t sz)
* Purpose : clear some user memory
* Params : addr - user memory address to clear
* : sz - number of bytes to clear
@@ -29,7 +29,7 @@
*
* Alignment fixed up by hardware.
*/
-ENTRY(__clear_user)
+ENTRY(__arch_clear_user)
uaccess_enable_not_uao x2, x3, x4
mov x2, x1 // save the size for fixup return
subs x1, x1, #8
@@ -52,7 +52,7 @@
5: mov x0, #0
uaccess_disable_not_uao x2, x3
ret
-ENDPROC(__clear_user)
+ENDPROC(__arch_clear_user)
.section .fixup,"ax"
.align 2
diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S
index e8bfaf1..800779e 100644
--- a/arch/arm64/lib/copy_in_user.S
+++ b/arch/arm64/lib/copy_in_user.S
@@ -64,14 +64,14 @@
.endm
end .req x5
-ENTRY(__copy_in_user)
+ENTRY(__arch_copy_in_user)
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
uaccess_disable_not_uao x3, x4
mov x0, #0
ret
-ENDPROC(__copy_in_user)
+ENDPROC(__arch_copy_in_user)
.section .fixup,"ax"
.align 2
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 2b8950e..6befc9c 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -343,7 +343,7 @@
mm_flags |= FAULT_FLAG_WRITE;
}
- if (addr < USER_DS && is_permission_fault(esr, regs)) {
+ if (addr < TASK_SIZE && is_permission_fault(esr, regs)) {
/* regs->orig_addr_limit may be 0 if we entered from EL0 */
if (regs->orig_addr_limit == KERNEL_DS)
die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
@@ -618,6 +618,12 @@
arm64_notify_die("", regs, &info, esr);
}
+asmlinkage void __exception do_el0_irq_bp_hardening(void)
+{
+ /* PC has already been checked in entry.S */
+ arm64_apply_bp_hardening();
+}
+
asmlinkage void __exception do_el0_ia_bp_hardening(unsigned long addr,
unsigned int esr,
struct pt_regs *regs)
@@ -644,6 +650,12 @@
struct siginfo info;
struct task_struct *tsk = current;
+ if (user_mode(regs)) {
+ if (instruction_pointer(regs) > TASK_SIZE)
+ arm64_apply_bp_hardening();
+ local_irq_enable();
+ }
+
if (show_unhandled_signals && unhandled_signal(tsk, SIGBUS))
pr_info_ratelimited("%s[%d]: %s exception: pc=%p sp=%p\n",
tsk->comm, task_pid_nr(tsk),
@@ -703,6 +715,9 @@
if (interrupts_enabled(regs))
trace_hardirqs_off();
+ if (user_mode(regs) && instruction_pointer(regs) > TASK_SIZE)
+ arm64_apply_bp_hardening();
+
if (!inf->fn(addr, esr, regs)) {
rv = 1;
} else {
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index 01c1717..caf75ab 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -18,6 +18,7 @@
#include <linux/elf.h>
#include <linux/fs.h>
+#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/export.h>
@@ -102,12 +103,18 @@
*/
int valid_phys_addr_range(phys_addr_t addr, size_t size)
{
- if (addr < PHYS_OFFSET)
- return 0;
- if (addr + size > __pa(high_memory - 1) + 1)
- return 0;
-
- return 1;
+ /*
+ * Check whether addr is covered by a memory region without the
+ * MEMBLOCK_NOMAP attribute, and whether that region covers the
+ * entire range. In theory, this could lead to false negatives
+ * if the range is covered by distinct but adjacent memory regions
+ * that only differ in other attributes. However, few of such
+ * attributes have been defined, and it is debatable whether it
+ * follows that /dev/mem read() calls should be able traverse
+ * such boundaries.
+ */
+ return memblock_is_region_memory(addr, size) &&
+ memblock_is_map_memory(addr);
}
/*
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index d8cdb16..6e989eb 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -466,7 +466,7 @@
{
extern char __entry_tramp_text_start[];
- pgprot_t prot = PAGE_KERNEL_EXEC;
+ pgprot_t prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;
phys_addr_t pa_start = __pa_symbol(__entry_tramp_text_start);
/* The trampoline is always mapped and can therefore be global */
@@ -861,3 +861,13 @@
pmd_clear(pmd);
return 1;
}
+
+int pud_free_pmd_page(pud_t *pud)
+{
+ return pud_none(*pud);
+}
+
+int pmd_free_pte_page(pmd_t *pmd)
+{
+ return pmd_none(*pmd);
+}
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 2e69a14..3a9af60 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -132,7 +132,7 @@
*
* x0: Address of context pointer
*/
- .pushsection ".idmap.text", "ax"
+ .pushsection ".idmap.text", "awx"
ENTRY(cpu_do_resume)
ldp x2, x3, [x0]
ldp x4, x5, [x0, #16]
@@ -197,7 +197,17 @@
b post_ttbr_update_workaround // Back to C code...
ENDPROC(cpu_do_switch_mm)
- .pushsection ".idmap.text", "ax"
+ .pushsection ".idmap.text", "awx"
+
+.macro __idmap_cpu_set_reserved_ttbr1, tmp1, tmp2
+ adrp \tmp1, empty_zero_page
+ msr ttbr1_el1, \tmp1
+ isb
+ tlbi vmalle1
+ dsb nsh
+ isb
+.endm
+
/*
* void idmap_cpu_replace_ttbr1(phys_addr_t new_pgd)
*
@@ -208,13 +218,7 @@
mrs x2, daif
msr daifset, #0xf
- adrp x1, empty_zero_page
- msr ttbr1_el1, x1
- isb
-
- tlbi vmalle1
- dsb nsh
- isb
+ __idmap_cpu_set_reserved_ttbr1 x1, x3
msr ttbr1_el1, x0
isb
@@ -225,13 +229,196 @@
ENDPROC(idmap_cpu_replace_ttbr1)
.popsection
+#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
+ .pushsection ".idmap.text", "awx"
+
+ .macro __idmap_kpti_get_pgtable_ent, type
+ dc cvac, cur_\()\type\()p // Ensure any existing dirty
+ dmb sy // lines are written back before
+ ldr \type, [cur_\()\type\()p] // loading the entry
+ tbz \type, #0, next_\()\type // Skip invalid entries
+ .endm
+
+ .macro __idmap_kpti_put_pgtable_ent_ng, type
+ orr \type, \type, #PTE_NG // Same bit for blocks and pages
+ str \type, [cur_\()\type\()p] // Update the entry and ensure it
+ dc civac, cur_\()\type\()p // is visible to all CPUs.
+ .endm
+
+/*
+ * void __kpti_install_ng_mappings(int cpu, int num_cpus, phys_addr_t swapper)
+ *
+ * Called exactly once from stop_machine context by each CPU found during boot.
+ */
+__idmap_kpti_flag:
+ .long 1
+ENTRY(idmap_kpti_install_ng_mappings)
+ cpu .req w0
+ num_cpus .req w1
+ swapper_pa .req x2
+ swapper_ttb .req x3
+ flag_ptr .req x4
+ cur_pgdp .req x5
+ end_pgdp .req x6
+ pgd .req x7
+ cur_pudp .req x8
+ end_pudp .req x9
+ pud .req x10
+ cur_pmdp .req x11
+ end_pmdp .req x12
+ pmd .req x13
+ cur_ptep .req x14
+ end_ptep .req x15
+ pte .req x16
+
+ mrs swapper_ttb, ttbr1_el1
+ adr flag_ptr, __idmap_kpti_flag
+
+ cbnz cpu, __idmap_kpti_secondary
+
+ /* We're the boot CPU. Wait for the others to catch up */
+ sevl
+1: wfe
+ ldaxr w18, [flag_ptr]
+ eor w18, w18, num_cpus
+ cbnz w18, 1b
+
+ /* We need to walk swapper, so turn off the MMU. */
+ mrs x18, sctlr_el1
+ bic x18, x18, #SCTLR_ELx_M
+ msr sctlr_el1, x18
+ isb
+
+ /* Everybody is enjoying the idmap, so we can rewrite swapper. */
+ /* PGD */
+ mov cur_pgdp, swapper_pa
+ add end_pgdp, cur_pgdp, #(PTRS_PER_PGD * 8)
+do_pgd: __idmap_kpti_get_pgtable_ent pgd
+ tbnz pgd, #1, walk_puds
+ __idmap_kpti_put_pgtable_ent_ng pgd
+next_pgd:
+ add cur_pgdp, cur_pgdp, #8
+ cmp cur_pgdp, end_pgdp
+ b.ne do_pgd
+
+ /* Publish the updated tables and nuke all the TLBs */
+ dsb sy
+ tlbi vmalle1is
+ dsb ish
+ isb
+
+ /* We're done: fire up the MMU again */
+ mrs x18, sctlr_el1
+ orr x18, x18, #SCTLR_ELx_M
+ msr sctlr_el1, x18
+ isb
+
+ /* Set the flag to zero to indicate that we're all done */
+ str wzr, [flag_ptr]
+ ret
+
+ /* PUD */
+walk_puds:
+ .if CONFIG_PGTABLE_LEVELS > 3
+ pte_to_phys cur_pudp, pgd
+ add end_pudp, cur_pudp, #(PTRS_PER_PUD * 8)
+do_pud: __idmap_kpti_get_pgtable_ent pud
+ tbnz pud, #1, walk_pmds
+ __idmap_kpti_put_pgtable_ent_ng pud
+next_pud:
+ add cur_pudp, cur_pudp, 8
+ cmp cur_pudp, end_pudp
+ b.ne do_pud
+ b next_pgd
+ .else /* CONFIG_PGTABLE_LEVELS <= 3 */
+ mov pud, pgd
+ b walk_pmds
+next_pud:
+ b next_pgd
+ .endif
+
+ /* PMD */
+walk_pmds:
+ .if CONFIG_PGTABLE_LEVELS > 2
+ pte_to_phys cur_pmdp, pud
+ add end_pmdp, cur_pmdp, #(PTRS_PER_PMD * 8)
+do_pmd: __idmap_kpti_get_pgtable_ent pmd
+ tbnz pmd, #1, walk_ptes
+ __idmap_kpti_put_pgtable_ent_ng pmd
+next_pmd:
+ add cur_pmdp, cur_pmdp, #8
+ cmp cur_pmdp, end_pmdp
+ b.ne do_pmd
+ b next_pud
+ .else /* CONFIG_PGTABLE_LEVELS <= 2 */
+ mov pmd, pud
+ b walk_ptes
+next_pmd:
+ b next_pud
+ .endif
+
+ /* PTE */
+walk_ptes:
+ pte_to_phys cur_ptep, pmd
+ add end_ptep, cur_ptep, #(PTRS_PER_PTE * 8)
+do_pte: __idmap_kpti_get_pgtable_ent pte
+ __idmap_kpti_put_pgtable_ent_ng pte
+next_pte:
+ add cur_ptep, cur_ptep, #8
+ cmp cur_ptep, end_ptep
+ b.ne do_pte
+ b next_pmd
+
+ /* Secondary CPUs end up here */
+__idmap_kpti_secondary:
+ /* Uninstall swapper before surgery begins */
+ __idmap_cpu_set_reserved_ttbr1 x18, x17
+
+ /* Increment the flag to let the boot CPU we're ready */
+1: ldxr w18, [flag_ptr]
+ add w18, w18, #1
+ stxr w17, w18, [flag_ptr]
+ cbnz w17, 1b
+
+ /* Wait for the boot CPU to finish messing around with swapper */
+ sevl
+1: wfe
+ ldxr w18, [flag_ptr]
+ cbnz w18, 1b
+
+ /* All done, act like nothing happened */
+ msr ttbr1_el1, swapper_ttb
+ isb
+ ret
+
+ .unreq cpu
+ .unreq num_cpus
+ .unreq swapper_pa
+ .unreq swapper_ttb
+ .unreq flag_ptr
+ .unreq cur_pgdp
+ .unreq end_pgdp
+ .unreq pgd
+ .unreq cur_pudp
+ .unreq end_pudp
+ .unreq pud
+ .unreq cur_pmdp
+ .unreq end_pmdp
+ .unreq pmd
+ .unreq cur_ptep
+ .unreq end_ptep
+ .unreq pte
+ENDPROC(idmap_kpti_install_ng_mappings)
+ .popsection
+#endif
+
/*
* __cpu_setup
*
* Initialise the processor for turning the MMU on. Return in x0 the
* value of the SCTLR_EL1 register.
*/
- .pushsection ".idmap.text", "ax"
+ .pushsection ".idmap.text", "awx"
ENTRY(__cpu_setup)
tlbi vmalle1 // Invalidate local TLB
dsb nsh
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index d8199e1..b47a26f 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -234,8 +234,9 @@
off = offsetof(struct bpf_array, map.max_entries);
emit_a64_mov_i64(tmp, off, ctx);
emit(A64_LDR32(tmp, r2, tmp), ctx);
+ emit(A64_MOV(0, r3, r3), ctx);
emit(A64_CMP(0, r3, tmp), ctx);
- emit(A64_B_(A64_COND_GE, jmp_offset), ctx);
+ emit(A64_B_(A64_COND_CS, jmp_offset), ctx);
/* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
* goto out;
@@ -243,7 +244,7 @@
*/
emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx);
emit(A64_CMP(1, tcc, tmp), ctx);
- emit(A64_B_(A64_COND_GT, jmp_offset), ctx);
+ emit(A64_B_(A64_COND_HI, jmp_offset), ctx);
emit(A64_ADD_I(1, tcc, tcc, 1), ctx);
/* prog = array->ptrs[index];
diff --git a/arch/frv/include/asm/futex.h b/arch/frv/include/asm/futex.h
index 4bea27f..2702bd8 100644
--- a/arch/frv/include/asm/futex.h
+++ b/arch/frv/include/asm/futex.h
@@ -7,7 +7,8 @@
#include <asm/errno.h>
#include <asm/uaccess.h>
-extern int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr);
+extern int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr);
static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
diff --git a/arch/frv/include/asm/timex.h b/arch/frv/include/asm/timex.h
index a89bdde..139093f 100644
--- a/arch/frv/include/asm/timex.h
+++ b/arch/frv/include/asm/timex.h
@@ -16,5 +16,11 @@
#define vxtime_lock() do {} while (0)
#define vxtime_unlock() do {} while (0)
+/* This attribute is used in include/linux/jiffies.h alongside with
+ * __cacheline_aligned_in_smp. It is assumed that __cacheline_aligned_in_smp
+ * for frv does not contain another section specification.
+ */
+#define __jiffy_arch_data __attribute__((__section__(".data")))
+
#endif
diff --git a/arch/frv/kernel/futex.c b/arch/frv/kernel/futex.c
index d155ca9..37f7b2b 100644
--- a/arch/frv/kernel/futex.c
+++ b/arch/frv/kernel/futex.c
@@ -186,20 +186,10 @@
/*
* do the futex operations
*/
-int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
-
pagefault_disable();
switch (op) {
@@ -225,18 +215,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS; break;
- }
- }
+ if (!ret)
+ *oval = oldval;
return ret;
-} /* end futex_atomic_op_inuser() */
+} /* end arch_futex_atomic_op_inuser() */
diff --git a/arch/hexagon/include/asm/futex.h b/arch/hexagon/include/asm/futex.h
index 7e597f8..c607b77 100644
--- a/arch/hexagon/include/asm/futex.h
+++ b/arch/hexagon/include/asm/futex.h
@@ -31,18 +31,9 @@
static inline int
-futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
- return -EFAULT;
pagefault_disable();
@@ -72,30 +63,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ:
- ret = (oldval == cmparg);
- break;
- case FUTEX_OP_CMP_NE:
- ret = (oldval != cmparg);
- break;
- case FUTEX_OP_CMP_LT:
- ret = (oldval < cmparg);
- break;
- case FUTEX_OP_CMP_GE:
- ret = (oldval >= cmparg);
- break;
- case FUTEX_OP_CMP_LE:
- ret = (oldval <= cmparg);
- break;
- case FUTEX_OP_CMP_GT:
- ret = (oldval > cmparg);
- break;
- default:
- ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/ia64/include/asm/futex.h b/arch/ia64/include/asm/futex.h
index 76acbcd..6d67dc1 100644
--- a/arch/ia64/include/asm/futex.h
+++ b/arch/ia64/include/asm/futex.h
@@ -45,18 +45,9 @@
} while (0)
static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
pagefault_disable();
@@ -84,17 +75,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/ia64/kernel/module.c b/arch/ia64/kernel/module.c
index 6ab0ae7..d1d945c 100644
--- a/arch/ia64/kernel/module.c
+++ b/arch/ia64/kernel/module.c
@@ -153,7 +153,7 @@
static int
apply_imm64 (struct module *mod, struct insn *insn, uint64_t val)
{
- if (slot(insn) != 2) {
+ if (slot(insn) != 1 && slot(insn) != 2) {
printk(KERN_ERR "%s: invalid slot number %d for IMM64\n",
mod->name, slot(insn));
return 0;
@@ -165,7 +165,7 @@
static int
apply_imm60 (struct module *mod, struct insn *insn, uint64_t val)
{
- if (slot(insn) != 2) {
+ if (slot(insn) != 1 && slot(insn) != 2) {
printk(KERN_ERR "%s: invalid slot number %d for IMM60\n",
mod->name, slot(insn));
return 0;
diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h
index 01848f0..a9dad9e 100644
--- a/arch/microblaze/include/asm/futex.h
+++ b/arch/microblaze/include/asm/futex.h
@@ -29,18 +29,9 @@
})
static inline int
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
pagefault_disable();
@@ -66,30 +57,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ:
- ret = (oldval == cmparg);
- break;
- case FUTEX_OP_CMP_NE:
- ret = (oldval != cmparg);
- break;
- case FUTEX_OP_CMP_LT:
- ret = (oldval < cmparg);
- break;
- case FUTEX_OP_CMP_GE:
- ret = (oldval >= cmparg);
- break;
- case FUTEX_OP_CMP_LE:
- ret = (oldval <= cmparg);
- break;
- case FUTEX_OP_CMP_GT:
- ret = (oldval > cmparg);
- break;
- default:
- ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 2d2fd79..34fbbf8 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -95,6 +95,7 @@
select PCI_DRIVERS_GENERIC
select PINCTRL
select SMP_UP if SMP
+ select SWAP_IO_SPACE
select SYS_HAS_CPU_MIPS32_R1
select SYS_HAS_CPU_MIPS32_R2
select SYS_HAS_CPU_MIPS32_R6
diff --git a/arch/mips/ath25/board.c b/arch/mips/ath25/board.c
index 9ab48ff..6d11ae5 100644
--- a/arch/mips/ath25/board.c
+++ b/arch/mips/ath25/board.c
@@ -135,6 +135,8 @@
}
board_data = kzalloc(BOARD_CONFIG_BUFSZ, GFP_KERNEL);
+ if (!board_data)
+ goto error;
ath25_board.config = (struct ath25_boarddata *)board_data;
memcpy_fromio(board_data, bcfg, 0x100);
if (broken_boarddata) {
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index c1eb1ff..6ed1ded 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -2277,6 +2277,8 @@
}
host_data = kzalloc(sizeof(*host_data), GFP_KERNEL);
+ if (!host_data)
+ return -ENOMEM;
raw_spin_lock_init(&host_data->lock);
addr = of_get_address(ciu_node, 0, NULL, NULL);
diff --git a/arch/mips/include/asm/futex.h b/arch/mips/include/asm/futex.h
index 1de190b..a9e61ea 100644
--- a/arch/mips/include/asm/futex.h
+++ b/arch/mips/include/asm/futex.h
@@ -83,18 +83,9 @@
}
static inline int
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
pagefault_disable();
@@ -125,17 +116,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/mips/include/asm/kprobes.h b/arch/mips/include/asm/kprobes.h
index daba1f9..174aedc 100644
--- a/arch/mips/include/asm/kprobes.h
+++ b/arch/mips/include/asm/kprobes.h
@@ -40,7 +40,8 @@
#define flush_insn_slot(p) \
do { \
- flush_icache_range((unsigned long)p->addr, \
+ if (p->addr) \
+ flush_icache_range((unsigned long)p->addr, \
(unsigned long)p->addr + \
(MAX_INSN_SIZE * sizeof(kprobe_opcode_t))); \
} while (0)
diff --git a/arch/mips/include/asm/pgtable-32.h b/arch/mips/include/asm/pgtable-32.h
index d21f3da..c0be540 100644
--- a/arch/mips/include/asm/pgtable-32.h
+++ b/arch/mips/include/asm/pgtable-32.h
@@ -18,6 +18,10 @@
#include <asm-generic/pgtable-nopmd.h>
+#ifdef CONFIG_HIGHMEM
+#include <asm/highmem.h>
+#endif
+
extern int temp_tlb_entry;
/*
@@ -61,7 +65,8 @@
#define VMALLOC_START MAP_BASE
-#define PKMAP_BASE (0xfe000000UL)
+#define PKMAP_END ((FIXADDR_START) & ~((LAST_PKMAP << PAGE_SHIFT)-1))
+#define PKMAP_BASE (PKMAP_END - PAGE_SIZE * LAST_PKMAP)
#ifdef CONFIG_HIGHMEM
# define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE)
diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h
index 89fa5c0b..c92f4c2 100644
--- a/arch/mips/include/asm/uaccess.h
+++ b/arch/mips/include/asm/uaccess.h
@@ -1257,6 +1257,13 @@
{
__kernel_size_t res;
+#ifdef CONFIG_CPU_MICROMIPS
+/* micromips memset / bzero also clobbers t7 & t8 */
+#define bzero_clobbers "$4", "$5", "$6", __UA_t0, __UA_t1, "$15", "$24", "$31"
+#else
+#define bzero_clobbers "$4", "$5", "$6", __UA_t0, __UA_t1, "$31"
+#endif /* CONFIG_CPU_MICROMIPS */
+
if (eva_kernel_access()) {
__asm__ __volatile__(
"move\t$4, %1\n\t"
@@ -1266,7 +1273,7 @@
"move\t%0, $6"
: "=r" (res)
: "r" (addr), "r" (size)
- : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31");
+ : bzero_clobbers);
} else {
might_fault();
__asm__ __volatile__(
@@ -1277,7 +1284,7 @@
"move\t%0, $6"
: "=r" (res)
: "r" (addr), "r" (size)
- : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31");
+ : bzero_clobbers);
}
return res;
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index d8227f2..9a4aed6 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -1096,10 +1096,20 @@
}
break;
- case beql_op:
- case bnel_op:
case blezl_op:
case bgtzl_op:
+ /*
+ * For BLEZL and BGTZL, rt field must be set to 0. If this
+ * is not the case, this may be an encoding of a MIPS R6
+ * instruction, so return to CPU execution if this occurs
+ */
+ if (MIPSInst_RT(inst)) {
+ err = SIGILL;
+ break;
+ }
+ /* fall through */
+ case beql_op:
+ case bnel_op:
if (delay_slot(regs)) {
err = SIGILL;
break;
@@ -2329,6 +2339,8 @@
__this_cpu_write((mipsr2bremustats).bgezl, 0);
__this_cpu_write((mipsr2bremustats).bltzll, 0);
__this_cpu_write((mipsr2bremustats).bgezll, 0);
+ __this_cpu_write((mipsr2bremustats).bltzall, 0);
+ __this_cpu_write((mipsr2bremustats).bgezall, 0);
__this_cpu_write((mipsr2bremustats).bltzal, 0);
__this_cpu_write((mipsr2bremustats).bgezal, 0);
__this_cpu_write((mipsr2bremustats).beql, 0);
diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
index 47c9646..d4a293b 100644
--- a/arch/mips/kernel/smp-bmips.c
+++ b/arch/mips/kernel/smp-bmips.c
@@ -166,11 +166,11 @@
return;
}
- if (request_irq(IPI0_IRQ, bmips_ipi_interrupt, IRQF_PERCPU,
- "smp_ipi0", NULL))
+ if (request_irq(IPI0_IRQ, bmips_ipi_interrupt,
+ IRQF_PERCPU | IRQF_NO_SUSPEND, "smp_ipi0", NULL))
panic("Can't request IPI0 interrupt");
- if (request_irq(IPI1_IRQ, bmips_ipi_interrupt, IRQF_PERCPU,
- "smp_ipi1", NULL))
+ if (request_irq(IPI1_IRQ, bmips_ipi_interrupt,
+ IRQF_PERCPU | IRQF_NO_SUSPEND, "smp_ipi1", NULL))
panic("Can't request IPI1 interrupt");
}
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 0344e57..fba4ca5 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -15,4 +15,5 @@
obj-$(CONFIG_CPU_TX39XX) += r3k_dump_tlb.o
# libgcc-style stuff needed in the kernel
-obj-y += ashldi3.o ashrdi3.o bswapsi.o bswapdi.o cmpdi2.o lshrdi3.o ucmpdi2.o
+obj-y += ashldi3.o ashrdi3.o bswapsi.o bswapdi.o cmpdi2.o lshrdi3.o multi3.o \
+ ucmpdi2.o
diff --git a/arch/mips/lib/libgcc.h b/arch/mips/lib/libgcc.h
index 05909d58..56ea0df 100644
--- a/arch/mips/lib/libgcc.h
+++ b/arch/mips/lib/libgcc.h
@@ -9,10 +9,18 @@
struct DWstruct {
int high, low;
};
+
+struct TWstruct {
+ long long high, low;
+};
#elif defined(__LITTLE_ENDIAN)
struct DWstruct {
int low, high;
};
+
+struct TWstruct {
+ long long low, high;
+};
#else
#error I feel sick.
#endif
@@ -22,4 +30,13 @@
long long ll;
} DWunion;
+#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6)
+typedef int ti_type __attribute__((mode(TI)));
+
+typedef union {
+ struct TWstruct s;
+ ti_type ti;
+} TWunion;
+#endif
+
#endif /* __ASM_LIBGCC_H */
diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S
index 18a1ccd..2b1bf93 100644
--- a/arch/mips/lib/memset.S
+++ b/arch/mips/lib/memset.S
@@ -218,7 +218,7 @@
1: PTR_ADDIU a0, 1 /* fill bytewise */
R10KCBARRIER(0(ra))
bne t1, a0, 1b
- sb a1, -1(a0)
+ EX(sb, a1, -1(a0), .Lsmall_fixup\@)
2: jr ra /* done */
move a2, zero
@@ -251,13 +251,18 @@
PTR_L t0, TI_TASK($28)
andi a2, STORMASK
LONG_L t0, THREAD_BUADDR(t0)
- LONG_ADDU a2, t1
+ LONG_ADDU a2, a0
jr ra
LONG_SUBU a2, t0
.Llast_fixup\@:
jr ra
- andi v1, a2, STORMASK
+ nop
+
+.Lsmall_fixup\@:
+ PTR_SUBU a2, t1, a0
+ jr ra
+ PTR_ADDIU a2, 1
.endm
diff --git a/arch/mips/lib/multi3.c b/arch/mips/lib/multi3.c
new file mode 100644
index 0000000..111ad47
--- /dev/null
+++ b/arch/mips/lib/multi3.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/export.h>
+
+#include "libgcc.h"
+
+/*
+ * GCC 7 suboptimally generates __multi3 calls for mips64r6, so for that
+ * specific case only we'll implement it here.
+ *
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981
+ */
+#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ == 7)
+
+/* multiply 64-bit values, low 64-bits returned */
+static inline long long notrace dmulu(long long a, long long b)
+{
+ long long res;
+
+ asm ("dmulu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b));
+ return res;
+}
+
+/* multiply 64-bit unsigned values, high 64-bits of 128-bit result returned */
+static inline long long notrace dmuhu(long long a, long long b)
+{
+ long long res;
+
+ asm ("dmuhu %0,%1,%2" : "=r" (res) : "r" (a), "r" (b));
+ return res;
+}
+
+/* multiply 128-bit values, low 128-bits returned */
+ti_type notrace __multi3(ti_type a, ti_type b)
+{
+ TWunion res, aa, bb;
+
+ aa.ti = a;
+ bb.ti = b;
+
+ /*
+ * a * b = (a.lo * b.lo)
+ * + 2^64 * (a.hi * b.lo + a.lo * b.hi)
+ * [+ 2^128 * (a.hi * b.hi)]
+ */
+ res.s.low = dmulu(aa.s.low, bb.s.low);
+ res.s.high = dmuhu(aa.s.low, bb.s.low);
+ res.s.high += dmulu(aa.s.high, bb.s.low);
+ res.s.high += dmulu(aa.s.low, bb.s.high);
+
+ return res.ti;
+}
+EXPORT_SYMBOL(__multi3);
+
+#endif /* 64BIT && CPU_MIPSR6 && GCC7 */
diff --git a/arch/mips/mm/pgtable-32.c b/arch/mips/mm/pgtable-32.c
index adc6911..b19a3c5 100644
--- a/arch/mips/mm/pgtable-32.c
+++ b/arch/mips/mm/pgtable-32.c
@@ -51,15 +51,15 @@
/*
* Fixed mappings:
*/
- vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
- fixrange_init(vaddr, vaddr + FIXADDR_SIZE, pgd_base);
+ vaddr = __fix_to_virt(__end_of_fixed_addresses - 1);
+ fixrange_init(vaddr & PMD_MASK, vaddr + FIXADDR_SIZE, pgd_base);
#ifdef CONFIG_HIGHMEM
/*
* Permanent kmaps:
*/
vaddr = PKMAP_BASE;
- fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);
+ fixrange_init(vaddr & PMD_MASK, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);
pgd = swapper_pg_dir + __pgd_offset(vaddr);
pud = pud_offset(pgd, vaddr);
diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c
index 49a2e22..2486037 100644
--- a/arch/mips/net/bpf_jit.c
+++ b/arch/mips/net/bpf_jit.c
@@ -526,7 +526,8 @@
u32 sflags, tmp_flags;
/* Adjust the stack pointer */
- emit_stack_offset(-align_sp(offset), ctx);
+ if (offset)
+ emit_stack_offset(-align_sp(offset), ctx);
tmp_flags = sflags = ctx->flags >> SEEN_SREG_SFT;
/* sflags is essentially a bitmap */
@@ -578,7 +579,8 @@
emit_load_stack_reg(r_ra, r_sp, real_off, ctx);
/* Restore the sp and discard the scrach memory */
- emit_stack_offset(align_sp(offset), ctx);
+ if (offset)
+ emit_stack_offset(align_sp(offset), ctx);
}
static unsigned int get_stack_depth(struct jit_ctx *ctx)
@@ -625,8 +627,14 @@
if (ctx->flags & SEEN_X)
emit_jit_reg_move(r_X, r_zero, ctx);
- /* Do not leak kernel data to userspace */
- if (bpf_needs_clear_a(&ctx->skf->insns[0]))
+ /*
+ * Do not leak kernel data to userspace, we only need to clear
+ * r_A if it is ever used. In fact if it is never used, we
+ * will not save/restore it, so clearing it in this case would
+ * corrupt the state of the caller.
+ */
+ if (bpf_needs_clear_a(&ctx->skf->insns[0]) &&
+ (ctx->flags & SEEN_A))
emit_jit_reg_move(r_A, r_zero, ctx);
}
diff --git a/arch/mips/net/bpf_jit_asm.S b/arch/mips/net/bpf_jit_asm.S
index 5d2e0c8..88a2075 100644
--- a/arch/mips/net/bpf_jit_asm.S
+++ b/arch/mips/net/bpf_jit_asm.S
@@ -90,18 +90,14 @@
is_offset_in_header(2, half)
/* Offset within header boundaries */
PTR_ADDU t1, $r_skb_data, offset
- .set reorder
- lh $r_A, 0(t1)
- .set noreorder
+ lhu $r_A, 0(t1)
#ifdef CONFIG_CPU_LITTLE_ENDIAN
# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
- wsbh t0, $r_A
- seh $r_A, t0
+ wsbh $r_A, $r_A
# else
- sll t0, $r_A, 24
- andi t1, $r_A, 0xff00
- sra t0, t0, 16
- srl t1, t1, 8
+ sll t0, $r_A, 8
+ srl t1, $r_A, 8
+ andi t0, t0, 0xff00
or $r_A, t0, t1
# endif
#endif
@@ -115,7 +111,7 @@
is_offset_in_header(1, byte)
/* Offset within header boundaries */
PTR_ADDU t1, $r_skb_data, offset
- lb $r_A, 0(t1)
+ lbu $r_A, 0(t1)
jr $r_ra
move $r_ret, zero
END(sk_load_byte)
@@ -139,6 +135,11 @@
* (void *to) is returned in r_s0
*
*/
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define DS_OFFSET(SIZE) (4 * SZREG)
+#else
+#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE))
+#endif
#define bpf_slow_path_common(SIZE) \
/* Quick check. Are we within reasonable boundaries? */ \
LONG_ADDIU $r_s1, $r_skb_len, -SIZE; \
@@ -150,7 +151,7 @@
PTR_LA t0, skb_copy_bits; \
PTR_S $r_ra, (5 * SZREG)($r_sp); \
/* Assign low slot to a2 */ \
- move a2, $r_sp; \
+ PTR_ADDIU a2, $r_sp, DS_OFFSET(SIZE); \
jalr t0; \
/* Reset our destination slot (DS but it's ok) */ \
INT_S zero, (4 * SZREG)($r_sp); \
diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c
index 64543d6..e9531fe 100644
--- a/arch/mips/ralink/reset.c
+++ b/arch/mips/ralink/reset.c
@@ -96,16 +96,9 @@
unreachable();
}
-static void ralink_halt(void)
-{
- local_irq_disable();
- unreachable();
-}
-
static int __init mips_reboot_setup(void)
{
_machine_restart = ralink_restart;
- _machine_halt = ralink_halt;
return 0;
}
diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h
index 1d8c24d..88290d3 100644
--- a/arch/parisc/include/asm/cacheflush.h
+++ b/arch/parisc/include/asm/cacheflush.h
@@ -25,6 +25,7 @@
void flush_kernel_icache_range_asm(unsigned long, unsigned long);
void flush_user_dcache_range_asm(unsigned long, unsigned long);
void flush_kernel_dcache_range_asm(unsigned long, unsigned long);
+void purge_kernel_dcache_range_asm(unsigned long, unsigned long);
void flush_kernel_dcache_page_asm(void *);
void flush_kernel_icache_page(void *);
void flush_user_dcache_range(unsigned long, unsigned long);
diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h
index ac8bd58..06a1a88 100644
--- a/arch/parisc/include/asm/futex.h
+++ b/arch/parisc/include/asm/futex.h
@@ -32,22 +32,12 @@
}
static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
unsigned long int flags;
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval, ret;
u32 tmp;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
- return -EFAULT;
-
_futex_spin_lock_irqsave(uaddr, &flags);
pagefault_disable();
@@ -85,17 +75,9 @@
pagefault_enable();
_futex_spin_unlock_irqrestore(uaddr, &flags);
- if (ret == 0) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index df757c9..8a08221 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -464,10 +464,10 @@
int __flush_tlb_range(unsigned long sid, unsigned long start,
unsigned long end)
{
- unsigned long flags, size;
+ unsigned long flags;
- size = (end - start);
- if (size >= parisc_tlb_flush_threshold) {
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ end - start >= parisc_tlb_flush_threshold) {
flush_tlb_all();
return 1;
}
@@ -538,13 +538,12 @@
struct vm_area_struct *vma;
pgd_t *pgd;
- /* Flush the TLB to avoid speculation if coherency is required. */
- if (parisc_requires_coherency())
- flush_tlb_all();
-
/* Flushing the whole cache on each cpu takes forever on
rp3440, etc. So, avoid it if the mm isn't too big. */
- if (mm_total_size(mm) >= parisc_cache_flush_threshold) {
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ mm_total_size(mm) >= parisc_cache_flush_threshold) {
+ if (mm->context)
+ flush_tlb_all();
flush_cache_all();
return;
}
@@ -552,9 +551,9 @@
if (mm->context == mfsp(3)) {
for (vma = mm->mmap; vma; vma = vma->vm_next) {
flush_user_dcache_range_asm(vma->vm_start, vma->vm_end);
- if ((vma->vm_flags & VM_EXEC) == 0)
- continue;
- flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
+ flush_tlb_range(vma, vma->vm_start, vma->vm_end);
}
return;
}
@@ -572,6 +571,8 @@
pfn = pte_pfn(*ptep);
if (!pfn_valid(pfn))
continue;
+ if (unlikely(mm->context))
+ flush_tlb_page(vma, addr);
__flush_cache_page(vma, addr, PFN_PHYS(pfn));
}
}
@@ -598,30 +599,45 @@
void flush_cache_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
- BUG_ON(!vma->vm_mm->context);
+ pgd_t *pgd;
+ unsigned long addr;
- /* Flush the TLB to avoid speculation if coherency is required. */
- if (parisc_requires_coherency())
- flush_tlb_range(vma, start, end);
-
- if ((end - start) >= parisc_cache_flush_threshold
- || vma->vm_mm->context != mfsp(3)) {
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ end - start >= parisc_cache_flush_threshold) {
+ if (vma->vm_mm->context)
+ flush_tlb_range(vma, start, end);
flush_cache_all();
return;
}
- flush_user_dcache_range_asm(start, end);
- if (vma->vm_flags & VM_EXEC)
- flush_user_icache_range_asm(start, end);
+ if (vma->vm_mm->context == mfsp(3)) {
+ flush_user_dcache_range_asm(start, end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(start, end);
+ flush_tlb_range(vma, start, end);
+ return;
+ }
+
+ pgd = vma->vm_mm->pgd;
+ for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
+ unsigned long pfn;
+ pte_t *ptep = get_ptep(pgd, addr);
+ if (!ptep)
+ continue;
+ pfn = pte_pfn(*ptep);
+ if (pfn_valid(pfn)) {
+ if (unlikely(vma->vm_mm->context))
+ flush_tlb_page(vma, addr);
+ __flush_cache_page(vma, addr, PFN_PHYS(pfn));
+ }
+ }
}
void
flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
{
- BUG_ON(!vma->vm_mm->context);
-
if (pfn_valid(pfn)) {
- if (parisc_requires_coherency())
+ if (likely(vma->vm_mm->context))
flush_tlb_page(vma, vmaddr);
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
}
@@ -630,21 +646,33 @@
void flush_kernel_vmap_range(void *vaddr, int size)
{
unsigned long start = (unsigned long)vaddr;
+ unsigned long end = start + size;
- if ((unsigned long)size > parisc_cache_flush_threshold)
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ (unsigned long)size >= parisc_cache_flush_threshold) {
+ flush_tlb_kernel_range(start, end);
flush_data_cache();
- else
- flush_kernel_dcache_range_asm(start, start + size);
+ return;
+ }
+
+ flush_kernel_dcache_range_asm(start, end);
+ flush_tlb_kernel_range(start, end);
}
EXPORT_SYMBOL(flush_kernel_vmap_range);
void invalidate_kernel_vmap_range(void *vaddr, int size)
{
unsigned long start = (unsigned long)vaddr;
+ unsigned long end = start + size;
- if ((unsigned long)size > parisc_cache_flush_threshold)
+ if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
+ (unsigned long)size >= parisc_cache_flush_threshold) {
+ flush_tlb_kernel_range(start, end);
flush_data_cache();
- else
- flush_kernel_dcache_range_asm(start, start + size);
+ return;
+ }
+
+ purge_kernel_dcache_range_asm(start, end);
+ flush_tlb_kernel_range(start, end);
}
EXPORT_SYMBOL(invalidate_kernel_vmap_range);
diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c
index 700e2d2..2e68ca1 100644
--- a/arch/parisc/kernel/drivers.c
+++ b/arch/parisc/kernel/drivers.c
@@ -648,6 +648,10 @@
(modpath->mod == PCI_FUNC(devfn)));
}
+ /* index might be out of bounds for bc[] */
+ if (index >= 6)
+ return 0;
+
id = PCI_SLOT(pdev->devfn) | (PCI_FUNC(pdev->devfn) << 5);
return (modpath->bc[index] == id);
}
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index 2d40c4f..67b0f75 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -1110,6 +1110,28 @@
.procend
ENDPROC_CFI(flush_kernel_dcache_range_asm)
+ENTRY_CFI(purge_kernel_dcache_range_asm)
+ .proc
+ .callinfo NO_CALLS
+ .entry
+
+ ldil L%dcache_stride, %r1
+ ldw R%dcache_stride(%r1), %r23
+ ldo -1(%r23), %r21
+ ANDCM %r26, %r21, %r26
+
+1: cmpb,COND(<<),n %r26, %r25,1b
+ pdc,m %r23(%r26)
+
+ sync
+ syncdma
+ bv %r0(%r2)
+ nop
+ .exit
+
+ .procend
+ENDPROC_CFI(purge_kernel_dcache_range_asm)
+
ENTRY_CFI(flush_user_icache_range_asm)
.proc
.callinfo NO_CALLS
diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h
index c0deafc..798ab37 100644
--- a/arch/powerpc/include/asm/barrier.h
+++ b/arch/powerpc/include/asm/barrier.h
@@ -34,7 +34,8 @@
#define rmb() __asm__ __volatile__ ("sync" : : : "memory")
#define wmb() __asm__ __volatile__ ("sync" : : : "memory")
-#ifdef __SUBARCH_HAS_LWSYNC
+/* The sub-arch has lwsync */
+#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC)
# define SMPWMB LWSYNC
#else
# define SMPWMB eieio
diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h
index 2015b07..b4ab1f4 100644
--- a/arch/powerpc/include/asm/code-patching.h
+++ b/arch/powerpc/include/asm/code-patching.h
@@ -30,6 +30,7 @@
int patch_instruction(unsigned int *addr, unsigned int instr);
int instr_is_relative_branch(unsigned int instr);
+int instr_is_relative_link_branch(unsigned int instr);
int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr);
unsigned long branch_target(const unsigned int *instr);
unsigned int translate_branch(const unsigned int *dest,
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index ab68d0e..4e54282 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -474,7 +474,8 @@
CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \
CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | \
CPU_FTR_ARCH_207S | CPU_FTR_TM_COMP | CPU_FTR_ARCH_300)
-#define CPU_FTRS_POWER9_DD1 (CPU_FTRS_POWER9 | CPU_FTR_POWER9_DD1)
+#define CPU_FTRS_POWER9_DD1 ((CPU_FTRS_POWER9 | CPU_FTR_POWER9_DD1) & \
+ (~CPU_FTR_SAO))
#define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \
diff --git a/arch/powerpc/include/asm/futex.h b/arch/powerpc/include/asm/futex.h
index 2a9cf84..f4c7467f 100644
--- a/arch/powerpc/include/asm/futex.h
+++ b/arch/powerpc/include/asm/futex.h
@@ -31,18 +31,10 @@
: "b" (uaddr), "i" (-EFAULT), "r" (oparg) \
: "cr0", "memory")
-static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
pagefault_disable();
@@ -68,17 +60,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
index cd4ffd8..bb9073a 100644
--- a/arch/powerpc/include/asm/module.h
+++ b/arch/powerpc/include/asm/module.h
@@ -14,6 +14,10 @@
#include <asm-generic/module.h>
+#ifdef CC_USING_MPROFILE_KERNEL
+#define MODULE_ARCH_VERMAGIC "mprofile-kernel"
+#endif
+
#ifndef __powerpc64__
/*
* Thanks to Paul M for explaining this.
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index e958b70..9e5e0d9 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -21,6 +21,9 @@
/* We calculate number of sg entries based on PAGE_SIZE */
#define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry))
+/* Default time to sleep or delay between OPAL_BUSY/OPAL_BUSY_EVENT loops */
+#define OPAL_BUSY_DELAY_MS 10
+
/* /sys/firmware/opal */
extern struct kobject *opal_kobj;
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 56398e7..71c6988 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -132,7 +132,19 @@
#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT)
#define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr))
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
+
+#ifdef CONFIG_PPC_BOOK3S_64
+/*
+ * On hash the vmalloc and other regions alias to the kernel region when passed
+ * through __pa(), which virt_to_pfn() uses. That means virt_addr_valid() can
+ * return true for some vmalloc addresses, which is incorrect. So explicitly
+ * check that the address is in the kernel region.
+ */
+#define virt_addr_valid(kaddr) (REGION_ID(kaddr) == KERNEL_REGION_ID && \
+ pfn_valid(virt_to_pfn(kaddr)))
+#else
#define virt_addr_valid(kaddr) pfn_valid(virt_to_pfn(kaddr))
+#endif
/*
* On Book-E parts we need __va to parse the device tree and we can't
diff --git a/arch/powerpc/include/asm/synch.h b/arch/powerpc/include/asm/synch.h
index 78efe8d..30f2d6d 100644
--- a/arch/powerpc/include/asm/synch.h
+++ b/arch/powerpc/include/asm/synch.h
@@ -5,10 +5,6 @@
#include <linux/stringify.h>
#include <asm/feature-fixups.h>
-#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC)
-#define __SUBARCH_HAS_LWSYNC
-#endif
-
#ifndef __ASSEMBLY__
extern unsigned int __start___lwsync_fixup, __stop___lwsync_fixup;
extern void do_lwsync_fixups(unsigned long value, void *fixup_start,
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 6ef8f0b..27843665 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -207,18 +207,18 @@
if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe))
return NULL;
+
+ device_lock(&dev->dev);
dev->error_state = pci_channel_io_frozen;
driver = eeh_pcid_get(dev);
- if (!driver) return NULL;
+ if (!driver) goto out_no_dev;
eeh_disable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->error_detected) {
- eeh_pcid_put(dev);
- return NULL;
- }
+ !driver->err_handler->error_detected)
+ goto out;
rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
@@ -227,7 +227,10 @@
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
edev->in_error = true;
+out:
eeh_pcid_put(dev);
+out_no_dev:
+ device_unlock(&dev->dev);
return NULL;
}
@@ -250,15 +253,14 @@
if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe))
return NULL;
+ device_lock(&dev->dev);
driver = eeh_pcid_get(dev);
- if (!driver) return NULL;
+ if (!driver) goto out_no_dev;
if (!driver->err_handler ||
!driver->err_handler->mmio_enabled ||
- (edev->mode & EEH_DEV_NO_HANDLER)) {
- eeh_pcid_put(dev);
- return NULL;
- }
+ (edev->mode & EEH_DEV_NO_HANDLER))
+ goto out;
rc = driver->err_handler->mmio_enabled(dev);
@@ -266,7 +268,10 @@
if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+out:
eeh_pcid_put(dev);
+out_no_dev:
+ device_unlock(&dev->dev);
return NULL;
}
@@ -289,20 +294,20 @@
if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe))
return NULL;
+
+ device_lock(&dev->dev);
dev->error_state = pci_channel_io_normal;
driver = eeh_pcid_get(dev);
- if (!driver) return NULL;
+ if (!driver) goto out_no_dev;
eeh_enable_irq(dev);
if (!driver->err_handler ||
!driver->err_handler->slot_reset ||
(edev->mode & EEH_DEV_NO_HANDLER) ||
- (!edev->in_error)) {
- eeh_pcid_put(dev);
- return NULL;
- }
+ (!edev->in_error))
+ goto out;
rc = driver->err_handler->slot_reset(dev);
if ((*res == PCI_ERS_RESULT_NONE) ||
@@ -310,7 +315,10 @@
if (*res == PCI_ERS_RESULT_DISCONNECT &&
rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
+out:
eeh_pcid_put(dev);
+out_no_dev:
+ device_unlock(&dev->dev);
return NULL;
}
@@ -361,10 +369,12 @@
if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe))
return NULL;
+
+ device_lock(&dev->dev);
dev->error_state = pci_channel_io_normal;
driver = eeh_pcid_get(dev);
- if (!driver) return NULL;
+ if (!driver) goto out_no_dev;
was_in_error = edev->in_error;
edev->in_error = false;
@@ -374,13 +384,15 @@
!driver->err_handler->resume ||
(edev->mode & EEH_DEV_NO_HANDLER) || !was_in_error) {
edev->mode &= ~EEH_DEV_NO_HANDLER;
- eeh_pcid_put(dev);
- return NULL;
+ goto out;
}
driver->err_handler->resume(dev);
+out:
eeh_pcid_put(dev);
+out_no_dev:
+ device_unlock(&dev->dev);
return NULL;
}
@@ -400,22 +412,25 @@
if (!dev || eeh_dev_removed(edev) || eeh_pe_passed(edev->pe))
return NULL;
+
+ device_lock(&dev->dev);
dev->error_state = pci_channel_io_perm_failure;
driver = eeh_pcid_get(dev);
- if (!driver) return NULL;
+ if (!driver) goto out_no_dev;
eeh_disable_irq(dev);
if (!driver->err_handler ||
- !driver->err_handler->error_detected) {
- eeh_pcid_put(dev);
- return NULL;
- }
+ !driver->err_handler->error_detected)
+ goto out;
driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
+out:
eeh_pcid_put(dev);
+out_no_dev:
+ device_unlock(&dev->dev);
return NULL;
}
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index de7d091..1abd8dd 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -795,7 +795,8 @@
eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]);
/* PCI Command: 0x4 */
- eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1]);
+ eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1] |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
/* Check the PCIe link is ready */
eeh_bridge_check_link(edev);
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 7614d1d..94b5dfb 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -723,7 +723,7 @@
ld r3, PACA_EXSLB+EX_DAR(r13)
std r3, _DAR(r1)
beq cr6, 2f
- li r10, 0x480 /* fix trap number for I-SLB miss */
+ li r10, 0x481 /* fix trap number for I-SLB miss */
std r10, _TRAP(r1)
2: bl save_nvgprs
addi r3, r1, STACK_FRAME_OVERHEAD
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 028a22b..ad713f7 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -372,6 +372,14 @@
*/
WARN_ON(!arch_irqs_disabled());
+ /*
+ * Interrupts must always be hard disabled before irq_happened is
+ * modified (to prevent lost update in case of interrupt between
+ * load and store).
+ */
+ __hard_irq_disable();
+ local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
+
/* Indicate in the PACA that we have an interrupt to replay */
local_paca->irq_happened |= PACA_IRQ_EE;
}
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index 183368e..99407cf 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -494,7 +494,17 @@
restore r2. */
static int restore_r2(u32 *instruction, struct module *me)
{
- if (is_early_mcount_callsite(instruction - 1))
+ u32 *prev_insn = instruction - 1;
+
+ if (is_early_mcount_callsite(prev_insn))
+ return 1;
+
+ /*
+ * Make sure the branch isn't a sibling call. Sibling calls aren't
+ * "link" branches and they don't return, so they don't need the r2
+ * restore afterwards.
+ */
+ if (!instr_is_relative_link_branch(*prev_insn))
return 1;
if (*instruction != PPC_INST_NOP) {
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index f516ac5..bf0f712 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -228,14 +228,6 @@
unsigned short maj;
unsigned short min;
- /* We only show online cpus: disable preempt (overzealous, I
- * knew) to prevent cpu going down. */
- preempt_disable();
- if (!cpu_online(cpu_id)) {
- preempt_enable();
- return 0;
- }
-
#ifdef CONFIG_SMP
pvr = per_cpu(cpu_pvr, cpu_id);
#else
@@ -340,9 +332,6 @@
#ifdef CONFIG_SMP
seq_printf(m, "\n");
#endif
-
- preempt_enable();
-
/* If this is the last cpu, print the summary */
if (cpumask_next(cpu_id, cpu_online_mask) >= nr_cpu_ids)
show_cpuinfo_summary(m);
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index f1d7e99..ab7b661 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -719,12 +719,20 @@
static void start_cpu_decrementer(void)
{
#if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
+ unsigned int tcr;
+
/* Clear any pending timer interrupts */
mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
- /* Enable decrementer interrupt */
- mtspr(SPRN_TCR, TCR_DIE);
-#endif /* defined(CONFIG_BOOKE) || defined(CONFIG_40x) */
+ tcr = mfspr(SPRN_TCR);
+ /*
+ * The watchdog may have already been enabled by u-boot. So leave
+ * TRC[WP] (Watchdog Period) alone.
+ */
+ tcr &= TCR_WP_MASK; /* Clear all bits except for TCR[WP] */
+ tcr |= TCR_DIE; /* Enable decrementer */
+ mtspr(SPRN_TCR, tcr);
+#endif
}
void __init generic_calibrate_decr(void)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c
index a587e8f..4b4e927 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_host.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_host.c
@@ -177,12 +177,15 @@
ret = mmu_hash_ops.hpte_insert(hpteg, vpn, hpaddr, rflags, vflags,
hpsize, hpsize, MMU_SEGSIZE_256M);
- if (ret < 0) {
+ if (ret == -1) {
/* If we couldn't map a primary PTE, try a secondary */
hash = ~hash;
vflags ^= HPTE_V_SECONDARY;
attempt++;
goto map_again;
+ } else if (ret < 0) {
+ r = -EIO;
+ goto out_unlock;
} else {
trace_kvm_book3s_64_mmu_map(rflags, hpteg,
vpn, hpaddr, orig_pte);
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 55fbc0c..79a180c 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -299,7 +299,6 @@
stw r12, STACK_SLOT_TRAP(r1)
bl kvmhv_commence_exit
nop
- lwz r12, STACK_SLOT_TRAP(r1)
b kvmhv_switch_to_host
/*
@@ -1023,6 +1022,7 @@
secondary_too_late:
li r12, 0
+ stw r12, STACK_SLOT_TRAP(r1)
cmpdi r4, 0
beq 11f
stw r12, VCPU_TRAP(r4)
@@ -1266,12 +1266,12 @@
bl kvmhv_accumulate_time
#endif
+ stw r12, STACK_SLOT_TRAP(r1)
mr r3, r12
/* Increment exit count, poke other threads to exit */
bl kvmhv_commence_exit
nop
ld r9, HSTATE_KVM_VCPU(r13)
- lwz r12, VCPU_TRAP(r9)
/* Stop others sending VCPU interrupts to this physical CPU */
li r0, -1
@@ -1549,6 +1549,7 @@
* POWER7/POWER8 guest -> host partition switch code.
* We don't have to lock against tlbies but we do
* have to coordinate the hardware threads.
+ * Here STACK_SLOT_TRAP(r1) contains the trap number.
*/
kvmhv_switch_to_host:
/* Secondary threads wait for primary to do partition switch */
@@ -1599,11 +1600,11 @@
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
/* If HMI, call kvmppc_realmode_hmi_handler() */
+ lwz r12, STACK_SLOT_TRAP(r1)
cmpwi r12, BOOK3S_INTERRUPT_HMI
bne 27f
bl kvmppc_realmode_hmi_handler
nop
- li r12, BOOK3S_INTERRUPT_HMI
/*
* At this point kvmppc_realmode_hmi_handler would have resync-ed
* the TB. Hence it is not required to subtract guest timebase
@@ -1678,6 +1679,7 @@
li r0, KVM_GUEST_MODE_NONE
stb r0, HSTATE_IN_GUEST(r13)
+ lwz r12, STACK_SLOT_TRAP(r1) /* return trap # in r12 */
ld r0, SFS+PPC_LR_STKOFF(r1)
addi r1, r1, SFS
mtlr r0
diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c
index 826c541..e0d88d0 100644
--- a/arch/powerpc/kvm/book3s_pr.c
+++ b/arch/powerpc/kvm/book3s_pr.c
@@ -627,7 +627,11 @@
kvmppc_mmu_unmap_page(vcpu, &pte);
}
/* The guest's PTE is not mapped yet. Map on the host */
- kvmppc_mmu_map_page(vcpu, &pte, iswrite);
+ if (kvmppc_mmu_map_page(vcpu, &pte, iswrite) == -EIO) {
+ /* Exit KVM if mapping failed */
+ run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ return RESUME_HOST;
+ }
if (data)
vcpu->stat.sp_storage++;
else if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c
index 02176fd..286a3b0 100644
--- a/arch/powerpc/kvm/book3s_pr_papr.c
+++ b/arch/powerpc/kvm/book3s_pr_papr.c
@@ -50,7 +50,9 @@
pteg_addr = get_pteg_addr(vcpu, pte_index);
mutex_lock(&vcpu->kvm->arch.hpt_mutex);
- copy_from_user(pteg, (void __user *)pteg_addr, sizeof(pteg));
+ ret = H_FUNCTION;
+ if (copy_from_user(pteg, (void __user *)pteg_addr, sizeof(pteg)))
+ goto done;
hpte = pteg;
ret = H_PTEG_FULL;
@@ -71,7 +73,9 @@
hpte[0] = cpu_to_be64(kvmppc_get_gpr(vcpu, 6));
hpte[1] = cpu_to_be64(kvmppc_get_gpr(vcpu, 7));
pteg_addr += i * HPTE_SIZE;
- copy_to_user((void __user *)pteg_addr, hpte, HPTE_SIZE);
+ ret = H_FUNCTION;
+ if (copy_to_user((void __user *)pteg_addr, hpte, HPTE_SIZE))
+ goto done;
kvmppc_set_gpr(vcpu, 4, pte_index | i);
ret = H_SUCCESS;
@@ -93,7 +97,9 @@
pteg = get_pteg_addr(vcpu, pte_index);
mutex_lock(&vcpu->kvm->arch.hpt_mutex);
- copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+ ret = H_FUNCTION;
+ if (copy_from_user(pte, (void __user *)pteg, sizeof(pte)))
+ goto done;
pte[0] = be64_to_cpu((__force __be64)pte[0]);
pte[1] = be64_to_cpu((__force __be64)pte[1]);
@@ -103,7 +109,9 @@
((flags & H_ANDCOND) && (pte[0] & avpn) != 0))
goto done;
- copy_to_user((void __user *)pteg, &v, sizeof(v));
+ ret = H_FUNCTION;
+ if (copy_to_user((void __user *)pteg, &v, sizeof(v)))
+ goto done;
rb = compute_tlbie_rb(pte[0], pte[1], pte_index);
vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
@@ -171,7 +179,10 @@
}
pteg = get_pteg_addr(vcpu, tsh & H_BULK_REMOVE_PTEX);
- copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+ if (copy_from_user(pte, (void __user *)pteg, sizeof(pte))) {
+ ret = H_FUNCTION;
+ break;
+ }
pte[0] = be64_to_cpu((__force __be64)pte[0]);
pte[1] = be64_to_cpu((__force __be64)pte[1]);
@@ -184,7 +195,10 @@
tsh |= H_BULK_REMOVE_NOT_FOUND;
} else {
/* Splat the pteg in (userland) hpt */
- copy_to_user((void __user *)pteg, &v, sizeof(v));
+ if (copy_to_user((void __user *)pteg, &v, sizeof(v))) {
+ ret = H_FUNCTION;
+ break;
+ }
rb = compute_tlbie_rb(pte[0], pte[1],
tsh & H_BULK_REMOVE_PTEX);
@@ -211,7 +225,9 @@
pteg = get_pteg_addr(vcpu, pte_index);
mutex_lock(&vcpu->kvm->arch.hpt_mutex);
- copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+ ret = H_FUNCTION;
+ if (copy_from_user(pte, (void __user *)pteg, sizeof(pte)))
+ goto done;
pte[0] = be64_to_cpu((__force __be64)pte[0]);
pte[1] = be64_to_cpu((__force __be64)pte[1]);
@@ -234,7 +250,9 @@
vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
pte[0] = (__force u64)cpu_to_be64(pte[0]);
pte[1] = (__force u64)cpu_to_be64(pte[1]);
- copy_to_user((void __user *)pteg, pte, sizeof(pte));
+ ret = H_FUNCTION;
+ if (copy_to_user((void __user *)pteg, pte, sizeof(pte)))
+ goto done;
ret = H_SUCCESS;
done:
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index d5edbeb..753d591 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -95,6 +95,11 @@
return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
}
+int instr_is_relative_link_branch(unsigned int instr)
+{
+ return instr_is_relative_branch(instr) && (instr & BRANCH_SET_LINK);
+}
+
static unsigned long branch_iform_target(const unsigned int *instr)
{
signed long imm;
diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c
index e86bfa1..46c8338 100644
--- a/arch/powerpc/lib/feature-fixups.c
+++ b/arch/powerpc/lib/feature-fixups.c
@@ -55,7 +55,7 @@
unsigned int *target = (unsigned int *)branch_target(src);
/* Branch within the section doesn't need translating */
- if (target < alt_start || target >= alt_end) {
+ if (target < alt_start || target > alt_end) {
instr = translate_branch(dest, src);
if (!instr)
return 1;
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index d0b137d..9376e8e 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -294,7 +294,7 @@
* can result in fault, which will cause a deadlock when called with
* mmap_sem held
*/
- if (user_mode(regs))
+ if (!is_exec && user_mode(regs))
store_update_sp = store_updates_sp(regs);
if (user_mode(regs))
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index a5d3ecd..035dfb6 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -765,6 +765,24 @@
if ((mmu_psize = shift_to_mmu_psize(shift)) < 0)
return -EINVAL;
+#ifdef CONFIG_PPC_BOOK3S_64
+ /*
+ * We need to make sure that for different page sizes reported by
+ * firmware we only add hugetlb support for page sizes that can be
+ * supported by linux page table layout.
+ * For now we have
+ * Radix: 2M
+ * Hash: 16M and 16G
+ */
+ if (radix_enabled()) {
+ if (mmu_psize != MMU_PAGE_2M)
+ return -EINVAL;
+ } else {
+ if (mmu_psize != MMU_PAGE_16M && mmu_psize != MMU_PAGE_16G)
+ return -EINVAL;
+ }
+#endif
+
BUG_ON(mmu_psize_defs[mmu_psize].shift != shift);
/* Return if huge page size has already been setup */
diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c
index 050badc..0b50019 100644
--- a/arch/powerpc/mm/tlb_nohash.c
+++ b/arch/powerpc/mm/tlb_nohash.c
@@ -751,7 +751,7 @@
* avoid going over total available memory just in case...
*/
#ifdef CONFIG_PPC_FSL_BOOK3E
- if (mmu_has_feature(MMU_FTR_TYPE_FSL_E)) {
+ if (early_mmu_has_feature(MMU_FTR_TYPE_FSL_E)) {
unsigned long linear_sz;
unsigned int num_cams;
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index 0fe98a5..be9d968 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -245,6 +245,7 @@
* goto out;
*/
PPC_LWZ(b2p[TMP_REG_1], b2p_bpf_array, offsetof(struct bpf_array, map.max_entries));
+ PPC_RLWINM(b2p_index, b2p_index, 0, 0, 31);
PPC_CMPLW(b2p_index, b2p[TMP_REG_1]);
PPC_BCC(COND_GE, out);
diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c
index 85c85eb..b4abf9d 100644
--- a/arch/powerpc/platforms/cell/spufs/coredump.c
+++ b/arch/powerpc/platforms/cell/spufs/coredump.c
@@ -175,6 +175,8 @@
skip = roundup(cprm->pos - total + sz, 4) - cprm->pos;
if (!dump_skip(cprm, skip))
goto Eio;
+
+ rc = 0;
out:
free_page((unsigned long)buf);
return rc;
diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c
index 9db4398..5584247 100644
--- a/arch/powerpc/platforms/powernv/opal-nvram.c
+++ b/arch/powerpc/platforms/powernv/opal-nvram.c
@@ -11,6 +11,7 @@
#define DEBUG
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
@@ -43,6 +44,10 @@
return count;
}
+/*
+ * This can be called in the panic path with interrupts off, so use
+ * mdelay in that case.
+ */
static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
{
s64 rc = OPAL_BUSY;
@@ -56,9 +61,23 @@
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
rc = opal_write_nvram(__pa(buf), count, off);
- if (rc == OPAL_BUSY_EVENT)
+ if (rc == OPAL_BUSY_EVENT) {
+ if (in_interrupt() || irqs_disabled())
+ mdelay(OPAL_BUSY_DELAY_MS);
+ else
+ msleep(OPAL_BUSY_DELAY_MS);
opal_poll_events(NULL);
+ } else if (rc == OPAL_BUSY) {
+ if (in_interrupt() || irqs_disabled())
+ mdelay(OPAL_BUSY_DELAY_MS);
+ else
+ msleep(OPAL_BUSY_DELAY_MS);
+ }
}
+
+ if (rc)
+ return -EIO;
+
*index += count;
return count;
}
diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c
index f886886..aa2a513 100644
--- a/arch/powerpc/platforms/powernv/opal-rtc.c
+++ b/arch/powerpc/platforms/powernv/opal-rtc.c
@@ -48,10 +48,12 @@
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
- if (rc == OPAL_BUSY_EVENT)
+ if (rc == OPAL_BUSY_EVENT) {
+ mdelay(OPAL_BUSY_DELAY_MS);
opal_poll_events(NULL);
- else if (rc == OPAL_BUSY)
- mdelay(10);
+ } else if (rc == OPAL_BUSY) {
+ mdelay(OPAL_BUSY_DELAY_MS);
+ }
}
if (rc != OPAL_SUCCESS)
return 0;
diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c
index 66e7227..85018a1 100644
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -708,7 +708,7 @@
* Return value:
* 0 on success / other on failure
**/
-static int cmm_set_disable(const char *val, struct kernel_param *kp)
+static int cmm_set_disable(const char *val, const struct kernel_param *kp)
{
int disable = simple_strtoul(val, NULL, 10);
diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c
index 3e828b2..2842f9d 100644
--- a/arch/powerpc/sysdev/mpc8xx_pic.c
+++ b/arch/powerpc/sysdev/mpc8xx_pic.c
@@ -79,7 +79,7 @@
irq = in_be32(&siu_reg->sc_sivec) >> 26;
if (irq == PIC_VEC_SPURRIOUS)
- irq = 0;
+ return 0;
return irq_linear_revmap(mpc8xx_pic_host, irq);
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 9aa0d04..1c4a595 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -118,6 +118,7 @@
select GENERIC_CLOCKEVENTS
select GENERIC_CPU_AUTOPROBE
select GENERIC_CPU_DEVICES if !SMP
+ select GENERIC_CPU_VULNERABILITIES
select GENERIC_FIND_FIRST_BIT
select GENERIC_SMP_IDLE_THREAD
select GENERIC_TIME_VSYSCALL
@@ -704,6 +705,51 @@
If unsure, say Y.
+config KERNEL_NOBP
+ def_bool n
+ prompt "Enable modified branch prediction for the kernel by default"
+ help
+ If this option is selected the kernel will switch to a modified
+ branch prediction mode if the firmware interface is available.
+ The modified branch prediction mode improves the behaviour in
+ regard to speculative execution.
+
+ With the option enabled the kernel parameter "nobp=0" or "nospec"
+ can be used to run the kernel in the normal branch prediction mode.
+
+ With the option disabled the modified branch prediction mode is
+ enabled with the "nobp=1" kernel parameter.
+
+ If unsure, say N.
+
+config EXPOLINE
+ def_bool n
+ prompt "Avoid speculative indirect branches in the kernel"
+ help
+ Compile the kernel with the expoline compiler options to guard
+ against kernel-to-user data leaks by avoiding speculative indirect
+ branches.
+ Requires a compiler with -mindirect-branch=thunk support for full
+ protection. The kernel may run slower.
+
+ If unsure, say N.
+
+choice
+ prompt "Expoline default"
+ depends on EXPOLINE
+ default EXPOLINE_FULL
+
+config EXPOLINE_OFF
+ bool "spectre_v2=off"
+
+config EXPOLINE_AUTO
+ bool "spectre_v2=auto"
+
+config EXPOLINE_FULL
+ bool "spectre_v2=on"
+
+endchoice
+
endmenu
menu "Power Management"
@@ -753,6 +799,7 @@
config SHARED_KERNEL
bool "VM shared kernel support"
depends on !JUMP_LABEL
+ depends on !ALTERNATIVES
help
Select this option, if you want to share the text segment of the
Linux kernel between different VM guests. This reduces memory
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index 54e0052..bef67c0 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -79,6 +79,16 @@
cflags-$(CONFIG_WARN_DYNAMIC_STACK) += -mwarn-dynamicstack
endif
+ifdef CONFIG_EXPOLINE
+ ifeq ($(call cc-option-yn,$(CC_FLAGS_MARCH) -mindirect-branch=thunk),y)
+ CC_FLAGS_EXPOLINE := -mindirect-branch=thunk
+ CC_FLAGS_EXPOLINE += -mfunction-return=thunk
+ CC_FLAGS_EXPOLINE += -mindirect-branch-table
+ export CC_FLAGS_EXPOLINE
+ cflags-y += $(CC_FLAGS_EXPOLINE) -DCC_USING_EXPOLINE
+ endif
+endif
+
ifdef CONFIG_FUNCTION_TRACER
# make use of hotpatch feature if the compiler supports it
cc_hotpatch := -mhotpatch=0,3
diff --git a/arch/s390/crypto/crc32be-vx.S b/arch/s390/crypto/crc32be-vx.S
index 8013989..096affb 100644
--- a/arch/s390/crypto/crc32be-vx.S
+++ b/arch/s390/crypto/crc32be-vx.S
@@ -12,6 +12,7 @@
*/
#include <linux/linkage.h>
+#include <asm/nospec-insn.h>
#include <asm/vx-insn.h>
/* Vector register range containing CRC-32 constants */
@@ -66,6 +67,8 @@
.previous
+ GEN_BR_THUNK %r14
+
.text
/*
* The CRC-32 function(s) use these calling conventions:
@@ -202,6 +205,6 @@
.Ldone:
VLGVF %r2,%v2,3
- br %r14
+ BR_EX %r14
.previous
diff --git a/arch/s390/crypto/crc32le-vx.S b/arch/s390/crypto/crc32le-vx.S
index 17f2504..8dc98c1 100644
--- a/arch/s390/crypto/crc32le-vx.S
+++ b/arch/s390/crypto/crc32le-vx.S
@@ -13,6 +13,7 @@
*/
#include <linux/linkage.h>
+#include <asm/nospec-insn.h>
#include <asm/vx-insn.h>
/* Vector register range containing CRC-32 constants */
@@ -75,6 +76,7 @@
.previous
+ GEN_BR_THUNK %r14
.text
@@ -263,6 +265,6 @@
.Ldone:
VLGVF %r2,%v2,2
- br %r14
+ BR_EX %r14
.previous
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 09bccb2..2a17123 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -318,7 +318,7 @@
if (sb->s_root)
hypfs_delete_tree(sb->s_root);
- if (sb_info->update_file)
+ if (sb_info && sb_info->update_file)
hypfs_remove(sb_info->update_file);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
diff --git a/arch/s390/include/asm/alternative-asm.h b/arch/s390/include/asm/alternative-asm.h
new file mode 100644
index 0000000..955d620
--- /dev/null
+++ b/arch/s390/include/asm/alternative-asm.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_ALTERNATIVE_ASM_H
+#define _ASM_S390_ALTERNATIVE_ASM_H
+
+#ifdef __ASSEMBLY__
+
+/*
+ * Check the length of an instruction sequence. The length may not be larger
+ * than 254 bytes and it has to be divisible by 2.
+ */
+.macro alt_len_check start,end
+ .if ( \end - \start ) > 254
+ .error "cpu alternatives does not support instructions blocks > 254 bytes\n"
+ .endif
+ .if ( \end - \start ) % 2
+ .error "cpu alternatives instructions length is odd\n"
+ .endif
+.endm
+
+/*
+ * Issue one struct alt_instr descriptor entry (need to put it into
+ * the section .altinstructions, see below). This entry contains
+ * enough information for the alternatives patching code to patch an
+ * instruction. See apply_alternatives().
+ */
+.macro alt_entry orig_start, orig_end, alt_start, alt_end, feature
+ .long \orig_start - .
+ .long \alt_start - .
+ .word \feature
+ .byte \orig_end - \orig_start
+ .byte \alt_end - \alt_start
+.endm
+
+/*
+ * Fill up @bytes with nops. The macro emits 6-byte nop instructions
+ * for the bulk of the area, possibly followed by a 4-byte and/or
+ * a 2-byte nop if the size of the area is not divisible by 6.
+ */
+.macro alt_pad_fill bytes
+ .fill ( \bytes ) / 6, 6, 0xc0040000
+ .fill ( \bytes ) % 6 / 4, 4, 0x47000000
+ .fill ( \bytes ) % 6 % 4 / 2, 2, 0x0700
+.endm
+
+/*
+ * Fill up @bytes with nops. If the number of bytes is larger
+ * than 6, emit a jg instruction to branch over all nops, then
+ * fill an area of size (@bytes - 6) with nop instructions.
+ */
+.macro alt_pad bytes
+ .if ( \bytes > 0 )
+ .if ( \bytes > 6 )
+ jg . + \bytes
+ alt_pad_fill \bytes - 6
+ .else
+ alt_pad_fill \bytes
+ .endif
+ .endif
+.endm
+
+/*
+ * Define an alternative between two instructions. If @feature is
+ * present, early code in apply_alternatives() replaces @oldinstr with
+ * @newinstr. ".skip" directive takes care of proper instruction padding
+ * in case @newinstr is longer than @oldinstr.
+ */
+.macro ALTERNATIVE oldinstr, newinstr, feature
+ .pushsection .altinstr_replacement,"ax"
+770: \newinstr
+771: .popsection
+772: \oldinstr
+773: alt_len_check 770b, 771b
+ alt_len_check 772b, 773b
+ alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) )
+774: .pushsection .altinstructions,"a"
+ alt_entry 772b, 774b, 770b, 771b, \feature
+ .popsection
+.endm
+
+/*
+ * Define an alternative between two instructions. If @feature is
+ * present, early code in apply_alternatives() replaces @oldinstr with
+ * @newinstr. ".skip" directive takes care of proper instruction padding
+ * in case @newinstr is longer than @oldinstr.
+ */
+.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
+ .pushsection .altinstr_replacement,"ax"
+770: \newinstr1
+771: \newinstr2
+772: .popsection
+773: \oldinstr
+774: alt_len_check 770b, 771b
+ alt_len_check 771b, 772b
+ alt_len_check 773b, 774b
+ .if ( 771b - 770b > 772b - 771b )
+ alt_pad ( ( 771b - 770b ) - ( 774b - 773b ) )
+ .else
+ alt_pad ( ( 772b - 771b ) - ( 774b - 773b ) )
+ .endif
+775: .pushsection .altinstructions,"a"
+ alt_entry 773b, 775b, 770b, 771b,\feature1
+ alt_entry 773b, 775b, 771b, 772b,\feature2
+ .popsection
+.endm
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_S390_ALTERNATIVE_ASM_H */
diff --git a/arch/s390/include/asm/alternative.h b/arch/s390/include/asm/alternative.h
new file mode 100644
index 0000000..a720020
--- /dev/null
+++ b/arch/s390/include/asm/alternative.h
@@ -0,0 +1,149 @@
+#ifndef _ASM_S390_ALTERNATIVE_H
+#define _ASM_S390_ALTERNATIVE_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/stringify.h>
+
+struct alt_instr {
+ s32 instr_offset; /* original instruction */
+ s32 repl_offset; /* offset to replacement instruction */
+ u16 facility; /* facility bit set for replacement */
+ u8 instrlen; /* length of original instruction */
+ u8 replacementlen; /* length of new instruction */
+} __packed;
+
+void apply_alternative_instructions(void);
+void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
+
+/*
+ * |661: |662: |6620 |663:
+ * +-----------+---------------------+
+ * | oldinstr | oldinstr_padding |
+ * | +----------+----------+
+ * | | | |
+ * | | >6 bytes |6/4/2 nops|
+ * | |6 bytes jg----------->
+ * +-----------+---------------------+
+ * ^^ static padding ^^
+ *
+ * .altinstr_replacement section
+ * +---------------------+-----------+
+ * |6641: |6651:
+ * | alternative instr 1 |
+ * +-----------+---------+- - - - - -+
+ * |6642: |6652: |
+ * | alternative instr 2 | padding
+ * +---------------------+- - - - - -+
+ * ^ runtime ^
+ *
+ * .altinstructions section
+ * +---------------------------------+
+ * | alt_instr entries for each |
+ * | alternative instr |
+ * +---------------------------------+
+ */
+
+#define b_altinstr(num) "664"#num
+#define e_altinstr(num) "665"#num
+
+#define e_oldinstr_pad_end "663"
+#define oldinstr_len "662b-661b"
+#define oldinstr_total_len e_oldinstr_pad_end"b-661b"
+#define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b"
+#define oldinstr_pad_len(num) \
+ "-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \
+ "((" altinstr_len(num) ")-(" oldinstr_len "))"
+
+#define INSTR_LEN_SANITY_CHECK(len) \
+ ".if " len " > 254\n" \
+ "\t.error \"cpu alternatives does not support instructions " \
+ "blocks > 254 bytes\"\n" \
+ ".endif\n" \
+ ".if (" len ") %% 2\n" \
+ "\t.error \"cpu alternatives instructions length is odd\"\n" \
+ ".endif\n"
+
+#define OLDINSTR_PADDING(oldinstr, num) \
+ ".if " oldinstr_pad_len(num) " > 6\n" \
+ "\tjg " e_oldinstr_pad_end "f\n" \
+ "6620:\n" \
+ "\t.fill (" oldinstr_pad_len(num) " - (6620b-662b)) / 2, 2, 0x0700\n" \
+ ".else\n" \
+ "\t.fill " oldinstr_pad_len(num) " / 6, 6, 0xc0040000\n" \
+ "\t.fill " oldinstr_pad_len(num) " %% 6 / 4, 4, 0x47000000\n" \
+ "\t.fill " oldinstr_pad_len(num) " %% 6 %% 4 / 2, 2, 0x0700\n" \
+ ".endif\n"
+
+#define OLDINSTR(oldinstr, num) \
+ "661:\n\t" oldinstr "\n662:\n" \
+ OLDINSTR_PADDING(oldinstr, num) \
+ e_oldinstr_pad_end ":\n" \
+ INSTR_LEN_SANITY_CHECK(oldinstr_len)
+
+#define OLDINSTR_2(oldinstr, num1, num2) \
+ "661:\n\t" oldinstr "\n662:\n" \
+ ".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \
+ OLDINSTR_PADDING(oldinstr, num2) \
+ ".else\n" \
+ OLDINSTR_PADDING(oldinstr, num1) \
+ ".endif\n" \
+ e_oldinstr_pad_end ":\n" \
+ INSTR_LEN_SANITY_CHECK(oldinstr_len)
+
+#define ALTINSTR_ENTRY(facility, num) \
+ "\t.long 661b - .\n" /* old instruction */ \
+ "\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \
+ "\t.word " __stringify(facility) "\n" /* facility bit */ \
+ "\t.byte " oldinstr_total_len "\n" /* source len */ \
+ "\t.byte " altinstr_len(num) "\n" /* alt instruction len */
+
+#define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \
+ b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \
+ INSTR_LEN_SANITY_CHECK(altinstr_len(num))
+
+/* alternative assembly primitive: */
+#define ALTERNATIVE(oldinstr, altinstr, facility) \
+ ".pushsection .altinstr_replacement, \"ax\"\n" \
+ ALTINSTR_REPLACEMENT(altinstr, 1) \
+ ".popsection\n" \
+ OLDINSTR(oldinstr, 1) \
+ ".pushsection .altinstructions,\"a\"\n" \
+ ALTINSTR_ENTRY(facility, 1) \
+ ".popsection\n"
+
+#define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2)\
+ ".pushsection .altinstr_replacement, \"ax\"\n" \
+ ALTINSTR_REPLACEMENT(altinstr1, 1) \
+ ALTINSTR_REPLACEMENT(altinstr2, 2) \
+ ".popsection\n" \
+ OLDINSTR_2(oldinstr, 1, 2) \
+ ".pushsection .altinstructions,\"a\"\n" \
+ ALTINSTR_ENTRY(facility1, 1) \
+ ALTINSTR_ENTRY(facility2, 2) \
+ ".popsection\n"
+
+/*
+ * Alternative instructions for different CPU types or capabilities.
+ *
+ * This allows to use optimized instructions even on generic binary
+ * kernels.
+ *
+ * oldinstr is padded with jump and nops at compile time if altinstr is
+ * longer. altinstr is padded with jump and nops at run-time during patching.
+ *
+ * For non barrier like inlines please define new variants
+ * without volatile and memory clobber.
+ */
+#define alternative(oldinstr, altinstr, facility) \
+ asm volatile(ALTERNATIVE(oldinstr, altinstr, facility) : : : "memory")
+
+#define alternative_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \
+ asm volatile(ALTERNATIVE_2(oldinstr, altinstr1, facility1, \
+ altinstr2, facility2) ::: "memory")
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_S390_ALTERNATIVE_H */
diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h
index 5c8db3c..03b2e5b 100644
--- a/arch/s390/include/asm/barrier.h
+++ b/arch/s390/include/asm/barrier.h
@@ -48,6 +48,30 @@
#define __smp_mb__before_atomic() barrier()
#define __smp_mb__after_atomic() barrier()
+/**
+ * array_index_mask_nospec - generate a mask for array_idx() that is
+ * ~0UL when the bounds check succeeds and 0 otherwise
+ * @index: array element index
+ * @size: number of elements in array
+ */
+#define array_index_mask_nospec array_index_mask_nospec
+static inline unsigned long array_index_mask_nospec(unsigned long index,
+ unsigned long size)
+{
+ unsigned long mask;
+
+ if (__builtin_constant_p(size) && size > 0) {
+ asm(" clgr %2,%1\n"
+ " slbgr %0,%0\n"
+ :"=d" (mask) : "d" (size-1), "d" (index) :"cc");
+ return mask;
+ }
+ asm(" clgr %1,%2\n"
+ " slbgr %0,%0\n"
+ :"=d" (mask) : "d" (size), "d" (index) :"cc");
+ return ~mask;
+}
+
#include <asm-generic/barrier.h>
#endif /* __ASM_BARRIER_H */
diff --git a/arch/s390/include/asm/facility.h b/arch/s390/include/asm/facility.h
index 09b406d..5811e78 100644
--- a/arch/s390/include/asm/facility.h
+++ b/arch/s390/include/asm/facility.h
@@ -15,7 +15,25 @@
#include <linux/preempt.h>
#include <asm/lowcore.h>
-#define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */
+#define MAX_FACILITY_BIT (sizeof(((struct lowcore *)0)->stfle_fac_list) * 8)
+
+static inline void __set_facility(unsigned long nr, void *facilities)
+{
+ unsigned char *ptr = (unsigned char *) facilities;
+
+ if (nr >= MAX_FACILITY_BIT)
+ return;
+ ptr[nr >> 3] |= 0x80 >> (nr & 7);
+}
+
+static inline void __clear_facility(unsigned long nr, void *facilities)
+{
+ unsigned char *ptr = (unsigned char *) facilities;
+
+ if (nr >= MAX_FACILITY_BIT)
+ return;
+ ptr[nr >> 3] &= ~(0x80 >> (nr & 7));
+}
static inline int __test_facility(unsigned long nr, void *facilities)
{
diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h
index a4811aa..8f8eec9e 100644
--- a/arch/s390/include/asm/futex.h
+++ b/arch/s390/include/asm/futex.h
@@ -21,17 +21,12 @@
: "0" (-EFAULT), "d" (oparg), "a" (uaddr), \
"m" (*uaddr) : "cc");
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, newval, ret;
load_kernel_asce();
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
pagefault_disable();
switch (op) {
@@ -60,17 +55,9 @@
}
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index a41faf3..5792590 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -181,7 +181,8 @@
__u16 ipa; /* 0x0056 */
__u32 ipb; /* 0x0058 */
__u32 scaoh; /* 0x005c */
- __u8 reserved60; /* 0x0060 */
+#define FPF_BPBC 0x20
+ __u8 fpf; /* 0x0060 */
__u8 ecb; /* 0x0061 */
__u8 ecb2; /* 0x0062 */
#define ECB3_AES 0x04
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index 7b93b78..ad4e0ce 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -135,7 +135,9 @@
/* Per cpu primary space access list */
__u32 paste[16]; /* 0x0400 */
- __u8 pad_0x04c0[0x0e00-0x0440]; /* 0x0440 */
+ /* br %r1 trampoline */
+ __u16 br_r1_trampoline; /* 0x0440 */
+ __u8 pad_0x0442[0x0e00-0x0442]; /* 0x0442 */
/*
* 0xe00 contains the address of the IPL Parameter Information
@@ -150,7 +152,8 @@
__u8 pad_0x0e20[0x0f00-0x0e20]; /* 0x0e20 */
/* Extended facility list */
- __u64 stfle_fac_list[32]; /* 0x0f00 */
+ __u64 stfle_fac_list[16]; /* 0x0f00 */
+ __u64 alt_stfle_fac_list[16]; /* 0x0f80 */
__u8 pad_0x1000[0x11b0-0x1000]; /* 0x1000 */
/* Pointer to vector register save area */
diff --git a/arch/s390/include/asm/nospec-branch.h b/arch/s390/include/asm/nospec-branch.h
new file mode 100644
index 0000000..b4bd8c4
--- /dev/null
+++ b/arch/s390/include/asm/nospec-branch.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_EXPOLINE_H
+#define _ASM_S390_EXPOLINE_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+extern int nospec_disable;
+
+void nospec_init_branches(void);
+void nospec_auto_detect(void);
+void nospec_revert(s32 *start, s32 *end);
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_S390_EXPOLINE_H */
diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h
new file mode 100644
index 0000000..9a56e73
--- /dev/null
+++ b/arch/s390/include/asm/nospec-insn.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_NOSPEC_ASM_H
+#define _ASM_S390_NOSPEC_ASM_H
+
+#include <asm/alternative-asm.h>
+#include <asm/asm-offsets.h>
+
+#ifdef __ASSEMBLY__
+
+#ifdef CONFIG_EXPOLINE
+
+_LC_BR_R1 = __LC_BR_R1
+
+/*
+ * The expoline macros are used to create thunks in the same format
+ * as gcc generates them. The 'comdat' section flag makes sure that
+ * the various thunks are merged into a single copy.
+ */
+ .macro __THUNK_PROLOG_NAME name
+ .pushsection .text.\name,"axG",@progbits,\name,comdat
+ .globl \name
+ .hidden \name
+ .type \name,@function
+\name:
+ .cfi_startproc
+ .endm
+
+ .macro __THUNK_EPILOG
+ .cfi_endproc
+ .popsection
+ .endm
+
+ .macro __THUNK_PROLOG_BR r1,r2
+ __THUNK_PROLOG_NAME __s390x_indirect_jump_r\r2\()use_r\r1
+ .endm
+
+ .macro __THUNK_PROLOG_BC d0,r1,r2
+ __THUNK_PROLOG_NAME __s390x_indirect_branch_\d0\()_\r2\()use_\r1
+ .endm
+
+ .macro __THUNK_BR r1,r2
+ jg __s390x_indirect_jump_r\r2\()use_r\r1
+ .endm
+
+ .macro __THUNK_BC d0,r1,r2
+ jg __s390x_indirect_branch_\d0\()_\r2\()use_\r1
+ .endm
+
+ .macro __THUNK_BRASL r1,r2,r3
+ brasl \r1,__s390x_indirect_jump_r\r3\()use_r\r2
+ .endm
+
+ .macro __DECODE_RR expand,reg,ruse
+ .set __decode_fail,1
+ .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \reg,%r\r1
+ .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \ruse,%r\r2
+ \expand \r1,\r2
+ .set __decode_fail,0
+ .endif
+ .endr
+ .endif
+ .endr
+ .if __decode_fail == 1
+ .error "__DECODE_RR failed"
+ .endif
+ .endm
+
+ .macro __DECODE_RRR expand,rsave,rtarget,ruse
+ .set __decode_fail,1
+ .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \rsave,%r\r1
+ .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \rtarget,%r\r2
+ .irp r3,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \ruse,%r\r3
+ \expand \r1,\r2,\r3
+ .set __decode_fail,0
+ .endif
+ .endr
+ .endif
+ .endr
+ .endif
+ .endr
+ .if __decode_fail == 1
+ .error "__DECODE_RRR failed"
+ .endif
+ .endm
+
+ .macro __DECODE_DRR expand,disp,reg,ruse
+ .set __decode_fail,1
+ .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \reg,%r\r1
+ .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ .ifc \ruse,%r\r2
+ \expand \disp,\r1,\r2
+ .set __decode_fail,0
+ .endif
+ .endr
+ .endif
+ .endr
+ .if __decode_fail == 1
+ .error "__DECODE_DRR failed"
+ .endif
+ .endm
+
+ .macro __THUNK_EX_BR reg,ruse
+ # Be very careful when adding instructions to this macro!
+ # The ALTERNATIVE replacement code has a .+10 which targets
+ # the "br \reg" after the code has been patched.
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
+ exrl 0,555f
+ j .
+#else
+ .ifc \reg,%r1
+ ALTERNATIVE "ex %r0,_LC_BR_R1", ".insn ril,0xc60000000000,0,.+10", 35
+ j .
+ .else
+ larl \ruse,555f
+ ex 0,0(\ruse)
+ j .
+ .endif
+#endif
+555: br \reg
+ .endm
+
+ .macro __THUNK_EX_BC disp,reg,ruse
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
+ exrl 0,556f
+ j .
+#else
+ larl \ruse,556f
+ ex 0,0(\ruse)
+ j .
+#endif
+556: b \disp(\reg)
+ .endm
+
+ .macro GEN_BR_THUNK reg,ruse=%r1
+ __DECODE_RR __THUNK_PROLOG_BR,\reg,\ruse
+ __THUNK_EX_BR \reg,\ruse
+ __THUNK_EPILOG
+ .endm
+
+ .macro GEN_B_THUNK disp,reg,ruse=%r1
+ __DECODE_DRR __THUNK_PROLOG_BC,\disp,\reg,\ruse
+ __THUNK_EX_BC \disp,\reg,\ruse
+ __THUNK_EPILOG
+ .endm
+
+ .macro BR_EX reg,ruse=%r1
+557: __DECODE_RR __THUNK_BR,\reg,\ruse
+ .pushsection .s390_indirect_branches,"a",@progbits
+ .long 557b-.
+ .popsection
+ .endm
+
+ .macro B_EX disp,reg,ruse=%r1
+558: __DECODE_DRR __THUNK_BC,\disp,\reg,\ruse
+ .pushsection .s390_indirect_branches,"a",@progbits
+ .long 558b-.
+ .popsection
+ .endm
+
+ .macro BASR_EX rsave,rtarget,ruse=%r1
+559: __DECODE_RRR __THUNK_BRASL,\rsave,\rtarget,\ruse
+ .pushsection .s390_indirect_branches,"a",@progbits
+ .long 559b-.
+ .popsection
+ .endm
+
+#else
+ .macro GEN_BR_THUNK reg,ruse=%r1
+ .endm
+
+ .macro GEN_B_THUNK disp,reg,ruse=%r1
+ .endm
+
+ .macro BR_EX reg,ruse=%r1
+ br \reg
+ .endm
+
+ .macro B_EX disp,reg,ruse=%r1
+ b \disp(\reg)
+ .endm
+
+ .macro BASR_EX rsave,rtarget,ruse=%r1
+ basr \rsave,\rtarget
+ .endm
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_S390_NOSPEC_ASM_H */
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 6bcbbec..d584212 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -84,6 +84,7 @@
extern const struct seq_operations cpuinfo_op;
extern int sysctl_ieee_emulation_warnings;
extern void execve_tail(void);
+extern void __bpon(void);
/*
* User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit.
@@ -359,6 +360,9 @@
memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \
}
+extern int s390_isolate_bp(void);
+extern int s390_isolate_bp_guest(void);
+
#endif /* __ASSEMBLY__ */
#endif /* __ASM_S390_PROCESSOR_H */
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h
index f15c039..84f2ae4 100644
--- a/arch/s390/include/asm/thread_info.h
+++ b/arch/s390/include/asm/thread_info.h
@@ -79,6 +79,8 @@
#define TIF_SECCOMP 5 /* secure computing */
#define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */
#define TIF_UPROBE 7 /* breakpointed or single-stepping */
+#define TIF_ISOLATE_BP 8 /* Run process with isolated BP */
+#define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests with isolated BP */
#define TIF_31BIT 16 /* 32bit process */
#define TIF_MEMDIE 17 /* is terminating due to OOM killer */
#define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */
@@ -94,6 +96,8 @@
#define _TIF_SECCOMP _BITUL(TIF_SECCOMP)
#define _TIF_SYSCALL_TRACEPOINT _BITUL(TIF_SYSCALL_TRACEPOINT)
#define _TIF_UPROBE _BITUL(TIF_UPROBE)
+#define _TIF_ISOLATE_BP _BITUL(TIF_ISOLATE_BP)
+#define _TIF_ISOLATE_BP_GUEST _BITUL(TIF_ISOLATE_BP_GUEST)
#define _TIF_31BIT _BITUL(TIF_31BIT)
#define _TIF_SINGLE_STEP _BITUL(TIF_SINGLE_STEP)
diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h
index a2ffec4..81c02e1 100644
--- a/arch/s390/include/uapi/asm/kvm.h
+++ b/arch/s390/include/uapi/asm/kvm.h
@@ -197,6 +197,7 @@
#define KVM_SYNC_VRS (1UL << 6)
#define KVM_SYNC_RICCB (1UL << 7)
#define KVM_SYNC_FPRS (1UL << 8)
+#define KVM_SYNC_BPBC (1UL << 10)
/* definition of registers in kvm_run */
struct kvm_sync_regs {
__u64 prefix; /* prefix register */
@@ -217,7 +218,9 @@
};
__u8 reserved[512]; /* for future vector expansion */
__u32 fpc; /* valid on KVM_SYNC_VRS or KVM_SYNC_FPRS */
- __u8 padding[52]; /* riccb needs to be 64byte aligned */
+ __u8 bpbc : 1; /* bp mode */
+ __u8 reserved2 : 7;
+ __u8 padding1[51]; /* riccb needs to be 64byte aligned */
__u8 riccb[64]; /* runtime instrumentation controls block */
};
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 1f0fe98..5b13997 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -42,6 +42,7 @@
CFLAGS_REMOVE_sclp.o += $(CC_FLAGS_MARCH)
CFLAGS_sclp.o += -march=z900
CFLAGS_REMOVE_als.o += $(CC_FLAGS_MARCH)
+CFLAGS_REMOVE_als.o += $(CC_FLAGS_EXPOLINE)
CFLAGS_als.o += -march=z900
AFLAGS_REMOVE_head.o += $(CC_FLAGS_MARCH)
AFLAGS_head.o += -march=z900
@@ -57,10 +58,14 @@
obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o als.o
obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o
-obj-y += entry.o reipl.o relocate_kernel.o
+obj-y += entry.o reipl.o relocate_kernel.o alternative.o
+obj-y += nospec-branch.o
extra-y += head.o head64.o vmlinux.lds
+obj-$(CONFIG_SYSFS) += nospec-sysfs.o
+CFLAGS_REMOVE_nospec-branch.o += $(CC_FLAGS_EXPOLINE)
+
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SCHED_TOPOLOGY) += topology.o
diff --git a/arch/s390/kernel/alternative.c b/arch/s390/kernel/alternative.c
new file mode 100644
index 0000000..b57b293
--- /dev/null
+++ b/arch/s390/kernel/alternative.c
@@ -0,0 +1,112 @@
+#include <linux/module.h>
+#include <asm/alternative.h>
+#include <asm/facility.h>
+#include <asm/nospec-branch.h>
+
+#define MAX_PATCH_LEN (255 - 1)
+
+static int __initdata_or_module alt_instr_disabled;
+
+static int __init disable_alternative_instructions(char *str)
+{
+ alt_instr_disabled = 1;
+ return 0;
+}
+
+early_param("noaltinstr", disable_alternative_instructions);
+
+struct brcl_insn {
+ u16 opc;
+ s32 disp;
+} __packed;
+
+static u16 __initdata_or_module nop16 = 0x0700;
+static u32 __initdata_or_module nop32 = 0x47000000;
+static struct brcl_insn __initdata_or_module nop48 = {
+ 0xc004, 0
+};
+
+static const void *nops[] __initdata_or_module = {
+ &nop16,
+ &nop32,
+ &nop48
+};
+
+static void __init_or_module add_jump_padding(void *insns, unsigned int len)
+{
+ struct brcl_insn brcl = {
+ 0xc0f4,
+ len / 2
+ };
+
+ memcpy(insns, &brcl, sizeof(brcl));
+ insns += sizeof(brcl);
+ len -= sizeof(brcl);
+
+ while (len > 0) {
+ memcpy(insns, &nop16, 2);
+ insns += 2;
+ len -= 2;
+ }
+}
+
+static void __init_or_module add_padding(void *insns, unsigned int len)
+{
+ if (len > 6)
+ add_jump_padding(insns, len);
+ else if (len >= 2)
+ memcpy(insns, nops[len / 2 - 1], len);
+}
+
+static void __init_or_module __apply_alternatives(struct alt_instr *start,
+ struct alt_instr *end)
+{
+ struct alt_instr *a;
+ u8 *instr, *replacement;
+ u8 insnbuf[MAX_PATCH_LEN];
+
+ /*
+ * The scan order should be from start to end. A later scanned
+ * alternative code can overwrite previously scanned alternative code.
+ */
+ for (a = start; a < end; a++) {
+ int insnbuf_sz = 0;
+
+ instr = (u8 *)&a->instr_offset + a->instr_offset;
+ replacement = (u8 *)&a->repl_offset + a->repl_offset;
+
+ if (!__test_facility(a->facility,
+ S390_lowcore.alt_stfle_fac_list))
+ continue;
+
+ if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
+ WARN_ONCE(1, "cpu alternatives instructions length is "
+ "odd, skipping patching\n");
+ continue;
+ }
+
+ memcpy(insnbuf, replacement, a->replacementlen);
+ insnbuf_sz = a->replacementlen;
+
+ if (a->instrlen > a->replacementlen) {
+ add_padding(insnbuf + a->replacementlen,
+ a->instrlen - a->replacementlen);
+ insnbuf_sz += a->instrlen - a->replacementlen;
+ }
+
+ s390_kernel_write(instr, insnbuf, insnbuf_sz);
+ }
+}
+
+void __init_or_module apply_alternatives(struct alt_instr *start,
+ struct alt_instr *end)
+{
+ if (!alt_instr_disabled)
+ __apply_alternatives(start, end);
+}
+
+extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
+void __init apply_alternative_instructions(void)
+{
+ apply_alternatives(__alt_instructions, __alt_instructions_end);
+}
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index f3df9e0..85c8ead 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -175,6 +175,7 @@
OFFSET(__LC_MACHINE_FLAGS, lowcore, machine_flags);
OFFSET(__LC_GMAP, lowcore, gmap);
OFFSET(__LC_PASTE, lowcore, paste);
+ OFFSET(__LC_BR_R1, lowcore, br_r1_trampoline);
/* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
OFFSET(__LC_DUMP_REIPL, lowcore, ipib);
/* hardware defined lowcore locations 0x1000 - 0x18ff */
diff --git a/arch/s390/kernel/base.S b/arch/s390/kernel/base.S
index 326f717..61fca54 100644
--- a/arch/s390/kernel/base.S
+++ b/arch/s390/kernel/base.S
@@ -8,18 +8,22 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
+#include <asm/nospec-insn.h>
#include <asm/ptrace.h>
#include <asm/sigp.h>
+ GEN_BR_THUNK %r9
+ GEN_BR_THUNK %r14
+
ENTRY(s390_base_mcck_handler)
basr %r13,0
0: lg %r15,__LC_PANIC_STACK # load panic stack
aghi %r15,-STACK_FRAME_OVERHEAD
larl %r1,s390_base_mcck_handler_fn
- lg %r1,0(%r1)
- ltgr %r1,%r1
+ lg %r9,0(%r1)
+ ltgr %r9,%r9
jz 1f
- basr %r14,%r1
+ BASR_EX %r14,%r9
1: la %r1,4095
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
lpswe __LC_MCK_OLD_PSW
@@ -36,10 +40,10 @@
basr %r13,0
0: aghi %r15,-STACK_FRAME_OVERHEAD
larl %r1,s390_base_ext_handler_fn
- lg %r1,0(%r1)
- ltgr %r1,%r1
+ lg %r9,0(%r1)
+ ltgr %r9,%r9
jz 1f
- basr %r14,%r1
+ BASR_EX %r14,%r9
1: lmg %r0,%r15,__LC_SAVE_AREA_ASYNC
ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
lpswe __LC_EXT_OLD_PSW
@@ -56,10 +60,10 @@
basr %r13,0
0: aghi %r15,-STACK_FRAME_OVERHEAD
larl %r1,s390_base_pgm_handler_fn
- lg %r1,0(%r1)
- ltgr %r1,%r1
+ lg %r9,0(%r1)
+ ltgr %r9,%r9
jz 1f
- basr %r14,%r1
+ BASR_EX %r14,%r9
lmg %r0,%r15,__LC_SAVE_AREA_SYNC
lpswe __LC_PGM_OLD_PSW
1: lpswe disabled_wait_psw-0b(%r13)
@@ -116,7 +120,7 @@
larl %r4,.Lcontinue_psw # Restore PSW flags
lpswe 0(%r4)
.Lcontinue:
- br %r14
+ BR_EX %r14
.align 16
.Lrestart_psw:
.long 0x00080000,0x80000000 + .Lrestart_part2
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 29d8744..0c7a7d5 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -299,6 +299,11 @@
{
stfle(S390_lowcore.stfle_fac_list,
ARRAY_SIZE(S390_lowcore.stfle_fac_list));
+ memcpy(S390_lowcore.alt_stfle_fac_list,
+ S390_lowcore.stfle_fac_list,
+ sizeof(S390_lowcore.alt_stfle_fac_list));
+ if (!IS_ENABLED(CONFIG_KERNEL_NOBP))
+ __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
}
static __init void detect_diag9c(void)
@@ -372,7 +377,7 @@
rc = kstrtobool(str, &enabled);
if (!rc && !enabled)
- S390_lowcore.machine_flags &= ~MACHINE_HAS_TOPOLOGY;
+ S390_lowcore.machine_flags &= ~MACHINE_FLAG_TOPOLOGY;
return rc;
}
early_param("topology", topology_setup);
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 3bc2825..a4fd000 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -24,6 +24,7 @@
#include <asm/setup.h>
#include <asm/nmi.h>
#include <asm/export.h>
+#include <asm/nospec-insn.h>
__PT_R0 = __PT_GPRS
__PT_R1 = __PT_GPRS + 8
@@ -105,6 +106,7 @@
j 3f
1: LAST_BREAK %r14
UPDATE_VTIME %r14,%r15,\timer
+ BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP
2: lg %r15,__LC_ASYNC_STACK # load async stack
3: la %r11,STACK_FRAME_OVERHEAD(%r15)
.endm
@@ -163,6 +165,72 @@
tm off+\addr, \mask
.endm
+ .macro BPOFF
+ .pushsection .altinstr_replacement, "ax"
+660: .long 0xb2e8c000
+ .popsection
+661: .long 0x47000000
+ .pushsection .altinstructions, "a"
+ .long 661b - .
+ .long 660b - .
+ .word 82
+ .byte 4
+ .byte 4
+ .popsection
+ .endm
+
+ .macro BPON
+ .pushsection .altinstr_replacement, "ax"
+662: .long 0xb2e8d000
+ .popsection
+663: .long 0x47000000
+ .pushsection .altinstructions, "a"
+ .long 663b - .
+ .long 662b - .
+ .word 82
+ .byte 4
+ .byte 4
+ .popsection
+ .endm
+
+ .macro BPENTER tif_ptr,tif_mask
+ .pushsection .altinstr_replacement, "ax"
+662: .word 0xc004, 0x0000, 0x0000 # 6 byte nop
+ .word 0xc004, 0x0000, 0x0000 # 6 byte nop
+ .popsection
+664: TSTMSK \tif_ptr,\tif_mask
+ jz . + 8
+ .long 0xb2e8d000
+ .pushsection .altinstructions, "a"
+ .long 664b - .
+ .long 662b - .
+ .word 82
+ .byte 12
+ .byte 12
+ .popsection
+ .endm
+
+ .macro BPEXIT tif_ptr,tif_mask
+ TSTMSK \tif_ptr,\tif_mask
+ .pushsection .altinstr_replacement, "ax"
+662: jnz . + 8
+ .long 0xb2e8d000
+ .popsection
+664: jz . + 8
+ .long 0xb2e8c000
+ .pushsection .altinstructions, "a"
+ .long 664b - .
+ .long 662b - .
+ .word 82
+ .byte 8
+ .byte 8
+ .popsection
+ .endm
+
+ GEN_BR_THUNK %r9
+ GEN_BR_THUNK %r14
+ GEN_BR_THUNK %r14,%r11
+
.section .kprobes.text, "ax"
.Ldummy:
/*
@@ -175,6 +243,11 @@
*/
nop 0
+ENTRY(__bpon)
+ .globl __bpon
+ BPON
+ BR_EX %r14
+
/*
* Scheduler resume function, called by switch_to
* gpr2 = (task_struct *) prev
@@ -201,9 +274,9 @@
mvc __LC_CURRENT_PID(4,%r0),__TASK_pid(%r3) # store pid of next
lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_LPP
- bzr %r14
+ jz 0f
.insn s,0xb2800000,__LC_LPP # set program parameter
- br %r14
+0: BR_EX %r14
.L__critical_start:
@@ -215,9 +288,11 @@
*/
ENTRY(sie64a)
stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers
+ lg %r12,__LC_CURRENT
stg %r2,__SF_EMPTY(%r15) # save control block pointer
stg %r3,__SF_EMPTY+8(%r15) # save guest register save area
xc __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # reason code = 0
+ mvc __SF_EMPTY+24(8,%r15),__TI_flags(%r12) # copy thread flags
TSTMSK __LC_CPU_FLAGS,_CIF_FPU # load guest fp/vx registers ?
jno .Lsie_load_guest_gprs
brasl %r14,load_fpu_regs # load guest fp/vx regs
@@ -234,7 +309,11 @@
jnz .Lsie_skip
TSTMSK __LC_CPU_FLAGS,_CIF_FPU
jo .Lsie_skip # exit if fp/vx regs changed
+ BPEXIT __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
sie 0(%r14)
+.Lsie_exit:
+ BPOFF
+ BPENTER __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
.Lsie_skip:
ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE
lctlg %c1,%c1,__LC_USER_ASCE # load primary asce
@@ -255,9 +334,15 @@
sie_exit:
lg %r14,__SF_EMPTY+8(%r15) # load guest register save area
stmg %r0,%r13,0(%r14) # save guest gprs 0-13
+ xgr %r0,%r0 # clear guest registers to
+ xgr %r1,%r1 # prevent speculative use
+ xgr %r2,%r2
+ xgr %r3,%r3
+ xgr %r4,%r4
+ xgr %r5,%r5
lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers
lg %r2,__SF_EMPTY+16(%r15) # return exit reason code
- br %r14
+ BR_EX %r14
.Lsie_fault:
lghi %r14,-EFAULT
stg %r14,__SF_EMPTY+16(%r15) # set exit reason code
@@ -280,6 +365,7 @@
stpt __LC_SYNC_ENTER_TIMER
.Lsysc_stmg:
stmg %r8,%r15,__LC_SAVE_AREA_SYNC
+ BPOFF
lg %r10,__LC_LAST_BREAK
lg %r12,__LC_THREAD_INFO
lghi %r14,_PIF_SYSCALL
@@ -289,12 +375,15 @@
LAST_BREAK %r13
.Lsysc_vtime:
UPDATE_VTIME %r10,%r13,__LC_SYNC_ENTER_TIMER
+ BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP
stmg %r0,%r7,__PT_R0(%r11)
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
mvc __PT_PSW(16,%r11),__LC_SVC_OLD_PSW
mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC
stg %r14,__PT_FLAGS(%r11)
.Lsysc_do_svc:
+ # clear user controlled register to prevent speculative use
+ xgr %r0,%r0
lg %r10,__TI_sysc_table(%r12) # address of system call table
llgh %r8,__PT_INT_CODE+2(%r11)
slag %r8,%r8,2 # shift and test for svc 0
@@ -312,7 +401,7 @@
lgf %r9,0(%r8,%r10) # get system call add.
TSTMSK __TI_flags(%r12),_TIF_TRACE
jnz .Lsysc_tracesys
- basr %r14,%r9 # call sys_xxxx
+ BASR_EX %r14,%r9 # call sys_xxxx
stg %r2,__PT_R2(%r11) # store return value
.Lsysc_return:
@@ -324,6 +413,7 @@
jnz .Lsysc_work # check for work
TSTMSK __LC_CPU_FLAGS,_CIF_WORK
jnz .Lsysc_work
+ BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
.Lsysc_restore:
lg %r14,__LC_VDSO_PER_CPU
lmg %r0,%r10,__PT_R0(%r11)
@@ -451,7 +541,7 @@
lmg %r3,%r7,__PT_R3(%r11)
stg %r7,STACK_FRAME_OVERHEAD(%r15)
lg %r2,__PT_ORIG_GPR2(%r11)
- basr %r14,%r9 # call sys_xxx
+ BASR_EX %r14,%r9 # call sys_xxx
stg %r2,__PT_R2(%r11) # store return value
.Lsysc_tracenogo:
TSTMSK __TI_flags(%r12),_TIF_TRACE
@@ -475,7 +565,7 @@
lmg %r9,%r10,__PT_R9(%r11) # load gprs
ENTRY(kernel_thread_starter)
la %r2,0(%r10)
- basr %r14,%r9
+ BASR_EX %r14,%r9
j .Lsysc_tracenogo
/*
@@ -484,6 +574,7 @@
ENTRY(pgm_check_handler)
stpt __LC_SYNC_ENTER_TIMER
+ BPOFF
stmg %r8,%r15,__LC_SAVE_AREA_SYNC
lg %r10,__LC_LAST_BREAK
lg %r12,__LC_THREAD_INFO
@@ -508,6 +599,7 @@
j 3f
2: LAST_BREAK %r14
UPDATE_VTIME %r14,%r15,__LC_SYNC_ENTER_TIMER
+ BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP
lg %r15,__LC_KERNEL_STACK
lg %r14,__TI_task(%r12)
aghi %r14,__TASK_thread # pointer to thread_struct
@@ -517,6 +609,15 @@
mvc __THREAD_trap_tdb(256,%r14),0(%r13)
3: la %r11,STACK_FRAME_OVERHEAD(%r15)
stmg %r0,%r7,__PT_R0(%r11)
+ # clear user controlled registers to prevent speculative use
+ xgr %r0,%r0
+ xgr %r1,%r1
+ xgr %r2,%r2
+ xgr %r3,%r3
+ xgr %r4,%r4
+ xgr %r5,%r5
+ xgr %r6,%r6
+ xgr %r7,%r7
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
stmg %r8,%r9,__PT_PSW(%r11)
mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC
@@ -538,9 +639,9 @@
nill %r10,0x007f
sll %r10,2
je .Lpgm_return
- lgf %r1,0(%r10,%r1) # load address of handler routine
+ lgf %r9,0(%r10,%r1) # load address of handler routine
lgr %r2,%r11 # pass pointer to pt_regs
- basr %r14,%r1 # branch to interrupt-handler
+ BASR_EX %r14,%r9 # branch to interrupt-handler
.Lpgm_return:
LOCKDEP_SYS_EXIT
tm __PT_PSW+1(%r11),0x01 # returning to user ?
@@ -573,6 +674,7 @@
ENTRY(io_int_handler)
STCK __LC_INT_CLOCK
stpt __LC_ASYNC_ENTER_TIMER
+ BPOFF
stmg %r8,%r15,__LC_SAVE_AREA_ASYNC
lg %r10,__LC_LAST_BREAK
lg %r12,__LC_THREAD_INFO
@@ -580,6 +682,16 @@
lmg %r8,%r9,__LC_IO_OLD_PSW
SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_ENTER_TIMER
stmg %r0,%r7,__PT_R0(%r11)
+ # clear user controlled registers to prevent speculative use
+ xgr %r0,%r0
+ xgr %r1,%r1
+ xgr %r2,%r2
+ xgr %r3,%r3
+ xgr %r4,%r4
+ xgr %r5,%r5
+ xgr %r6,%r6
+ xgr %r7,%r7
+ xgr %r10,%r10
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
stmg %r8,%r9,__PT_PSW(%r11)
mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID
@@ -614,9 +726,13 @@
lg %r14,__LC_VDSO_PER_CPU
lmg %r0,%r10,__PT_R0(%r11)
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jno .Lio_exit_kernel
+ BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
.Lio_exit_timer:
stpt __LC_EXIT_TIMER
mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
+.Lio_exit_kernel:
lmg %r11,%r15,__PT_R11(%r11)
lpswe __LC_RETURN_PSW
.Lio_done:
@@ -748,6 +864,7 @@
ENTRY(ext_int_handler)
STCK __LC_INT_CLOCK
stpt __LC_ASYNC_ENTER_TIMER
+ BPOFF
stmg %r8,%r15,__LC_SAVE_AREA_ASYNC
lg %r10,__LC_LAST_BREAK
lg %r12,__LC_THREAD_INFO
@@ -755,6 +872,16 @@
lmg %r8,%r9,__LC_EXT_OLD_PSW
SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_ENTER_TIMER
stmg %r0,%r7,__PT_R0(%r11)
+ # clear user controlled registers to prevent speculative use
+ xgr %r0,%r0
+ xgr %r1,%r1
+ xgr %r2,%r2
+ xgr %r3,%r3
+ xgr %r4,%r4
+ xgr %r5,%r5
+ xgr %r6,%r6
+ xgr %r7,%r7
+ xgr %r10,%r10
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
stmg %r8,%r9,__PT_PSW(%r11)
lghi %r1,__LC_EXT_PARAMS2
@@ -787,11 +914,12 @@
.Lpsw_idle_stcctm:
#endif
oi __LC_CPU_FLAGS+7,_CIF_ENABLED_WAIT
+ BPON
STCK __CLOCK_IDLE_ENTER(%r2)
stpt __TIMER_IDLE_ENTER(%r2)
.Lpsw_idle_lpsw:
lpswe __SF_EMPTY(%r15)
- br %r14
+ BR_EX %r14
.Lpsw_idle_end:
/*
@@ -805,7 +933,7 @@
lg %r2,__LC_CURRENT
aghi %r2,__TASK_thread
TSTMSK __LC_CPU_FLAGS,_CIF_FPU
- bor %r14
+ jo .Lsave_fpu_regs_exit
stfpc __THREAD_FPU_fpc(%r2)
.Lsave_fpu_regs_fpc_end:
lg %r3,__THREAD_FPU_regs(%r2)
@@ -835,7 +963,8 @@
std 15,120(%r3)
.Lsave_fpu_regs_done:
oi __LC_CPU_FLAGS+7,_CIF_FPU
- br %r14
+.Lsave_fpu_regs_exit:
+ BR_EX %r14
.Lsave_fpu_regs_end:
#if IS_ENABLED(CONFIG_KVM)
EXPORT_SYMBOL(save_fpu_regs)
@@ -855,7 +984,7 @@
lg %r4,__LC_CURRENT
aghi %r4,__TASK_thread
TSTMSK __LC_CPU_FLAGS,_CIF_FPU
- bnor %r14
+ jno .Lload_fpu_regs_exit
lfpc __THREAD_FPU_fpc(%r4)
TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX
lg %r4,__THREAD_FPU_regs(%r4) # %r4 <- reg save area
@@ -884,7 +1013,8 @@
ld 15,120(%r4)
.Lload_fpu_regs_done:
ni __LC_CPU_FLAGS+7,255-_CIF_FPU
- br %r14
+.Lload_fpu_regs_exit:
+ BR_EX %r14
.Lload_fpu_regs_end:
.L__critical_end:
@@ -894,6 +1024,7 @@
*/
ENTRY(mcck_int_handler)
STCK __LC_MCCK_CLOCK
+ BPOFF
la %r1,4095 # revalidate r1
spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # revalidate cpu timer
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# revalidate gprs
@@ -925,6 +1056,16 @@
.Lmcck_skip:
lghi %r14,__LC_GPREGS_SAVE_AREA+64
stmg %r0,%r7,__PT_R0(%r11)
+ # clear user controlled registers to prevent speculative use
+ xgr %r0,%r0
+ xgr %r1,%r1
+ xgr %r2,%r2
+ xgr %r3,%r3
+ xgr %r4,%r4
+ xgr %r5,%r5
+ xgr %r6,%r6
+ xgr %r7,%r7
+ xgr %r10,%r10
mvc __PT_R8(64,%r11),0(%r14)
stmg %r8,%r9,__PT_PSW(%r11)
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
@@ -950,6 +1091,7 @@
mvc __LC_RETURN_MCCK_PSW(16),__PT_PSW(%r11) # move return PSW
tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ?
jno 0f
+ BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
stpt __LC_EXIT_TIMER
mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
0: lmg %r11,%r15,__PT_R11(%r11)
@@ -1045,7 +1187,7 @@
jl 0f
clg %r9,BASED(.Lcleanup_table+104) # .Lload_fpu_regs_end
jl .Lcleanup_load_fpu_regs
-0: br %r14
+0: BR_EX %r14
.align 8
.Lcleanup_table:
@@ -1070,11 +1212,12 @@
.quad .Lsie_done
.Lcleanup_sie:
+ BPENTER __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
lg %r9,__SF_EMPTY(%r15) # get control block pointer
ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE
lctlg %c1,%c1,__LC_USER_ASCE # load primary asce
larl %r9,sie_exit # skip forward to sie_exit
- br %r14
+ BR_EX %r14
#endif
.Lcleanup_system_call:
@@ -1116,7 +1259,8 @@
srag %r9,%r9,23
jz 0f
mvc __TI_last_break(8,%r12),16(%r11)
-0: # set up saved register r11
+0: BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP
+ # set up saved register r11
lg %r15,__LC_KERNEL_STACK
la %r9,STACK_FRAME_OVERHEAD(%r15)
stg %r9,24(%r11) # r11 pt_regs pointer
@@ -1131,7 +1275,7 @@
stg %r15,56(%r11) # r15 stack pointer
# set new psw address and exit
larl %r9,.Lsysc_do_svc
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_system_call_insn:
.quad system_call
.quad .Lsysc_stmg
@@ -1141,7 +1285,7 @@
.Lcleanup_sysc_tif:
larl %r9,.Lsysc_tif
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_sysc_restore:
# check if stpt has been executed
@@ -1158,14 +1302,14 @@
mvc 0(64,%r11),__PT_R8(%r9)
lmg %r0,%r7,__PT_R0(%r9)
1: lmg %r8,%r9,__LC_RETURN_PSW
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_sysc_restore_insn:
.quad .Lsysc_exit_timer
.quad .Lsysc_done - 4
.Lcleanup_io_tif:
larl %r9,.Lio_tif
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_io_restore:
# check if stpt has been executed
@@ -1179,7 +1323,7 @@
mvc 0(64,%r11),__PT_R8(%r9)
lmg %r0,%r7,__PT_R0(%r9)
1: lmg %r8,%r9,__LC_RETURN_PSW
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_io_restore_insn:
.quad .Lio_exit_timer
.quad .Lio_done - 4
@@ -1232,17 +1376,17 @@
# prepare return psw
nihh %r8,0xfcfd # clear irq & wait state bits
lg %r9,48(%r11) # return from psw_idle
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_idle_insn:
.quad .Lpsw_idle_lpsw
.Lcleanup_save_fpu_regs:
larl %r9,save_fpu_regs
- br %r14
+ BR_EX %r14,%r11
.Lcleanup_load_fpu_regs:
larl %r9,load_fpu_regs
- br %r14
+ BR_EX %r14,%r11
/*
* Integer constants
@@ -1258,7 +1402,6 @@
.Lsie_critical_length:
.quad .Lsie_done - .Lsie_gmap
#endif
-
.section .rodata, "a"
#define SYSCALL(esame,emu) .long esame
.globl sys_call_table
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index 295bfb7..df49f2a1 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -563,6 +563,7 @@
static void __ipl_run(void *unused)
{
+ __bpon();
diag308(DIAG308_LOAD_CLEAR, NULL);
if (MACHINE_IS_VM)
__cpcmd("IPL", NULL, 0, NULL);
@@ -798,6 +799,7 @@
/* copy and convert to ebcdic */
memcpy(ipb->hdr.loadparm, buf, lp_len);
ASCEBC(ipb->hdr.loadparm, LOADPARM_LEN);
+ ipb->hdr.flags |= DIAG308_FLAGS_LP_VALID;
return len;
}
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index 285d656..7ff9767 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -173,10 +173,9 @@
new -= STACK_FRAME_OVERHEAD;
((struct stack_frame *) new)->back_chain = old;
asm volatile(" la 15,0(%0)\n"
- " basr 14,%2\n"
+ " brasl 14,__do_softirq\n"
" la 15,0(%1)\n"
- : : "a" (new), "a" (old),
- "a" (__do_softirq)
+ : : "a" (new), "a" (old)
: "0", "1", "2", "3", "4", "5", "14",
"cc", "memory" );
} else {
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index 9a17e44..be75e8e 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -8,13 +8,17 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/ftrace.h>
+#include <asm/nospec-insn.h>
#include <asm/ptrace.h>
#include <asm/export.h>
+ GEN_BR_THUNK %r1
+ GEN_BR_THUNK %r14
+
.section .kprobes.text, "ax"
ENTRY(ftrace_stub)
- br %r14
+ BR_EX %r14
#define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE)
#define STACK_PTREGS (STACK_FRAME_OVERHEAD)
@@ -22,7 +26,7 @@
#define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW)
ENTRY(_mcount)
- br %r14
+ BR_EX %r14
EXPORT_SYMBOL(_mcount)
@@ -52,7 +56,7 @@
#endif
lgr %r3,%r14
la %r5,STACK_PTREGS(%r15)
- basr %r14,%r1
+ BASR_EX %r14,%r1
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
# The j instruction gets runtime patched to a nop instruction.
# See ftrace_enable_ftrace_graph_caller.
@@ -67,7 +71,7 @@
#endif
lg %r1,(STACK_PTREGS_PSW+8)(%r15)
lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15)
- br %r1
+ BR_EX %r1
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -80,6 +84,6 @@
aghi %r15,STACK_FRAME_OVERHEAD
lgr %r14,%r2
lmg %r2,%r5,32(%r15)
- br %r14
+ BR_EX %r14
#endif
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
index fbc0789..64ccfdf 100644
--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -31,6 +31,9 @@
#include <linux/kernel.h>
#include <linux/moduleloader.h>
#include <linux/bug.h>
+#include <asm/alternative.h>
+#include <asm/nospec-branch.h>
+#include <asm/facility.h>
#if 0
#define DEBUGP printk
@@ -167,7 +170,11 @@
me->arch.got_offset = me->core_layout.size;
me->core_layout.size += me->arch.got_size;
me->arch.plt_offset = me->core_layout.size;
- me->core_layout.size += me->arch.plt_size;
+ if (me->arch.plt_size) {
+ if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable)
+ me->arch.plt_size += PLT_ENTRY_SIZE;
+ me->core_layout.size += me->arch.plt_size;
+ }
return 0;
}
@@ -321,9 +328,20 @@
unsigned int *ip;
ip = me->core_layout.base + me->arch.plt_offset +
info->plt_offset;
- ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
- ip[1] = 0x100a0004;
- ip[2] = 0x07f10000;
+ ip[0] = 0x0d10e310; /* basr 1,0 */
+ ip[1] = 0x100a0004; /* lg 1,10(1) */
+ if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) {
+ unsigned int *ij;
+ ij = me->core_layout.base +
+ me->arch.plt_offset +
+ me->arch.plt_size - PLT_ENTRY_SIZE;
+ ip[2] = 0xa7f40000 + /* j __jump_r1 */
+ (unsigned int)(u16)
+ (((unsigned long) ij - 8 -
+ (unsigned long) ip) / 2);
+ } else {
+ ip[2] = 0x07f10000; /* br %r1 */
+ }
ip[3] = (unsigned int) (val >> 32);
ip[4] = (unsigned int) val;
info->plt_initialized = 1;
@@ -428,6 +446,45 @@
const Elf_Shdr *sechdrs,
struct module *me)
{
+ const Elf_Shdr *s;
+ char *secstrings, *secname;
+ void *aseg;
+
+ if (IS_ENABLED(CONFIG_EXPOLINE) &&
+ !nospec_disable && me->arch.plt_size) {
+ unsigned int *ij;
+
+ ij = me->core_layout.base + me->arch.plt_offset +
+ me->arch.plt_size - PLT_ENTRY_SIZE;
+ if (test_facility(35)) {
+ ij[0] = 0xc6000000; /* exrl %r0,.+10 */
+ ij[1] = 0x0005a7f4; /* j . */
+ ij[2] = 0x000007f1; /* br %r1 */
+ } else {
+ ij[0] = 0x44000000 | (unsigned int)
+ offsetof(struct lowcore, br_r1_trampoline);
+ ij[1] = 0xa7f40000; /* j . */
+ }
+ }
+
+ secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+ for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
+ aseg = (void *) s->sh_addr;
+ secname = secstrings + s->sh_name;
+
+ if (!strcmp(".altinstructions", secname))
+ /* patch .altinstructions */
+ apply_alternatives(aseg, aseg + s->sh_size);
+
+ if (IS_ENABLED(CONFIG_EXPOLINE) &&
+ (!strncmp(".s390_indirect", secname, 14)))
+ nospec_revert(aseg, aseg + s->sh_size);
+
+ if (IS_ENABLED(CONFIG_EXPOLINE) &&
+ (!strncmp(".s390_return", secname, 12)))
+ nospec_revert(aseg, aseg + s->sh_size);
+ }
+
jump_label_apply_nops(me);
return 0;
}
diff --git a/arch/s390/kernel/nospec-branch.c b/arch/s390/kernel/nospec-branch.c
new file mode 100644
index 0000000..d5eed65
--- /dev/null
+++ b/arch/s390/kernel/nospec-branch.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/device.h>
+#include <asm/facility.h>
+#include <asm/nospec-branch.h>
+
+static int __init nobp_setup_early(char *str)
+{
+ bool enabled;
+ int rc;
+
+ rc = kstrtobool(str, &enabled);
+ if (rc)
+ return rc;
+ if (enabled && test_facility(82)) {
+ /*
+ * The user explicitely requested nobp=1, enable it and
+ * disable the expoline support.
+ */
+ __set_facility(82, S390_lowcore.alt_stfle_fac_list);
+ if (IS_ENABLED(CONFIG_EXPOLINE))
+ nospec_disable = 1;
+ } else {
+ __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+ }
+ return 0;
+}
+early_param("nobp", nobp_setup_early);
+
+static int __init nospec_setup_early(char *str)
+{
+ __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+ return 0;
+}
+early_param("nospec", nospec_setup_early);
+
+static int __init nospec_report(void)
+{
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
+ pr_info("Spectre V2 mitigation: execute trampolines.\n");
+ if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
+ pr_info("Spectre V2 mitigation: limited branch prediction.\n");
+ return 0;
+}
+arch_initcall(nospec_report);
+
+#ifdef CONFIG_EXPOLINE
+
+int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
+
+static int __init nospectre_v2_setup_early(char *str)
+{
+ nospec_disable = 1;
+ return 0;
+}
+early_param("nospectre_v2", nospectre_v2_setup_early);
+
+void __init nospec_auto_detect(void)
+{
+ if (IS_ENABLED(CC_USING_EXPOLINE)) {
+ /*
+ * The kernel has been compiled with expolines.
+ * Keep expolines enabled and disable nobp.
+ */
+ nospec_disable = 0;
+ __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+ }
+ /*
+ * If the kernel has not been compiled with expolines the
+ * nobp setting decides what is done, this depends on the
+ * CONFIG_KERNEL_NP option and the nobp/nospec parameters.
+ */
+}
+
+static int __init spectre_v2_setup_early(char *str)
+{
+ if (str && !strncmp(str, "on", 2)) {
+ nospec_disable = 0;
+ __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+ }
+ if (str && !strncmp(str, "off", 3))
+ nospec_disable = 1;
+ if (str && !strncmp(str, "auto", 4))
+ nospec_auto_detect();
+ return 0;
+}
+early_param("spectre_v2", spectre_v2_setup_early);
+
+static void __init_or_module __nospec_revert(s32 *start, s32 *end)
+{
+ enum { BRCL_EXPOLINE, BRASL_EXPOLINE } type;
+ u8 *instr, *thunk, *br;
+ u8 insnbuf[6];
+ s32 *epo;
+
+ /* Second part of the instruction replace is always a nop */
+ for (epo = start; epo < end; epo++) {
+ instr = (u8 *) epo + *epo;
+ if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
+ type = BRCL_EXPOLINE; /* brcl instruction */
+ else if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x05)
+ type = BRASL_EXPOLINE; /* brasl instruction */
+ else
+ continue;
+ thunk = instr + (*(int *)(instr + 2)) * 2;
+ if (thunk[0] == 0xc6 && thunk[1] == 0x00)
+ /* exrl %r0,<target-br> */
+ br = thunk + (*(int *)(thunk + 2)) * 2;
+ else if (thunk[0] == 0xc0 && (thunk[1] & 0x0f) == 0x00 &&
+ thunk[6] == 0x44 && thunk[7] == 0x00 &&
+ (thunk[8] & 0x0f) == 0x00 && thunk[9] == 0x00 &&
+ (thunk[1] & 0xf0) == (thunk[8] & 0xf0))
+ /* larl %rx,<target br> + ex %r0,0(%rx) */
+ br = thunk + (*(int *)(thunk + 2)) * 2;
+ else
+ continue;
+ /* Check for unconditional branch 0x07f? or 0x47f???? */
+ if ((br[0] & 0xbf) != 0x07 || (br[1] & 0xf0) != 0xf0)
+ continue;
+
+ memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x07, 0x00 }, 4);
+ switch (type) {
+ case BRCL_EXPOLINE:
+ insnbuf[0] = br[0];
+ insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
+ if (br[0] == 0x47) {
+ /* brcl to b, replace with bc + nopr */
+ insnbuf[2] = br[2];
+ insnbuf[3] = br[3];
+ } else {
+ /* brcl to br, replace with bcr + nop */
+ }
+ break;
+ case BRASL_EXPOLINE:
+ insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
+ if (br[0] == 0x47) {
+ /* brasl to b, replace with bas + nopr */
+ insnbuf[0] = 0x4d;
+ insnbuf[2] = br[2];
+ insnbuf[3] = br[3];
+ } else {
+ /* brasl to br, replace with basr + nop */
+ insnbuf[0] = 0x0d;
+ }
+ break;
+ }
+
+ s390_kernel_write(instr, insnbuf, 6);
+ }
+}
+
+void __init_or_module nospec_revert(s32 *start, s32 *end)
+{
+ if (nospec_disable)
+ __nospec_revert(start, end);
+}
+
+extern s32 __nospec_call_start[], __nospec_call_end[];
+extern s32 __nospec_return_start[], __nospec_return_end[];
+void __init nospec_init_branches(void)
+{
+ nospec_revert(__nospec_call_start, __nospec_call_end);
+ nospec_revert(__nospec_return_start, __nospec_return_end);
+}
+
+#endif /* CONFIG_EXPOLINE */
diff --git a/arch/s390/kernel/nospec-sysfs.c b/arch/s390/kernel/nospec-sysfs.c
new file mode 100644
index 0000000..8affad5
--- /dev/null
+++ b/arch/s390/kernel/nospec-sysfs.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/device.h>
+#include <linux/cpu.h>
+#include <asm/facility.h>
+#include <asm/nospec-branch.h>
+
+ssize_t cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Mitigation: __user pointer sanitization\n");
+}
+
+ssize_t cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
+ return sprintf(buf, "Mitigation: execute trampolines\n");
+ if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
+ return sprintf(buf, "Mitigation: limited branch prediction\n");
+ return sprintf(buf, "Vulnerable\n");
+}
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index fcc634c..96e4fca 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -739,6 +739,10 @@
*/
rate = 0;
if (attr->freq) {
+ if (!attr->sample_freq) {
+ err = -EINVAL;
+ goto out;
+ }
rate = freq_to_sample_rate(&si, attr->sample_freq);
rate = hw_limit_rate(&si, rate);
attr->freq = 0;
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 81d0808..d856263 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -179,3 +179,21 @@
.stop = c_stop,
.show = show_cpuinfo,
};
+
+int s390_isolate_bp(void)
+{
+ if (!test_facility(82))
+ return -EOPNOTSUPP;
+ set_thread_flag(TIF_ISOLATE_BP);
+ return 0;
+}
+EXPORT_SYMBOL(s390_isolate_bp);
+
+int s390_isolate_bp_guest(void)
+{
+ if (!test_facility(82))
+ return -EOPNOTSUPP;
+ set_thread_flag(TIF_ISOLATE_BP_GUEST);
+ return 0;
+}
+EXPORT_SYMBOL(s390_isolate_bp_guest);
diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S
index 89ea8c2..70d635d 100644
--- a/arch/s390/kernel/reipl.S
+++ b/arch/s390/kernel/reipl.S
@@ -6,8 +6,11 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
+#include <asm/nospec-insn.h>
#include <asm/sigp.h>
+ GEN_BR_THUNK %r9
+
#
# Issue "store status" for the current CPU to its prefix page
# and call passed function afterwards
@@ -66,9 +69,9 @@
st %r4,0(%r1)
st %r5,4(%r1)
stg %r2,8(%r1)
- lgr %r1,%r2
+ lgr %r9,%r2
lgr %r2,%r3
- br %r1
+ BR_EX %r9
.section .bss
.align 8
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index e974e53..feb9d97 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -63,6 +63,8 @@
#include <asm/sclp.h>
#include <asm/sysinfo.h>
#include <asm/numa.h>
+#include <asm/alternative.h>
+#include <asm/nospec-branch.h>
#include "entry.h"
/*
@@ -335,7 +337,9 @@
lc->machine_flags = S390_lowcore.machine_flags;
lc->stfl_fac_list = S390_lowcore.stfl_fac_list;
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
- MAX_FACILITY_BIT/8);
+ sizeof(lc->stfle_fac_list));
+ memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
+ sizeof(lc->alt_stfle_fac_list));
if (MACHINE_HAS_VX)
lc->vector_save_area_addr =
(unsigned long) &lc->vector_save_area;
@@ -372,6 +376,7 @@
#ifdef CONFIG_SMP
lc->spinlock_lockval = arch_spin_lockval(0);
#endif
+ lc->br_r1_trampoline = 0x07f1; /* br %r1 */
set_prefix((u32)(unsigned long) lc);
lowcore_ptr[0] = lc;
@@ -871,6 +876,9 @@
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
+ if (IS_ENABLED(CONFIG_EXPOLINE_AUTO))
+ nospec_auto_detect();
+
parse_early_param();
#ifdef CONFIG_CRASH_DUMP
/* Deactivate elfcorehdr= kernel parameter */
@@ -931,6 +939,10 @@
conmode_default();
set_preferred_console();
+ apply_alternative_instructions();
+ if (IS_ENABLED(CONFIG_EXPOLINE))
+ nospec_init_branches();
+
/* Setup zfcpdump support */
setup_zfcpdump();
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 35531fe..0a31110 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -205,6 +205,7 @@
lc->panic_stack = panic_stack + PANIC_FRAME_OFFSET;
lc->cpu_nr = cpu;
lc->spinlock_lockval = arch_spin_lockval(cpu);
+ lc->br_r1_trampoline = 0x07f1; /* br %r1 */
if (MACHINE_HAS_VX)
lc->vector_save_area_addr =
(unsigned long) &lc->vector_save_area;
@@ -253,7 +254,9 @@
__ctl_store(lc->cregs_save_area, 0, 15);
save_access_regs((unsigned int *) lc->access_regs_save_area);
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
- MAX_FACILITY_BIT/8);
+ sizeof(lc->stfle_fac_list));
+ memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
+ sizeof(lc->alt_stfle_fac_list));
}
static void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk)
@@ -302,6 +305,7 @@
mem_assign_absolute(lc->restart_fn, (unsigned long) func);
mem_assign_absolute(lc->restart_data, (unsigned long) data);
mem_assign_absolute(lc->restart_source, source_cpu);
+ __bpon();
asm volatile(
"0: sigp 0,%0,%2 # sigp restart to target cpu\n"
" brc 2,0b # busy, try again\n"
@@ -875,6 +879,7 @@
void __noreturn cpu_die(void)
{
idle_task_exit();
+ __bpon();
pcpu_sigp_retry(pcpu_devices + smp_processor_id(), SIGP_STOP, 0);
for (;;) ;
}
diff --git a/arch/s390/kernel/swsusp.S b/arch/s390/kernel/swsusp.S
index 2d6b6e8..4e76aaf 100644
--- a/arch/s390/kernel/swsusp.S
+++ b/arch/s390/kernel/swsusp.S
@@ -12,6 +12,7 @@
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
+#include <asm/nospec-insn.h>
#include <asm/sigp.h>
/*
@@ -23,6 +24,8 @@
* (see below) in the resume process.
* This function runs with disabled interrupts.
*/
+ GEN_BR_THUNK %r14
+
.section .text
ENTRY(swsusp_arch_suspend)
stmg %r6,%r15,__SF_GPRS(%r15)
@@ -102,7 +105,7 @@
spx 0x318(%r1)
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
lghi %r2,0
- br %r14
+ BR_EX %r14
/*
* Restore saved memory image to correct place and restore register context.
@@ -200,7 +203,7 @@
lghi %r1,0
sam31
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
- basr %r14,%r3
+ brasl %r14,_sclp_print_early
larl %r3,.Ldisabled_wait_31
lpsw 0(%r3)
4:
@@ -266,7 +269,7 @@
/* Return 0 */
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
lghi %r2,0
- br %r14
+ BR_EX %r14
.section .data..nosave,"aw",@progbits
.align 8
diff --git a/arch/s390/kernel/uprobes.c b/arch/s390/kernel/uprobes.c
index 66956c0..3d04dfd 100644
--- a/arch/s390/kernel/uprobes.c
+++ b/arch/s390/kernel/uprobes.c
@@ -147,6 +147,15 @@
return orig;
}
+bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
+ struct pt_regs *regs)
+{
+ if (ctx == RP_CHECK_CHAIN_CALL)
+ return user_stack_pointer(regs) <= ret->stack;
+ else
+ return user_stack_pointer(regs) < ret->stack;
+}
+
/* Instruction Emulation */
static void adjust_psw_addr(psw_t *psw, unsigned long len)
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index 3667d20..dd96b46 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -31,8 +31,14 @@
{
. = 0x00000000;
.text : {
- _text = .; /* Text and read-only data */
+ /* Text and read-only data */
HEAD_TEXT
+ /*
+ * E.g. perf doesn't like symbols starting at address zero,
+ * therefore skip the initial PSW and channel program located
+ * at address zero and let _text start at 0x200.
+ */
+ _text = 0x200;
TEXT_TEXT
SCHED_TEXT
CPUIDLE_TEXT
@@ -93,6 +99,43 @@
EXIT_DATA
}
+ /*
+ * struct alt_inst entries. From the header (alternative.h):
+ * "Alternative instructions for different CPU types or capabilities"
+ * Think locking instructions on spinlocks.
+ * Note, that it is a part of __init region.
+ */
+ . = ALIGN(8);
+ .altinstructions : {
+ __alt_instructions = .;
+ *(.altinstructions)
+ __alt_instructions_end = .;
+ }
+
+ /*
+ * And here are the replacement instructions. The linker sticks
+ * them as binary blobs. The .altinstructions has enough data to
+ * get the address and the length of them to patch the kernel safely.
+ * Note, that it is a part of __init region.
+ */
+ .altinstr_replacement : {
+ *(.altinstr_replacement)
+ }
+
+ /*
+ * Table with the patch locations to undo expolines
+ */
+ .nospec_call_table : {
+ __nospec_call_start = . ;
+ *(.s390_indirect*)
+ __nospec_call_end = . ;
+ }
+ .nospec_return_table : {
+ __nospec_return_start = . ;
+ *(.s390_return*)
+ __nospec_return_end = . ;
+ }
+
/* early.c uses stsi, which requires page aligned data. */
. = ALIGN(PAGE_SIZE);
INIT_DATA_SECTION(0x100)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 5ba494e..2032ab8 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -401,6 +401,9 @@
case KVM_CAP_S390_RI:
r = test_facility(64);
break;
+ case KVM_CAP_S390_BPB:
+ r = test_facility(82);
+ break;
default:
r = 0;
}
@@ -1601,6 +1604,7 @@
/* we still need the basic sca for the ipte control */
vcpu->arch.sie_block->scaoh = (__u32)(((__u64)sca) >> 32);
vcpu->arch.sie_block->scaol = (__u32)(__u64)sca;
+ return;
}
read_lock(&vcpu->kvm->arch.sca_lock);
if (vcpu->kvm->arch.use_esca) {
@@ -1712,6 +1716,8 @@
kvm_s390_set_prefix(vcpu, 0);
if (test_kvm_facility(vcpu->kvm, 64))
vcpu->run->kvm_valid_regs |= KVM_SYNC_RICCB;
+ if (test_kvm_facility(vcpu->kvm, 82))
+ vcpu->run->kvm_valid_regs |= KVM_SYNC_BPBC;
/* fprs can be synchronized via vrs, even if the guest has no vx. With
* MACHINE_HAS_VX, (load|store)_fpu_regs() will work with vrs format.
*/
@@ -1828,7 +1834,6 @@
if (test_fp_ctl(current->thread.fpu.fpc))
/* User space provided an invalid FPC, let's clear it */
current->thread.fpu.fpc = 0;
-
save_access_regs(vcpu->arch.host_acrs);
restore_access_regs(vcpu->run->s.regs.acrs);
gmap_enable(vcpu->arch.enabled_gmap);
@@ -1876,6 +1881,7 @@
current->thread.fpu.fpc = 0;
vcpu->arch.sie_block->gbea = 1;
vcpu->arch.sie_block->pp = 0;
+ vcpu->arch.sie_block->fpf &= ~FPF_BPBC;
vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
kvm_clear_async_pf_completion_queue(vcpu);
if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm))
@@ -2743,6 +2749,11 @@
if (riccb->valid)
vcpu->arch.sie_block->ecb3 |= 0x01;
}
+ if ((kvm_run->kvm_dirty_regs & KVM_SYNC_BPBC) &&
+ test_kvm_facility(vcpu->kvm, 82)) {
+ vcpu->arch.sie_block->fpf &= ~FPF_BPBC;
+ vcpu->arch.sie_block->fpf |= kvm_run->s.regs.bpbc ? FPF_BPBC : 0;
+ }
kvm_run->kvm_dirty_regs = 0;
}
@@ -2761,6 +2772,7 @@
kvm_run->s.regs.pft = vcpu->arch.pfault_token;
kvm_run->s.regs.pfs = vcpu->arch.pfault_select;
kvm_run->s.regs.pfc = vcpu->arch.pfault_compare;
+ kvm_run->s.regs.bpbc = (vcpu->arch.sie_block->fpf & FPF_BPBC) == FPF_BPBC;
}
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c
index d8673e2..ced6c9b 100644
--- a/arch/s390/kvm/vsie.c
+++ b/arch/s390/kvm/vsie.c
@@ -217,6 +217,12 @@
memcpy(scb_o->gcr, scb_s->gcr, 128);
scb_o->pp = scb_s->pp;
+ /* branch prediction */
+ if (test_kvm_facility(vcpu->kvm, 82)) {
+ scb_o->fpf &= ~FPF_BPBC;
+ scb_o->fpf |= scb_s->fpf & FPF_BPBC;
+ }
+
/* interrupt intercept */
switch (scb_s->icptcode) {
case ICPT_PROGI:
@@ -259,6 +265,7 @@
scb_s->ecb3 = 0;
scb_s->ecd = 0;
scb_s->fac = 0;
+ scb_s->fpf = 0;
rc = prepare_cpuflags(vcpu, vsie_page);
if (rc)
@@ -316,6 +323,9 @@
prefix_unmapped(vsie_page);
scb_s->ecb |= scb_o->ecb & 0x10U;
}
+ /* branch prediction */
+ if (test_kvm_facility(vcpu->kvm, 82))
+ scb_s->fpf |= scb_o->fpf & FPF_BPBC;
/* SIMD */
if (test_kvm_facility(vcpu->kvm, 129)) {
scb_s->eca |= scb_o->eca & 0x00020000U;
@@ -754,6 +764,7 @@
{
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
struct kvm_s390_sie_block *scb_o = vsie_page->scb_o;
+ int guest_bp_isolation;
int rc;
handle_last_fault(vcpu, vsie_page);
@@ -764,6 +775,20 @@
s390_handle_mcck();
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
+
+ /* save current guest state of bp isolation override */
+ guest_bp_isolation = test_thread_flag(TIF_ISOLATE_BP_GUEST);
+
+ /*
+ * The guest is running with BPBC, so we have to force it on for our
+ * nested guest. This is done by enabling BPBC globally, so the BPBC
+ * control in the SCB (which the nested guest can modify) is simply
+ * ignored.
+ */
+ if (test_kvm_facility(vcpu->kvm, 82) &&
+ vcpu->arch.sie_block->fpf & FPF_BPBC)
+ set_thread_flag(TIF_ISOLATE_BP_GUEST);
+
local_irq_disable();
guest_enter_irqoff();
local_irq_enable();
@@ -773,6 +798,11 @@
local_irq_disable();
guest_exit_irqoff();
local_irq_enable();
+
+ /* restore guest state for bp isolation override */
+ if (!guest_bp_isolation)
+ clear_thread_flag(TIF_ISOLATE_BP_GUEST);
+
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
if (rc > 0)
diff --git a/arch/s390/lib/mem.S b/arch/s390/lib/mem.S
index be9fa65..e7672ed 100644
--- a/arch/s390/lib/mem.S
+++ b/arch/s390/lib/mem.S
@@ -6,6 +6,9 @@
#include <linux/linkage.h>
#include <asm/export.h>
+#include <asm/nospec-insn.h>
+
+ GEN_BR_THUNK %r14
/*
* memset implementation
@@ -39,7 +42,7 @@
.Lmemset_clear_rest:
larl %r3,.Lmemset_xc
ex %r4,0(%r3)
- br %r14
+ BR_EX %r14
.Lmemset_fill:
stc %r3,0(%r2)
cghi %r4,1
@@ -56,7 +59,7 @@
.Lmemset_fill_rest:
larl %r3,.Lmemset_mvc
ex %r4,0(%r3)
- br %r14
+ BR_EX %r14
.Lmemset_xc:
xc 0(1,%r1),0(%r1)
.Lmemset_mvc:
@@ -79,7 +82,7 @@
.Lmemcpy_rest:
larl %r5,.Lmemcpy_mvc
ex %r4,0(%r5)
- br %r14
+ BR_EX %r14
.Lmemcpy_loop:
mvc 0(256,%r1),0(%r3)
la %r1,256(%r1)
diff --git a/arch/s390/net/bpf_jit.S b/arch/s390/net/bpf_jit.S
index a1c917d..fa716f2 100644
--- a/arch/s390/net/bpf_jit.S
+++ b/arch/s390/net/bpf_jit.S
@@ -8,6 +8,7 @@
*/
#include <linux/linkage.h>
+#include <asm/nospec-insn.h>
#include "bpf_jit.h"
/*
@@ -53,7 +54,7 @@
clg %r3,STK_OFF_HLEN(%r15); /* Offset + SIZE > hlen? */ \
jh sk_load_##NAME##_slow; \
LOAD %r14,-SIZE(%r3,%r12); /* Get data from skb */ \
- b OFF_OK(%r6); /* Return */ \
+ B_EX OFF_OK,%r6; /* Return */ \
\
sk_load_##NAME##_slow:; \
lgr %r2,%r7; /* Arg1 = skb pointer */ \
@@ -63,11 +64,14 @@
brasl %r14,skb_copy_bits; /* Get data from skb */ \
LOAD %r14,STK_OFF_TMP(%r15); /* Load from temp bufffer */ \
ltgr %r2,%r2; /* Set cc to (%r2 != 0) */ \
- br %r6; /* Return */
+ BR_EX %r6; /* Return */
sk_load_common(word, 4, llgf) /* r14 = *(u32 *) (skb->data+offset) */
sk_load_common(half, 2, llgh) /* r14 = *(u16 *) (skb->data+offset) */
+ GEN_BR_THUNK %r6
+ GEN_B_THUNK OFF_OK,%r6
+
/*
* Load 1 byte from SKB (optimized version)
*/
@@ -79,7 +83,7 @@
clg %r3,STK_OFF_HLEN(%r15) # Offset >= hlen?
jnl sk_load_byte_slow
llgc %r14,0(%r3,%r12) # Get byte from skb
- b OFF_OK(%r6) # Return OK
+ B_EX OFF_OK,%r6 # Return OK
sk_load_byte_slow:
lgr %r2,%r7 # Arg1 = skb pointer
@@ -89,7 +93,7 @@
brasl %r14,skb_copy_bits # Get data from skb
llgc %r14,STK_OFF_TMP(%r15) # Load result from temp buffer
ltgr %r2,%r2 # Set cc to (%r2 != 0)
- br %r6 # Return cc
+ BR_EX %r6 # Return cc
#define sk_negative_common(NAME, SIZE, LOAD) \
sk_load_##NAME##_slow_neg:; \
@@ -103,7 +107,7 @@
jz bpf_error; \
LOAD %r14,0(%r2); /* Get data from pointer */ \
xr %r3,%r3; /* Set cc to zero */ \
- br %r6; /* Return cc */
+ BR_EX %r6; /* Return cc */
sk_negative_common(word, 4, llgf)
sk_negative_common(half, 2, llgh)
@@ -112,4 +116,4 @@
bpf_error:
# force a return 0 from jit handler
ltgr %r15,%r15 # Set condition code
- br %r6
+ BR_EX %r6
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index e8dee62..e7ce257 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -24,6 +24,8 @@
#include <linux/bpf.h>
#include <asm/cacheflush.h>
#include <asm/dis.h>
+#include <asm/facility.h>
+#include <asm/nospec-branch.h>
#include "bpf_jit.h"
int bpf_jit_enable __read_mostly;
@@ -41,6 +43,8 @@
int base_ip; /* Base address for literal pool */
int ret0_ip; /* Address of return 0 */
int exit_ip; /* Address of exit */
+ int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */
+ int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */
int tail_call_start; /* Tail call start offset */
int labels[1]; /* Labels for local jumps */
};
@@ -251,6 +255,19 @@
REG_SET_SEEN(b2); \
})
+#define EMIT6_PCREL_RILB(op, b, target) \
+({ \
+ int rel = (target - jit->prg) / 2; \
+ _EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff); \
+ REG_SET_SEEN(b); \
+})
+
+#define EMIT6_PCREL_RIL(op, target) \
+({ \
+ int rel = (target - jit->prg) / 2; \
+ _EMIT6(op | rel >> 16, rel & 0xffff); \
+})
+
#define _EMIT6_IMM(op, imm) \
({ \
unsigned int __imm = (imm); \
@@ -470,8 +487,45 @@
EMIT4(0xb9040000, REG_2, BPF_REG_0);
/* Restore registers */
save_restore_regs(jit, REGS_RESTORE);
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) {
+ jit->r14_thunk_ip = jit->prg;
+ /* Generate __s390_indirect_jump_r14 thunk */
+ if (test_facility(35)) {
+ /* exrl %r0,.+10 */
+ EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
+ } else {
+ /* larl %r1,.+14 */
+ EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14);
+ /* ex 0,0(%r1) */
+ EMIT4_DISP(0x44000000, REG_0, REG_1, 0);
+ }
+ /* j . */
+ EMIT4_PCREL(0xa7f40000, 0);
+ }
/* br %r14 */
_EMIT2(0x07fe);
+
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable &&
+ (jit->seen & SEEN_FUNC)) {
+ jit->r1_thunk_ip = jit->prg;
+ /* Generate __s390_indirect_jump_r1 thunk */
+ if (test_facility(35)) {
+ /* exrl %r0,.+10 */
+ EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
+ /* j . */
+ EMIT4_PCREL(0xa7f40000, 0);
+ /* br %r1 */
+ _EMIT2(0x07f1);
+ } else {
+ /* larl %r1,.+14 */
+ EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14);
+ /* ex 0,S390_lowcore.br_r1_tampoline */
+ EMIT4_DISP(0x44000000, REG_0, REG_0,
+ offsetof(struct lowcore, br_r1_trampoline));
+ /* j . */
+ EMIT4_PCREL(0xa7f40000, 0);
+ }
+ }
}
/*
@@ -977,8 +1031,13 @@
/* lg %w1,<d(imm)>(%l) */
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L,
EMIT_CONST_U64(func));
- /* basr %r14,%w1 */
- EMIT2(0x0d00, REG_14, REG_W1);
+ if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) {
+ /* brasl %r14,__s390_indirect_jump_r1 */
+ EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip);
+ } else {
+ /* basr %r14,%w1 */
+ EMIT2(0x0d00, REG_14, REG_W1);
+ }
/* lgr %b0,%r2: load return value into %b0 */
EMIT4(0xb9040000, BPF_REG_0, REG_2);
if (bpf_helper_changes_skb_data((void *)func)) {
diff --git a/arch/sh/boards/mach-se/770x/setup.c b/arch/sh/boards/mach-se/770x/setup.c
index 658326f..5e02676 100644
--- a/arch/sh/boards/mach-se/770x/setup.c
+++ b/arch/sh/boards/mach-se/770x/setup.c
@@ -8,6 +8,7 @@
*/
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/sh_eth.h>
#include <mach-se/mach/se.h>
#include <mach-se/mach/mrshpc.h>
#include <asm/machvec.h>
@@ -114,6 +115,11 @@
#if defined(CONFIG_CPU_SUBTYPE_SH7710) ||\
defined(CONFIG_CPU_SUBTYPE_SH7712)
/* SH771X Ethernet driver */
+static struct sh_eth_plat_data sh_eth_plat = {
+ .phy = PHY_ID,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
+};
+
static struct resource sh_eth0_resources[] = {
[0] = {
.start = SH_ETH0_BASE,
@@ -131,7 +137,7 @@
.name = "sh771x-ether",
.id = 0,
.dev = {
- .platform_data = PHY_ID,
+ .platform_data = &sh_eth_plat,
},
.num_resources = ARRAY_SIZE(sh_eth0_resources),
.resource = sh_eth0_resources,
@@ -154,7 +160,7 @@
.name = "sh771x-ether",
.id = 1,
.dev = {
- .platform_data = PHY_ID,
+ .platform_data = &sh_eth_plat,
},
.num_resources = ARRAY_SIZE(sh_eth1_resources),
.resource = sh_eth1_resources,
diff --git a/arch/sh/include/asm/futex.h b/arch/sh/include/asm/futex.h
index d007874..8f8cf94 100644
--- a/arch/sh/include/asm/futex.h
+++ b/arch/sh/include/asm/futex.h
@@ -27,21 +27,12 @@
return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval);
}
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- u32 oparg = (encoded_op << 8) >> 20;
- u32 cmparg = (encoded_op << 20) >> 20;
u32 oldval, newval, prev;
int ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
-
pagefault_disable();
do {
@@ -80,17 +71,8 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = ((int)oldval < (int)cmparg); break;
- case FUTEX_OP_CMP_GE: ret = ((int)oldval >= (int)cmparg); break;
- case FUTEX_OP_CMP_LE: ret = ((int)oldval <= (int)cmparg); break;
- case FUTEX_OP_CMP_GT: ret = ((int)oldval > (int)cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
return ret;
}
diff --git a/arch/sparc/include/asm/futex_64.h b/arch/sparc/include/asm/futex_64.h
index 4e899b0..1cfd89d 100644
--- a/arch/sparc/include/asm/futex_64.h
+++ b/arch/sparc/include/asm/futex_64.h
@@ -29,22 +29,14 @@
: "r" (uaddr), "r" (oparg), "i" (-EFAULT) \
: "memory")
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret, tem;
- if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))))
- return -EFAULT;
if (unlikely((((unsigned long) uaddr) & 0x3UL)))
return -EINVAL;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
pagefault_disable();
switch (op) {
@@ -69,17 +61,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c
index 59d5038..9cc600b 100644
--- a/arch/sparc/kernel/ldc.c
+++ b/arch/sparc/kernel/ldc.c
@@ -1733,9 +1733,14 @@
lp->rcv_nxt = p->seqid;
+ /*
+ * If this is a control-only packet, there is nothing
+ * else to do but advance the rx queue since the packet
+ * was already processed above.
+ */
if (!(p->type & LDC_DATA)) {
new = rx_advance(lp, new);
- goto no_data;
+ break;
}
if (p->stype & (LDC_ACK | LDC_NACK)) {
err = data_ack_nack(lp, p);
diff --git a/arch/tile/include/asm/futex.h b/arch/tile/include/asm/futex.h
index e64a1b7..83c1e63 100644
--- a/arch/tile/include/asm/futex.h
+++ b/arch/tile/include/asm/futex.h
@@ -106,12 +106,9 @@
lock = __atomic_hashed_lock((int __force *)uaddr)
#endif
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int uninitialized_var(val), ret;
__futex_prolog();
@@ -119,12 +116,6 @@
/* The 32-bit futex code makes this assumption, so validate it here. */
BUILD_BUG_ON(sizeof(atomic_t) != sizeof(int));
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
-
pagefault_disable();
switch (op) {
case FUTEX_OP_SET:
@@ -148,30 +139,9 @@
}
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ:
- ret = (val == cmparg);
- break;
- case FUTEX_OP_CMP_NE:
- ret = (val != cmparg);
- break;
- case FUTEX_OP_CMP_LT:
- ret = (val < cmparg);
- break;
- case FUTEX_OP_CMP_GE:
- ret = (val >= cmparg);
- break;
- case FUTEX_OP_CMP_LE:
- ret = (val <= cmparg);
- break;
- case FUTEX_OP_CMP_GT:
- ret = (val > cmparg);
- break;
- default:
- ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = val;
+
return ret;
}
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
index 2db18cb..c019709 100644
--- a/arch/um/os-Linux/file.c
+++ b/arch/um/os-Linux/file.c
@@ -12,6 +12,7 @@
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/un.h>
#include <sys/types.h>
#include <os.h>
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index a86d7cc..bf0acb8 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -16,6 +16,7 @@
#include <os.h>
#include <sysdep/mcontext.h>
#include <um_malloc.h>
+#include <sys/ucontext.h>
void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
[SIGTRAP] = relay_signal,
@@ -159,7 +160,7 @@
static void hard_handler(int sig, siginfo_t *si, void *p)
{
- struct ucontext *uc = p;
+ ucontext_t *uc = p;
mcontext_t *mc = &uc->uc_mcontext;
unsigned long pending = 1UL << sig;
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index ea24114..93b170e 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -193,6 +193,15 @@
LDFLAGS := -m elf_$(UTS_MACHINE)
+#
+# The 64-bit kernel must be aligned to 2MB. Pass -z max-page-size=0x200000 to
+# the linker to force 2MB page size regardless of the default page size used
+# by the linker.
+#
+ifdef CONFIG_X86_64
+LDFLAGS += $(call ld-option, -z max-page-size=0x200000)
+endif
+
# Speed up the build
KBUILD_CFLAGS += -pipe
# Workaround for a gcc prelease that unfortunately was shipped in a suse release
@@ -205,7 +214,10 @@
# Avoid indirect branches in kernel to deal with Spectre
ifdef CONFIG_RETPOLINE
- RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch=thunk-extern -mindirect-branch-register)
+ RETPOLINE_CFLAGS_GCC := -mindirect-branch=thunk-extern -mindirect-branch-register
+ RETPOLINE_CFLAGS_CLANG := -mretpoline-external-thunk
+
+ RETPOLINE_CFLAGS += $(call cc-option,$(RETPOLINE_CFLAGS_GCC),$(call cc-option,$(RETPOLINE_CFLAGS_CLANG)))
ifneq ($(RETPOLINE_CFLAGS),)
KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) -DRETPOLINE
endif
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index cc69e37..c0ad1bb 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -330,7 +330,8 @@
if (status != EFI_SUCCESS)
goto free_struct;
- memcpy(rom->romdata, pci->romimage, pci->romsize);
+ memcpy(rom->romdata, (void *)(unsigned long)pci->romimage,
+ pci->romsize);
return status;
free_struct:
@@ -436,7 +437,8 @@
if (status != EFI_SUCCESS)
goto free_struct;
- memcpy(rom->romdata, pci->romimage, pci->romsize);
+ memcpy(rom->romdata, (void *)(unsigned long)pci->romimage,
+ pci->romsize);
return status;
free_struct:
diff --git a/arch/x86/boot/compressed/error.h b/arch/x86/boot/compressed/error.h
index 2e59dac..d732e60 100644
--- a/arch/x86/boot/compressed/error.h
+++ b/arch/x86/boot/compressed/error.h
@@ -1,7 +1,9 @@
#ifndef BOOT_COMPRESSED_ERROR_H
#define BOOT_COMPRESSED_ERROR_H
+#include <linux/compiler.h>
+
void warn(char *m);
-void error(char *m);
+void error(char *m) __noreturn;
#endif /* BOOT_COMPRESSED_ERROR_H */
diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 6de58f1..eb5ff66 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -460,10 +460,17 @@
add_identity_map(random_addr, output_size);
*output = random_addr;
}
+
+ /*
+ * This loads the identity mapping page table.
+ * This should only be done if a new physical address
+ * is found for the kernel, otherwise we should keep
+ * the old page table to make it be like the "nokaslr"
+ * case.
+ */
+ finalize_identity_maps();
}
- /* This actually loads the identity pagetable on x86_64. */
- finalize_identity_maps();
/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
if (IS_ENABLED(CONFIG_X86_64))
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 5955954..4884098 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -299,6 +299,10 @@
switch (phdr->p_type) {
case PT_LOAD:
+#ifdef CONFIG_X86_64
+ if ((phdr->p_align % 0x200000) != 0)
+ error("Alignment of LOAD segment isn't multiple of 2MB");
+#endif
#ifdef CONFIG_RELOCATABLE
dest = output;
dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig
new file mode 100644
index 0000000..5b06edd
--- /dev/null
+++ b/arch/x86/configs/x86_64_cuttlefish_defconfig
@@ -0,0 +1,454 @@
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_FHANDLE is not set
+# CONFIG_USELIB is not set
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_LZ4 is not set
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_PCSPKR_PLATFORM is not set
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_JUMP_LABEL=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_SMP=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+CONFIG_PARAVIRT_SPINLOCKS=y
+CONFIG_MCORE2=y
+CONFIG_PROCESSOR_SELECT=y
+# CONFIG_CPU_SUP_CENTAUR is not set
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT=y
+# CONFIG_MICROCODE is not set
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_KSM=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
+CONFIG_TRANSPARENT_HUGEPAGE=y
+# CONFIG_MTRR is not set
+CONFIG_HZ_100=y
+CONFIG_KEXEC=y
+CONFIG_CRASH_DUMP=y
+CONFIG_PHYSICAL_START=0x200000
+CONFIG_RANDOMIZE_BASE=y
+CONFIG_PHYSICAL_ALIGN=0x1000000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyS0 reboot=p nopti"
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_ACPI_PROCFS_POWER=y
+# CONFIG_ACPI_FAN is not set
+# CONFIG_ACPI_THERMAL is not set
+# CONFIG_X86_PM_TIMER is not set
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_X86_ACPI_CPUFREQ=y
+# CONFIG_X86_ACPI_CPUFREQ_CPB is not set
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCI_MSI=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_IA32_EMULATION=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BIC is not set
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_HTCP is not set
+CONFIG_TCP_MD5SIG=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETLABEL=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_RFKILL=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_OF=y
+CONFIG_OF_UNITTEST=y
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_VIRTIO=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_NETCONSOLE=y
+CONFIG_NETCONSOLE_DYNAMIC=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+# CONFIG_ETHERNET is not set
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_CDCETHER is not set
+# CONFIG_USB_NET_CDC_NCM is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
+CONFIG_MAC80211_HWSIM=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=48
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_INTEL is not set
+# CONFIG_HW_RANDOM_AMD is not set
+# CONFIG_HW_RANDOM_VIA is not set
+CONFIG_HW_RANDOM_VIRTIO=y
+CONFIG_HPET=y
+# CONFIG_HPET_MMAP_DEFAULT is not set
+# CONFIG_DEVPORT is not set
+# CONFIG_ACPI_I2C_OPREGION is not set
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_PTP_1588_CLOCK=y
+# CONFIG_HWMON is not set
+# CONFIG_X86_PKG_TEMP_THERMAL is not set
+CONFIG_WATCHDOG=y
+CONFIG_SOFT_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+# CONFIG_VGA_ARB is not set
+CONFIG_DRM=y
+# CONFIG_DRM_FBDEV_EMULATION is not set
+CONFIG_DRM_VIRTIO_GPU=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+# CONFIG_HID_GENERIC is not set
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_DUMMY_HCD=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_ACC=y
+CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
+CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_SW_SYNC=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_VIRTIO_BALLOON=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_VSOC=y
+CONFIG_ION=y
+# CONFIG_X86_PLATFORM_DEVICES is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+# CONFIG_FIRMWARE_MEMMAP is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_QFMT_V2=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_SDCARD_FS=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_PANIC_TIMEOUT=5
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_IO_DELAY_NONE=y
+CONFIG_DEBUG_BOOT_PARAMS=y
+CONFIG_OPTIMIZE_INLINING=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_PATH=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
+# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set
diff --git a/arch/x86/crypto/cast5_avx_glue.c b/arch/x86/crypto/cast5_avx_glue.c
index 8648158..f8fe11d 100644
--- a/arch/x86/crypto/cast5_avx_glue.c
+++ b/arch/x86/crypto/cast5_avx_glue.c
@@ -66,8 +66,6 @@
void (*fn)(struct cast5_ctx *ctx, u8 *dst, const u8 *src);
int err;
- fn = (enc) ? cast5_ecb_enc_16way : cast5_ecb_dec_16way;
-
err = blkcipher_walk_virt(desc, walk);
desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
@@ -79,6 +77,7 @@
/* Process multi-block batch */
if (nbytes >= bsize * CAST5_PARALLEL_BLOCKS) {
+ fn = (enc) ? cast5_ecb_enc_16way : cast5_ecb_dec_16way;
do {
fn(ctx, wdst, wsrc);
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index f5434b4..a76dc73 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -237,8 +237,7 @@
* exist, overwrite the RSB with entries which capture
* speculative execution to prevent attack.
*/
- /* Clobbers %ebx */
- FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+ FILL_RETURN_BUFFER %ebx, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif
/* restore callee-saved registers */
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 8be2aaa..3a31fd4 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -176,13 +176,26 @@
pushq %r8 /* pt_regs->r8 */
pushq %r9 /* pt_regs->r9 */
pushq %r10 /* pt_regs->r10 */
+ /*
+ * Clear extra registers that a speculation attack might
+ * otherwise want to exploit. Interleave XOR with PUSH
+ * for better uop scheduling:
+ */
+ xorq %r10, %r10 /* nospec r10 */
pushq %r11 /* pt_regs->r11 */
+ xorq %r11, %r11 /* nospec r11 */
pushq %rbx /* pt_regs->rbx */
+ xorl %ebx, %ebx /* nospec rbx */
pushq %rbp /* pt_regs->rbp */
+ xorl %ebp, %ebp /* nospec rbp */
pushq %r12 /* pt_regs->r12 */
+ xorq %r12, %r12 /* nospec r12 */
pushq %r13 /* pt_regs->r13 */
+ xorq %r13, %r13 /* nospec r13 */
pushq %r14 /* pt_regs->r14 */
+ xorq %r14, %r14 /* nospec r14 */
pushq %r15 /* pt_regs->r15 */
+ xorq %r15, %r15 /* nospec r15 */
/* IRQs are off. */
movq %rsp, %rdi
@@ -318,8 +331,7 @@
* exist, overwrite the RSB with entries which capture
* speculative execution to prevent attack.
*/
- /* Clobbers %rbx */
- FILL_RETURN_BUFFER RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+ FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif
/* restore callee-saved registers */
@@ -926,7 +938,7 @@
#endif /* CONFIG_HYPERV */
idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
-idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
+idtentry int3 do_int3 has_error_code=0
idtentry stack_segment do_stack_segment has_error_code=1
#ifdef CONFIG_XEN
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index d540966..51a858e 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -171,7 +171,8 @@
sh $(srctree)/$(src)/checkundef.sh '$(NM)' '$@'
VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=both) \
- $(call cc-ldoption, -Wl$(comma)--build-id) -Wl,-Bsymbolic $(LTO_CFLAGS)
+ $(call cc-ldoption, -Wl$(comma)--build-id) -Wl,-Bsymbolic $(LTO_CFLAGS) \
+ $(filter --target=% --gcc-toolchain=%,$(KBUILD_CFLAGS))
GCOV_PROFILE := n
#
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index f73796d..02e547f 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -26,6 +26,7 @@
#include <linux/cpu.h>
#include <linux/bitops.h>
#include <linux/device.h>
+#include <linux/nospec.h>
#include <asm/apic.h>
#include <asm/stacktrace.h>
@@ -303,17 +304,20 @@
config = attr->config;
- cache_type = (config >> 0) & 0xff;
+ cache_type = (config >> 0) & 0xff;
if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
return -EINVAL;
+ cache_type = array_index_nospec(cache_type, PERF_COUNT_HW_CACHE_MAX);
cache_op = (config >> 8) & 0xff;
if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
return -EINVAL;
+ cache_op = array_index_nospec(cache_op, PERF_COUNT_HW_CACHE_OP_MAX);
cache_result = (config >> 16) & 0xff;
if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
return -EINVAL;
+ cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX);
val = hw_cache_event_ids[cache_type][cache_op][cache_result];
@@ -420,6 +424,8 @@
if (attr->config >= x86_pmu.max_events)
return -EINVAL;
+ attr->config = array_index_nospec((unsigned long)attr->config, x86_pmu.max_events);
+
/*
* The generic map:
*/
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 0bd0c1c..6f353a8 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -3025,7 +3025,7 @@
X86_CONFIG(.event=0xc0, .umask=0x01)) {
if (left < 128)
left = 128;
- left &= ~0x3fu;
+ left &= ~0x3fULL;
}
return left;
}
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index 1076c9a..47d526c 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -90,6 +90,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
+#include <linux/nospec.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include "../perf_event.h"
@@ -300,6 +301,7 @@
} else if (event->pmu == &cstate_pkg_pmu) {
if (cfg >= PERF_CSTATE_PKG_EVENT_MAX)
return -EINVAL;
+ cfg = array_index_nospec((unsigned long)cfg, PERF_CSTATE_PKG_EVENT_MAX);
if (!pkg_msr[cfg].attr)
return -EINVAL;
event->hw.event_base = pkg_msr[cfg].msr;
diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c
index afe8024..6bc3694 100644
--- a/arch/x86/events/intel/uncore_snbep.c
+++ b/arch/x86/events/intel/uncore_snbep.c
@@ -3522,24 +3522,27 @@
NULL,
};
+/*
+ * To determine the number of CHAs, it should read bits 27:0 in the CAPID6
+ * register which located at Device 30, Function 3, Offset 0x9C. PCI ID 0x2083.
+ */
+#define SKX_CAPID6 0x9c
+#define SKX_CHA_BIT_MASK GENMASK(27, 0)
+
static int skx_count_chabox(void)
{
- struct pci_dev *chabox_dev = NULL;
- int bus, count = 0;
+ struct pci_dev *dev = NULL;
+ u32 val = 0;
- while (1) {
- chabox_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x208d, chabox_dev);
- if (!chabox_dev)
- break;
- if (count == 0)
- bus = chabox_dev->bus->number;
- if (bus != chabox_dev->bus->number)
- break;
- count++;
- }
+ dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x2083, dev);
+ if (!dev)
+ goto out;
- pci_dev_put(chabox_dev);
- return count;
+ pci_read_config_dword(dev, SKX_CAPID6, &val);
+ val &= SKX_CHA_BIT_MASK;
+out:
+ pci_dev_put(dev);
+ return hweight32(val);
}
void skx_uncore_cpu_init(void)
@@ -3566,7 +3569,7 @@
};
static struct attribute *skx_upi_uncore_formats_attr[] = {
- &format_attr_event_ext.attr,
+ &format_attr_event.attr,
&format_attr_umask_ext.attr,
&format_attr_edge.attr,
&format_attr_inv.attr,
diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c
index 4bb3ec6..be0b196 100644
--- a/arch/x86/events/msr.c
+++ b/arch/x86/events/msr.c
@@ -1,4 +1,5 @@
#include <linux/perf_event.h>
+#include <linux/nospec.h>
#include <asm/intel-family.h>
enum perf_msr_id {
@@ -136,9 +137,6 @@
if (event->attr.type != event->pmu->type)
return -ENOENT;
- if (cfg >= PERF_MSR_EVENT_MAX)
- return -EINVAL;
-
/* unsupported modes and filters */
if (event->attr.exclude_user ||
event->attr.exclude_kernel ||
@@ -149,6 +147,11 @@
event->attr.sample_period) /* no sampling */
return -EINVAL;
+ if (cfg >= PERF_MSR_EVENT_MAX)
+ return -EINVAL;
+
+ cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX);
+
if (!msr[cfg].attr)
return -EINVAL;
diff --git a/arch/x86/include/asm/apm.h b/arch/x86/include/asm/apm.h
index 93eebc63..46e40ae 100644
--- a/arch/x86/include/asm/apm.h
+++ b/arch/x86/include/asm/apm.h
@@ -6,6 +6,8 @@
#ifndef _ASM_X86_MACH_DEFAULT_APM_H
#define _ASM_X86_MACH_DEFAULT_APM_H
+#include <asm/nospec-branch.h>
+
#ifdef APM_ZERO_SEGS
# define APM_DO_ZERO_SEGS \
"pushl %%ds\n\t" \
@@ -31,6 +33,7 @@
* N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags.
*/
+ firmware_restrict_branch_speculation_start();
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
@@ -43,6 +46,7 @@
"=S" (*esi)
: "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
+ firmware_restrict_branch_speculation_end();
}
static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in,
@@ -55,6 +59,7 @@
* N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags.
*/
+ firmware_restrict_branch_speculation_start();
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
@@ -67,6 +72,7 @@
"=S" (si)
: "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
+ firmware_restrict_branch_speculation_end();
return error;
}
diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h
index 1666542..5a25ada 100644
--- a/arch/x86/include/asm/asm-prototypes.h
+++ b/arch/x86/include/asm/asm-prototypes.h
@@ -37,7 +37,4 @@
INDIRECT_THUNK(si)
INDIRECT_THUNK(di)
INDIRECT_THUNK(bp)
-asmlinkage void __fill_rsb(void);
-asmlinkage void __clear_rsb(void);
-
#endif /* CONFIG_RETPOLINE */
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 08684b3..8d8c24f 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -129,6 +129,7 @@
#endif
#ifndef __ASSEMBLY__
+#ifndef __BPF__
/*
* This output constraint should be used for any inline asm which has a "call"
* instruction. Otherwise the asm may be inserted before the frame pointer
@@ -138,5 +139,6 @@
register unsigned long current_stack_pointer asm(_ASM_SP);
#define ASM_CALL_CONSTRAINT "+r" (current_stack_pointer)
#endif
+#endif
#endif /* _ASM_X86_ASM_H */
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 8eb23f5..c278f27 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -197,12 +197,23 @@
#define X86_FEATURE_RETPOLINE ( 7*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */
#define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* "" AMD Retpoline mitigation for Spectre variant 2 */
+#define X86_FEATURE_MSR_SPEC_CTRL ( 7*32+16) /* "" MSR SPEC_CTRL is implemented */
+#define X86_FEATURE_SSBD ( 7*32+17) /* Speculative Store Bypass Disable */
+
#define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* "" Fill RSB on context switches */
/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
#define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */
+#define X86_FEATURE_USE_IBRS_FW ( 7*32+22) /* "" Use IBRS during runtime firmware calls */
+#define X86_FEATURE_SPEC_STORE_BYPASS_DISABLE ( 7*32+23) /* "" Disable Speculative Store Bypass. */
+#define X86_FEATURE_LS_CFG_SSBD ( 7*32+24) /* "" AMD SSBD implementation */
+#define X86_FEATURE_IBRS ( 7*32+25) /* Indirect Branch Restricted Speculation */
+#define X86_FEATURE_IBPB ( 7*32+26) /* Indirect Branch Prediction Barrier */
+#define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */
+#define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */
+
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
@@ -260,9 +271,10 @@
/* AMD-defined CPU features, CPUID level 0x80000008 (ebx), word 13 */
#define X86_FEATURE_CLZERO (13*32+0) /* CLZERO instruction */
#define X86_FEATURE_IRPERF (13*32+1) /* Instructions Retired Count */
-#define X86_FEATURE_IBPB (13*32+12) /* Indirect Branch Prediction Barrier */
-#define X86_FEATURE_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */
-#define X86_FEATURE_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */
+#define X86_FEATURE_AMD_IBPB (13*32+12) /* Indirect Branch Prediction Barrier */
+#define X86_FEATURE_AMD_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */
+#define X86_FEATURE_AMD_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */
+#define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */
/* Thermal and Power Management Leaf, CPUID level 0x00000006 (eax), word 14 */
#define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */
@@ -298,12 +310,15 @@
#define X86_FEATURE_SUCCOR (17*32+1) /* Uncorrectable error containment and recovery */
#define X86_FEATURE_SMCA (17*32+3) /* Scalable MCA */
+
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
#define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */
+#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
#define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
+#define X86_FEATURE_SPEC_CTRL_SSBD (18*32+31) /* "" Speculative Store Bypass Disable */
/*
* BUG word(s)
@@ -333,5 +348,6 @@
#define X86_BUG_CPU_MELTDOWN X86_BUG(14) /* CPU is affected by meltdown attack and needs kernel page table isolation */
#define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */
#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
+#define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 389d700..9df22bb 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -5,6 +5,7 @@
#include <asm/pgtable.h>
#include <asm/processor-flags.h>
#include <asm/tlb.h>
+#include <asm/nospec-branch.h>
/*
* We map the EFI regions needed for runtime services non-contiguously,
@@ -35,8 +36,18 @@
extern unsigned long asmlinkage efi_call_phys(void *, ...);
-#define arch_efi_call_virt_setup() kernel_fpu_begin()
-#define arch_efi_call_virt_teardown() kernel_fpu_end()
+#define arch_efi_call_virt_setup() \
+({ \
+ kernel_fpu_begin(); \
+ firmware_restrict_branch_speculation_start(); \
+})
+
+#define arch_efi_call_virt_teardown() \
+({ \
+ firmware_restrict_branch_speculation_end(); \
+ kernel_fpu_end(); \
+})
+
/*
* Wrap all the virtual calls in a way that forces the parameters on the stack.
@@ -72,6 +83,7 @@
efi_sync_low_kernel_mappings(); \
preempt_disable(); \
__kernel_fpu_begin(); \
+ firmware_restrict_branch_speculation_start(); \
\
if (efi_scratch.use_pgd) { \
efi_scratch.prev_cr3 = read_cr3(); \
@@ -90,6 +102,7 @@
__flush_tlb_all(); \
} \
\
+ firmware_restrict_branch_speculation_end(); \
__kernel_fpu_end(); \
preempt_enable(); \
})
diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h
index b4c1f54..f4dc9b6 100644
--- a/arch/x86/include/asm/futex.h
+++ b/arch/x86/include/asm/futex.h
@@ -41,20 +41,11 @@
"+m" (*uaddr), "=&r" (tem) \
: "r" (oparg), "i" (-EFAULT), "1" (0))
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret, tem;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
-
pagefault_disable();
switch (op) {
@@ -80,30 +71,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ:
- ret = (oldval == cmparg);
- break;
- case FUTEX_OP_CMP_NE:
- ret = (oldval != cmparg);
- break;
- case FUTEX_OP_CMP_LT:
- ret = (oldval < cmparg);
- break;
- case FUTEX_OP_CMP_GE:
- ret = (oldval >= cmparg);
- break;
- case FUTEX_OP_CMP_LE:
- ret = (oldval <= cmparg);
- break;
- case FUTEX_OP_CMP_GT:
- ret = (oldval > cmparg);
- break;
- default:
- ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 20cfeeb..7598a6c 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -864,7 +864,7 @@
int (*hardware_setup)(void); /* __init */
void (*hardware_unsetup)(void); /* __exit */
bool (*cpu_has_accelerated_tpr)(void);
- bool (*cpu_has_high_real_mode_segbase)(void);
+ bool (*has_emulated_msr)(int index);
void (*cpuid_update)(struct kvm_vcpu *vcpu);
int (*vm_init)(struct kvm *kvm);
diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
index 8b272a0..e2e0934 100644
--- a/arch/x86/include/asm/mmu.h
+++ b/arch/x86/include/asm/mmu.h
@@ -3,12 +3,18 @@
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/atomic.h>
/*
- * The x86 doesn't have a mmu context, but
- * we put the segment information here.
+ * x86 has arch-specific MMU state beyond what lives in mm_struct.
*/
typedef struct {
+ /*
+ * ctx_id uniquely identifies this mm_struct. A ctx_id will never
+ * be reused, and zero is not a valid ctx_id.
+ */
+ u64 ctx_id;
+
#ifdef CONFIG_MODIFY_LDT_SYSCALL
struct ldt_struct *ldt;
#endif
@@ -33,6 +39,11 @@
#endif
} mm_context_t;
+#define INIT_MM_CONTEXT(mm) \
+ .context = { \
+ .ctx_id = 1, \
+ }
+
void leave_mm(int cpu);
#endif /* _ASM_X86_MMU_H */
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index d23e355..7336508 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -12,6 +12,9 @@
#include <asm/tlbflush.h>
#include <asm/paravirt.h>
#include <asm/mpx.h>
+
+extern atomic64_t last_mm_ctx_id;
+
#ifndef CONFIG_PARAVIRT
static inline void paravirt_activate_mm(struct mm_struct *prev,
struct mm_struct *next)
@@ -106,9 +109,11 @@
static inline int init_new_context(struct task_struct *tsk,
struct mm_struct *mm)
{
+ mm->context.ctx_id = atomic64_inc_return(&last_mm_ctx_id);
+
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
if (cpu_feature_enabled(X86_FEATURE_OSPKE)) {
- /* pkey 0 is the default and always allocated */
+ /* pkey 0 is the default and allocated implicitly */
mm->context.pkey_allocation_map = 0x1;
/* -1 means unallocated or invalid */
mm->context.execute_only_pkey = -1;
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index c768bc1..1ec13e2 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -40,6 +40,8 @@
#define MSR_IA32_SPEC_CTRL 0x00000048 /* Speculation Control */
#define SPEC_CTRL_IBRS (1 << 0) /* Indirect Branch Restricted Speculation */
#define SPEC_CTRL_STIBP (1 << 1) /* Single Thread Indirect Branch Predictors */
+#define SPEC_CTRL_SSBD_SHIFT 2 /* Speculative Store Bypass Disable bit */
+#define SPEC_CTRL_SSBD (1 << SPEC_CTRL_SSBD_SHIFT) /* Speculative Store Bypass Disable */
#define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */
#define PRED_CMD_IBPB (1 << 0) /* Indirect Branch Prediction Barrier */
@@ -61,6 +63,11 @@
#define MSR_IA32_ARCH_CAPABILITIES 0x0000010a
#define ARCH_CAP_RDCL_NO (1 << 0) /* Not susceptible to Meltdown */
#define ARCH_CAP_IBRS_ALL (1 << 1) /* Enhanced IBRS support */
+#define ARCH_CAP_SSB_NO (1 << 4) /*
+ * Not susceptible to Speculative Store Bypass
+ * attack, so no Speculative Store Bypass
+ * control required.
+ */
#define MSR_IA32_BBL_CR_CTL 0x00000119
#define MSR_IA32_BBL_CR_CTL3 0x0000011e
@@ -135,6 +142,7 @@
/* DEBUGCTLMSR bits (others vary by model): */
#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */
+#define DEBUGCTLMSR_BTF_SHIFT 1
#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */
#define DEBUGCTLMSR_TR (1UL << 6)
#define DEBUGCTLMSR_BTS (1UL << 7)
@@ -315,6 +323,8 @@
#define MSR_AMD64_IBSOPDATA4 0xc001103d
#define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */
+#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
+
/* Fam 17h MSRs */
#define MSR_F17H_IRPERF 0xc00000e9
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index 76b0585..8b38df9 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -8,6 +8,50 @@
#include <asm/cpufeatures.h>
#include <asm/msr-index.h>
+/*
+ * Fill the CPU return stack buffer.
+ *
+ * Each entry in the RSB, if used for a speculative 'ret', contains an
+ * infinite 'pause; lfence; jmp' loop to capture speculative execution.
+ *
+ * This is required in various cases for retpoline and IBRS-based
+ * mitigations for the Spectre variant 2 vulnerability. Sometimes to
+ * eliminate potentially bogus entries from the RSB, and sometimes
+ * purely to ensure that it doesn't get empty, which on some CPUs would
+ * allow predictions from other (unwanted!) sources to be used.
+ *
+ * We define a CPP macro such that it can be used from both .S files and
+ * inline assembly. It's possible to do a .macro and then include that
+ * from C via asm(".include <asm/nospec-branch.h>") but let's not go there.
+ */
+
+#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
+#define RSB_FILL_LOOPS 16 /* To avoid underflow */
+
+/*
+ * Google experimented with loop-unrolling and this turned out to be
+ * the optimal version — two calls, each with their own speculation
+ * trap should their return address end up getting used, in a loop.
+ */
+#define __FILL_RETURN_BUFFER(reg, nr, sp) \
+ mov $(nr/2), reg; \
+771: \
+ call 772f; \
+773: /* speculation trap */ \
+ pause; \
+ lfence; \
+ jmp 773b; \
+772: \
+ call 774f; \
+775: /* speculation trap */ \
+ pause; \
+ lfence; \
+ jmp 775b; \
+774: \
+ dec reg; \
+ jnz 771b; \
+ add $(BITS_PER_LONG/8) * nr, sp;
+
#ifdef __ASSEMBLY__
/*
@@ -24,6 +68,18 @@
.endm
/*
+ * This should be used immediately before an indirect jump/call. It tells
+ * objtool the subsequent indirect jump/call is vouched safe for retpoline
+ * builds.
+ */
+.macro ANNOTATE_RETPOLINE_SAFE
+ .Lannotate_\@:
+ .pushsection .discard.retpoline_safe
+ _ASM_PTR .Lannotate_\@
+ .popsection
+.endm
+
+/*
* These are the bare retpoline primitives for indirect jmp and call.
* Do not use these directly; they only exist to make the ALTERNATIVE
* invocation below less ugly.
@@ -59,9 +115,9 @@
.macro JMP_NOSPEC reg:req
#ifdef CONFIG_RETPOLINE
ANNOTATE_NOSPEC_ALTERNATIVE
- ALTERNATIVE_2 __stringify(jmp *\reg), \
+ ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *\reg), \
__stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE, \
- __stringify(lfence; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
+ __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
#else
jmp *\reg
#endif
@@ -70,18 +126,25 @@
.macro CALL_NOSPEC reg:req
#ifdef CONFIG_RETPOLINE
ANNOTATE_NOSPEC_ALTERNATIVE
- ALTERNATIVE_2 __stringify(call *\reg), \
+ ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *\reg), \
__stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
- __stringify(lfence; call *\reg), X86_FEATURE_RETPOLINE_AMD
+ __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *\reg), X86_FEATURE_RETPOLINE_AMD
#else
call *\reg
#endif
.endm
-/* This clobbers the BX register */
-.macro FILL_RETURN_BUFFER nr:req ftr:req
+ /*
+ * A simpler FILL_RETURN_BUFFER macro. Don't make people use the CPP
+ * monstrosity above, manually.
+ */
+.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
#ifdef CONFIG_RETPOLINE
- ALTERNATIVE "", "call __clear_rsb", \ftr
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE "jmp .Lskip_rsb_\@", \
+ __stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)) \
+ \ftr
+.Lskip_rsb_\@:
#endif
.endm
@@ -93,6 +156,12 @@
".long 999b - .\n\t" \
".popsection\n\t"
+#define ANNOTATE_RETPOLINE_SAFE \
+ "999:\n\t" \
+ ".pushsection .discard.retpoline_safe\n\t" \
+ _ASM_PTR " 999b\n\t" \
+ ".popsection\n\t"
+
#if defined(CONFIG_X86_64) && defined(RETPOLINE)
/*
@@ -102,6 +171,7 @@
# define CALL_NOSPEC \
ANNOTATE_NOSPEC_ALTERNATIVE \
ALTERNATIVE( \
+ ANNOTATE_RETPOLINE_SAFE \
"call *%[thunk_target]\n", \
"call __x86_indirect_thunk_%V[thunk_target]\n", \
X86_FEATURE_RETPOLINE)
@@ -113,7 +183,10 @@
* otherwise we'll run out of registers. We don't care about CET
* here, anyway.
*/
-# define CALL_NOSPEC ALTERNATIVE("call *%[thunk_target]\n", \
+# define CALL_NOSPEC \
+ ALTERNATIVE( \
+ ANNOTATE_RETPOLINE_SAFE \
+ "call *%[thunk_target]\n", \
" jmp 904f;\n" \
" .align 16\n" \
"901: call 903f;\n" \
@@ -144,6 +217,14 @@
SPECTRE_V2_IBRS,
};
+/* The Speculative Store Bypass disable variants */
+enum ssb_mitigation {
+ SPEC_STORE_BYPASS_NONE,
+ SPEC_STORE_BYPASS_DISABLE,
+ SPEC_STORE_BYPASS_PRCTL,
+ SPEC_STORE_BYPASS_SECCOMP,
+};
+
extern char __indirect_thunk_start[];
extern char __indirect_thunk_end[];
@@ -156,25 +237,99 @@
static inline void vmexit_fill_RSB(void)
{
#ifdef CONFIG_RETPOLINE
- alternative_input("",
- "call __fill_rsb",
- X86_FEATURE_RETPOLINE,
- ASM_NO_INPUT_CLOBBER(_ASM_BX, "memory"));
+ unsigned long loops;
+
+ asm volatile (ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE("jmp 910f",
+ __stringify(__FILL_RETURN_BUFFER(%0, RSB_CLEAR_LOOPS, %1)),
+ X86_FEATURE_RETPOLINE)
+ "910:"
+ : "=r" (loops), ASM_CALL_CONSTRAINT
+ : : "memory" );
#endif
}
+static __always_inline
+void alternative_msr_write(unsigned int msr, u64 val, unsigned int feature)
+{
+ asm volatile(ALTERNATIVE("", "wrmsr", %c[feature])
+ : : "c" (msr),
+ "a" ((u32)val),
+ "d" ((u32)(val >> 32)),
+ [feature] "i" (feature)
+ : "memory");
+}
+
static inline void indirect_branch_prediction_barrier(void)
{
- asm volatile(ALTERNATIVE("",
- "movl %[msr], %%ecx\n\t"
- "movl %[val], %%eax\n\t"
- "movl $0, %%edx\n\t"
- "wrmsr",
- X86_FEATURE_USE_IBPB)
- : : [msr] "i" (MSR_IA32_PRED_CMD),
- [val] "i" (PRED_CMD_IBPB)
- : "eax", "ecx", "edx", "memory");
+ u64 val = PRED_CMD_IBPB;
+
+ alternative_msr_write(MSR_IA32_PRED_CMD, val, X86_FEATURE_USE_IBPB);
}
+/* The Intel SPEC CTRL MSR base value cache */
+extern u64 x86_spec_ctrl_base;
+
+/*
+ * With retpoline, we must use IBRS to restrict branch prediction
+ * before calling into firmware.
+ *
+ * (Implemented as CPP macros due to header hell.)
+ */
+#define firmware_restrict_branch_speculation_start() \
+do { \
+ u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS; \
+ \
+ preempt_disable(); \
+ alternative_msr_write(MSR_IA32_SPEC_CTRL, val, \
+ X86_FEATURE_USE_IBRS_FW); \
+} while (0)
+
+#define firmware_restrict_branch_speculation_end() \
+do { \
+ u64 val = x86_spec_ctrl_base; \
+ \
+ alternative_msr_write(MSR_IA32_SPEC_CTRL, val, \
+ X86_FEATURE_USE_IBRS_FW); \
+ preempt_enable(); \
+} while (0)
+
#endif /* __ASSEMBLY__ */
+
+/*
+ * Below is used in the eBPF JIT compiler and emits the byte sequence
+ * for the following assembly:
+ *
+ * With retpolines configured:
+ *
+ * callq do_rop
+ * spec_trap:
+ * pause
+ * lfence
+ * jmp spec_trap
+ * do_rop:
+ * mov %rax,(%rsp)
+ * retq
+ *
+ * Without retpolines configured:
+ *
+ * jmp *%rax
+ */
+#ifdef CONFIG_RETPOLINE
+# define RETPOLINE_RAX_BPF_JIT_SIZE 17
+# define RETPOLINE_RAX_BPF_JIT() \
+ EMIT1_off32(0xE8, 7); /* callq do_rop */ \
+ /* spec_trap: */ \
+ EMIT2(0xF3, 0x90); /* pause */ \
+ EMIT3(0x0F, 0xAE, 0xE8); /* lfence */ \
+ EMIT2(0xEB, 0xF9); /* jmp spec_trap */ \
+ /* do_rop: */ \
+ EMIT4(0x48, 0x89, 0x04, 0x24); /* mov %rax,(%rsp) */ \
+ EMIT1(0xC3); /* retq */
+#else
+# define RETPOLINE_RAX_BPF_JIT_SIZE 2
+# define RETPOLINE_RAX_BPF_JIT() \
+ EMIT2(0xFF, 0xE0); /* jmp *%rax */
+#endif
+
#endif /* _ASM_X86_NOSPEC_BRANCH_H_ */
diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
index ce93281..24af8b1 100644
--- a/arch/x86/include/asm/paravirt.h
+++ b/arch/x86/include/asm/paravirt.h
@@ -6,6 +6,7 @@
#ifdef CONFIG_PARAVIRT
#include <asm/pgtable_types.h>
#include <asm/asm.h>
+#include <asm/nospec-branch.h>
#include <asm/paravirt_types.h>
@@ -869,23 +870,27 @@
#define INTERRUPT_RETURN \
PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_iret), CLBR_NONE, \
- jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_iret))
+ ANNOTATE_RETPOLINE_SAFE; \
+ jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_iret);)
#define DISABLE_INTERRUPTS(clobbers) \
PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_irq_disable), clobbers, \
PV_SAVE_REGS(clobbers | CLBR_CALLEE_SAVE); \
+ ANNOTATE_RETPOLINE_SAFE; \
call PARA_INDIRECT(pv_irq_ops+PV_IRQ_irq_disable); \
PV_RESTORE_REGS(clobbers | CLBR_CALLEE_SAVE);)
#define ENABLE_INTERRUPTS(clobbers) \
PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_irq_enable), clobbers, \
PV_SAVE_REGS(clobbers | CLBR_CALLEE_SAVE); \
+ ANNOTATE_RETPOLINE_SAFE; \
call PARA_INDIRECT(pv_irq_ops+PV_IRQ_irq_enable); \
PV_RESTORE_REGS(clobbers | CLBR_CALLEE_SAVE);)
#ifdef CONFIG_X86_32
#define GET_CR0_INTO_EAX \
push %ecx; push %edx; \
+ ANNOTATE_RETPOLINE_SAFE; \
call PARA_INDIRECT(pv_cpu_ops+PV_CPU_read_cr0); \
pop %edx; pop %ecx
#else /* !CONFIG_X86_32 */
@@ -907,11 +912,13 @@
*/
#define SWAPGS \
PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_swapgs), CLBR_NONE, \
- call PARA_INDIRECT(pv_cpu_ops+PV_CPU_swapgs) \
+ ANNOTATE_RETPOLINE_SAFE; \
+ call PARA_INDIRECT(pv_cpu_ops+PV_CPU_swapgs); \
)
#define GET_CR2_INTO_RAX \
- call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2)
+ ANNOTATE_RETPOLINE_SAFE; \
+ call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2);
#define PARAVIRT_ADJUST_EXCEPTION_FRAME \
PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_adjust_exception_frame), \
@@ -921,7 +928,8 @@
#define USERGS_SYSRET64 \
PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_usergs_sysret64), \
CLBR_NONE, \
- jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_usergs_sysret64))
+ ANNOTATE_RETPOLINE_SAFE; \
+ jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_usergs_sysret64);)
#endif /* CONFIG_X86_32 */
#endif /* __ASSEMBLY__ */
diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h
index bc5902c..98dbad6 100644
--- a/arch/x86/include/asm/paravirt_types.h
+++ b/arch/x86/include/asm/paravirt_types.h
@@ -42,6 +42,7 @@
#include <asm/desc_defs.h>
#include <asm/kmap_types.h>
#include <asm/pgtable_types.h>
+#include <asm/nospec-branch.h>
struct page;
struct thread_struct;
@@ -391,7 +392,9 @@
* offset into the paravirt_patch_template structure, and can therefore be
* freely converted back into a structure offset.
*/
-#define PARAVIRT_CALL "call *%c[paravirt_opptr];"
+#define PARAVIRT_CALL \
+ ANNOTATE_RETPOLINE_SAFE \
+ "call *%c[paravirt_opptr];"
/*
* These macros are intended to wrap calls through one of the paravirt
diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
index b3b09b9..c50d6dc 100644
--- a/arch/x86/include/asm/pkeys.h
+++ b/arch/x86/include/asm/pkeys.h
@@ -1,6 +1,8 @@
#ifndef _ASM_X86_PKEYS_H
#define _ASM_X86_PKEYS_H
+#define ARCH_DEFAULT_PKEY 0
+
#define arch_max_pkey() (boot_cpu_has(X86_FEATURE_OSPKE) ? 16 : 1)
extern int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
@@ -14,7 +16,7 @@
static inline int execute_only_pkey(struct mm_struct *mm)
{
if (!boot_cpu_has(X86_FEATURE_OSPKE))
- return 0;
+ return ARCH_DEFAULT_PKEY;
return __execute_only_pkey(mm);
}
@@ -48,13 +50,21 @@
{
/*
* "Allocated" pkeys are those that have been returned
- * from pkey_alloc(). pkey 0 is special, and never
- * returned from pkey_alloc().
+ * from pkey_alloc() or pkey 0 which is allocated
+ * implicitly when the mm is created.
*/
- if (pkey <= 0)
+ if (pkey < 0)
return false;
if (pkey >= arch_max_pkey())
return false;
+ /*
+ * The exec-only pkey is set in the allocation map, but
+ * is not available to any of the user interfaces like
+ * mprotect_pkey().
+ */
+ if (pkey == mm->context.execute_only_pkey)
+ return false;
+
return mm_pkey_allocation_map(mm) & (1U << pkey);
}
diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h
index 2cb1cc2..fc62ba8 100644
--- a/arch/x86/include/asm/reboot.h
+++ b/arch/x86/include/asm/reboot.h
@@ -15,6 +15,7 @@
};
extern struct machine_ops machine_ops;
+extern int crashing_cpu;
void native_machine_crash_shutdown(struct pt_regs *regs);
void native_machine_shutdown(void);
diff --git a/arch/x86/include/asm/spec-ctrl.h b/arch/x86/include/asm/spec-ctrl.h
new file mode 100644
index 0000000..ae7c2c5
--- /dev/null
+++ b/arch/x86/include/asm/spec-ctrl.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_SPECCTRL_H_
+#define _ASM_X86_SPECCTRL_H_
+
+#include <linux/thread_info.h>
+#include <asm/nospec-branch.h>
+
+/*
+ * On VMENTER we must preserve whatever view of the SPEC_CTRL MSR
+ * the guest has, while on VMEXIT we restore the host view. This
+ * would be easier if SPEC_CTRL were architecturally maskable or
+ * shadowable for guests but this is not (currently) the case.
+ * Takes the guest view of SPEC_CTRL MSR as a parameter and also
+ * the guest's version of VIRT_SPEC_CTRL, if emulated.
+ */
+extern void x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool guest);
+
+/**
+ * x86_spec_ctrl_set_guest - Set speculation control registers for the guest
+ * @guest_spec_ctrl: The guest content of MSR_SPEC_CTRL
+ * @guest_virt_spec_ctrl: The guest controlled bits of MSR_VIRT_SPEC_CTRL
+ * (may get translated to MSR_AMD64_LS_CFG bits)
+ *
+ * Avoids writing to the MSR if the content/bits are the same
+ */
+static inline
+void x86_spec_ctrl_set_guest(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl)
+{
+ x86_virt_spec_ctrl(guest_spec_ctrl, guest_virt_spec_ctrl, true);
+}
+
+/**
+ * x86_spec_ctrl_restore_host - Restore host speculation control registers
+ * @guest_spec_ctrl: The guest content of MSR_SPEC_CTRL
+ * @guest_virt_spec_ctrl: The guest controlled bits of MSR_VIRT_SPEC_CTRL
+ * (may get translated to MSR_AMD64_LS_CFG bits)
+ *
+ * Avoids writing to the MSR if the content/bits are the same
+ */
+static inline
+void x86_spec_ctrl_restore_host(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl)
+{
+ x86_virt_spec_ctrl(guest_spec_ctrl, guest_virt_spec_ctrl, false);
+}
+
+/* AMD specific Speculative Store Bypass MSR data */
+extern u64 x86_amd_ls_cfg_base;
+extern u64 x86_amd_ls_cfg_ssbd_mask;
+
+static inline u64 ssbd_tif_to_spec_ctrl(u64 tifn)
+{
+ BUILD_BUG_ON(TIF_SSBD < SPEC_CTRL_SSBD_SHIFT);
+ return (tifn & _TIF_SSBD) >> (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT);
+}
+
+static inline unsigned long ssbd_spec_ctrl_to_tif(u64 spec_ctrl)
+{
+ BUILD_BUG_ON(TIF_SSBD < SPEC_CTRL_SSBD_SHIFT);
+ return (spec_ctrl & SPEC_CTRL_SSBD) << (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT);
+}
+
+static inline u64 ssbd_tif_to_amd_ls_cfg(u64 tifn)
+{
+ return (tifn & _TIF_SSBD) ? x86_amd_ls_cfg_ssbd_mask : 0ULL;
+}
+
+#ifdef CONFIG_SMP
+extern void speculative_store_bypass_ht_init(void);
+#else
+static inline void speculative_store_bypass_ht_init(void) { }
+#endif
+
+extern void speculative_store_bypass_update(unsigned long tif);
+
+static inline void speculative_store_bypass_update_current(void)
+{
+ speculative_store_bypass_update(current_thread_info()->flags);
+}
+
+#endif
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index 89978b9..2d8788a 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -83,6 +83,7 @@
#define TIF_SIGPENDING 2 /* signal pending */
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/
+#define TIF_SSBD 5 /* Reduced data speculation */
#define TIF_SYSCALL_EMU 6 /* syscall emulation active */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
#define TIF_SECCOMP 8 /* secure computing */
@@ -104,8 +105,9 @@
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
-#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
+#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
+#define _TIF_SSBD (1 << TIF_SSBD)
#define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU)
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1 << TIF_SECCOMP)
@@ -139,7 +141,7 @@
/* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \
- (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP)
+ (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP|_TIF_SSBD)
#define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 94146f6..686a58d 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -68,6 +68,8 @@
struct tlb_state {
struct mm_struct *active_mm;
int state;
+ /* last user mm's ctx id */
+ u64 last_ctx_id;
/*
* Access to this CR4 shadow and to H/W CR4 is protected by
@@ -109,6 +111,16 @@
}
}
+static inline void cr4_toggle_bits(unsigned long mask)
+{
+ unsigned long cr4;
+
+ cr4 = this_cpu_read(cpu_tlbstate.cr4);
+ cr4 ^= mask;
+ this_cpu_write(cpu_tlbstate.cr4, cr4);
+ __write_cr4(cr4);
+}
+
/* Read the CR4 shadow. */
static inline unsigned long cr4_read_shadow(void)
{
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 6899cf1..9cbfbef 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -309,6 +309,7 @@
#define INTR_TYPE_NMI_INTR (2 << 8) /* NMI */
#define INTR_TYPE_HARD_EXCEPTION (3 << 8) /* processor exception */
#define INTR_TYPE_SOFT_INTR (4 << 8) /* software interrupt */
+#define INTR_TYPE_PRIV_SW_EXCEPTION (5 << 8) /* ICE breakpoint - undocumented */
#define INTR_TYPE_SOFT_EXCEPTION (6 << 8) /* software exception */
/* GUEST_INTERRUPTIBILITY_INFO flags. */
diff --git a/arch/x86/include/uapi/asm/msgbuf.h b/arch/x86/include/uapi/asm/msgbuf.h
index 809134c..90ab9a7 100644
--- a/arch/x86/include/uapi/asm/msgbuf.h
+++ b/arch/x86/include/uapi/asm/msgbuf.h
@@ -1 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __ASM_X64_MSGBUF_H
+#define __ASM_X64_MSGBUF_H
+
+#if !defined(__x86_64__) || !defined(__ILP32__)
#include <asm-generic/msgbuf.h>
+#else
+/*
+ * The msqid64_ds structure for x86 architecture with x32 ABI.
+ *
+ * On x86-32 and x86-64 we can just use the generic definition, but
+ * x32 uses the same binary layout as x86_64, which is differnet
+ * from other 32-bit architectures.
+ */
+
+struct msqid64_ds {
+ struct ipc64_perm msg_perm;
+ __kernel_time_t msg_stime; /* last msgsnd time */
+ __kernel_time_t msg_rtime; /* last msgrcv time */
+ __kernel_time_t msg_ctime; /* last change time */
+ __kernel_ulong_t msg_cbytes; /* current number of bytes on queue */
+ __kernel_ulong_t msg_qnum; /* number of messages in queue */
+ __kernel_ulong_t msg_qbytes; /* max number of bytes on queue */
+ __kernel_pid_t msg_lspid; /* pid of last msgsnd */
+ __kernel_pid_t msg_lrpid; /* last receive pid */
+ __kernel_ulong_t __unused4;
+ __kernel_ulong_t __unused5;
+};
+
+#endif
+
+#endif /* __ASM_GENERIC_MSGBUF_H */
diff --git a/arch/x86/include/uapi/asm/shmbuf.h b/arch/x86/include/uapi/asm/shmbuf.h
index 83c05fc..644421f 100644
--- a/arch/x86/include/uapi/asm/shmbuf.h
+++ b/arch/x86/include/uapi/asm/shmbuf.h
@@ -1 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __ASM_X86_SHMBUF_H
+#define __ASM_X86_SHMBUF_H
+
+#if !defined(__x86_64__) || !defined(__ILP32__)
#include <asm-generic/shmbuf.h>
+#else
+/*
+ * The shmid64_ds structure for x86 architecture with x32 ABI.
+ *
+ * On x86-32 and x86-64 we can just use the generic definition, but
+ * x32 uses the same binary layout as x86_64, which is differnet
+ * from other 32-bit architectures.
+ */
+
+struct shmid64_ds {
+ struct ipc64_perm shm_perm; /* operation perms */
+ size_t shm_segsz; /* size of segment (bytes) */
+ __kernel_time_t shm_atime; /* last attach time */
+ __kernel_time_t shm_dtime; /* last detach time */
+ __kernel_time_t shm_ctime; /* last change time */
+ __kernel_pid_t shm_cpid; /* pid of creator */
+ __kernel_pid_t shm_lpid; /* pid of last operator */
+ __kernel_ulong_t shm_nattch; /* no. of current attaches */
+ __kernel_ulong_t __unused4;
+ __kernel_ulong_t __unused5;
+};
+
+struct shminfo64 {
+ __kernel_ulong_t shmmax;
+ __kernel_ulong_t shmmin;
+ __kernel_ulong_t shmmni;
+ __kernel_ulong_t shmseg;
+ __kernel_ulong_t shmall;
+ __kernel_ulong_t __unused1;
+ __kernel_ulong_t __unused2;
+ __kernel_ulong_t __unused3;
+ __kernel_ulong_t __unused4;
+};
+
+#endif
+
+#endif /* __ASM_X86_SHMBUF_H */
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index b5229ab..4922ab6 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -93,8 +93,12 @@
return NULL;
}
-static void free_apic_chip_data(struct apic_chip_data *data)
+static void free_apic_chip_data(unsigned int virq, struct apic_chip_data *data)
{
+#ifdef CONFIG_X86_IO_APIC
+ if (virq < nr_legacy_irqs())
+ legacy_irq_data[virq] = NULL;
+#endif
if (data) {
free_cpumask_var(data->domain);
free_cpumask_var(data->old_domain);
@@ -318,11 +322,7 @@
apic_data = irq_data->chip_data;
irq_domain_reset_irq_data(irq_data);
raw_spin_unlock_irqrestore(&vector_lock, flags);
- free_apic_chip_data(apic_data);
-#ifdef CONFIG_X86_IO_APIC
- if (virq + i < nr_legacy_irqs())
- legacy_irq_data[virq + i] = NULL;
-#endif
+ free_apic_chip_data(virq + i, apic_data);
}
}
}
@@ -363,7 +363,7 @@
err = assign_irq_vector_policy(virq + i, node, data, info);
if (err) {
irq_data->chip_data = NULL;
- free_apic_chip_data(data);
+ free_apic_chip_data(virq + i, data);
goto error;
}
}
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index c375bc6..4c2be99 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -9,6 +9,7 @@
#include <asm/processor.h>
#include <asm/apic.h>
#include <asm/cpu.h>
+#include <asm/spec-ctrl.h>
#include <asm/smp.h>
#include <asm/pci-direct.h>
#include <asm/delay.h>
@@ -542,6 +543,26 @@
rdmsrl(MSR_FAM10H_NODE_ID, value);
nodes_per_socket = ((value >> 3) & 7) + 1;
}
+
+ if (c->x86 >= 0x15 && c->x86 <= 0x17) {
+ unsigned int bit;
+
+ switch (c->x86) {
+ case 0x15: bit = 54; break;
+ case 0x16: bit = 33; break;
+ case 0x17: bit = 10; break;
+ default: return;
+ }
+ /*
+ * Try to cache the base value so further operations can
+ * avoid RMW. If that faults, do not enable SSBD.
+ */
+ if (!rdmsrl_safe(MSR_AMD64_LS_CFG, &x86_amd_ls_cfg_base)) {
+ setup_force_cpu_cap(X86_FEATURE_LS_CFG_SSBD);
+ setup_force_cpu_cap(X86_FEATURE_SSBD);
+ x86_amd_ls_cfg_ssbd_mask = 1ULL << bit;
+ }
+ }
}
static void early_init_amd(struct cpuinfo_x86 *c)
@@ -728,6 +749,17 @@
}
}
+static void init_amd_zn(struct cpuinfo_x86 *c)
+{
+ set_cpu_cap(c, X86_FEATURE_ZEN);
+ /*
+ * Fix erratum 1076: CPB feature bit not being set in CPUID. It affects
+ * all up to and including B1.
+ */
+ if (c->x86_model <= 1 && c->x86_stepping <= 1)
+ set_cpu_cap(c, X86_FEATURE_CPB);
+}
+
static void init_amd(struct cpuinfo_x86 *c)
{
u32 dummy;
@@ -758,6 +790,7 @@
case 0x10: init_amd_gh(c); break;
case 0x12: init_amd_ln(c); break;
case 0x15: init_amd_bd(c); break;
+ case 0x17: init_amd_zn(c); break;
}
/* Enable workaround for FXSAVE leak */
@@ -824,8 +857,9 @@
if (cpu_has(c, X86_FEATURE_3DNOW) || cpu_has(c, X86_FEATURE_LM))
set_cpu_cap(c, X86_FEATURE_3DNOWPREFETCH);
- /* AMD CPUs don't reset SS attributes on SYSRET */
- set_cpu_bug(c, X86_BUG_SYSRET_SS_ATTRS);
+ /* AMD CPUs don't reset SS attributes on SYSRET, Xen does. */
+ if (!cpu_has(c, X86_FEATURE_XENPV))
+ set_cpu_bug(c, X86_BUG_SYSRET_SS_ATTRS);
}
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index baddc9e..86af9b1 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -11,8 +11,10 @@
#include <linux/utsname.h>
#include <linux/cpu.h>
#include <linux/module.h>
+#include <linux/nospec.h>
+#include <linux/prctl.h>
-#include <asm/nospec-branch.h>
+#include <asm/spec-ctrl.h>
#include <asm/cmdline.h>
#include <asm/bugs.h>
#include <asm/processor.h>
@@ -26,6 +28,27 @@
#include <asm/intel-family.h>
static void __init spectre_v2_select_mitigation(void);
+static void __init ssb_select_mitigation(void);
+
+/*
+ * Our boot-time value of the SPEC_CTRL MSR. We read it once so that any
+ * writes to SPEC_CTRL contain whatever reserved bits have been set.
+ */
+u64 __ro_after_init x86_spec_ctrl_base;
+EXPORT_SYMBOL_GPL(x86_spec_ctrl_base);
+
+/*
+ * The vendor and possibly platform specific bits which can be modified in
+ * x86_spec_ctrl_base.
+ */
+static u64 __ro_after_init x86_spec_ctrl_mask = SPEC_CTRL_IBRS;
+
+/*
+ * AMD specific MSR info for Speculative Store Bypass control.
+ * x86_amd_ls_cfg_ssbd_mask is initialized in identify_boot_cpu().
+ */
+u64 __ro_after_init x86_amd_ls_cfg_base;
+u64 __ro_after_init x86_amd_ls_cfg_ssbd_mask;
void __init check_bugs(void)
{
@@ -36,9 +59,27 @@
print_cpu_info(&boot_cpu_data);
}
+ /*
+ * Read the SPEC_CTRL MSR to account for reserved bits which may
+ * have unknown values. AMD64_LS_CFG MSR is cached in the early AMD
+ * init code as it is not enumerated and depends on the family.
+ */
+ if (boot_cpu_has(X86_FEATURE_MSR_SPEC_CTRL))
+ rdmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+
+ /* Allow STIBP in MSR_SPEC_CTRL if supported */
+ if (boot_cpu_has(X86_FEATURE_STIBP))
+ x86_spec_ctrl_mask |= SPEC_CTRL_STIBP;
+
/* Select the proper spectre mitigation before patching alternatives */
spectre_v2_select_mitigation();
+ /*
+ * Select proper mitigation for any exposure to the Speculative Store
+ * Bypass vulnerability.
+ */
+ ssb_select_mitigation();
+
#ifdef CONFIG_X86_32
/*
* Check whether we are able to run this kernel safely on SMP.
@@ -92,7 +133,76 @@
#undef pr_fmt
#define pr_fmt(fmt) "Spectre V2 : " fmt
-static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
+static enum spectre_v2_mitigation spectre_v2_enabled __ro_after_init =
+ SPECTRE_V2_NONE;
+
+void
+x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest)
+{
+ u64 msrval, guestval, hostval = x86_spec_ctrl_base;
+ struct thread_info *ti = current_thread_info();
+
+ /* Is MSR_SPEC_CTRL implemented ? */
+ if (static_cpu_has(X86_FEATURE_MSR_SPEC_CTRL)) {
+ /*
+ * Restrict guest_spec_ctrl to supported values. Clear the
+ * modifiable bits in the host base value and or the
+ * modifiable bits from the guest value.
+ */
+ guestval = hostval & ~x86_spec_ctrl_mask;
+ guestval |= guest_spec_ctrl & x86_spec_ctrl_mask;
+
+ /* SSBD controlled in MSR_SPEC_CTRL */
+ if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD))
+ hostval |= ssbd_tif_to_spec_ctrl(ti->flags);
+
+ if (hostval != guestval) {
+ msrval = setguest ? guestval : hostval;
+ wrmsrl(MSR_IA32_SPEC_CTRL, msrval);
+ }
+ }
+
+ /*
+ * If SSBD is not handled in MSR_SPEC_CTRL on AMD, update
+ * MSR_AMD64_L2_CFG or MSR_VIRT_SPEC_CTRL if supported.
+ */
+ if (!static_cpu_has(X86_FEATURE_LS_CFG_SSBD) &&
+ !static_cpu_has(X86_FEATURE_VIRT_SSBD))
+ return;
+
+ /*
+ * If the host has SSBD mitigation enabled, force it in the host's
+ * virtual MSR value. If its not permanently enabled, evaluate
+ * current's TIF_SSBD thread flag.
+ */
+ if (static_cpu_has(X86_FEATURE_SPEC_STORE_BYPASS_DISABLE))
+ hostval = SPEC_CTRL_SSBD;
+ else
+ hostval = ssbd_tif_to_spec_ctrl(ti->flags);
+
+ /* Sanitize the guest value */
+ guestval = guest_virt_spec_ctrl & SPEC_CTRL_SSBD;
+
+ if (hostval != guestval) {
+ unsigned long tif;
+
+ tif = setguest ? ssbd_spec_ctrl_to_tif(guestval) :
+ ssbd_spec_ctrl_to_tif(hostval);
+
+ speculative_store_bypass_update(tif);
+ }
+}
+EXPORT_SYMBOL_GPL(x86_virt_spec_ctrl);
+
+static void x86_amd_ssb_disable(void)
+{
+ u64 msrval = x86_amd_ls_cfg_base | x86_amd_ls_cfg_ssbd_mask;
+
+ if (boot_cpu_has(X86_FEATURE_VIRT_SSBD))
+ wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, SPEC_CTRL_SSBD);
+ else if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD))
+ wrmsrl(MSR_AMD64_LS_CFG, msrval);
+}
#ifdef RETPOLINE
static bool spectre_v2_bad_module;
@@ -299,34 +409,301 @@
setup_force_cpu_cap(X86_FEATURE_USE_IBPB);
pr_info("Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier\n");
}
+
+ /*
+ * Retpoline means the kernel is safe because it has no indirect
+ * branches. But firmware isn't, so use IBRS to protect that.
+ */
+ if (boot_cpu_has(X86_FEATURE_IBRS)) {
+ setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
+ pr_info("Enabling Restricted Speculation for firmware calls\n");
+ }
}
#undef pr_fmt
+#define pr_fmt(fmt) "Speculative Store Bypass: " fmt
+
+static enum ssb_mitigation ssb_mode __ro_after_init = SPEC_STORE_BYPASS_NONE;
+
+/* The kernel command line selection */
+enum ssb_mitigation_cmd {
+ SPEC_STORE_BYPASS_CMD_NONE,
+ SPEC_STORE_BYPASS_CMD_AUTO,
+ SPEC_STORE_BYPASS_CMD_ON,
+ SPEC_STORE_BYPASS_CMD_PRCTL,
+ SPEC_STORE_BYPASS_CMD_SECCOMP,
+};
+
+static const char *ssb_strings[] = {
+ [SPEC_STORE_BYPASS_NONE] = "Vulnerable",
+ [SPEC_STORE_BYPASS_DISABLE] = "Mitigation: Speculative Store Bypass disabled",
+ [SPEC_STORE_BYPASS_PRCTL] = "Mitigation: Speculative Store Bypass disabled via prctl",
+ [SPEC_STORE_BYPASS_SECCOMP] = "Mitigation: Speculative Store Bypass disabled via prctl and seccomp",
+};
+
+static const struct {
+ const char *option;
+ enum ssb_mitigation_cmd cmd;
+} ssb_mitigation_options[] = {
+ { "auto", SPEC_STORE_BYPASS_CMD_AUTO }, /* Platform decides */
+ { "on", SPEC_STORE_BYPASS_CMD_ON }, /* Disable Speculative Store Bypass */
+ { "off", SPEC_STORE_BYPASS_CMD_NONE }, /* Don't touch Speculative Store Bypass */
+ { "prctl", SPEC_STORE_BYPASS_CMD_PRCTL }, /* Disable Speculative Store Bypass via prctl */
+ { "seccomp", SPEC_STORE_BYPASS_CMD_SECCOMP }, /* Disable Speculative Store Bypass via prctl and seccomp */
+};
+
+static enum ssb_mitigation_cmd __init ssb_parse_cmdline(void)
+{
+ enum ssb_mitigation_cmd cmd = SPEC_STORE_BYPASS_CMD_AUTO;
+ char arg[20];
+ int ret, i;
+
+ if (cmdline_find_option_bool(boot_command_line, "nospec_store_bypass_disable")) {
+ return SPEC_STORE_BYPASS_CMD_NONE;
+ } else {
+ ret = cmdline_find_option(boot_command_line, "spec_store_bypass_disable",
+ arg, sizeof(arg));
+ if (ret < 0)
+ return SPEC_STORE_BYPASS_CMD_AUTO;
+
+ for (i = 0; i < ARRAY_SIZE(ssb_mitigation_options); i++) {
+ if (!match_option(arg, ret, ssb_mitigation_options[i].option))
+ continue;
+
+ cmd = ssb_mitigation_options[i].cmd;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(ssb_mitigation_options)) {
+ pr_err("unknown option (%s). Switching to AUTO select\n", arg);
+ return SPEC_STORE_BYPASS_CMD_AUTO;
+ }
+ }
+
+ return cmd;
+}
+
+static enum ssb_mitigation __init __ssb_select_mitigation(void)
+{
+ enum ssb_mitigation mode = SPEC_STORE_BYPASS_NONE;
+ enum ssb_mitigation_cmd cmd;
+
+ if (!boot_cpu_has(X86_FEATURE_SSBD))
+ return mode;
+
+ cmd = ssb_parse_cmdline();
+ if (!boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS) &&
+ (cmd == SPEC_STORE_BYPASS_CMD_NONE ||
+ cmd == SPEC_STORE_BYPASS_CMD_AUTO))
+ return mode;
+
+ switch (cmd) {
+ case SPEC_STORE_BYPASS_CMD_AUTO:
+ case SPEC_STORE_BYPASS_CMD_SECCOMP:
+ /*
+ * Choose prctl+seccomp as the default mode if seccomp is
+ * enabled.
+ */
+ if (IS_ENABLED(CONFIG_SECCOMP))
+ mode = SPEC_STORE_BYPASS_SECCOMP;
+ else
+ mode = SPEC_STORE_BYPASS_PRCTL;
+ break;
+ case SPEC_STORE_BYPASS_CMD_ON:
+ mode = SPEC_STORE_BYPASS_DISABLE;
+ break;
+ case SPEC_STORE_BYPASS_CMD_PRCTL:
+ mode = SPEC_STORE_BYPASS_PRCTL;
+ break;
+ case SPEC_STORE_BYPASS_CMD_NONE:
+ break;
+ }
+
+ /*
+ * We have three CPU feature flags that are in play here:
+ * - X86_BUG_SPEC_STORE_BYPASS - CPU is susceptible.
+ * - X86_FEATURE_SSBD - CPU is able to turn off speculative store bypass
+ * - X86_FEATURE_SPEC_STORE_BYPASS_DISABLE - engage the mitigation
+ */
+ if (mode == SPEC_STORE_BYPASS_DISABLE) {
+ setup_force_cpu_cap(X86_FEATURE_SPEC_STORE_BYPASS_DISABLE);
+ /*
+ * Intel uses the SPEC CTRL MSR Bit(2) for this, while AMD uses
+ * a completely different MSR and bit dependent on family.
+ */
+ switch (boot_cpu_data.x86_vendor) {
+ case X86_VENDOR_INTEL:
+ x86_spec_ctrl_base |= SPEC_CTRL_SSBD;
+ x86_spec_ctrl_mask |= SPEC_CTRL_SSBD;
+ wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+ break;
+ case X86_VENDOR_AMD:
+ x86_amd_ssb_disable();
+ break;
+ }
+ }
+
+ return mode;
+}
+
+static void ssb_select_mitigation(void)
+{
+ ssb_mode = __ssb_select_mitigation();
+
+ if (boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS))
+ pr_info("%s\n", ssb_strings[ssb_mode]);
+}
+
+#undef pr_fmt
+#define pr_fmt(fmt) "Speculation prctl: " fmt
+
+static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
+{
+ bool update;
+
+ if (ssb_mode != SPEC_STORE_BYPASS_PRCTL &&
+ ssb_mode != SPEC_STORE_BYPASS_SECCOMP)
+ return -ENXIO;
+
+ switch (ctrl) {
+ case PR_SPEC_ENABLE:
+ /* If speculation is force disabled, enable is not allowed */
+ if (task_spec_ssb_force_disable(task))
+ return -EPERM;
+ task_clear_spec_ssb_disable(task);
+ update = test_and_clear_tsk_thread_flag(task, TIF_SSBD);
+ break;
+ case PR_SPEC_DISABLE:
+ task_set_spec_ssb_disable(task);
+ update = !test_and_set_tsk_thread_flag(task, TIF_SSBD);
+ break;
+ case PR_SPEC_FORCE_DISABLE:
+ task_set_spec_ssb_disable(task);
+ task_set_spec_ssb_force_disable(task);
+ update = !test_and_set_tsk_thread_flag(task, TIF_SSBD);
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /*
+ * If being set on non-current task, delay setting the CPU
+ * mitigation until it is next scheduled.
+ */
+ if (task == current && update)
+ speculative_store_bypass_update_current();
+
+ return 0;
+}
+
+int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
+ unsigned long ctrl)
+{
+ switch (which) {
+ case PR_SPEC_STORE_BYPASS:
+ return ssb_prctl_set(task, ctrl);
+ default:
+ return -ENODEV;
+ }
+}
+
+#ifdef CONFIG_SECCOMP
+void arch_seccomp_spec_mitigate(struct task_struct *task)
+{
+ if (ssb_mode == SPEC_STORE_BYPASS_SECCOMP)
+ ssb_prctl_set(task, PR_SPEC_FORCE_DISABLE);
+}
+#endif
+
+static int ssb_prctl_get(struct task_struct *task)
+{
+ switch (ssb_mode) {
+ case SPEC_STORE_BYPASS_DISABLE:
+ return PR_SPEC_DISABLE;
+ case SPEC_STORE_BYPASS_SECCOMP:
+ case SPEC_STORE_BYPASS_PRCTL:
+ if (task_spec_ssb_force_disable(task))
+ return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
+ if (task_spec_ssb_disable(task))
+ return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
+ return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
+ default:
+ if (boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS))
+ return PR_SPEC_ENABLE;
+ return PR_SPEC_NOT_AFFECTED;
+ }
+}
+
+int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which)
+{
+ switch (which) {
+ case PR_SPEC_STORE_BYPASS:
+ return ssb_prctl_get(task);
+ default:
+ return -ENODEV;
+ }
+}
+
+void x86_spec_ctrl_setup_ap(void)
+{
+ if (boot_cpu_has(X86_FEATURE_MSR_SPEC_CTRL))
+ wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+
+ if (ssb_mode == SPEC_STORE_BYPASS_DISABLE)
+ x86_amd_ssb_disable();
+}
#ifdef CONFIG_SYSFS
+
+static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr,
+ char *buf, unsigned int bug)
+{
+ if (!boot_cpu_has_bug(bug))
+ return sprintf(buf, "Not affected\n");
+
+ switch (bug) {
+ case X86_BUG_CPU_MELTDOWN:
+ if (boot_cpu_has(X86_FEATURE_KAISER))
+ return sprintf(buf, "Mitigation: PTI\n");
+
+ break;
+
+ case X86_BUG_SPECTRE_V1:
+ return sprintf(buf, "Mitigation: __user pointer sanitization\n");
+
+ case X86_BUG_SPECTRE_V2:
+ return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
+ boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "",
+ boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
+ spectre_v2_module_string());
+
+ case X86_BUG_SPEC_STORE_BYPASS:
+ return sprintf(buf, "%s\n", ssb_strings[ssb_mode]);
+
+ default:
+ break;
+ }
+
+ return sprintf(buf, "Vulnerable\n");
+}
+
ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
{
- if (!boot_cpu_has_bug(X86_BUG_CPU_MELTDOWN))
- return sprintf(buf, "Not affected\n");
- if (boot_cpu_has(X86_FEATURE_KAISER))
- return sprintf(buf, "Mitigation: PTI\n");
- return sprintf(buf, "Vulnerable\n");
+ return cpu_show_common(dev, attr, buf, X86_BUG_CPU_MELTDOWN);
}
ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
{
- if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1))
- return sprintf(buf, "Not affected\n");
- return sprintf(buf, "Mitigation: __user pointer sanitization\n");
+ return cpu_show_common(dev, attr, buf, X86_BUG_SPECTRE_V1);
}
ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
{
- if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
- return sprintf(buf, "Not affected\n");
+ return cpu_show_common(dev, attr, buf, X86_BUG_SPECTRE_V2);
+}
- return sprintf(buf, "%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
- boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "",
- spectre_v2_module_string());
+ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return cpu_show_common(dev, attr, buf, X86_BUG_SPEC_STORE_BYPASS);
}
#endif
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 301bbd1..b0fd028 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -725,17 +725,32 @@
* and they also have a different bit for STIBP support. Also,
* a hypervisor might have set the individual AMD bits even on
* Intel CPUs, for finer-grained selection of what's available.
- *
- * We use the AMD bits in 0x8000_0008 EBX as the generic hardware
- * features, which are visible in /proc/cpuinfo and used by the
- * kernel. So set those accordingly from the Intel bits.
*/
if (cpu_has(c, X86_FEATURE_SPEC_CTRL)) {
set_cpu_cap(c, X86_FEATURE_IBRS);
set_cpu_cap(c, X86_FEATURE_IBPB);
+ set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL);
}
+
if (cpu_has(c, X86_FEATURE_INTEL_STIBP))
set_cpu_cap(c, X86_FEATURE_STIBP);
+
+ if (cpu_has(c, X86_FEATURE_SPEC_CTRL_SSBD) ||
+ cpu_has(c, X86_FEATURE_VIRT_SSBD))
+ set_cpu_cap(c, X86_FEATURE_SSBD);
+
+ if (cpu_has(c, X86_FEATURE_AMD_IBRS)) {
+ set_cpu_cap(c, X86_FEATURE_IBRS);
+ set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL);
+ }
+
+ if (cpu_has(c, X86_FEATURE_AMD_IBPB))
+ set_cpu_cap(c, X86_FEATURE_IBPB);
+
+ if (cpu_has(c, X86_FEATURE_AMD_STIBP)) {
+ set_cpu_cap(c, X86_FEATURE_STIBP);
+ set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL);
+ }
}
void get_cpu_cap(struct cpuinfo_x86 *c)
@@ -879,21 +894,55 @@
{}
};
-static bool __init cpu_vulnerable_to_meltdown(struct cpuinfo_x86 *c)
+static const __initconst struct x86_cpu_id cpu_no_spec_store_bypass[] = {
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PINEVIEW },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_LINCROFT },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PENWELL },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CLOVERVIEW },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CEDARVIEW },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT2 },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MERRIFIELD },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_CORE_YONAH },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL },
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM },
+ { X86_VENDOR_CENTAUR, 5, },
+ { X86_VENDOR_INTEL, 5, },
+ { X86_VENDOR_NSC, 5, },
+ { X86_VENDOR_AMD, 0x12, },
+ { X86_VENDOR_AMD, 0x11, },
+ { X86_VENDOR_AMD, 0x10, },
+ { X86_VENDOR_AMD, 0xf, },
+ { X86_VENDOR_ANY, 4, },
+ {}
+};
+
+static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
{
u64 ia32_cap = 0;
- if (x86_match_cpu(cpu_no_meltdown))
- return false;
-
if (cpu_has(c, X86_FEATURE_ARCH_CAPABILITIES))
rdmsrl(MSR_IA32_ARCH_CAPABILITIES, ia32_cap);
+ if (!x86_match_cpu(cpu_no_spec_store_bypass) &&
+ !(ia32_cap & ARCH_CAP_SSB_NO))
+ setup_force_cpu_bug(X86_BUG_SPEC_STORE_BYPASS);
+
+ if (x86_match_cpu(cpu_no_speculation))
+ return;
+
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
+
+ if (x86_match_cpu(cpu_no_meltdown))
+ return;
+
/* Rogue Data Cache Load? No! */
if (ia32_cap & ARCH_CAP_RDCL_NO)
- return false;
+ return;
- return true;
+ setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
}
/*
@@ -942,12 +991,7 @@
setup_force_cpu_cap(X86_FEATURE_ALWAYS);
- if (!x86_match_cpu(cpu_no_speculation)) {
- if (cpu_vulnerable_to_meltdown(c))
- setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
- setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
- setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
- }
+ cpu_set_bug_bits(c);
fpu__init_system(c);
@@ -1315,6 +1359,7 @@
#endif
mtrr_ap_init();
validate_apic_and_package_id(c);
+ x86_spec_ctrl_setup_ap();
}
struct msr_range {
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h
index 2584265..3b19d82 100644
--- a/arch/x86/kernel/cpu/cpu.h
+++ b/arch/x86/kernel/cpu/cpu.h
@@ -46,4 +46,7 @@
extern void get_cpu_cap(struct cpuinfo_x86 *c);
extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c);
+
+extern void x86_spec_ctrl_setup_ap(void);
+
#endif /* ARCH_X86_CPU_H */
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 6ed206b..93781e3 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -64,7 +64,7 @@
/*
* Early microcode releases for the Spectre v2 mitigation were broken.
* Information taken from;
- * - https://newsroom.intel.com/wp-content/uploads/sites/11/2018/01/microcode-update-guidance.pdf
+ * - https://newsroom.intel.com/wp-content/uploads/sites/11/2018/03/microcode-update-guidance.pdf
* - https://kb.vmware.com/s/article/52345
* - Microcode revisions observed in the wild
* - Release note from 20180108 microcode release
@@ -82,7 +82,6 @@
{ INTEL_FAM6_KABYLAKE_MOBILE, 0x09, 0x80 },
{ INTEL_FAM6_SKYLAKE_X, 0x03, 0x0100013e },
{ INTEL_FAM6_SKYLAKE_X, 0x04, 0x0200003c },
- { INTEL_FAM6_SKYLAKE_DESKTOP, 0x03, 0xc2 },
{ INTEL_FAM6_BROADWELL_CORE, 0x04, 0x28 },
{ INTEL_FAM6_BROADWELL_GT3E, 0x01, 0x1b },
{ INTEL_FAM6_BROADWELL_XEON_D, 0x02, 0x14 },
@@ -103,6 +102,13 @@
{
int i;
+ /*
+ * We know that the hypervisor lie to us on the microcode version so
+ * we may as well hope that it is running the correct version.
+ */
+ if (cpu_has(c, X86_FEATURE_HYPERVISOR))
+ return false;
+
for (i = 0; i < ARRAY_SIZE(spectre_bad_microcodes); i++) {
if (c->x86_model == spectre_bad_microcodes[i].model &&
c->x86_stepping == spectre_bad_microcodes[i].stepping)
@@ -147,7 +153,10 @@
setup_clear_cpu_cap(X86_FEATURE_IBPB);
setup_clear_cpu_cap(X86_FEATURE_STIBP);
setup_clear_cpu_cap(X86_FEATURE_SPEC_CTRL);
+ setup_clear_cpu_cap(X86_FEATURE_MSR_SPEC_CTRL);
setup_clear_cpu_cap(X86_FEATURE_INTEL_STIBP);
+ setup_clear_cpu_cap(X86_FEATURE_SSBD);
+ setup_clear_cpu_cap(X86_FEATURE_SPEC_CTRL_SSBD);
}
/*
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index fe5cd6e..7bbd50f 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -48,6 +48,7 @@
#include <asm/tlbflush.h>
#include <asm/mce.h>
#include <asm/msr.h>
+#include <asm/reboot.h>
#include "mce-internal.h"
@@ -61,6 +62,9 @@
smp_load_acquire(&(p)); \
})
+/* sysfs synchronization */
+static DEFINE_MUTEX(mce_sysfs_mutex);
+
#define CREATE_TRACE_POINTS
#include <trace/events/mce.h>
@@ -1078,9 +1082,22 @@
* on Intel.
*/
int lmce = 1;
+ int cpu = smp_processor_id();
- /* If this CPU is offline, just bail out. */
- if (cpu_is_offline(smp_processor_id())) {
+ /*
+ * Cases where we avoid rendezvous handler timeout:
+ * 1) If this CPU is offline.
+ *
+ * 2) If crashing_cpu was set, e.g. we're entering kdump and we need to
+ * skip those CPUs which remain looping in the 1st kernel - see
+ * crash_nmi_callback().
+ *
+ * Note: there still is a small window between kexec-ing and the new,
+ * kdump kernel establishing a new #MC handler where a broadcasted MCE
+ * might not get handled properly.
+ */
+ if (cpu_is_offline(cpu) ||
+ (crashing_cpu != -1 && crashing_cpu != cpu)) {
u64 mcgstatus;
mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
@@ -1678,6 +1695,25 @@
return 0;
}
+/*
+ * Init basic CPU features needed for early decoding of MCEs.
+ */
+static void __mcheck_cpu_init_early(struct cpuinfo_x86 *c)
+{
+ if (c->x86_vendor == X86_VENDOR_AMD) {
+ mce_flags.overflow_recov = !!cpu_has(c, X86_FEATURE_OVERFLOW_RECOV);
+ mce_flags.succor = !!cpu_has(c, X86_FEATURE_SUCCOR);
+ mce_flags.smca = !!cpu_has(c, X86_FEATURE_SMCA);
+
+ if (mce_flags.smca) {
+ msr_ops.ctl = smca_ctl_reg;
+ msr_ops.status = smca_status_reg;
+ msr_ops.addr = smca_addr_reg;
+ msr_ops.misc = smca_misc_reg;
+ }
+ }
+}
+
static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
{
switch (c->x86_vendor) {
@@ -1687,21 +1723,7 @@
break;
case X86_VENDOR_AMD: {
- mce_flags.overflow_recov = !!cpu_has(c, X86_FEATURE_OVERFLOW_RECOV);
- mce_flags.succor = !!cpu_has(c, X86_FEATURE_SUCCOR);
- mce_flags.smca = !!cpu_has(c, X86_FEATURE_SMCA);
-
- /*
- * Install proper ops for Scalable MCA enabled processors
- */
- if (mce_flags.smca) {
- msr_ops.ctl = smca_ctl_reg;
- msr_ops.status = smca_status_reg;
- msr_ops.addr = smca_addr_reg;
- msr_ops.misc = smca_misc_reg;
- }
mce_amd_feature_init(c);
-
break;
}
@@ -1787,6 +1809,7 @@
machine_check_vector = do_machine_check;
+ __mcheck_cpu_init_early(c);
__mcheck_cpu_init_generic();
__mcheck_cpu_init_vendor(c);
__mcheck_cpu_init_clear_banks();
@@ -2308,6 +2331,7 @@
if (kstrtou64(buf, 0, &new) < 0)
return -EINVAL;
+ mutex_lock(&mce_sysfs_mutex);
if (mca_cfg.ignore_ce ^ !!new) {
if (new) {
/* disable ce features */
@@ -2320,6 +2344,8 @@
on_each_cpu(mce_enable_ce, (void *)1, 1);
}
}
+ mutex_unlock(&mce_sysfs_mutex);
+
return size;
}
@@ -2332,6 +2358,7 @@
if (kstrtou64(buf, 0, &new) < 0)
return -EINVAL;
+ mutex_lock(&mce_sysfs_mutex);
if (mca_cfg.cmci_disabled ^ !!new) {
if (new) {
/* disable cmci */
@@ -2343,6 +2370,8 @@
on_each_cpu(mce_enable_ce, NULL, 1);
}
}
+ mutex_unlock(&mce_sysfs_mutex);
+
return size;
}
@@ -2350,8 +2379,19 @@
struct device_attribute *attr,
const char *buf, size_t size)
{
- ssize_t ret = device_store_int(s, attr, buf, size);
+ unsigned long old_check_interval = check_interval;
+ ssize_t ret = device_store_ulong(s, attr, buf, size);
+
+ if (check_interval == old_check_interval)
+ return ret;
+
+ if (check_interval < 1)
+ check_interval = 1;
+
+ mutex_lock(&mce_sysfs_mutex);
mce_restart();
+ mutex_unlock(&mce_sysfs_mutex);
+
return ret;
}
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 4bcd30c..79291d6 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -474,7 +474,6 @@
*/
static void save_mc_for_early(u8 *mc)
{
-#ifdef CONFIG_HOTPLUG_CPU
/* Synchronization during CPU hotplug. */
static DEFINE_MUTEX(x86_cpu_microcode_mutex);
@@ -521,7 +520,6 @@
out:
mutex_unlock(&x86_cpu_microcode_mutex);
-#endif
}
static bool __init load_builtin_intel_microcode(struct cpio_data *cp)
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index 67cd7c1..9d72cf5 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -22,6 +22,7 @@
#include <asm/nops.h>
#include "../entry/calling.h"
#include <asm/export.h>
+#include <asm/nospec-branch.h>
#ifdef CONFIG_PARAVIRT
#include <asm/asm-offsets.h>
@@ -200,6 +201,7 @@
/* Ensure I am executing from virtual addresses */
movq $1f, %rax
+ ANNOTATE_RETPOLINE_SAFE
jmp *%rax
1:
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
index be22f5a..4e3b8a5 100644
--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -418,6 +418,7 @@
};
struct legacy_pic *legacy_pic = &default_legacy_pic;
+EXPORT_SYMBOL(legacy_pic);
static int __init i8259A_init_ops(void)
{
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index b55d07b..91c48cd 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -51,6 +51,7 @@
#include <linux/ftrace.h>
#include <linux/frame.h>
#include <linux/kasan.h>
+#include <linux/moduleloader.h>
#include <asm/text-patching.h>
#include <asm/cacheflush.h>
@@ -199,6 +200,8 @@
return (opcode != 0x62 && opcode != 0x67);
case 0x70:
return 0; /* can't boost conditional jump */
+ case 0x90:
+ return opcode != 0x9a; /* can't boost call far */
case 0xc0:
/* can't boost software-interruptions */
return (0xc1 < opcode && opcode < 0xcc) || opcode == 0xcf;
@@ -403,10 +406,20 @@
return length;
}
+/* Recover page to RW mode before releasing it */
+void free_insn_page(void *page)
+{
+ set_memory_nx((unsigned long)page & PAGE_MASK, 1);
+ set_memory_rw((unsigned long)page & PAGE_MASK, 1);
+ module_memfree(page);
+}
+
static int arch_copy_kprobe(struct kprobe *p)
{
int ret;
+ set_memory_rw((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
+
/* Copy an instruction with recovering if other optprobe modifies it.*/
ret = __copy_instruction(p->ainsn.insn, p->addr);
if (!ret)
@@ -421,6 +434,8 @@
else
p->ainsn.boostable = -1;
+ set_memory_ro((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
+
/* Check whether the instruction modifies Interrupt Flag or not */
p->ainsn.if_modifier = is_IF_modifier(p->ainsn.insn);
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index dc20da1c..fa671b9 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -371,6 +371,7 @@
}
buf = (u8 *)op->optinsn.insn;
+ set_memory_rw((unsigned long)buf & PAGE_MASK, 1);
/* Copy instructions into the out-of-line buffer */
ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr);
@@ -393,6 +394,8 @@
synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
(u8 *)op->kp.addr + op->optinsn.size);
+ set_memory_ro((unsigned long)buf & PAGE_MASK, 1);
+
flush_icache_range((unsigned long) buf,
(unsigned long) buf + TMPL_END_IDX +
op->optinsn.size + RELATIVEJUMP_SIZE);
diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c
index 469b23d..fd7e993 100644
--- a/arch/x86/kernel/machine_kexec_32.c
+++ b/arch/x86/kernel/machine_kexec_32.c
@@ -71,12 +71,17 @@
static void machine_kexec_free_page_tables(struct kimage *image)
{
free_page((unsigned long)image->arch.pgd);
+ image->arch.pgd = NULL;
#ifdef CONFIG_X86_PAE
free_page((unsigned long)image->arch.pmd0);
+ image->arch.pmd0 = NULL;
free_page((unsigned long)image->arch.pmd1);
+ image->arch.pmd1 = NULL;
#endif
free_page((unsigned long)image->arch.pte0);
+ image->arch.pte0 = NULL;
free_page((unsigned long)image->arch.pte1);
+ image->arch.pte1 = NULL;
}
static int machine_kexec_alloc_page_tables(struct kimage *image)
@@ -93,7 +98,6 @@
!image->arch.pmd0 || !image->arch.pmd1 ||
#endif
!image->arch.pte0 || !image->arch.pte1) {
- machine_kexec_free_page_tables(image);
return -ENOMEM;
}
return 0;
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 8c1f218..eae59ca 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -37,8 +37,11 @@
static void free_transition_pgtable(struct kimage *image)
{
free_page((unsigned long)image->arch.pud);
+ image->arch.pud = NULL;
free_page((unsigned long)image->arch.pmd);
+ image->arch.pmd = NULL;
free_page((unsigned long)image->arch.pte);
+ image->arch.pte = NULL;
}
static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
@@ -79,7 +82,6 @@
set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC));
return 0;
err:
- free_transition_pgtable(image);
return result;
}
@@ -524,6 +526,7 @@
goto overflow;
break;
case R_X86_64_PC32:
+ case R_X86_64_PLT32:
value -= (u64)address;
*(u32 *)location = value;
break;
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
index 477ae80..19977d2 100644
--- a/arch/x86/kernel/module.c
+++ b/arch/x86/kernel/module.c
@@ -171,19 +171,28 @@
case R_X86_64_NONE:
break;
case R_X86_64_64:
+ if (*(u64 *)loc != 0)
+ goto invalid_relocation;
*(u64 *)loc = val;
break;
case R_X86_64_32:
+ if (*(u32 *)loc != 0)
+ goto invalid_relocation;
*(u32 *)loc = val;
if (val != *(u32 *)loc)
goto overflow;
break;
case R_X86_64_32S:
+ if (*(s32 *)loc != 0)
+ goto invalid_relocation;
*(s32 *)loc = val;
if ((s64)val != *(s32 *)loc)
goto overflow;
break;
case R_X86_64_PC32:
+ case R_X86_64_PLT32:
+ if (*(u32 *)loc != 0)
+ goto invalid_relocation;
val -= (u64)loc;
*(u32 *)loc = val;
#if 0
@@ -199,6 +208,11 @@
}
return 0;
+invalid_relocation:
+ pr_err("x86/modules: Skipping invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
+ (int)ELF64_R_TYPE(rel[i].r_info), loc, val);
+ return -ENOEXEC;
+
overflow:
pr_err("overflow in relocation type %d val %Lx\n",
(int)ELF64_R_TYPE(rel[i].r_info), val);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 54b2711..e9195a1 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -33,6 +33,7 @@
#include <asm/mce.h>
#include <asm/vm86.h>
#include <asm/switch_to.h>
+#include <asm/spec-ctrl.h>
/*
* per-CPU TSS segments. Threads are completely 'soft' on Linux,
@@ -121,11 +122,6 @@
fpu__clear(&tsk->thread.fpu);
}
-static void hard_disable_TSC(void)
-{
- cr4_set_bits(X86_CR4_TSD);
-}
-
void disable_TSC(void)
{
preempt_disable();
@@ -134,15 +130,10 @@
* Must flip the CPU state synchronously with
* TIF_NOTSC in the current running context.
*/
- hard_disable_TSC();
+ cr4_set_bits(X86_CR4_TSD);
preempt_enable();
}
-static void hard_enable_TSC(void)
-{
- cr4_clear_bits(X86_CR4_TSD);
-}
-
static void enable_TSC(void)
{
preempt_disable();
@@ -151,7 +142,7 @@
* Must flip the CPU state synchronously with
* TIF_NOTSC in the current running context.
*/
- hard_enable_TSC();
+ cr4_clear_bits(X86_CR4_TSD);
preempt_enable();
}
@@ -179,48 +170,199 @@
return 0;
}
-void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
- struct tss_struct *tss)
+static inline void switch_to_bitmap(struct tss_struct *tss,
+ struct thread_struct *prev,
+ struct thread_struct *next,
+ unsigned long tifp, unsigned long tifn)
{
- struct thread_struct *prev, *next;
-
- prev = &prev_p->thread;
- next = &next_p->thread;
-
- if (test_tsk_thread_flag(prev_p, TIF_BLOCKSTEP) ^
- test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) {
- unsigned long debugctl = get_debugctlmsr();
-
- debugctl &= ~DEBUGCTLMSR_BTF;
- if (test_tsk_thread_flag(next_p, TIF_BLOCKSTEP))
- debugctl |= DEBUGCTLMSR_BTF;
-
- update_debugctlmsr(debugctl);
- }
-
- if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
- test_tsk_thread_flag(next_p, TIF_NOTSC)) {
- /* prev and next are different */
- if (test_tsk_thread_flag(next_p, TIF_NOTSC))
- hard_disable_TSC();
- else
- hard_enable_TSC();
- }
-
- if (test_tsk_thread_flag(next_p, TIF_IO_BITMAP)) {
+ if (tifn & _TIF_IO_BITMAP) {
/*
* Copy the relevant range of the IO bitmap.
* Normally this is 128 bytes or less:
*/
memcpy(tss->io_bitmap, next->io_bitmap_ptr,
max(prev->io_bitmap_max, next->io_bitmap_max));
- } else if (test_tsk_thread_flag(prev_p, TIF_IO_BITMAP)) {
+ } else if (tifp & _TIF_IO_BITMAP) {
/*
* Clear any possible leftover bits:
*/
memset(tss->io_bitmap, 0xff, prev->io_bitmap_max);
}
+}
+
+#ifdef CONFIG_SMP
+
+struct ssb_state {
+ struct ssb_state *shared_state;
+ raw_spinlock_t lock;
+ unsigned int disable_state;
+ unsigned long local_state;
+};
+
+#define LSTATE_SSB 0
+
+static DEFINE_PER_CPU(struct ssb_state, ssb_state);
+
+void speculative_store_bypass_ht_init(void)
+{
+ struct ssb_state *st = this_cpu_ptr(&ssb_state);
+ unsigned int this_cpu = smp_processor_id();
+ unsigned int cpu;
+
+ st->local_state = 0;
+
+ /*
+ * Shared state setup happens once on the first bringup
+ * of the CPU. It's not destroyed on CPU hotunplug.
+ */
+ if (st->shared_state)
+ return;
+
+ raw_spin_lock_init(&st->lock);
+
+ /*
+ * Go over HT siblings and check whether one of them has set up the
+ * shared state pointer already.
+ */
+ for_each_cpu(cpu, topology_sibling_cpumask(this_cpu)) {
+ if (cpu == this_cpu)
+ continue;
+
+ if (!per_cpu(ssb_state, cpu).shared_state)
+ continue;
+
+ /* Link it to the state of the sibling: */
+ st->shared_state = per_cpu(ssb_state, cpu).shared_state;
+ return;
+ }
+
+ /*
+ * First HT sibling to come up on the core. Link shared state of
+ * the first HT sibling to itself. The siblings on the same core
+ * which come up later will see the shared state pointer and link
+ * themself to the state of this CPU.
+ */
+ st->shared_state = st;
+}
+
+/*
+ * Logic is: First HT sibling enables SSBD for both siblings in the core
+ * and last sibling to disable it, disables it for the whole core. This how
+ * MSR_SPEC_CTRL works in "hardware":
+ *
+ * CORE_SPEC_CTRL = THREAD0_SPEC_CTRL | THREAD1_SPEC_CTRL
+ */
+static __always_inline void amd_set_core_ssb_state(unsigned long tifn)
+{
+ struct ssb_state *st = this_cpu_ptr(&ssb_state);
+ u64 msr = x86_amd_ls_cfg_base;
+
+ if (!static_cpu_has(X86_FEATURE_ZEN)) {
+ msr |= ssbd_tif_to_amd_ls_cfg(tifn);
+ wrmsrl(MSR_AMD64_LS_CFG, msr);
+ return;
+ }
+
+ if (tifn & _TIF_SSBD) {
+ /*
+ * Since this can race with prctl(), block reentry on the
+ * same CPU.
+ */
+ if (__test_and_set_bit(LSTATE_SSB, &st->local_state))
+ return;
+
+ msr |= x86_amd_ls_cfg_ssbd_mask;
+
+ raw_spin_lock(&st->shared_state->lock);
+ /* First sibling enables SSBD: */
+ if (!st->shared_state->disable_state)
+ wrmsrl(MSR_AMD64_LS_CFG, msr);
+ st->shared_state->disable_state++;
+ raw_spin_unlock(&st->shared_state->lock);
+ } else {
+ if (!__test_and_clear_bit(LSTATE_SSB, &st->local_state))
+ return;
+
+ raw_spin_lock(&st->shared_state->lock);
+ st->shared_state->disable_state--;
+ if (!st->shared_state->disable_state)
+ wrmsrl(MSR_AMD64_LS_CFG, msr);
+ raw_spin_unlock(&st->shared_state->lock);
+ }
+}
+#else
+static __always_inline void amd_set_core_ssb_state(unsigned long tifn)
+{
+ u64 msr = x86_amd_ls_cfg_base | ssbd_tif_to_amd_ls_cfg(tifn);
+
+ wrmsrl(MSR_AMD64_LS_CFG, msr);
+}
+#endif
+
+static __always_inline void amd_set_ssb_virt_state(unsigned long tifn)
+{
+ /*
+ * SSBD has the same definition in SPEC_CTRL and VIRT_SPEC_CTRL,
+ * so ssbd_tif_to_spec_ctrl() just works.
+ */
+ wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, ssbd_tif_to_spec_ctrl(tifn));
+}
+
+static __always_inline void intel_set_ssb_state(unsigned long tifn)
+{
+ u64 msr = x86_spec_ctrl_base | ssbd_tif_to_spec_ctrl(tifn);
+
+ wrmsrl(MSR_IA32_SPEC_CTRL, msr);
+}
+
+static __always_inline void __speculative_store_bypass_update(unsigned long tifn)
+{
+ if (static_cpu_has(X86_FEATURE_VIRT_SSBD))
+ amd_set_ssb_virt_state(tifn);
+ else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD))
+ amd_set_core_ssb_state(tifn);
+ else
+ intel_set_ssb_state(tifn);
+}
+
+void speculative_store_bypass_update(unsigned long tif)
+{
+ preempt_disable();
+ __speculative_store_bypass_update(tif);
+ preempt_enable();
+}
+
+void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
+ struct tss_struct *tss)
+{
+ struct thread_struct *prev, *next;
+ unsigned long tifp, tifn;
+
+ prev = &prev_p->thread;
+ next = &next_p->thread;
+
+ tifn = READ_ONCE(task_thread_info(next_p)->flags);
+ tifp = READ_ONCE(task_thread_info(prev_p)->flags);
+ switch_to_bitmap(tss, prev, next, tifp, tifn);
+
propagate_user_return_notify(prev_p, next_p);
+
+ if ((tifp & _TIF_BLOCKSTEP || tifn & _TIF_BLOCKSTEP) &&
+ arch_has_block_step()) {
+ unsigned long debugctl, msk;
+
+ rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+ debugctl &= ~DEBUGCTLMSR_BTF;
+ msk = tifn & _TIF_BLOCKSTEP;
+ debugctl |= (msk >> TIF_BLOCKSTEP) << DEBUGCTLMSR_BTF_SHIFT;
+ wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
+ }
+
+ if ((tifp ^ tifn) & _TIF_NOTSC)
+ cr4_toggle_bits(X86_CR4_TSD);
+
+ if ((tifp ^ tifn) & _TIF_SSBD)
+ __speculative_store_bypass_update(tifn);
}
/*
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index ce020a6..03f21db 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -769,10 +769,11 @@
#endif
+/* This is the CPU performing the emergency shutdown work. */
+int crashing_cpu = -1;
+
#if defined(CONFIG_SMP)
-/* This keeps a track of which one is crashing cpu. */
-static int crashing_cpu;
static nmi_shootdown_cb shootdown_callback;
static atomic_t waiting_for_crash_ipi;
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c
index 2bbd27f..f9da471 100644
--- a/arch/x86/kernel/setup_percpu.c
+++ b/arch/x86/kernel/setup_percpu.c
@@ -287,4 +287,25 @@
/* Setup cpu initialized, callin, callout masks */
setup_cpu_local_masks();
+
+#ifdef CONFIG_X86_32
+ /*
+ * Sync back kernel address range again. We already did this in
+ * setup_arch(), but percpu data also needs to be available in
+ * the smpboot asm. We can't reliably pick up percpu mappings
+ * using vmalloc_fault(), because exception dispatch needs
+ * percpu data.
+ */
+ clone_pgd_range(initial_page_table + KERNEL_PGD_BOUNDARY,
+ swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ KERNEL_PGD_PTRS);
+
+ /*
+ * sync back low identity map too. It is used for example
+ * in the 32-bit EFI stub.
+ */
+ clone_pgd_range(initial_page_table,
+ swapper_pg_dir + KERNEL_PGD_BOUNDARY,
+ min(KERNEL_PGD_PTRS, KERNEL_PGD_BOUNDARY));
+#endif
}
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index ca69967..ea217ca 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -33,6 +33,7 @@
#include <asm/mce.h>
#include <asm/trace/irq_vectors.h>
#include <asm/kexec.h>
+#include <asm/virtext.h>
/*
* Some notes on x86 processor bugs affecting SMP operation:
@@ -162,6 +163,7 @@
if (raw_smp_processor_id() == atomic_read(&stopping_cpu))
return NMI_HANDLED;
+ cpu_emergency_vmxoff();
stop_this_cpu(NULL);
return NMI_HANDLED;
@@ -174,6 +176,7 @@
asmlinkage __visible void smp_reboot_interrupt(void)
{
ipi_entering_ack_irq();
+ cpu_emergency_vmxoff();
stop_this_cpu(NULL);
irq_exit();
}
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index e803d72..cb94514 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -75,6 +75,7 @@
#include <asm/i8259.h>
#include <asm/realmode.h>
#include <asm/misc.h>
+#include <asm/spec-ctrl.h>
/* Number of siblings per CPU package */
int smp_num_siblings = 1;
@@ -229,6 +230,8 @@
*/
check_tsc_sync_target();
+ speculative_store_bypass_ht_init();
+
/*
* Lock vector_lock and initialize the vectors on this cpu
* before setting the cpu online. We must set it online with
@@ -1325,6 +1328,8 @@
set_mtrr_aps_delayed_init();
smp_quirk_init_udelay();
+
+ speculative_store_bypass_ht_init();
}
void arch_enable_nonboot_cpus_begin(void)
@@ -1591,6 +1596,8 @@
void *mwait_ptr;
int i;
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ return;
if (!this_cpu_has(X86_FEATURE_MWAIT))
return;
if (!this_cpu_has(X86_FEATURE_CLFLUSH))
diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
index 1119414..1d4e7fd 100644
--- a/arch/x86/kernel/sys_x86_64.c
+++ b/arch/x86/kernel/sys_x86_64.c
@@ -16,6 +16,7 @@
#include <linux/uaccess.h>
#include <linux/elf.h>
+#include <asm/compat.h>
#include <asm/ia32.h>
#include <asm/syscalls.h>
@@ -100,7 +101,7 @@
static void find_start_end(unsigned long flags, unsigned long *begin,
unsigned long *end)
{
- if (!test_thread_flag(TIF_ADDR32) && (flags & MAP_32BIT)) {
+ if (!in_compat_syscall() && (flags & MAP_32BIT)) {
/* This is usually used needed to map code in small
model, so it needs to be in the first 31bit. Limit
it to that. This means we need to move the
@@ -175,7 +176,7 @@
return addr;
/* for MAP_32BIT mappings we force the legacy mmap base */
- if (!test_thread_flag(TIF_ADDR32) && (flags & MAP_32BIT))
+ if (!in_compat_syscall() && (flags & MAP_32BIT))
goto bottomup;
/* requesting a specific address */
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 322f433..f214293 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -526,7 +526,6 @@
}
NOKPROBE_SYMBOL(do_general_protection);
-/* May run on IST stack. */
dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
{
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -541,7 +540,15 @@
if (poke_int3_handler(regs))
return;
+ /*
+ * Use ist_enter despite the fact that we don't use an IST stack.
+ * We can be called from a kprobe in non-CONTEXT_KERNEL kernel
+ * mode or even during context tracking state changes.
+ *
+ * This means that we can't schedule. That's okay.
+ */
ist_enter(regs);
+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
@@ -558,17 +565,11 @@
SIGTRAP) == NOTIFY_STOP)
goto exit;
- /*
- * Let others (NMI) know that the debug stack is in use
- * as we may switch to the interrupt stack.
- */
- debug_stack_usage_inc();
preempt_disable();
cond_local_irq_enable(regs);
do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL);
cond_local_irq_disable(regs);
preempt_enable_no_resched();
- debug_stack_usage_dec();
exit:
ist_exit(regs);
}
@@ -989,19 +990,16 @@
cpu_init();
/*
- * X86_TRAP_DB and X86_TRAP_BP have been set
- * in early_trap_init(). However, ITS works only after
- * cpu_init() loads TSS. See comments in early_trap_init().
+ * X86_TRAP_DB was installed in early_trap_init(). However,
+ * IST works only after cpu_init() loads TSS. See comments
+ * in early_trap_init().
*/
set_intr_gate_ist(X86_TRAP_DB, &debug, DEBUG_STACK);
- /* int3 can be called from all */
- set_system_intr_gate_ist(X86_TRAP_BP, &int3, DEBUG_STACK);
x86_init.irqs.trap_init();
#ifdef CONFIG_X86_64
memcpy(&debug_idt_table, &idt_table, IDT_ENTRIES * 16);
set_nmi_gate(X86_TRAP_DB, &debug);
- set_nmi_gate(X86_TRAP_BP, &int3);
#endif
}
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index d07a9390..da6a287 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -366,6 +366,8 @@
tsc_clocksource_reliable = 1;
if (!strncmp(str, "noirqtime", 9))
no_sched_irq_time = 1;
+ if (!strcmp(str, "unstable"))
+ mark_tsc_unstable("boot parameter");
return 1;
}
@@ -407,7 +409,7 @@
hpet2 -= hpet1;
tmp = ((u64)hpet2 * hpet_readl(HPET_PERIOD));
do_div(tmp, 1000000);
- do_div(deltatsc, tmp);
+ deltatsc = div64_u64(deltatsc, tmp);
return (unsigned long) deltatsc;
}
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 8a1d635..961831b 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -719,7 +719,8 @@
return;
check_vip:
- if (VEFLAGS & X86_EFLAGS_VIP) {
+ if ((VEFLAGS & (X86_EFLAGS_VIP | X86_EFLAGS_VIF)) ==
+ (X86_EFLAGS_VIP | X86_EFLAGS_VIF)) {
save_v86_state(regs, VM86_STI);
return;
}
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 93f924d..a69f18d 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -357,7 +357,7 @@
/* cpuid 0x80000008.ebx */
const u32 kvm_cpuid_8000_0008_ebx_x86_features =
- F(IBPB) | F(IBRS);
+ F(AMD_IBPB) | F(AMD_IBRS) | F(VIRT_SSBD);
/* cpuid 0xC0000001.edx */
const u32 kvm_cpuid_C000_0001_edx_x86_features =
@@ -382,7 +382,7 @@
/* cpuid 7.0.edx*/
const u32 kvm_cpuid_7_0_edx_x86_features =
- F(SPEC_CTRL) | F(ARCH_CAPABILITIES);
+ F(SPEC_CTRL) | F(SSBD) | F(ARCH_CAPABILITIES);
/* all calls to cpuid_count() should be made on the same cpu */
get_cpu();
@@ -618,13 +618,20 @@
g_phys_as = phys_as;
entry->eax = g_phys_as | (virt_as << 8);
entry->edx = 0;
- /* IBRS and IBPB aren't necessarily present in hardware cpuid */
- if (boot_cpu_has(X86_FEATURE_IBPB))
- entry->ebx |= F(IBPB);
- if (boot_cpu_has(X86_FEATURE_IBRS))
- entry->ebx |= F(IBRS);
+ /*
+ * IBRS, IBPB and VIRT_SSBD aren't necessarily present in
+ * hardware cpuid
+ */
+ if (boot_cpu_has(X86_FEATURE_AMD_IBPB))
+ entry->ebx |= F(AMD_IBPB);
+ if (boot_cpu_has(X86_FEATURE_AMD_IBRS))
+ entry->ebx |= F(AMD_IBRS);
+ if (boot_cpu_has(X86_FEATURE_VIRT_SSBD))
+ entry->ebx |= F(VIRT_SSBD);
entry->ebx &= kvm_cpuid_8000_0008_ebx_x86_features;
cpuid_mask(&entry->ebx, CPUID_8000_0008_EBX);
+ if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD))
+ entry->ebx |= F(VIRT_SSBD);
break;
}
case 0x80000019:
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h
index d1beb71..c383697 100644
--- a/arch/x86/kvm/cpuid.h
+++ b/arch/x86/kvm/cpuid.h
@@ -165,21 +165,21 @@
struct kvm_cpuid_entry2 *best;
best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0);
- if (best && (best->ebx & bit(X86_FEATURE_IBPB)))
+ if (best && (best->ebx & bit(X86_FEATURE_AMD_IBPB)))
return true;
best = kvm_find_cpuid_entry(vcpu, 7, 0);
return best && (best->edx & bit(X86_FEATURE_SPEC_CTRL));
}
-static inline bool guest_cpuid_has_ibrs(struct kvm_vcpu *vcpu)
+static inline bool guest_cpuid_has_spec_ctrl(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0);
- if (best && (best->ebx & bit(X86_FEATURE_IBRS)))
+ if (best && (best->ebx & bit(X86_FEATURE_AMD_IBRS)))
return true;
best = kvm_find_cpuid_entry(vcpu, 7, 0);
- return best && (best->edx & bit(X86_FEATURE_SPEC_CTRL));
+ return best && (best->edx & (bit(X86_FEATURE_SPEC_CTRL) | bit(X86_FEATURE_SSBD)));
}
static inline bool guest_cpuid_has_arch_capabilities(struct kvm_vcpu *vcpu)
@@ -190,6 +190,15 @@
return best && (best->edx & bit(X86_FEATURE_ARCH_CAPABILITIES));
}
+static inline bool guest_cpuid_has_virt_ssbd(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0);
+ return best && (best->ebx & bit(X86_FEATURE_VIRT_SSBD));
+}
+
+
/*
* NRIPS is provided through cpuidfn 0x8000000a.edx bit 3
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index b24b3c6..5c3d416f 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1363,8 +1363,10 @@
static void cancel_hv_tscdeadline(struct kvm_lapic *apic)
{
+ preempt_disable();
kvm_x86_ops->cancel_hv_timer(apic->vcpu);
apic->lapic_timer.hv_timer_in_use = false;
+ preempt_enable();
}
void kvm_lapic_expired_hv_timer(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index be644af..a27f9e4 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -44,7 +44,8 @@
#include <asm/debugreg.h>
#include <asm/kvm_para.h>
#include <asm/irq_remapping.h>
-#include <asm/nospec-branch.h>
+#include <asm/microcode.h>
+#include <asm/spec-ctrl.h>
#include <asm/virtext.h>
#include "trace.h"
@@ -184,6 +185,12 @@
} host;
u64 spec_ctrl;
+ /*
+ * Contains guest-controlled bits of VIRT_SPEC_CTRL, which will be
+ * translated into the appropriate L2_CFG bits on the host to
+ * perform speculative control.
+ */
+ u64 virt_spec_ctrl;
u32 *msrpm;
@@ -1560,6 +1567,7 @@
u32 eax = 1;
svm->spec_ctrl = 0;
+ svm->virt_spec_ctrl = 0;
if (!init_event) {
svm->vcpu.arch.apic_base = APIC_DEFAULT_PHYS_BASE |
@@ -1878,6 +1886,7 @@
*/
if (var->unusable)
var->db = 0;
+ /* This is symmetric with svm_set_segment() */
var->dpl = to_svm(vcpu)->vmcb->save.cpl;
break;
}
@@ -2023,18 +2032,14 @@
s->base = var->base;
s->limit = var->limit;
s->selector = var->selector;
- if (var->unusable)
- s->attrib = 0;
- else {
- s->attrib = (var->type & SVM_SELECTOR_TYPE_MASK);
- s->attrib |= (var->s & 1) << SVM_SELECTOR_S_SHIFT;
- s->attrib |= (var->dpl & 3) << SVM_SELECTOR_DPL_SHIFT;
- s->attrib |= (var->present & 1) << SVM_SELECTOR_P_SHIFT;
- s->attrib |= (var->avl & 1) << SVM_SELECTOR_AVL_SHIFT;
- s->attrib |= (var->l & 1) << SVM_SELECTOR_L_SHIFT;
- s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
- s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
- }
+ s->attrib = (var->type & SVM_SELECTOR_TYPE_MASK);
+ s->attrib |= (var->s & 1) << SVM_SELECTOR_S_SHIFT;
+ s->attrib |= (var->dpl & 3) << SVM_SELECTOR_DPL_SHIFT;
+ s->attrib |= ((var->present & 1) && !var->unusable) << SVM_SELECTOR_P_SHIFT;
+ s->attrib |= (var->avl & 1) << SVM_SELECTOR_AVL_SHIFT;
+ s->attrib |= (var->l & 1) << SVM_SELECTOR_L_SHIFT;
+ s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
+ s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
/*
* This is always accurate, except if SYSRET returned to a segment
@@ -2043,7 +2048,8 @@
* would entail passing the CPL to userspace and back.
*/
if (seg == VCPU_SREG_SS)
- svm->vmcb->save.cpl = (s->attrib >> SVM_SELECTOR_DPL_SHIFT) & 3;
+ /* This is symmetric with svm_get_segment() */
+ svm->vmcb->save.cpl = (var->dpl & 3);
mark_dirty(svm->vmcb, VMCB_SEG);
}
@@ -3546,11 +3552,18 @@
break;
case MSR_IA32_SPEC_CTRL:
if (!msr_info->host_initiated &&
- !guest_cpuid_has_ibrs(vcpu))
+ !guest_cpuid_has_spec_ctrl(vcpu))
return 1;
msr_info->data = svm->spec_ctrl;
break;
+ case MSR_AMD64_VIRT_SPEC_CTRL:
+ if (!msr_info->host_initiated &&
+ !guest_cpuid_has_virt_ssbd(vcpu))
+ return 1;
+
+ msr_info->data = svm->virt_spec_ctrl;
+ break;
case MSR_IA32_UCODE_REV:
msr_info->data = 0x01000065;
break;
@@ -3644,7 +3657,7 @@
break;
case MSR_IA32_SPEC_CTRL:
if (!msr->host_initiated &&
- !guest_cpuid_has_ibrs(vcpu))
+ !guest_cpuid_has_spec_ctrl(vcpu))
return 1;
/* The STIBP bit doesn't fault even if it's not advertised */
@@ -3685,6 +3698,16 @@
break;
set_msr_interception(svm->msrpm, MSR_IA32_PRED_CMD, 0, 1);
break;
+ case MSR_AMD64_VIRT_SPEC_CTRL:
+ if (!msr->host_initiated &&
+ !guest_cpuid_has_virt_ssbd(vcpu))
+ return 1;
+
+ if (data & ~SPEC_CTRL_SSBD)
+ return 1;
+
+ svm->virt_spec_ctrl = data;
+ break;
case MSR_STAR:
svm->vmcb->save.star = data;
break;
@@ -4918,8 +4941,7 @@
* is no need to worry about the conditional branch over the wrmsr
* being speculatively taken.
*/
- if (svm->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
+ x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl);
asm volatile (
"push %%" _ASM_BP "; \n\t"
@@ -5013,6 +5035,18 @@
#endif
);
+ /* Eliminate branch target predictions from guest mode */
+ vmexit_fill_RSB();
+
+#ifdef CONFIG_X86_64
+ wrmsrl(MSR_GS_BASE, svm->host.gs_base);
+#else
+ loadsegment(fs, svm->host.fs);
+#ifndef CONFIG_X86_32_LAZY_GS
+ loadsegment(gs, svm->host.gs);
+#endif
+#endif
+
/*
* We do not use IBRS in the kernel. If this vCPU has used the
* SPEC_CTRL MSR it may have left it on; save the value and
@@ -5028,23 +5062,10 @@
* If the L02 MSR bitmap does not intercept the MSR, then we need to
* save it.
*/
- if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))
- rdmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl);
+ if (unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)))
+ svm->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);
- if (svm->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, 0);
-
- /* Eliminate branch target predictions from guest mode */
- vmexit_fill_RSB();
-
-#ifdef CONFIG_X86_64
- wrmsrl(MSR_GS_BASE, svm->host.gs_base);
-#else
- loadsegment(fs, svm->host.fs);
-#ifndef CONFIG_X86_32_LAZY_GS
- loadsegment(gs, svm->host.gs);
-#endif
-#endif
+ x86_spec_ctrl_restore_host(svm->spec_ctrl, svm->virt_spec_ctrl);
reload_tss(vcpu);
@@ -5146,7 +5167,7 @@
return false;
}
-static bool svm_has_high_real_mode_segbase(void)
+static bool svm_has_emulated_msr(int index)
{
return true;
}
@@ -5448,6 +5469,12 @@
avic_handle_ldr_update(vcpu);
}
+static void svm_setup_mce(struct kvm_vcpu *vcpu)
+{
+ /* [63:9] are reserved. */
+ vcpu->arch.mcg_cap &= 0x1ff;
+}
+
static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.cpu_has_kvm_support = has_svm,
.disabled_by_bios = is_disabled,
@@ -5457,7 +5484,7 @@
.hardware_enable = svm_hardware_enable,
.hardware_disable = svm_hardware_disable,
.cpu_has_accelerated_tpr = svm_cpu_has_accelerated_tpr,
- .cpu_has_high_real_mode_segbase = svm_has_high_real_mode_segbase,
+ .has_emulated_msr = svm_has_emulated_msr,
.vcpu_create = svm_create_vcpu,
.vcpu_free = svm_free_vcpu,
@@ -5563,6 +5590,7 @@
.pmu_ops = &amd_pmu_ops,
.deliver_posted_interrupt = svm_deliver_avic_intr,
.update_pi_irte = svm_update_pi_irte,
+ .setup_mce = svm_setup_mce,
};
static int __init svm_init(void)
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index f2cd6bd..c263708 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -49,7 +49,8 @@
#include <asm/kexec.h>
#include <asm/apic.h>
#include <asm/irq_remapping.h>
-#include <asm/nospec-branch.h>
+#include <asm/microcode.h>
+#include <asm/spec-ctrl.h>
#include "trace.h"
#include "pmu.h"
@@ -1052,6 +1053,13 @@
(INTR_TYPE_HARD_EXCEPTION | MC_VECTOR | INTR_INFO_VALID_MASK);
}
+/* Undocumented: icebp/int1 */
+static inline bool is_icebp(u32 intr_info)
+{
+ return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VALID_MASK))
+ == (INTR_TYPE_PRIV_SW_EXCEPTION | INTR_INFO_VALID_MASK);
+}
+
static inline bool cpu_has_vmx_msr_bitmap(void)
{
return vmcs_config.cpu_based_exec_ctrl & CPU_BASED_USE_MSR_BITMAPS;
@@ -3012,7 +3020,7 @@
break;
case MSR_IA32_SPEC_CTRL:
if (!msr_info->host_initiated &&
- !guest_cpuid_has_ibrs(vcpu))
+ !guest_cpuid_has_spec_ctrl(vcpu))
return 1;
msr_info->data = to_vmx(vcpu)->spec_ctrl;
@@ -3129,11 +3137,11 @@
break;
case MSR_IA32_SPEC_CTRL:
if (!msr_info->host_initiated &&
- !guest_cpuid_has_ibrs(vcpu))
+ !guest_cpuid_has_spec_ctrl(vcpu))
return 1;
/* The STIBP bit doesn't fault even if it's not advertised */
- if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP))
+ if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP | SPEC_CTRL_SSBD))
return 1;
vmx->spec_ctrl = data;
@@ -5732,7 +5740,7 @@
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
vcpu->arch.dr6 &= ~15;
vcpu->arch.dr6 |= dr6 | DR6_RTM;
- if (!(dr6 & ~DR6_RESERVED)) /* icebp */
+ if (is_icebp(intr_info))
skip_emulated_instruction(vcpu);
kvm_queue_exception(vcpu, DB_VECTOR);
@@ -7916,11 +7924,13 @@
{
unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
int cr = exit_qualification & 15;
- int reg = (exit_qualification >> 8) & 15;
- unsigned long val = kvm_register_readl(vcpu, reg);
+ int reg;
+ unsigned long val;
switch ((exit_qualification >> 4) & 3) {
case 0: /* mov to cr */
+ reg = (exit_qualification >> 8) & 15;
+ val = kvm_register_readl(vcpu, reg);
switch (cr) {
case 0:
if (vmcs12->cr0_guest_host_mask &
@@ -7975,6 +7985,7 @@
* lmsw can change bits 1..3 of cr0, and only set bit 0 of
* cr0. Other attempted changes are ignored, with no exit.
*/
+ val = (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f;
if (vmcs12->cr0_guest_host_mask & 0xe &
(val ^ vmcs12->cr0_read_shadow))
return true;
@@ -8679,9 +8690,21 @@
}
}
-static bool vmx_has_high_real_mode_segbase(void)
+static bool vmx_has_emulated_msr(int index)
{
- return enable_unrestricted_guest || emulate_invalid_guest_state;
+ switch (index) {
+ case MSR_IA32_SMBASE:
+ /*
+ * We cannot do SMM unless we can run the guest in big
+ * real mode.
+ */
+ return enable_unrestricted_guest || emulate_invalid_guest_state;
+ case MSR_AMD64_VIRT_SPEC_CTRL:
+ /* This is AMD only. */
+ return false;
+ default:
+ return true;
+ }
}
static bool vmx_mpx_supported(void)
@@ -8904,10 +8927,10 @@
* is no need to worry about the conditional branch over the wrmsr
* being speculatively taken.
*/
- if (vmx->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
+ x86_spec_ctrl_set_guest(vmx->spec_ctrl, 0);
vmx->__launched = vmx->loaded_vmcs->launched;
+
asm(
/* Store host registers */
"push %%" _ASM_DX "; push %%" _ASM_BP ";"
@@ -9040,11 +9063,10 @@
* If the L02 MSR bitmap does not intercept the MSR, then we need to
* save it.
*/
- if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))
- rdmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl);
+ if (unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)))
+ vmx->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);
- if (vmx->spec_ctrl)
- wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+ x86_spec_ctrl_restore_host(vmx->spec_ctrl, 0);
/* Eliminate branch target predictions from guest mode */
vmexit_fill_RSB();
@@ -10652,8 +10674,7 @@
vmcs12->guest_pdptr3 = vmcs_read64(GUEST_PDPTR3);
}
- if (nested_cpu_has_ept(vmcs12))
- vmcs12->guest_linear_address = vmcs_readl(GUEST_LINEAR_ADDRESS);
+ vmcs12->guest_linear_address = vmcs_readl(GUEST_LINEAR_ADDRESS);
if (nested_cpu_has_vid(vmcs12))
vmcs12->guest_intr_status = vmcs_read16(GUEST_INTR_STATUS);
@@ -11336,7 +11357,7 @@
.hardware_enable = hardware_enable,
.hardware_disable = hardware_disable,
.cpu_has_accelerated_tpr = report_flexpriority,
- .cpu_has_high_real_mode_segbase = vmx_has_high_real_mode_segbase,
+ .has_emulated_msr = vmx_has_emulated_msr,
.vcpu_create = vmx_create_vcpu,
.vcpu_free = vmx_free_vcpu,
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4b19ec1..a0cb85f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1002,6 +1002,7 @@
MSR_IA32_MCG_CTL,
MSR_IA32_MCG_EXT_CTL,
MSR_IA32_SMBASE,
+ MSR_AMD64_VIRT_SPEC_CTRL,
};
static unsigned num_emulated_msrs;
@@ -2664,7 +2665,7 @@
* fringe case that is not enabled except via specific settings
* of the module parameters.
*/
- r = kvm_x86_ops->cpu_has_high_real_mode_segbase();
+ r = kvm_x86_ops->has_emulated_msr(MSR_IA32_SMBASE);
break;
case KVM_CAP_COALESCED_MMIO:
r = KVM_COALESCED_MMIO_PAGE_OFFSET;
@@ -3070,7 +3071,8 @@
return -EINVAL;
if (events->exception.injected &&
- (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR))
+ (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR ||
+ is_guest_mode(vcpu)))
return -EINVAL;
/* INITs are latched while in SMM */
@@ -4225,14 +4227,8 @@
num_msrs_to_save = j;
for (i = j = 0; i < ARRAY_SIZE(emulated_msrs); i++) {
- switch (emulated_msrs[i]) {
- case MSR_IA32_SMBASE:
- if (!kvm_x86_ops->cpu_has_high_real_mode_segbase())
- continue;
- break;
- default:
- break;
- }
+ if (!kvm_x86_ops->has_emulated_msr(emulated_msrs[i]))
+ continue;
if (j < i)
emulated_msrs[j] = emulated_msrs[i];
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 4ad7c4d..6bf1898 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -26,7 +26,6 @@
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
lib-$(CONFIG_RETPOLINE) += retpoline.o
-OBJECT_FILES_NON_STANDARD_retpoline.o :=y
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
diff --git a/arch/x86/lib/csum-copy_64.S b/arch/x86/lib/csum-copy_64.S
index 7e48807..45a53df 100644
--- a/arch/x86/lib/csum-copy_64.S
+++ b/arch/x86/lib/csum-copy_64.S
@@ -55,7 +55,7 @@
movq %r12, 3*8(%rsp)
movq %r14, 4*8(%rsp)
movq %r13, 5*8(%rsp)
- movq %rbp, 6*8(%rsp)
+ movq %r15, 6*8(%rsp)
movq %r8, (%rsp)
movq %r9, 1*8(%rsp)
@@ -74,7 +74,7 @@
/* main loop. clear in 64 byte blocks */
/* r9: zero, r8: temp2, rbx: temp1, rax: sum, rcx: saved length */
/* r11: temp3, rdx: temp4, r12 loopcnt */
- /* r10: temp5, rbp: temp6, r14 temp7, r13 temp8 */
+ /* r10: temp5, r15: temp6, r14 temp7, r13 temp8 */
.p2align 4
.Lloop:
source
@@ -89,7 +89,7 @@
source
movq 32(%rdi), %r10
source
- movq 40(%rdi), %rbp
+ movq 40(%rdi), %r15
source
movq 48(%rdi), %r14
source
@@ -103,7 +103,7 @@
adcq %r11, %rax
adcq %rdx, %rax
adcq %r10, %rax
- adcq %rbp, %rax
+ adcq %r15, %rax
adcq %r14, %rax
adcq %r13, %rax
@@ -121,7 +121,7 @@
dest
movq %r10, 32(%rsi)
dest
- movq %rbp, 40(%rsi)
+ movq %r15, 40(%rsi)
dest
movq %r14, 48(%rsi)
dest
@@ -203,7 +203,7 @@
movq 3*8(%rsp), %r12
movq 4*8(%rsp), %r14
movq 5*8(%rsp), %r13
- movq 6*8(%rsp), %rbp
+ movq 6*8(%rsp), %r15
addq $7*8, %rsp
ret
diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
index 480edc3..c909961 100644
--- a/arch/x86/lib/retpoline.S
+++ b/arch/x86/lib/retpoline.S
@@ -7,7 +7,6 @@
#include <asm/alternative-asm.h>
#include <asm/export.h>
#include <asm/nospec-branch.h>
-#include <asm/bitsperlong.h>
.macro THUNK reg
.section .text.__x86.indirect_thunk
@@ -47,58 +46,3 @@
GENERATE_THUNK(r14)
GENERATE_THUNK(r15)
#endif
-
-/*
- * Fill the CPU return stack buffer.
- *
- * Each entry in the RSB, if used for a speculative 'ret', contains an
- * infinite 'pause; lfence; jmp' loop to capture speculative execution.
- *
- * This is required in various cases for retpoline and IBRS-based
- * mitigations for the Spectre variant 2 vulnerability. Sometimes to
- * eliminate potentially bogus entries from the RSB, and sometimes
- * purely to ensure that it doesn't get empty, which on some CPUs would
- * allow predictions from other (unwanted!) sources to be used.
- *
- * Google experimented with loop-unrolling and this turned out to be
- * the optimal version - two calls, each with their own speculation
- * trap should their return address end up getting used, in a loop.
- */
-.macro STUFF_RSB nr:req sp:req
- mov $(\nr / 2), %_ASM_BX
- .align 16
-771:
- call 772f
-773: /* speculation trap */
- pause
- lfence
- jmp 773b
- .align 16
-772:
- call 774f
-775: /* speculation trap */
- pause
- lfence
- jmp 775b
- .align 16
-774:
- dec %_ASM_BX
- jnz 771b
- add $((BITS_PER_LONG/8) * \nr), \sp
-.endm
-
-#define RSB_FILL_LOOPS 16 /* To avoid underflow */
-
-ENTRY(__fill_rsb)
- STUFF_RSB RSB_FILL_LOOPS, %_ASM_SP
- ret
-END(__fill_rsb)
-EXPORT_SYMBOL_GPL(__fill_rsb)
-
-#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
-
-ENTRY(__clear_rsb)
- STUFF_RSB RSB_CLEAR_LOOPS, %_ASM_SP
- ret
-END(__clear_rsb)
-EXPORT_SYMBOL_GPL(__clear_rsb)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 3d7f60f..54efa85 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -343,7 +343,7 @@
if (!pmd_k)
return -1;
- if (pmd_huge(*pmd_k))
+ if (pmd_large(*pmd_k))
return 0;
pte_k = pte_offset_kernel(pmd_k, address);
@@ -463,7 +463,7 @@
if (pud_none(*pud) || pud_pfn(*pud) != pud_pfn(*pud_ref))
BUG();
- if (pud_huge(*pud))
+ if (pud_large(*pud))
return 0;
pmd = pmd_offset(pud, address);
@@ -474,7 +474,7 @@
if (pmd_none(*pmd) || pmd_pfn(*pmd) != pmd_pfn(*pmd_ref))
BUG();
- if (pmd_huge(*pmd))
+ if (pmd_large(*pmd))
return 0;
pte_ref = pte_offset_kernel(pmd_ref, address);
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 209b946..b97ef29 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -643,4 +643,52 @@
return 0;
}
+
+/**
+ * pud_free_pmd_page - Clear pud entry and free pmd page.
+ * @pud: Pointer to a PUD.
+ *
+ * Context: The pud range has been unmaped and TLB purged.
+ * Return: 1 if clearing the entry succeeded. 0 otherwise.
+ */
+int pud_free_pmd_page(pud_t *pud)
+{
+ pmd_t *pmd;
+ int i;
+
+ if (pud_none(*pud))
+ return 1;
+
+ pmd = (pmd_t *)pud_page_vaddr(*pud);
+
+ for (i = 0; i < PTRS_PER_PMD; i++)
+ if (!pmd_free_pte_page(&pmd[i]))
+ return 0;
+
+ pud_clear(pud);
+ free_page((unsigned long)pmd);
+
+ return 1;
+}
+
+/**
+ * pmd_free_pte_page - Clear pmd entry and free pte page.
+ * @pmd: Pointer to a PMD.
+ *
+ * Context: The pmd range has been unmaped and TLB purged.
+ * Return: 1 if clearing the entry succeeded. 0 otherwise.
+ */
+int pmd_free_pte_page(pmd_t *pmd)
+{
+ pte_t *pte;
+
+ if (pmd_none(*pmd))
+ return 1;
+
+ pte = (pte_t *)pmd_page_vaddr(*pmd);
+ pmd_clear(pmd);
+ free_page((unsigned long)pte);
+
+ return 1;
+}
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
diff --git a/arch/x86/mm/pkeys.c b/arch/x86/mm/pkeys.c
index f88ce0e..0bbec04 100644
--- a/arch/x86/mm/pkeys.c
+++ b/arch/x86/mm/pkeys.c
@@ -95,26 +95,27 @@
*/
if (pkey != -1)
return pkey;
- /*
- * Look for a protection-key-drive execute-only mapping
- * which is now being given permissions that are not
- * execute-only. Move it back to the default pkey.
- */
- if (vma_is_pkey_exec_only(vma) &&
- (prot & (PROT_READ|PROT_WRITE))) {
- return 0;
- }
+
/*
* The mapping is execute-only. Go try to get the
* execute-only protection key. If we fail to do that,
* fall through as if we do not have execute-only
- * support.
+ * support in this mm.
*/
if (prot == PROT_EXEC) {
pkey = execute_only_pkey(vma->vm_mm);
if (pkey > 0)
return pkey;
+ } else if (vma_is_pkey_exec_only(vma)) {
+ /*
+ * Protections are *not* PROT_EXEC, but the mapping
+ * is using the exec-only pkey. This mapping was
+ * PROT_EXEC and will no longer be. Move back to
+ * the default pkey.
+ */
+ return ARCH_DEFAULT_PKEY;
}
+
/*
* This is a vanilla, non-pkey mprotect (or we failed to
* setup execute-only), inherit the pkey from the VMA we
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 578973a..eac92e2 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -10,6 +10,7 @@
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
+#include <asm/nospec-branch.h>
#include <asm/cache.h>
#include <asm/apic.h>
#include <asm/uv/uv.h>
@@ -29,6 +30,8 @@
* Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi
*/
+atomic64_t last_mm_ctx_id = ATOMIC64_INIT(1);
+
struct flush_tlb_info {
struct mm_struct *flush_mm;
unsigned long flush_start;
@@ -104,6 +107,28 @@
unsigned cpu = smp_processor_id();
if (likely(prev != next)) {
+ u64 last_ctx_id = this_cpu_read(cpu_tlbstate.last_ctx_id);
+
+ /*
+ * Avoid user/user BTB poisoning by flushing the branch
+ * predictor when switching between processes. This stops
+ * one process from doing Spectre-v2 attacks on another.
+ *
+ * As an optimization, flush indirect branches only when
+ * switching into processes that disable dumping. This
+ * protects high value processes like gpg, without having
+ * too high performance overhead. IBPB is *expensive*!
+ *
+ * This will not flush branches when switching into kernel
+ * threads. It will also not flush if we switch to idle
+ * thread and back to the same process. It will flush if we
+ * switch to a different non-dumpable process.
+ */
+ if (tsk && tsk->mm &&
+ tsk->mm->context.ctx_id != last_ctx_id &&
+ get_dumpable(tsk->mm) != SUID_DUMP_USER)
+ indirect_branch_prediction_barrier();
+
if (IS_ENABLED(CONFIG_VMAP_STACK)) {
/*
* If our current stack is in vmalloc space and isn't
@@ -118,6 +143,14 @@
set_pgd(pgd, init_mm.pgd[stack_pgd_index]);
}
+ /*
+ * Record last user mm's context id, so we can avoid
+ * flushing branch buffer with IBPB if we switch back
+ * to the same user.
+ */
+ if (next != &init_mm)
+ this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id);
+
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
this_cpu_write(cpu_tlbstate.active_mm, next);
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 7840331..cd97645 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -12,6 +12,7 @@
#include <linux/filter.h>
#include <linux/if_vlan.h>
#include <asm/cacheflush.h>
+#include <asm/nospec-branch.h>
#include <linux/bpf.h>
int bpf_jit_enable __read_mostly;
@@ -281,7 +282,7 @@
EMIT2(0x89, 0xD2); /* mov edx, edx */
EMIT3(0x39, 0x56, /* cmp dword ptr [rsi + 16], edx */
offsetof(struct bpf_array, map.max_entries));
-#define OFFSET1 43 /* number of bytes to jump */
+#define OFFSET1 (41 + RETPOLINE_RAX_BPF_JIT_SIZE) /* number of bytes to jump */
EMIT2(X86_JBE, OFFSET1); /* jbe out */
label1 = cnt;
@@ -290,7 +291,7 @@
*/
EMIT2_off32(0x8B, 0x85, -STACKSIZE + 36); /* mov eax, dword ptr [rbp - 516] */
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
-#define OFFSET2 32
+#define OFFSET2 (30 + RETPOLINE_RAX_BPF_JIT_SIZE)
EMIT2(X86_JA, OFFSET2); /* ja out */
label2 = cnt;
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
@@ -304,7 +305,7 @@
* goto out;
*/
EMIT3(0x48, 0x85, 0xC0); /* test rax,rax */
-#define OFFSET3 10
+#define OFFSET3 (8 + RETPOLINE_RAX_BPF_JIT_SIZE)
EMIT2(X86_JE, OFFSET3); /* je out */
label3 = cnt;
@@ -317,7 +318,7 @@
* rdi == ctx (1st arg)
* rax == prog->bpf_func + prologue_size
*/
- EMIT2(0xFF, 0xE0); /* jmp rax */
+ RETPOLINE_RAX_BPF_JIT();
/* out: */
BUILD_BUG_ON(cnt - label1 != OFFSET1);
@@ -1134,7 +1135,7 @@
* may converge on the last pass. In such case do one more
* pass to emit the final image
*/
- for (pass = 0; pass < 10 || image; pass++) {
+ for (pass = 0; pass < 20 || image; pass++) {
proglen = do_jit(prog, addrs, image, oldproglen, &ctx);
if (proglen <= 0) {
image = NULL;
@@ -1161,6 +1162,7 @@
}
}
oldproglen = proglen;
+ cond_resched();
}
if (bpf_jit_enable > 1)
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index 28c0412..18f9dad 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -472,7 +472,7 @@
goto fail;
for_each_possible_cpu(cpu) {
- if (!cpu)
+ if (!IS_ENABLED(CONFIG_SMP) || !cpu)
continue;
memcpy(per_cpu(cpu_msrs, cpu).counters,
@@ -615,7 +615,7 @@
static int force_cpu_type;
-static int set_cpu_type(const char *str, struct kernel_param *kp)
+static int set_cpu_type(const char *str, const struct kernel_param *kp)
{
if (!strcmp(str, "timer")) {
force_cpu_type = timer;
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 274dfc4..a0e85f2 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -832,9 +832,11 @@
/*
* We don't do virtual mode, since we don't do runtime services, on
- * non-native EFI
+ * non-native EFI. With efi=old_map, we don't do runtime services in
+ * kexec kernel because in the initial boot something else might
+ * have been mapped at these virtual addresses.
*/
- if (!efi_is_native()) {
+ if (!efi_is_native() || efi_enabled(EFI_OLD_MEMMAP)) {
efi_memmap_unmap();
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return;
diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c
index 7850128..834783b 100644
--- a/arch/x86/platform/intel-mid/intel-mid.c
+++ b/arch/x86/platform/intel-mid/intel-mid.c
@@ -79,7 +79,7 @@
static void intel_mid_reboot(void)
{
- intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
+ intel_scu_ipc_simple_command(IPCMSG_COLD_RESET, 0);
}
static unsigned long __init intel_mid_calibrate_tsc(void)
diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index 73eb7fd..5b6c848 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -769,9 +769,12 @@
break;
case R_X86_64_PC32:
+ case R_X86_64_PLT32:
/*
* PC relative relocations don't need to be adjusted unless
* referencing a percpu symbol.
+ *
+ * NB: R_X86_64_PLT32 can be treated as R_X86_64_PC32.
*/
if (is_percpu_sym(sym, symname))
add_reloc(&relocs32neg, offset);
diff --git a/arch/x86/um/stub_segv.c b/arch/x86/um/stub_segv.c
index 1518d28..27361cb 100644
--- a/arch/x86/um/stub_segv.c
+++ b/arch/x86/um/stub_segv.c
@@ -6,11 +6,12 @@
#include <sysdep/stub.h>
#include <sysdep/faultinfo.h>
#include <sysdep/mcontext.h>
+#include <sys/ucontext.h>
void __attribute__ ((__section__ (".__syscall_stub")))
stub_segv_handler(int sig, siginfo_t *info, void *p)
{
- struct ucontext *uc = p;
+ ucontext_t *uc = p;
GET_FAULTINFO_FROM_MC(*((struct faultinfo *) STUB_DATA),
&uc->uc_mcontext);
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 2bea87c..081437b 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -1977,10 +1977,8 @@
static void xen_set_cpu_features(struct cpuinfo_x86 *c)
{
- if (xen_pv_domain()) {
- clear_cpu_bug(c, X86_BUG_SYSRET_SS_ATTRS);
+ if (xen_pv_domain())
set_cpu_cap(c, X86_FEATURE_XENPV);
- }
}
static void xen_pin_vcpu(int cpu)
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index 418f1b8..c92f75f 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1317,8 +1317,6 @@
struct mmuext_op *op;
struct multicall_space mcs;
- trace_xen_mmu_flush_tlb_all(0);
-
preempt_disable();
mcs = xen_mc_entry(sizeof(*op));
@@ -1336,8 +1334,6 @@
struct mmuext_op *op;
struct multicall_space mcs;
- trace_xen_mmu_flush_tlb(0);
-
preempt_disable();
mcs = xen_mc_entry(sizeof(*op));
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index 137afbb..a11540e 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -299,35 +299,46 @@
}
-static void __init xen_smp_prepare_boot_cpu(void)
+static void __init xen_pv_smp_prepare_boot_cpu(void)
{
BUG_ON(smp_processor_id() != 0);
native_smp_prepare_boot_cpu();
- if (xen_pv_domain()) {
- if (!xen_feature(XENFEAT_writable_page_tables))
- /* We've switched to the "real" per-cpu gdt, so make
- * sure the old memory can be recycled. */
- make_lowmem_page_readwrite(xen_initial_gdt);
+ if (!xen_feature(XENFEAT_writable_page_tables))
+ /* We've switched to the "real" per-cpu gdt, so make
+ * sure the old memory can be recycled. */
+ make_lowmem_page_readwrite(xen_initial_gdt);
#ifdef CONFIG_X86_32
- /*
- * Xen starts us with XEN_FLAT_RING1_DS, but linux code
- * expects __USER_DS
- */
- loadsegment(ds, __USER_DS);
- loadsegment(es, __USER_DS);
+ /*
+ * Xen starts us with XEN_FLAT_RING1_DS, but linux code
+ * expects __USER_DS
+ */
+ loadsegment(ds, __USER_DS);
+ loadsegment(es, __USER_DS);
#endif
- xen_filter_cpu_maps();
- xen_setup_vcpu_info_placement();
- }
+ xen_filter_cpu_maps();
+ xen_setup_vcpu_info_placement();
+
+ /*
+ * The alternative logic (which patches the unlock/lock) runs before
+ * the smp bootup up code is activated. Hence we need to set this up
+ * the core kernel is being patched. Otherwise we will have only
+ * modules patched but not core code.
+ */
+ xen_init_spinlocks();
+}
+
+static void __init xen_hvm_smp_prepare_boot_cpu(void)
+{
+ BUG_ON(smp_processor_id() != 0);
+ native_smp_prepare_boot_cpu();
/*
* Setup vcpu_info for boot CPU.
*/
- if (xen_hvm_domain())
- xen_vcpu_setup(0);
+ xen_vcpu_setup(0);
/*
* The alternative logic (which patches the unlock/lock) runs before
@@ -733,7 +744,7 @@
}
static const struct smp_ops xen_smp_ops __initconst = {
- .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu,
+ .smp_prepare_boot_cpu = xen_pv_smp_prepare_boot_cpu,
.smp_prepare_cpus = xen_smp_prepare_cpus,
.smp_cpus_done = xen_smp_cpus_done,
@@ -772,5 +783,5 @@
smp_ops.cpu_die = xen_cpu_die;
smp_ops.send_call_func_ipi = xen_smp_send_call_function_ipi;
smp_ops.send_call_func_single_ipi = xen_smp_send_call_function_single_ipi;
- smp_ops.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu;
+ smp_ops.smp_prepare_boot_cpu = xen_hvm_smp_prepare_boot_cpu;
}
diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c
index 7f664c4..4ecd0de 100644
--- a/arch/x86/xen/suspend.c
+++ b/arch/x86/xen/suspend.c
@@ -1,11 +1,14 @@
#include <linux/types.h>
#include <linux/tick.h>
+#include <linux/percpu-defs.h>
#include <xen/xen.h>
#include <xen/interface/xen.h>
#include <xen/grant_table.h>
#include <xen/events.h>
+#include <asm/cpufeatures.h>
+#include <asm/msr-index.h>
#include <asm/xen/hypercall.h>
#include <asm/xen/page.h>
#include <asm/fixmap.h>
@@ -68,6 +71,8 @@
xen_mm_unpin_all();
}
+static DEFINE_PER_CPU(u64, spec_ctrl);
+
void xen_arch_pre_suspend(void)
{
if (xen_pv_domain())
@@ -84,6 +89,9 @@
static void xen_vcpu_notify_restore(void *data)
{
+ if (xen_pv_domain() && boot_cpu_has(X86_FEATURE_SPEC_CTRL))
+ wrmsrl(MSR_IA32_SPEC_CTRL, this_cpu_read(spec_ctrl));
+
/* Boot processor notified via generic timekeeping_resume() */
if (smp_processor_id() == 0)
return;
@@ -93,7 +101,15 @@
static void xen_vcpu_notify_suspend(void *data)
{
+ u64 tmp;
+
tick_suspend_local();
+
+ if (xen_pv_domain() && boot_cpu_has(X86_FEATURE_SPEC_CTRL)) {
+ rdmsrl(MSR_IA32_SPEC_CTRL, tmp);
+ this_cpu_write(spec_ctrl, tmp);
+ wrmsrl(MSR_IA32_SPEC_CTRL, 0);
+ }
}
void xen_arch_resume(void)
diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h
index 72bfc1c..5bfbc1c 100644
--- a/arch/xtensa/include/asm/futex.h
+++ b/arch/xtensa/include/asm/futex.h
@@ -44,18 +44,10 @@
: "r" (uaddr), "I" (-EFAULT), "r" (oparg) \
: "memory")
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+ u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
#if !XCHAL_HAVE_S32C1I
return -ENOSYS;
@@ -89,19 +81,10 @@
pagefault_enable();
- if (ret)
- return ret;
+ if (!ret)
+ *oval = oldval;
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: return (oldval == cmparg);
- case FUTEX_OP_CMP_NE: return (oldval != cmparg);
- case FUTEX_OP_CMP_LT: return (oldval < cmparg);
- case FUTEX_OP_CMP_GE: return (oldval >= cmparg);
- case FUTEX_OP_CMP_LE: return (oldval <= cmparg);
- case FUTEX_OP_CMP_GT: return (oldval > cmparg);
- }
-
- return -ENOSYS;
+ return ret;
}
static inline int
diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c
index 80e4cfb..d5abdf8 100644
--- a/arch/xtensa/mm/init.c
+++ b/arch/xtensa/mm/init.c
@@ -77,19 +77,75 @@
free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL);
}
+#ifdef CONFIG_HIGHMEM
+static void __init free_area_high(unsigned long pfn, unsigned long end)
+{
+ for (; pfn < end; pfn++)
+ free_highmem_page(pfn_to_page(pfn));
+}
+
+static void __init free_highpages(void)
+{
+ unsigned long max_low = max_low_pfn;
+ struct memblock_region *mem, *res;
+
+ reset_all_zones_managed_pages();
+ /* set highmem page free */
+ for_each_memblock(memory, mem) {
+ unsigned long start = memblock_region_memory_base_pfn(mem);
+ unsigned long end = memblock_region_memory_end_pfn(mem);
+
+ /* Ignore complete lowmem entries */
+ if (end <= max_low)
+ continue;
+
+ if (memblock_is_nomap(mem))
+ continue;
+
+ /* Truncate partial highmem entries */
+ if (start < max_low)
+ start = max_low;
+
+ /* Find and exclude any reserved regions */
+ for_each_memblock(reserved, res) {
+ unsigned long res_start, res_end;
+
+ res_start = memblock_region_reserved_base_pfn(res);
+ res_end = memblock_region_reserved_end_pfn(res);
+
+ if (res_end < start)
+ continue;
+ if (res_start < start)
+ res_start = start;
+ if (res_start > end)
+ res_start = end;
+ if (res_end > end)
+ res_end = end;
+ if (res_start != start)
+ free_area_high(start, res_start);
+ start = res_end;
+ if (start == end)
+ break;
+ }
+
+ /* And now free anything which remains */
+ if (start < end)
+ free_area_high(start, end);
+ }
+}
+#else
+static void __init free_highpages(void)
+{
+}
+#endif
+
/*
* Initialize memory pages.
*/
void __init mem_init(void)
{
-#ifdef CONFIG_HIGHMEM
- unsigned long tmp;
-
- reset_all_zones_managed_pages();
- for (tmp = max_low_pfn; tmp < max_pfn; tmp++)
- free_highmem_page(pfn_to_page(tmp));
-#endif
+ free_highpages();
max_mapnr = max_pfn - ARCH_PFN_OFFSET;
high_memory = (void *)__va(max_low_pfn << PAGE_SHIFT);
diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index 63f72f0..80dedde 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -175,6 +175,9 @@
if (!bio_is_rw(bio))
return false;
+ if (!bio_sectors(bio))
+ return false;
+
/* Already protected? */
if (bio_integrity(bio))
return false;
diff --git a/block/bio.c b/block/bio.c
index 07f287b..91b6462 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -42,9 +42,9 @@
* break badly! cannot be bigger than what you can fit into an
* unsigned short
*/
-#define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) }
+#define BV(x, n) { .nr_vecs = x, .name = "biovec-"#n }
static struct biovec_slab bvec_slabs[BVEC_POOL_NR] __read_mostly = {
- BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES),
+ BV(1, 1), BV(4, 4), BV(16, 16), BV(64, 64), BV(128, 128), BV(BIO_MAX_PAGES, max),
};
#undef BV
@@ -565,6 +565,15 @@
}
EXPORT_SYMBOL(bio_phys_segments);
+static inline void bio_clone_crypt_key(struct bio *dst, const struct bio *src)
+{
+#ifdef CONFIG_PFK
+ dst->bi_crypt_key = src->bi_crypt_key;
+ dst->bi_iter.bi_dun = src->bi_iter.bi_dun;
+#endif
+ dst->bi_dio_inode = src->bi_dio_inode;
+}
+
/**
* __bio_clone_fast - clone a bio that shares the original bio's biovec
* @bio: destination bio
@@ -589,7 +598,8 @@
bio->bi_opf = bio_src->bi_opf;
bio->bi_iter = bio_src->bi_iter;
bio->bi_io_vec = bio_src->bi_io_vec;
-
+ bio->bi_dio_inode = bio_src->bi_dio_inode;
+ bio_clone_crypt_key(bio, bio_src);
bio_clone_blkcg_association(bio, bio_src);
}
EXPORT_SYMBOL(__bio_clone_fast);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index d673a69..a28ce59 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1079,10 +1079,8 @@
if (preloaded)
radix_tree_preload_end();
- if (IS_ERR(blkg)) {
- blkg_free(new_blkg);
+ if (IS_ERR(blkg))
return PTR_ERR(blkg);
- }
q->root_blkg = blkg;
q->root_rl.blkg = blkg;
diff --git a/block/blk-merge.c b/block/blk-merge.c
index abde370..f44daa1 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -6,9 +6,9 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/scatterlist.h>
-
+#include <linux/pfk.h>
#include <trace/events/block.h>
-
+#include <linux/pfk.h>
#include "blk.h"
static struct bio *blk_bio_discard_split(struct request_queue *q,
@@ -725,6 +725,11 @@
}
}
+static bool crypto_not_mergeable(const struct bio *bio, const struct bio *nxt)
+{
+ return (!pfk_allow_merge_bio(bio, nxt));
+}
+
/*
* Has to be called with the request spinlock acquired
*/
@@ -752,6 +757,8 @@
!blk_write_same_mergeable(req->bio, next->bio))
return 0;
+ if (crypto_not_mergeable(req->bio, next->bio))
+ return 0;
/*
* If we are allowed to merge, then append bio list
* from next to rq and release next. merge_requests_fn
@@ -862,6 +869,8 @@
!blk_write_same_mergeable(rq->bio, bio))
return false;
+ if (crypto_not_mergeable(rq->bio, bio))
+ return false;
return true;
}
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 74ff73f..0668b66 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1265,13 +1265,13 @@
blk_queue_bounce(q, &bio);
+ blk_queue_split(q, &bio, q->bio_split);
+
if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) {
bio_io_error(bio);
return BLK_QC_T_NONE;
}
- blk_queue_split(q, &bio, q->bio_split);
-
if (!is_flush_fua && !blk_queue_nomerges(q) &&
blk_attempt_plug_merge(q, bio, &request_count, &same_queue_rq))
return BLK_QC_T_NONE;
@@ -1592,7 +1592,8 @@
{
unsigned flush_start_tag = set->queue_depth;
- blk_mq_tag_idle(hctx);
+ if (blk_mq_hw_queue_mapped(hctx))
+ blk_mq_tag_idle(hctx);
if (set->ops->exit_request)
set->ops->exit_request(set->driver_data,
@@ -1907,6 +1908,9 @@
struct blk_mq_hw_ctx **hctxs = q->queue_hw_ctx;
blk_mq_sysfs_unregister(q);
+
+ /* protect against switching io scheduler */
+ mutex_lock(&q->sysfs_lock);
for (i = 0; i < set->nr_hw_queues; i++) {
int node;
@@ -1956,6 +1960,7 @@
}
}
q->nr_hw_queues = i;
+ mutex_unlock(&q->sysfs_lock);
blk_mq_sysfs_register(q);
}
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index a3ea826..3a4c9a3 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -499,6 +499,17 @@
static void throtl_schedule_pending_timer(struct throtl_service_queue *sq,
unsigned long expires)
{
+ unsigned long max_expire = jiffies + 8 * throtl_slice;
+
+ /*
+ * Since we are adjusting the throttle limit dynamically, the sleep
+ * time calculated according to previous limit might be invalid. It's
+ * possible the cgroup sleep time is very long and no other cgroups
+ * have IO running so notify the limit changes. Make sure the cgroup
+ * doesn't sleep too long to avoid the missed notification.
+ */
+ if (time_after(expires, max_expire))
+ expires = max_expire;
mod_timer(&sq->pending_timer, expires);
throtl_log(sq, "schedule timer. delay=%lu jiffies=%lu",
expires - jiffies, jiffies);
diff --git a/block/elevator.c b/block/elevator.c
index f7d973a..6dd2ca4 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -425,7 +425,10 @@
/*
* First try one-hit cache.
*/
- if (q->last_merge && elv_bio_merge_ok(q->last_merge, bio)) {
+ if (q->last_merge) {
+ if (!elv_bio_merge_ok(q->last_merge, bio))
+ return ELEVATOR_NO_MERGE;
+
ret = blk_try_merge(q->last_merge, bio);
if (ret != ELEVATOR_NO_MERGE) {
*req = q->last_merge;
diff --git a/block/ioctl.c b/block/ioctl.c
index d4a78d0..c4555b1 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -564,8 +564,6 @@
if ((size >> 9) > ~0UL)
return -EFBIG;
return put_ulong(arg, size >> 9);
- case BLKGETSTPART:
- return put_ulong(arg, bdev->bd_part->start_sect);
case BLKGETSIZE64:
return put_u64(arg, i_size_read(bdev->bd_inode));
case BLKTRACESTART:
diff --git a/block/partition-generic.c b/block/partition-generic.c
index a2437c0..298c05f 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -321,8 +321,10 @@
if (info) {
struct partition_meta_info *pinfo = alloc_part_info(disk);
- if (!pinfo)
+ if (!pinfo) {
+ err = -ENOMEM;
goto out_free_stats;
+ }
memcpy(pinfo, info, sizeof(*info));
p->info = pinfo;
}
diff --git a/block/partitions/msdos.c b/block/partitions/msdos.c
index 5610cd5..7d8d50c 100644
--- a/block/partitions/msdos.c
+++ b/block/partitions/msdos.c
@@ -300,7 +300,9 @@
continue;
bsd_start = le32_to_cpu(p->p_offset);
bsd_size = le32_to_cpu(p->p_size);
- if (memcmp(flavour, "bsd\0", 4) == 0)
+ /* FreeBSD has relative offset if C partition offset is zero */
+ if (memcmp(flavour, "bsd\0", 4) == 0 &&
+ le32_to_cpu(l->d_partitions[2].p_offset) == 0)
bsd_start += offset;
if (offset == bsd_start && size == bsd_size)
/* full parent partition, we have it already */
diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64
new file mode 100644
index 0000000..8d56143
--- /dev/null
+++ b/build.config.cuttlefish.x86_64
@@ -0,0 +1,16 @@
+ARCH=x86_64
+BRANCH=android-4.9
+CLANG_TRIPLE=x86_64-linux-gnu-
+CROSS_COMPILE=x86_64-linux-androidkernel-
+DEFCONFIG=x86_64_cuttlefish_defconfig
+EXTRA_CMDS=''
+KERNEL_DIR=common
+POST_DEFCONFIG_CMDS="check_defconfig"
+CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r328903/bin
+LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin
+FILES="
+arch/x86/boot/bzImage
+vmlinux
+System.map
+"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm
index 866da93..ff5646a 100644
--- a/build.config.goldfish.arm
+++ b/build.config.goldfish.arm
@@ -10,3 +10,4 @@
vmlinux
System.map
"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64
index 9c963cf..4c896a6 100644
--- a/build.config.goldfish.arm64
+++ b/build.config.goldfish.arm64
@@ -10,3 +10,4 @@
vmlinux
System.map
"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips
index 8af53d2..9a14a44 100644
--- a/build.config.goldfish.mips
+++ b/build.config.goldfish.mips
@@ -9,3 +9,4 @@
vmlinux
System.map
"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64
index 2a33d36..6ad9759 100644
--- a/build.config.goldfish.mips64
+++ b/build.config.goldfish.mips64
@@ -9,3 +9,4 @@
vmlinux
System.map
"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86
index f86253f..2266c62 100644
--- a/build.config.goldfish.x86
+++ b/build.config.goldfish.x86
@@ -10,3 +10,4 @@
vmlinux
System.map
"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64
index e173886..08c42c2 100644
--- a/build.config.goldfish.x86_64
+++ b/build.config.goldfish.x86_64
@@ -10,3 +10,4 @@
vmlinux
System.map
"
+STOP_SHIP_TRACEPRINTK=1
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index ca50eeb..b5953f1 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -157,16 +157,16 @@
void *private;
int err;
- /* If caller uses non-allowed flag, return error. */
- if ((sa->salg_feat & ~allowed) || (sa->salg_mask & ~allowed))
- return -EINVAL;
-
if (sock->state == SS_CONNECTED)
return -EINVAL;
if (addr_len != sizeof(*sa))
return -EINVAL;
+ /* If caller uses non-allowed flag, return error. */
+ if ((sa->salg_feat & ~allowed) || (sa->salg_mask & ~allowed))
+ return -EINVAL;
+
sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
diff --git a/crypto/ahash.c b/crypto/ahash.c
index 14402ef..90d73a2 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -91,13 +91,14 @@
if (nbytes && walk->offset & alignmask && !err) {
walk->offset = ALIGN(walk->offset, alignmask + 1);
- walk->data += walk->offset;
-
nbytes = min(nbytes,
((unsigned int)(PAGE_SIZE)) - walk->offset);
walk->entrylen -= nbytes;
- return nbytes;
+ if (nbytes) {
+ walk->data += walk->offset;
+ return nbytes;
+ }
}
if (walk->flags & CRYPTO_ALG_ASYNC)
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 5a37962..df1bde2 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -261,7 +261,7 @@
sinfo->index);
return 0;
}
- ret = public_key_verify_signature(p->pub, p->sig);
+ ret = public_key_verify_signature(p->pub, x509->sig);
if (ret < 0)
return ret;
x509->signer = p;
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 4955eb6..8525fe4 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -93,9 +93,11 @@
BUG_ON(!pkey);
BUG_ON(!sig);
- BUG_ON(!sig->digest);
BUG_ON(!sig->s);
+ if (!sig->digest)
+ return -ENOPKG;
+
alg_name = sig->pkey_algo;
if (strcmp(sig->pkey_algo, "rsa") == 0) {
/* The data wangled by the RSA algorithm is typically padded
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index 19d1afb9..09b1374 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -66,8 +66,9 @@
*
* Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a
* matching parent certificate in the trusted list, -EKEYREJECTED if the
- * signature check fails or the key is blacklisted and some other error if
- * there is a matching certificate but the signature check cannot be performed.
+ * signature check fails or the key is blacklisted, -ENOPKG if the signature
+ * uses unsupported crypto, or some other error if there is a matching
+ * certificate but the signature check cannot be performed.
*/
int restrict_link_by_signature(struct key *trust_keyring,
const struct key_type *type,
@@ -86,6 +87,8 @@
return -EOPNOTSUPP;
sig = payload->data[asym_auth];
+ if (!sig)
+ return -ENOPKG;
if (!sig->auth_ids[0] && !sig->auth_ids[1])
return -ENOKEY;
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 029f705..ce2df8c 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -102,6 +102,7 @@
}
}
+ ret = -ENOMEM;
cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
if (!cert->pub->key)
goto error_decode;
diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c
index f83de99..56bd612 100644
--- a/crypto/async_tx/async_pq.c
+++ b/crypto/async_tx/async_pq.c
@@ -62,9 +62,6 @@
dma_addr_t dma_dest[2];
int src_off = 0;
- if (submit->flags & ASYNC_TX_FENCE)
- dma_flags |= DMA_PREP_FENCE;
-
while (src_cnt > 0) {
submit->flags = flags_orig;
pq_src_cnt = min(src_cnt, dma_maxpq(dma, dma_flags));
@@ -83,6 +80,8 @@
if (cb_fn_orig)
dma_flags |= DMA_PREP_INTERRUPT;
}
+ if (submit->flags & ASYNC_TX_FENCE)
+ dma_flags |= DMA_PREP_FENCE;
/* Drivers force forward progress in case they can not provide
* a descriptor
diff --git a/crypto/drbg.c b/crypto/drbg.c
index 942ddff..4bb5f93 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -1134,8 +1134,10 @@
if (!drbg)
return;
kzfree(drbg->Vbuf);
+ drbg->Vbuf = NULL;
drbg->V = NULL;
kzfree(drbg->Cbuf);
+ drbg->Cbuf = NULL;
drbg->C = NULL;
kzfree(drbg->scratchpadbuf);
drbg->scratchpadbuf = NULL;
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index c5557d0..667dc5c 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -87,8 +87,8 @@
static bool device_id_scheme = false;
module_param(device_id_scheme, bool, 0444);
-static bool only_lcd = false;
-module_param(only_lcd, bool, 0444);
+static int only_lcd = -1;
+module_param(only_lcd, int, 0444);
static int register_count;
static DEFINE_MUTEX(register_count_mutex);
@@ -2069,6 +2069,25 @@
return opregion;
}
+static bool dmi_is_desktop(void)
+{
+ const char *chassis_type;
+
+ chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
+ if (!chassis_type)
+ return false;
+
+ if (!strcmp(chassis_type, "3") || /* 3: Desktop */
+ !strcmp(chassis_type, "4") || /* 4: Low Profile Desktop */
+ !strcmp(chassis_type, "5") || /* 5: Pizza Box */
+ !strcmp(chassis_type, "6") || /* 6: Mini Tower */
+ !strcmp(chassis_type, "7") || /* 7: Tower */
+ !strcmp(chassis_type, "11")) /* 11: Main Server Chassis */
+ return true;
+
+ return false;
+}
+
int acpi_video_register(void)
{
int ret = 0;
@@ -2082,6 +2101,20 @@
goto leave;
}
+ /*
+ * We're seeing a lot of bogus backlight interfaces on newer machines
+ * without a LCD such as desktops, servers and HDMI sticks. Checking
+ * the lcd flag fixes this, so enable this on any machines which are
+ * win8 ready (where we also prefer the native backlight driver, so
+ * normally the acpi_video code should not register there anyways).
+ */
+ if (only_lcd == -1) {
+ if (dmi_is_desktop() && acpi_osi_is_win8())
+ only_lcd = true;
+ else
+ only_lcd = false;
+ }
+
dmi_check_system(video_dmi_table);
ret = acpi_bus_register_driver(&acpi_video_bus);
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
index 13caebd..ce8fc68 100644
--- a/drivers/acpi/acpi_watchdog.c
+++ b/drivers/acpi/acpi_watchdog.c
@@ -74,10 +74,10 @@
res.start = gas->address;
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
res.flags = IORESOURCE_MEM;
- res.end = res.start + ALIGN(gas->access_width, 4);
+ res.end = res.start + ALIGN(gas->access_width, 4) - 1;
} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
res.flags = IORESOURCE_IO;
- res.end = res.start + gas->access_width;
+ res.end = res.start + gas->access_width - 1;
} else {
pr_warn("Unsupported address space: %u\n",
gas->space_id);
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 9179e9a..35a2d9e 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -180,6 +180,12 @@
ACPI_FUNCTION_TRACE(acpi_enable_event);
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
/* Decode the Fixed Event */
if (event > ACPI_EVENT_MAX) {
@@ -237,6 +243,12 @@
ACPI_FUNCTION_TRACE(acpi_disable_event);
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
/* Decode the Fixed Event */
if (event > ACPI_EVENT_MAX) {
@@ -290,6 +302,12 @@
ACPI_FUNCTION_TRACE(acpi_clear_event);
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (acpi_gbl_reduced_hardware) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
/* Decode the Fixed Event */
if (event > ACPI_EVENT_MAX) {
diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c
index db0e903..ac2e8df 100644
--- a/drivers/acpi/acpica/psobject.c
+++ b/drivers/acpi/acpica/psobject.c
@@ -121,6 +121,9 @@
(u32)(aml_offset +
sizeof(struct acpi_table_header)));
+ ACPI_ERROR((AE_INFO,
+ "Aborting disassembly, AML byte code is corrupt"));
+
/* Dump the context surrounding the invalid opcode */
acpi_ut_dump_buffer(((u8 *)walk_state->parser_state.
@@ -129,6 +132,14 @@
sizeof(struct acpi_table_header) -
16));
acpi_os_printf(" */\n");
+
+ /*
+ * Just abort the disassembly, cannot continue because the
+ * parser is essentially lost. The disassembler can then
+ * randomly fail because an ill-constructed parse tree
+ * can result.
+ */
+ return_ACPI_STATUS(AE_AML_BAD_OPCODE);
#endif
}
@@ -293,6 +304,9 @@
if (status == AE_CTRL_PARSE_CONTINUE) {
return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE);
}
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
/* Create Op structure and append to parent's argument list */
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index e19f530..f7c4301 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -556,7 +556,8 @@
return 0;
}
-static int param_set_lid_init_state(const char *val, struct kernel_param *kp)
+static int param_set_lid_init_state(const char *val,
+ const struct kernel_param *kp)
{
int result = 0;
@@ -574,7 +575,8 @@
return result;
}
-static int param_get_lid_init_state(char *buffer, struct kernel_param *kp)
+static int param_get_lid_init_state(char *buffer,
+ const struct kernel_param *kp)
{
switch (lid_init_state) {
case ACPI_BUTTON_LID_INIT_OPEN:
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index c3bcb7f..7b665aa 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1518,7 +1518,7 @@
}
acpi_handle_info(ec->handle,
- "GPE=0x%lx, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n",
+ "GPE=0x%x, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n",
ec->gpe, ec->command_addr, ec->data_addr);
return ret;
}
@@ -1891,7 +1891,8 @@
SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
};
-static int param_set_event_clearing(const char *val, struct kernel_param *kp)
+static int param_set_event_clearing(const char *val,
+ const struct kernel_param *kp)
{
int result = 0;
@@ -1909,7 +1910,8 @@
return result;
}
-static int param_get_event_clearing(char *buffer, struct kernel_param *kp)
+static int param_get_event_clearing(char *buffer,
+ const struct kernel_param *kp)
{
switch (ec_event_clearing) {
case ACPI_EC_EVT_TIMING_STATUS:
diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c
index 6c7dd7a..dd70d6c 100644
--- a/drivers/acpi/ec_sys.c
+++ b/drivers/acpi/ec_sys.c
@@ -128,7 +128,7 @@
return -ENOMEM;
}
- if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe))
+ if (!debugfs_create_x32("gpe", 0444, dev_dir, &first_ec->gpe))
goto error;
if (!debugfs_create_bool("use_global_lock", 0444, dev_dir,
&first_ec->global_lock))
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 08b3ca0..b012e94 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -158,7 +158,7 @@
-------------------------------------------------------------------------- */
struct acpi_ec {
acpi_handle handle;
- unsigned long gpe;
+ u32 gpe;
unsigned long command_addr;
unsigned long data_addr;
bool global_lock;
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index b1815b2..3874eec 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -967,8 +967,11 @@
if (nd_desc) {
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ mutex_lock(&acpi_desc->init_mutex);
rc = sprintf(buf, "%d%s", acpi_desc->scrub_count,
- (work_busy(&acpi_desc->work)) ? "+\n" : "\n");
+ work_busy(&acpi_desc->work)
+ && !acpi_desc->cancel ? "+\n" : "\n");
+ mutex_unlock(&acpi_desc->init_mutex);
}
device_unlock(dev);
return rc;
@@ -2547,15 +2550,21 @@
static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
{
struct nfit_spa *nfit_spa;
- int rc;
- list_for_each_entry(nfit_spa, &acpi_desc->spas, list)
- if (nfit_spa_type(nfit_spa->spa) == NFIT_SPA_DCR) {
- /* BLK regions don't need to wait for ars results */
- rc = acpi_nfit_register_region(acpi_desc, nfit_spa);
- if (rc)
- return rc;
- }
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ int rc, type = nfit_spa_type(nfit_spa->spa);
+
+ /* PMEM and VMEM will be registered by the ARS workqueue */
+ if (type == NFIT_SPA_PM || type == NFIT_SPA_VOLATILE)
+ continue;
+ /* BLK apertures belong to BLK region registration below */
+ if (type == NFIT_SPA_BDW)
+ continue;
+ /* BLK regions don't need to wait for ARS results */
+ rc = acpi_nfit_register_region(acpi_desc, nfit_spa);
+ if (rc)
+ return rc;
+ }
queue_work(nfit_wq, &acpi_desc->work);
return 0;
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index ce3a7a1..17b518c 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -103,25 +103,27 @@
*/
int acpi_map_pxm_to_online_node(int pxm)
{
- int node, n, dist, min_dist;
+ int node, min_node;
node = acpi_map_pxm_to_node(pxm);
if (node == NUMA_NO_NODE)
node = 0;
+ min_node = node;
if (!node_online(node)) {
- min_dist = INT_MAX;
+ int min_dist = INT_MAX, dist, n;
+
for_each_online_node(n) {
dist = node_distance(node, n);
if (dist < min_dist) {
min_dist = dist;
- node = n;
+ min_node = n;
}
}
}
- return node;
+ return min_node;
}
EXPORT_SYMBOL(acpi_map_pxm_to_online_node);
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index e6e991a..0c6874e 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -28,97 +28,97 @@
.address = 0x00,
.reg = 0x13,
.bit = 0x05,
- },
+ }, /* ALD1 */
{
.address = 0x04,
.reg = 0x13,
.bit = 0x06,
- },
+ }, /* ALD2 */
{
.address = 0x08,
.reg = 0x13,
.bit = 0x07,
- },
+ }, /* ALD3 */
{
.address = 0x0c,
.reg = 0x12,
.bit = 0x03,
- },
+ }, /* DLD1 */
{
.address = 0x10,
.reg = 0x12,
.bit = 0x04,
- },
+ }, /* DLD2 */
{
.address = 0x14,
.reg = 0x12,
.bit = 0x05,
- },
+ }, /* DLD3 */
{
.address = 0x18,
.reg = 0x12,
.bit = 0x06,
- },
+ }, /* DLD4 */
{
.address = 0x1c,
.reg = 0x12,
.bit = 0x00,
- },
+ }, /* ELD1 */
{
.address = 0x20,
.reg = 0x12,
.bit = 0x01,
- },
+ }, /* ELD2 */
{
.address = 0x24,
.reg = 0x12,
.bit = 0x02,
- },
+ }, /* ELD3 */
{
.address = 0x28,
.reg = 0x13,
.bit = 0x02,
- },
+ }, /* FLD1 */
{
.address = 0x2c,
.reg = 0x13,
.bit = 0x03,
- },
+ }, /* FLD2 */
{
.address = 0x30,
.reg = 0x13,
.bit = 0x04,
- },
+ }, /* FLD3 */
+ {
+ .address = 0x34,
+ .reg = 0x10,
+ .bit = 0x03,
+ }, /* BUC1 */
{
.address = 0x38,
.reg = 0x10,
- .bit = 0x03,
- },
+ .bit = 0x06,
+ }, /* BUC2 */
{
.address = 0x3c,
.reg = 0x10,
- .bit = 0x06,
- },
+ .bit = 0x05,
+ }, /* BUC3 */
{
.address = 0x40,
.reg = 0x10,
- .bit = 0x05,
- },
+ .bit = 0x04,
+ }, /* BUC4 */
{
.address = 0x44,
.reg = 0x10,
- .bit = 0x04,
- },
+ .bit = 0x01,
+ }, /* BUC5 */
{
.address = 0x48,
.reg = 0x10,
- .bit = 0x01,
- },
- {
- .address = 0x4c,
- .reg = 0x10,
.bit = 0x00
- },
+ }, /* BUC6 */
};
/* TMP0 - TMP5 are the same, all from GPADC */
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 1c2b846..3a6c9b7 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -864,6 +864,16 @@
mutex_unlock(&resource->resource_lock);
}
+
+ mutex_unlock(&power_resource_list_lock);
+}
+
+void acpi_turn_off_unused_power_resources(void)
+{
+ struct acpi_power_resource *resource;
+
+ mutex_lock(&power_resource_list_lock);
+
list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
int result, state;
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 9d5f0c7..8697a82 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -251,6 +251,9 @@
if (ACPI_SUCCESS(status))
return 0;
+ result = -ENODEV;
+ acpi_pss_perf_exit(pr, device);
+
err_power_exit:
acpi_processor_power_exit(pr);
return result;
@@ -259,11 +262,16 @@
static int acpi_processor_start(struct device *dev)
{
struct acpi_device *device = ACPI_COMPANION(dev);
+ int ret;
if (!device)
return -ENODEV;
- return __acpi_processor_start(device);
+ /* Protect against concurrent CPU hotplug operations */
+ get_online_cpus();
+ ret = __acpi_processor_start(device);
+ put_online_cpus();
+ return ret;
}
static int acpi_processor_stop(struct device *dev)
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index d51ca1c..207e9bb 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -62,8 +62,8 @@
#define THROTTLING_POSTCHANGE (2)
static int acpi_processor_get_throttling(struct acpi_processor *pr);
-int acpi_processor_set_throttling(struct acpi_processor *pr,
- int state, bool force);
+static int __acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force, bool direct);
static int acpi_processor_update_tsd_coord(void)
{
@@ -891,7 +891,8 @@
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Invalid throttling state, reset\n"));
state = 0;
- ret = acpi_processor_set_throttling(pr, state, true);
+ ret = __acpi_processor_set_throttling(pr, state, true,
+ true);
if (ret)
return ret;
}
@@ -901,36 +902,31 @@
return 0;
}
+static long __acpi_processor_get_throttling(void *data)
+{
+ struct acpi_processor *pr = data;
+
+ return pr->throttling.acpi_processor_get_throttling(pr);
+}
+
static int acpi_processor_get_throttling(struct acpi_processor *pr)
{
- cpumask_var_t saved_mask;
- int ret;
-
if (!pr)
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
- if (!alloc_cpumask_var(&saved_mask, GFP_KERNEL))
- return -ENOMEM;
-
/*
- * Migrate task to the cpu pointed by pr.
+ * This is either called from the CPU hotplug callback of
+ * processor_driver or via the ACPI probe function. In the latter
+ * case the CPU is not guaranteed to be online. Both call sites are
+ * protected against CPU hotplug.
*/
- cpumask_copy(saved_mask, ¤t->cpus_allowed);
- /* FIXME: use work_on_cpu() */
- if (set_cpus_allowed_ptr(current, cpumask_of(pr->id))) {
- /* Can't migrate to the target pr->id CPU. Exit */
- free_cpumask_var(saved_mask);
+ if (!cpu_online(pr->id))
return -ENODEV;
- }
- ret = pr->throttling.acpi_processor_get_throttling(pr);
- /* restore the previous state */
- set_cpus_allowed_ptr(current, saved_mask);
- free_cpumask_var(saved_mask);
- return ret;
+ return work_on_cpu(pr->id, __acpi_processor_get_throttling, pr);
}
static int acpi_processor_get_fadt_info(struct acpi_processor *pr)
@@ -1080,8 +1076,15 @@
arg->target_state, arg->force);
}
-int acpi_processor_set_throttling(struct acpi_processor *pr,
- int state, bool force)
+static int call_on_cpu(int cpu, long (*fn)(void *), void *arg, bool direct)
+{
+ if (direct)
+ return fn(arg);
+ return work_on_cpu(cpu, fn, arg);
+}
+
+static int __acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force, bool direct)
{
int ret = 0;
unsigned int i;
@@ -1130,7 +1133,8 @@
arg.pr = pr;
arg.target_state = state;
arg.force = force;
- ret = work_on_cpu(pr->id, acpi_processor_throttling_fn, &arg);
+ ret = call_on_cpu(pr->id, acpi_processor_throttling_fn, &arg,
+ direct);
} else {
/*
* When the T-state coordination is SW_ALL or HW_ALL,
@@ -1163,8 +1167,8 @@
arg.pr = match_pr;
arg.target_state = state;
arg.force = force;
- ret = work_on_cpu(pr->id, acpi_processor_throttling_fn,
- &arg);
+ ret = call_on_cpu(pr->id, acpi_processor_throttling_fn,
+ &arg, direct);
}
}
/*
@@ -1182,6 +1186,12 @@
return ret;
}
+int acpi_processor_set_throttling(struct acpi_processor *pr, int state,
+ bool force)
+{
+ return __acpi_processor_set_throttling(pr, state, force, false);
+}
+
int acpi_processor_get_throttling_info(struct acpi_processor *pr)
{
int result = 0;
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index a4327af..097d630 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -474,6 +474,7 @@
*/
static void acpi_pm_end(void)
{
+ acpi_turn_off_unused_power_resources();
acpi_scan_lock_release();
/*
* This is necessary in case acpi_pm_finish() is not called during a
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index a9cc34e..a82ff74 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -6,6 +6,7 @@
extern struct mutex acpi_device_lock;
extern void acpi_resume_power_resources(void);
+extern void acpi_turn_off_unused_power_resources(void);
static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)
{
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index cf05ae9..34328e1 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -227,7 +227,8 @@
module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644);
module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644);
-static int param_set_trace_state(const char *val, struct kernel_param *kp)
+static int param_set_trace_state(const char *val,
+ const struct kernel_param *kp)
{
acpi_status status;
const char *method = trace_method_name;
@@ -263,7 +264,7 @@
return 0;
}
-static int param_get_trace_state(char *buffer, struct kernel_param *kp)
+static int param_get_trace_state(char *buffer, const struct kernel_param *kp)
{
if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED))
return sprintf(buffer, "disable");
@@ -292,7 +293,8 @@
"To enable/disable the ACPI Debug Object output.");
/* /sys/module/acpi/parameters/acpica_version */
-static int param_get_acpica_version(char *buffer, struct kernel_param *kp)
+static int param_get_acpica_version(char *buffer,
+ const struct kernel_param *kp)
{
int result;
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 02ded25..cdc4737 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -214,6 +214,15 @@
},
},
{
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 670Z5E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"),
+ },
+ },
+ {
/* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
.callback = video_detect_force_video,
.ident = "SAMSUNG 730U3E/740U3E",
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index a56fa2a..93888cc 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -69,11 +69,12 @@
struct device_attribute *attr, char *buf)
{
struct amba_device *dev = to_amba_device(_dev);
+ ssize_t len;
- if (!dev->driver_override)
- return 0;
-
- return sprintf(buf, "%s\n", dev->driver_override);
+ device_lock(_dev);
+ len = sprintf(buf, "%s\n", dev->driver_override);
+ device_unlock(_dev);
+ return len;
}
static ssize_t driver_override_store(struct device *_dev,
@@ -81,9 +82,10 @@
const char *buf, size_t count)
{
struct amba_device *dev = to_amba_device(_dev);
- char *driver_override, *old = dev->driver_override, *cp;
+ char *driver_override, *old, *cp;
- if (count > PATH_MAX)
+ /* We need to keep extra room for a newline */
+ if (count >= (PAGE_SIZE - 1))
return -EINVAL;
driver_override = kstrndup(buf, count, GFP_KERNEL);
@@ -94,12 +96,15 @@
if (cp)
*cp = '\0';
+ device_lock(_dev);
+ old = dev->driver_override;
if (strlen(driver_override)) {
dev->driver_override = driver_override;
} else {
kfree(driver_override);
dev->driver_override = NULL;
}
+ device_unlock(_dev);
kfree(old);
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index ff367b8fa..957eb3c 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -151,7 +151,7 @@
static int binder_stop_on_user_error;
static int binder_set_stop_on_user_error(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
int ret;
@@ -2147,8 +2147,14 @@
&target_thread->reply_error.work);
wake_up_interruptible(&target_thread->wait);
} else {
- WARN(1, "Unexpected reply error: %u\n",
- target_thread->reply_error.cmd);
+ /*
+ * Cannot get here for normal operation, but
+ * we can if multiple synchronous transactions
+ * are sent without blocking for responses.
+ * Just ignore the 2nd error in this case.
+ */
+ pr_warn("Unexpected reply error: %u\n",
+ target_thread->reply_error.cmd);
}
binder_inner_proc_unlock(target_thread->proc);
binder_thread_dec_tmpref(target_thread);
@@ -2995,6 +3001,14 @@
else
return_error = BR_DEAD_REPLY;
mutex_unlock(&context->context_mgr_node_lock);
+ if (target_node && target_proc == proc) {
+ binder_user_error("%d:%d got transaction to context manager from process owning it\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
+ goto err_invalid_target_handle;
+ }
}
if (!target_node) {
/*
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 9b46ef4..4d4b5f6 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -539,7 +539,9 @@
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230),
.driver_data = board_ahci_yes_fbs },
- { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642),
+ { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642), /* highpoint rocketraid 642L */
+ .driver_data = board_ahci_yes_fbs },
+ { PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0645), /* highpoint rocketraid 644L */
.driver_data = board_ahci_yes_fbs },
/* Promise */
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index aaa761b..cd2eab6 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -514,8 +514,9 @@
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
- dev_err(dev, "no irq\n");
- return -EINVAL;
+ if (irq != -EPROBE_DEFER)
+ dev_err(dev, "no irq\n");
+ return irq;
}
hpriv->irq = irq;
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index aee3952..4fe3ec1 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4403,6 +4403,28 @@
{ "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
+ /* Crucial BX100 SSD 500GB has broken LPM support */
+ { "CT500BX100SSD1", NULL, ATA_HORKAGE_NOLPM },
+
+ /* 512GB MX100 with MU01 firmware has both queued TRIM and LPM issues */
+ { "Crucial_CT512MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+ /* 512GB MX100 with newer firmware has only LPM issues */
+ { "Crucial_CT512MX100*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+
+ /* 480GB+ M500 SSDs have both queued TRIM and LPM issues */
+ { "Crucial_CT480M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+ { "Crucial_CT960M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM |
+ ATA_HORKAGE_NOLPM, },
+
+ /* Sandisk devices which are known to not handle LPM well */
+ { "SanDisk SD7UB3Q*G1001", NULL, ATA_HORKAGE_NOLPM, },
+
/* devices that don't properly handle queued TRIM commands */
{ "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
@@ -4414,7 +4436,9 @@
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
- { "Samsung SSD 8*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ { "Samsung SSD 840*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Samsung SSD 850*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "FCCT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
@@ -5265,8 +5289,7 @@
* We guarantee to LLDs that they will have at least one
* non-zero sg if the command is a data command.
*/
- if (WARN_ON_ONCE(ata_is_data(prot) &&
- (!qc->sg || !qc->n_elem || !qc->nbytes)))
+ if (ata_is_data(prot) && (!qc->sg || !qc->n_elem || !qc->nbytes))
goto sys_err;
if (ata_is_dma(prot) || (ata_is_pio(prot) &&
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index e3e10e8..9babbc8 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3226,6 +3226,12 @@
goto invalid_fld;
}
+ /* We may not issue NCQ commands to devices not supporting NCQ */
+ if (ata_is_ncq(tf->protocol) && !ata_ncq_enabled(dev)) {
+ fp = 1;
+ goto invalid_fld;
+ }
+
/* sanity check for pio multi commands */
if ((cdb[1] & 0xe0) && !is_multi_taskfile(tf)) {
fp = 1;
@@ -4177,7 +4183,9 @@
if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
/* relay SCSI command to ATAPI device */
int len = COMMAND_SIZE(scsi_op);
- if (unlikely(len > scmd->cmd_len || len > dev->cdb_len))
+ if (unlikely(len > scmd->cmd_len ||
+ len > dev->cdb_len ||
+ scmd->cmd_len > ATAPI_CDB_LEN))
goto bad_cdb_len;
xlat_func = atapi_xlat;
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
index d3dc954..81bfeec 100644
--- a/drivers/atm/zatm.c
+++ b/drivers/atm/zatm.c
@@ -23,6 +23,7 @@
#include <linux/bitops.h>
#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
#include <asm/byteorder.h>
#include <asm/string.h>
#include <asm/io.h>
@@ -1458,6 +1459,8 @@
return -EFAULT;
if (pool < 0 || pool > ZATM_LAST_POOL)
return -EINVAL;
+ pool = array_index_nospec(pool,
+ ZATM_LAST_POOL + 1);
spin_lock_irqsave(&zatm_dev->lock, flags);
info = zatm_dev->pool_info[pool];
if (cmd == ZATM_GETPOOLZ) {
diff --git a/drivers/base/core.c b/drivers/base/core.c
index ac43d6f..4272868 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -876,8 +876,8 @@
struct device_node *of_node = dev_of_node(dev);
int error;
- if (of_node) {
- error = sysfs_create_link(&dev->kobj, &of_node->kobj,"of_node");
+ if (of_node && of_node_kobj(of_node)) {
+ error = sysfs_create_link(&dev->kobj, of_node_kobj(of_node), "of_node");
if (error)
dev_warn(dev, "Error %d creating of_node link\n",error);
/* An error here doesn't warrant bringing down the device */
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 4fe86f7..f9e1010 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -610,14 +610,22 @@
return sprintf(buf, "Not affected\n");
}
+ssize_t __weak cpu_show_spec_store_bypass(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
+static DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL);
static struct attribute *cpu_root_vulnerabilities_attrs[] = {
&dev_attr_meltdown.attr,
&dev_attr_spectre_v1.attr,
&dev_attr_spectre_v2.attr,
+ &dev_attr_spec_store_bypass.attr,
NULL
};
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index a7c5b79..94001aa 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -708,7 +708,7 @@
struct opp_table *opp_table)
{
opp_debug_unregister(opp_dev, opp_table);
- list_del(&opp_dev->node);
+ list_del_rcu(&opp_dev->node);
call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head,
_kfree_opp_dev_rcu);
}
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 3331ed8..f74f3ca 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1736,7 +1736,7 @@
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
- if (map->max_raw_write && map->max_raw_write > val_len)
+ if (map->max_raw_write && map->max_raw_write < val_len)
return -E2BIG;
map->lock(map->lock_arg);
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 402254d..ff1c4d7 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -263,7 +263,7 @@
struct iov_iter i;
ssize_t bw;
- iov_iter_bvec(&i, ITER_BVEC, bvec, 1, bvec->bv_len);
+ iov_iter_bvec(&i, ITER_BVEC | WRITE, bvec, 1, bvec->bv_len);
file_start_write(file);
bw = vfs_iter_write(file, &i, ppos);
@@ -612,6 +612,9 @@
*/
static int loop_flush(struct loop_device *lo)
{
+ /* loop not yet configured, no running thread, nothing to flush */
+ if (lo->lo_state != Lo_bound)
+ return 0;
return loop_switch(lo, NULL);
}
@@ -1107,11 +1110,15 @@
if (info->lo_encrypt_type) {
unsigned int type = info->lo_encrypt_type;
- if (type >= MAX_LO_CRYPT)
- return -EINVAL;
+ if (type >= MAX_LO_CRYPT) {
+ err = -EINVAL;
+ goto exit;
+ }
xfer = xfer_funcs[type];
- if (xfer == NULL)
- return -EINVAL;
+ if (xfer == NULL) {
+ err = -EINVAL;
+ goto exit;
+ }
} else
xfer = NULL;
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
index f927756..7f7c942 100644
--- a/drivers/bluetooth/bluetooth-power.c
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2010, 2013-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2010, 2013-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,6 +43,7 @@
static const struct of_device_id bt_power_match_table[] = {
{ .compatible = "qca,ar3002" },
{ .compatible = "qca,qca6174" },
+ { .compatible = "qca,qca9379" },
{ .compatible = "qca,wcn3990" },
{}
};
diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c
index 08c2c93..3aac78a 100644
--- a/drivers/bluetooth/btqcomsmd.c
+++ b/drivers/bluetooth/btqcomsmd.c
@@ -85,7 +85,8 @@
break;
}
- kfree_skb(skb);
+ if (!ret)
+ kfree_skb(skb);
return ret;
}
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 3257647..bff67c5 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -345,6 +345,9 @@
{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK },
+ /* Additional Realtek 8723BU Bluetooth devices */
+ { USB_DEVICE(0x7392, 0xa611), .driver_info = BTUSB_REALTEK },
+
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
@@ -352,6 +355,9 @@
{ USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
+ /* Additional Realtek 8822BE Bluetooth devices */
+ { USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK },
+
/* Silicon Wave based devices */
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 9497c46..2230f93 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -113,16 +113,21 @@
{
struct sk_buff *skb = hu->tx_skb;
- if (!skb)
- skb = hu->proto->dequeue(hu);
- else
+ if (!skb) {
+ if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ skb = hu->proto->dequeue(hu);
+ } else {
hu->tx_skb = NULL;
+ }
return skb;
}
int hci_uart_tx_wakeup(struct hci_uart *hu)
{
+ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+ return 0;
+
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
return 0;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 6c867fb..74b2f4a 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -936,6 +936,9 @@
if (!ret) {
set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
qca_debugfs_init(hdev);
+ } else if (ret == -ENOENT) {
+ /* No patch/nvm-config found, run with original fw/config */
+ ret = 0;
}
/* Setup bdaddr */
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c
index 72fe0a5..017c37b 100644
--- a/drivers/bus/brcmstb_gisb.c
+++ b/drivers/bus/brcmstb_gisb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Broadcom Corporation
+ * Copyright (C) 2014-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -37,8 +37,6 @@
#define ARB_ERR_CAP_CLEAR (1 << 0)
#define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12)
#define ARB_ERR_CAP_STATUS_TEA (1 << 11)
-#define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2)
-#define ARB_ERR_CAP_STATUS_BS_MASK 0x3c
#define ARB_ERR_CAP_STATUS_WRITE (1 << 1)
#define ARB_ERR_CAP_STATUS_VALID (1 << 0)
@@ -47,7 +45,6 @@
ARB_ERR_CAP_CLR,
ARB_ERR_CAP_HI_ADDR,
ARB_ERR_CAP_ADDR,
- ARB_ERR_CAP_DATA,
ARB_ERR_CAP_STATUS,
ARB_ERR_CAP_MASTER,
};
@@ -57,7 +54,6 @@
[ARB_ERR_CAP_CLR] = 0x0c4,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x0c8,
- [ARB_ERR_CAP_DATA] = 0x0cc,
[ARB_ERR_CAP_STATUS] = 0x0d0,
[ARB_ERR_CAP_MASTER] = -1,
};
@@ -67,7 +63,6 @@
[ARB_ERR_CAP_CLR] = 0x0c8,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x0cc,
- [ARB_ERR_CAP_DATA] = 0x0d0,
[ARB_ERR_CAP_STATUS] = 0x0d4,
[ARB_ERR_CAP_MASTER] = 0x0d8,
};
@@ -77,7 +72,6 @@
[ARB_ERR_CAP_CLR] = 0x168,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x16c,
- [ARB_ERR_CAP_DATA] = 0x170,
[ARB_ERR_CAP_STATUS] = 0x174,
[ARB_ERR_CAP_MASTER] = 0x178,
};
@@ -87,7 +81,6 @@
[ARB_ERR_CAP_CLR] = 0x7e4,
[ARB_ERR_CAP_HI_ADDR] = 0x7e8,
[ARB_ERR_CAP_ADDR] = 0x7ec,
- [ARB_ERR_CAP_DATA] = 0x7f0,
[ARB_ERR_CAP_STATUS] = 0x7f4,
[ARB_ERR_CAP_MASTER] = 0x7f8,
};
@@ -109,9 +102,13 @@
{
int offset = gdev->gisb_offsets[reg];
- /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
- if (offset == -1)
- return 1;
+ if (offset < 0) {
+ /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
+ if (reg == ARB_ERR_CAP_MASTER)
+ return 1;
+ else
+ return 0;
+ }
if (gdev->big_endian)
return ioread32be(gdev->base + offset);
@@ -119,6 +116,16 @@
return ioread32(gdev->base + offset);
}
+static u64 gisb_read_address(struct brcmstb_gisb_arb_device *gdev)
+{
+ u64 value;
+
+ value = gisb_read(gdev, ARB_ERR_CAP_ADDR);
+ value |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
+
+ return value;
+}
+
static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg)
{
int offset = gdev->gisb_offsets[reg];
@@ -127,9 +134,9 @@
return;
if (gdev->big_endian)
- iowrite32be(val, gdev->base + reg);
+ iowrite32be(val, gdev->base + offset);
else
- iowrite32(val, gdev->base + reg);
+ iowrite32(val, gdev->base + offset);
}
static ssize_t gisb_arb_get_timeout(struct device *dev,
@@ -185,7 +192,7 @@
const char *reason)
{
u32 cap_status;
- unsigned long arb_addr;
+ u64 arb_addr;
u32 master;
const char *m_name;
char m_fmt[11];
@@ -197,10 +204,7 @@
return 1;
/* Read the address and master */
- arb_addr = gisb_read(gdev, ARB_ERR_CAP_ADDR) & 0xffffffff;
-#if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
- arb_addr |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
-#endif
+ arb_addr = gisb_read_address(gdev);
master = gisb_read(gdev, ARB_ERR_CAP_MASTER);
m_name = brcmstb_gisb_master_to_str(gdev, master);
@@ -209,7 +213,7 @@
m_name = m_fmt;
}
- pr_crit("%s: %s at 0x%lx [%c %s], core: %s\n",
+ pr_crit("%s: %s at 0x%llx [%c %s], core: %s\n",
__func__, reason, arb_addr,
cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R',
cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "",
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 5d475b3..128ebd4 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -2368,7 +2368,7 @@
if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
return media_changed(cdi, 1);
- if ((unsigned int)arg >= cdi->capacity)
+ if (arg >= cdi->capacity)
return -EINVAL;
info = kmalloc(sizeof(*info), GFP_KERNEL);
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 7f8a158..d52c80c 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -1236,6 +1236,25 @@
}
+
+static void fastrpc_notify_users_staticpd_pdr(struct fastrpc_file *me)
+{
+ struct smq_invoke_ctx *ictx;
+ struct hlist_node *n;
+
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(ictx, n, &me->clst.pending, hn) {
+ if (ictx->msg.pid)
+ complete(&ictx->work);
+ }
+ hlist_for_each_entry_safe(ictx, n, &me->clst.interrupted, hn) {
+ if (ictx->msg.pid)
+ complete(&ictx->work);
+ }
+ spin_unlock(&me->hlock);
+}
+
+
static void fastrpc_notify_drivers(struct fastrpc_apps *me, int cid)
{
struct fastrpc_file *fl;
@@ -1258,7 +1277,7 @@
spin_lock(&me->hlock);
hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
if (fl->spdname && !strcmp(spdname, fl->spdname))
- fastrpc_notify_users(fl);
+ fastrpc_notify_users_staticpd_pdr(fl);
}
spin_unlock(&me->hlock);
@@ -1548,9 +1567,18 @@
if (map && (map->attr & FASTRPC_ATTR_COHERENT))
continue;
- if (rpra && rpra[i].buf.len && ctx->overps[oix]->mstart)
- dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv),
- uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len));
+ if (rpra && rpra[i].buf.len && ctx->overps[oix]->mstart) {
+ if (map && map->handle)
+ msm_ion_do_cache_op(ctx->fl->apps->client,
+ map->handle,
+ uint64_to_ptr(rpra[i].buf.pv),
+ rpra[i].buf.len,
+ ION_IOC_CLEAN_INV_CACHES);
+ else
+ dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv),
+ uint64_to_ptr(rpra[i].buf.pv
+ + rpra[i].buf.len));
+ }
}
PERF_END);
for (i = bufs; rpra && i < bufs + handles; i++) {
@@ -1559,11 +1587,6 @@
rpra[i].dma.offset = (uint32_t)(uintptr_t)lpra[i].buf.pv;
}
- if (!ctx->fl->sctx->smmu.coherent) {
- PERF(ctx->fl->profile, GET_COUNTER(perf_counter, PERF_FLUSH),
- dmac_flush_range((char *)rpra, (char *)rpra + ctx->used);
- PERF_END);
- }
bail:
return err;
}
@@ -1652,14 +1675,33 @@
if (buf_page_start(ptr_to_uint64((void *)rpra)) ==
buf_page_start(rpra[i].buf.pv))
continue;
- if (!IS_CACHE_ALIGNED((uintptr_t)uint64_to_ptr(rpra[i].buf.pv)))
- dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv),
- (char *)(uint64_to_ptr(rpra[i].buf.pv + 1)));
+ if (!IS_CACHE_ALIGNED((uintptr_t)
+ uint64_to_ptr(rpra[i].buf.pv))) {
+ if (map && map->handle)
+ msm_ion_do_cache_op(ctx->fl->apps->client,
+ map->handle,
+ uint64_to_ptr(rpra[i].buf.pv),
+ sizeof(uintptr_t),
+ ION_IOC_CLEAN_INV_CACHES);
+ else
+ dmac_flush_range(
+ uint64_to_ptr(rpra[i].buf.pv), (char *)
+ uint64_to_ptr(rpra[i].buf.pv + 1));
+ }
+
end = (uintptr_t)uint64_to_ptr(rpra[i].buf.pv +
rpra[i].buf.len);
- if (!IS_CACHE_ALIGNED(end))
- dmac_flush_range((char *)end,
- (char *)end + 1);
+ if (!IS_CACHE_ALIGNED(end)) {
+ if (map && map->handle)
+ msm_ion_do_cache_op(ctx->fl->apps->client,
+ map->handle,
+ uint64_to_ptr(end),
+ sizeof(uintptr_t),
+ ION_IOC_CLEAN_INV_CACHES);
+ else
+ dmac_flush_range((char *)end,
+ (char *)end + 1);
+ }
}
}
@@ -1668,7 +1710,6 @@
int i, inbufs, outbufs;
uint32_t sc = ctx->sc;
remote_arg64_t *rpra = ctx->rpra;
- int used = ctx->used;
inbufs = REMOTE_SCALARS_INBUFS(sc);
outbufs = REMOTE_SCALARS_OUTBUFS(sc);
@@ -1699,8 +1740,6 @@
+ rpra[i].buf.len));
}
- if (rpra)
- dmac_inv_range(rpra, (char *)rpra + used);
}
static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx,
@@ -2745,6 +2784,7 @@
mutex_unlock(&fl->perf_mutex);
mutex_destroy(&fl->perf_mutex);
mutex_destroy(&fl->fl_map_mutex);
+ mutex_destroy(&fl->map_mutex);
kfree(fl);
return 0;
}
@@ -2758,7 +2798,6 @@
pm_qos_remove_request(&fl->pm_qos_req);
if (fl->debugfs_file != NULL)
debugfs_remove(fl->debugfs_file);
- mutex_destroy(&fl->map_mutex);
fastrpc_file_free(fl);
file->private_data = NULL;
}
@@ -3240,6 +3279,28 @@
if (err)
goto bail;
break;
+ case FASTRPC_IOCTL_MMAP_64:
+ K_COPY_FROM_USER(err, 0, &p.mmap, param,
+ sizeof(p.mmap));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_mmap(fl, &p.mmap)));
+ if (err)
+ goto bail;
+ K_COPY_TO_USER(err, 0, param, &p.mmap, sizeof(p.mmap));
+ if (err)
+ goto bail;
+ break;
+ case FASTRPC_IOCTL_MUNMAP_64:
+ K_COPY_FROM_USER(err, 0, &p.munmap, param,
+ sizeof(p.munmap));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_munmap(fl,
+ &p.munmap)));
+ if (err)
+ goto bail;
+ break;
case FASTRPC_IOCTL_MUNMAP_FD:
K_COPY_FROM_USER(err, 0, &p.munmap_fd, param,
sizeof(p.munmap_fd));
diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c
index 0f07483..804ceda 100644
--- a/drivers/char/adsprpc_compat.c
+++ b/drivers/char/adsprpc_compat.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,10 @@
_IOWR('R', 11, struct compat_fastrpc_ioctl_invoke_crc)
#define COMPAT_FASTRPC_IOCTL_CONTROL \
_IOWR('R', 12, struct compat_fastrpc_ioctl_control)
+#define COMPAT_FASTRPC_IOCTL_MMAP_64 \
+ _IOWR('R', 14, struct compat_fastrpc_ioctl_mmap_64)
+#define COMPAT_FASTRPC_IOCTL_MUNMAP_64 \
+ _IOWR('R', 15, struct compat_fastrpc_ioctl_munmap_64)
struct compat_remote_buf {
compat_uptr_t pv; /* buffer pointer */
@@ -82,11 +86,24 @@
compat_uptr_t vaddrout; /* dsps virtual address */
};
+struct compat_fastrpc_ioctl_mmap_64 {
+ compat_int_t fd; /* ion fd */
+ compat_uint_t flags; /* flags for dsp to map with */
+ compat_u64 vaddrin; /* optional virtual address */
+ compat_size_t size; /* size */
+ compat_u64 vaddrout; /* dsps virtual address */
+};
+
struct compat_fastrpc_ioctl_munmap {
compat_uptr_t vaddrout; /* address to unmap */
compat_size_t size; /* size */
};
+struct compat_fastrpc_ioctl_munmap_64 {
+ compat_u64 vaddrout; /* address to unmap */
+ compat_size_t size; /* size */
+};
+
struct compat_fastrpc_ioctl_init {
compat_uint_t flags; /* one of FASTRPC_INIT_* macros */
compat_uptr_t file; /* pointer to elf file */
@@ -206,6 +223,28 @@
return err;
}
+static int compat_get_fastrpc_ioctl_mmap_64(
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32,
+ struct fastrpc_ioctl_mmap __user *map)
+{
+ compat_uint_t u;
+ compat_int_t i;
+ compat_size_t s;
+ compat_u64 p;
+ int err;
+
+ err = get_user(i, &map32->fd);
+ err |= put_user(i, &map->fd);
+ err |= get_user(u, &map32->flags);
+ err |= put_user(u, &map->flags);
+ err |= get_user(p, &map32->vaddrin);
+ err |= put_user(p, &map->vaddrin);
+ err |= get_user(s, &map32->size);
+ err |= put_user(s, &map->size);
+
+ return err;
+}
+
static int compat_put_fastrpc_ioctl_mmap(
struct compat_fastrpc_ioctl_mmap __user *map32,
struct fastrpc_ioctl_mmap __user *map)
@@ -219,6 +258,19 @@
return err;
}
+static int compat_put_fastrpc_ioctl_mmap_64(
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32,
+ struct fastrpc_ioctl_mmap __user *map)
+{
+ compat_u64 p;
+ int err;
+
+ err = get_user(p, &map->vaddrout);
+ err |= put_user(p, &map32->vaddrout);
+
+ return err;
+}
+
static int compat_get_fastrpc_ioctl_munmap(
struct compat_fastrpc_ioctl_munmap __user *unmap32,
struct fastrpc_ioctl_munmap __user *unmap)
@@ -235,6 +287,22 @@
return err;
}
+static int compat_get_fastrpc_ioctl_munmap_64(
+ struct compat_fastrpc_ioctl_munmap_64 __user *unmap32,
+ struct fastrpc_ioctl_munmap __user *unmap)
+{
+ compat_u64 p;
+ compat_size_t s;
+ int err;
+
+ err = get_user(p, &unmap32->vaddrout);
+ err |= put_user(p, &unmap->vaddrout);
+ err |= get_user(s, &unmap32->size);
+ err |= put_user(s, &unmap->size);
+
+ return err;
+}
+
static int compat_get_fastrpc_ioctl_perf(
struct compat_fastrpc_ioctl_perf __user *perf32,
struct fastrpc_ioctl_perf __user *perf)
@@ -355,6 +423,27 @@
VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap(map32, map));
return err;
}
+ case COMPAT_FASTRPC_IOCTL_MMAP_64:
+ {
+ struct compat_fastrpc_ioctl_mmap_64 __user *map32;
+ struct fastrpc_ioctl_mmap __user *map;
+ long ret;
+
+ map32 = compat_ptr(arg);
+ VERIFY(err, NULL != (map = compat_alloc_user_space(
+ sizeof(*map))));
+ if (err)
+ return -EFAULT;
+ VERIFY(err, 0 == compat_get_fastrpc_ioctl_mmap_64(map32, map));
+ if (err)
+ return err;
+ ret = filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MMAP_64,
+ (unsigned long)map);
+ if (ret)
+ return ret;
+ VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap_64(map32, map));
+ return err;
+ }
case COMPAT_FASTRPC_IOCTL_MUNMAP:
{
struct compat_fastrpc_ioctl_munmap __user *unmap32;
@@ -372,6 +461,23 @@
return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP,
(unsigned long)unmap);
}
+ case COMPAT_FASTRPC_IOCTL_MUNMAP_64:
+ {
+ struct compat_fastrpc_ioctl_munmap_64 __user *unmap32;
+ struct fastrpc_ioctl_munmap __user *unmap;
+
+ unmap32 = compat_ptr(arg);
+ VERIFY(err, NULL != (unmap = compat_alloc_user_space(
+ sizeof(*unmap))));
+ if (err)
+ return -EFAULT;
+ VERIFY(err, 0 == compat_get_fastrpc_ioctl_munmap_64(unmap32,
+ unmap));
+ if (err)
+ return err;
+ return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP_64,
+ (unsigned long)unmap);
+ }
case COMPAT_FASTRPC_IOCTL_INIT:
/* fall through */
case COMPAT_FASTRPC_IOCTL_INIT_ATTRS:
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index de0dd01..952b87c 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -19,6 +19,8 @@
#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke)
#define FASTRPC_IOCTL_MMAP _IOWR('R', 2, struct fastrpc_ioctl_mmap)
#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 3, struct fastrpc_ioctl_munmap)
+#define FASTRPC_IOCTL_MMAP_64 _IOWR('R', 14, struct fastrpc_ioctl_mmap_64)
+#define FASTRPC_IOCTL_MUNMAP_64 _IOWR('R', 15, struct fastrpc_ioctl_munmap_64)
#define FASTRPC_IOCTL_INVOKE_FD _IOWR('R', 4, struct fastrpc_ioctl_invoke_fd)
#define FASTRPC_IOCTL_SETMODE _IOWR('R', 5, uint32_t)
#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init)
@@ -204,6 +206,11 @@
size_t size; /* size */
};
+struct fastrpc_ioctl_munmap_64 {
+ uint64_t vaddrout; /* address to unmap */
+ size_t size; /* size */
+};
+
struct fastrpc_ioctl_mmap {
int fd; /* ion fd */
uint32_t flags; /* flags for dsp to map with */
@@ -212,6 +219,14 @@
uintptr_t vaddrout; /* dsps virtual address */
};
+struct fastrpc_ioctl_mmap_64 {
+ int fd; /* ion fd */
+ uint32_t flags; /* flags for dsp to map with */
+ uint64_t vaddrin; /* optional virtual address */
+ size_t size; /* size */
+ uint64_t vaddrout; /* dsps virtual address */
+};
+
struct fastrpc_ioctl_munmap_fd {
int fd; /* fd */
uint32_t flags; /* control flags */
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 0f7d28a..a7cc5b7 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -871,6 +871,8 @@
}
}
wmb();
+ if (intel_private.driver->chipset_flush)
+ intel_private.driver->chipset_flush();
}
EXPORT_SYMBOL(intel_gtt_insert_sg_entries);
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 286418f..a089e7c 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -2713,7 +2713,7 @@
}
}
-static int diag_dci_init_remote(void)
+int diag_dci_init_remote(void)
{
int i;
struct dci_ops_tbl_t *temp = NULL;
@@ -2740,11 +2740,6 @@
return 0;
}
-#else
-static int diag_dci_init_remote(void)
-{
- return 0;
-}
#endif
static int diag_dci_init_ops_tbl(void)
@@ -2754,10 +2749,6 @@
err = diag_dci_init_local();
if (err)
goto err;
- err = diag_dci_init_remote();
- if (err)
- goto err;
-
return 0;
err:
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 61eb3f5..2fb0e3f 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -323,6 +323,7 @@
int diag_dci_write_bridge(int token, unsigned char *buf, int len);
int diag_dci_write_done_bridge(int index, unsigned char *buf, int len);
int diag_dci_send_handshake_pkt(int index);
+int diag_dci_init_remote(void);
#endif
#endif
diff --git a/drivers/char/diag/diag_ipc_logging.h b/drivers/char/diag/diag_ipc_logging.h
index b9958a4..4b8dd1b 100644
--- a/drivers/char/diag/diag_ipc_logging.h
+++ b/drivers/char/diag/diag_ipc_logging.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
#define DIAG_DEBUG_MASKS 0x0010
#define DIAG_DEBUG_POWER 0x0020
#define DIAG_DEBUG_BRIDGE 0x0040
+#define DIAG_DEBUG_CONTROL 0x0080
#define DIAG_DEBUG
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index b7d56a9..c20dd2e 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -2189,9 +2189,12 @@
if (driver->time_sync_enabled)
diag_send_time_sync_update(peripheral);
mutex_lock(&driver->md_session_lock);
- diag_send_msg_mask_update(peripheral, ALL_SSID, ALL_SSID);
- diag_send_log_mask_update(peripheral, ALL_EQUIP_ID);
- diag_send_event_mask_update(peripheral);
+ if (driver->set_mask_cmd) {
+ diag_send_msg_mask_update(peripheral,
+ ALL_SSID, ALL_SSID);
+ diag_send_log_mask_update(peripheral, ALL_EQUIP_ID);
+ diag_send_event_mask_update(peripheral);
+ }
mutex_unlock(&driver->md_session_lock);
diag_send_real_time_update(peripheral,
driver->real_time_mode[DIAG_LOCAL_PROC]);
@@ -2228,6 +2231,7 @@
break;
case DIAG_CMD_OP_SET_LOG_MASK:
hdlr = diag_cmd_set_log_mask;
+ driver->set_mask_cmd = 1;
break;
case DIAG_CMD_OP_GET_LOG_MASK:
hdlr = diag_cmd_get_log_mask;
@@ -2247,17 +2251,21 @@
break;
case DIAG_CMD_OP_SET_MSG_MASK:
hdlr = diag_cmd_set_msg_mask;
+ driver->set_mask_cmd = 1;
break;
case DIAG_CMD_OP_SET_ALL_MSG_MASK:
hdlr = diag_cmd_set_all_msg_mask;
+ driver->set_mask_cmd = 1;
break;
}
} else if (*buf == DIAG_CMD_GET_EVENT_MASK) {
hdlr = diag_cmd_get_event_mask;
} else if (*buf == DIAG_CMD_SET_EVENT_MASK) {
hdlr = diag_cmd_update_event_mask;
+ driver->set_mask_cmd = 1;
} else if (*buf == DIAG_CMD_EVENT_TOGGLE) {
hdlr = diag_cmd_toggle_events;
+ driver->set_mask_cmd = 1;
}
if (hdlr)
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index ce0c7bb..c00fbfc 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -37,6 +37,7 @@
.ctx = 0,
.mempool = POOL_TYPE_MUX_APPS,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
},
@@ -46,6 +47,7 @@
.ctx = 0,
.mempool = POOL_TYPE_MDM_MUX,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
},
@@ -54,6 +56,7 @@
.ctx = 0,
.mempool = POOL_TYPE_MDM2_MUX,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
},
@@ -62,6 +65,7 @@
.ctx = 0,
.mempool = POOL_TYPE_QSC_MUX,
.num_tbl_entries = 0,
+ .md_info_inited = 0,
.tbl = NULL,
.ops = NULL,
}
@@ -85,6 +89,8 @@
for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
ch = &diag_md[i];
+ if (!ch->md_info_inited)
+ continue;
if (ch->ops && ch->ops->open)
ch->ops->open(ch->ctx, DIAG_MEMORY_DEVICE_MODE);
}
@@ -99,6 +105,8 @@
for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
ch = &diag_md[i];
+ if (!ch->md_info_inited)
+ continue;
if (ch->ops && ch->ops->close)
ch->ops->close(ch->ctx, DIAG_MEMORY_DEVICE_MODE);
@@ -155,7 +163,7 @@
mutex_unlock(&driver->md_session_lock);
ch = &diag_md[id];
- if (!ch)
+ if (!ch || !ch->md_info_inited)
return -EINVAL;
spin_lock_irqsave(&ch->lock, flags);
@@ -232,6 +240,8 @@
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
+ if (!ch->md_info_inited)
+ continue;
for (j = 0; j < ch->num_tbl_entries && !err; j++) {
entry = &ch->tbl[j];
if (entry->len <= 0 || entry->buf == NULL)
@@ -352,6 +362,8 @@
return -EINVAL;
ch = &diag_md[id];
+ if (!ch || !ch->md_info_inited)
+ return -EINVAL;
spin_lock_irqsave(&ch->lock, flags);
for (i = 0; i < ch->num_tbl_entries && !found; i++) {
@@ -384,7 +396,7 @@
int i, j;
struct diag_md_info *ch = NULL;
- for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
+ for (i = 0; i < DIAG_MD_LOCAL_LAST; i++) {
ch = &diag_md[i];
ch->num_tbl_entries = diag_mempools[ch->mempool].poolsize;
ch->tbl = kzalloc(ch->num_tbl_entries *
@@ -399,6 +411,7 @@
ch->tbl[j].ctx = 0;
}
spin_lock_init(&(ch->lock));
+ ch->md_info_inited = 1;
}
return 0;
@@ -408,12 +421,54 @@
return -ENOMEM;
}
+int diag_md_mdm_init(void)
+{
+ int i, j;
+ struct diag_md_info *ch = NULL;
+
+ for (i = DIAG_MD_BRIDGE_BASE; i < NUM_DIAG_MD_DEV; i++) {
+ ch = &diag_md[i];
+ ch->num_tbl_entries = diag_mempools[ch->mempool].poolsize;
+ ch->tbl = kcalloc(ch->num_tbl_entries, sizeof(*ch->tbl),
+ GFP_KERNEL);
+ if (!ch->tbl)
+ goto fail;
+
+ for (j = 0; j < ch->num_tbl_entries; j++) {
+ ch->tbl[j].buf = NULL;
+ ch->tbl[j].len = 0;
+ ch->tbl[j].ctx = 0;
+ }
+ spin_lock_init(&(ch->lock));
+ ch->md_info_inited = 1;
+ }
+
+ return 0;
+
+fail:
+ diag_md_mdm_exit();
+ return -ENOMEM;
+}
+
void diag_md_exit(void)
{
int i;
struct diag_md_info *ch = NULL;
- for (i = 0; i < NUM_DIAG_MD_DEV; i++) {
+ for (i = 0; i < DIAG_MD_LOCAL_LAST; i++) {
+ ch = &diag_md[i];
+ kfree(ch->tbl);
+ ch->num_tbl_entries = 0;
+ ch->ops = NULL;
+ }
+}
+
+void diag_md_mdm_exit(void)
+{
+ int i;
+ struct diag_md_info *ch = NULL;
+
+ for (i = DIAG_MD_BRIDGE_BASE; i < NUM_DIAG_MD_DEV; i++) {
ch = &diag_md[i];
kfree(ch->tbl);
ch->num_tbl_entries = 0;
diff --git a/drivers/char/diag/diag_memorydevice.h b/drivers/char/diag/diag_memorydevice.h
index 35a1ee3..4d65ded 100644
--- a/drivers/char/diag/diag_memorydevice.h
+++ b/drivers/char/diag/diag_memorydevice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -38,6 +38,7 @@
int ctx;
int mempool;
int num_tbl_entries;
+ int md_info_inited;
spinlock_t lock;
struct diag_buf_tbl_t *tbl;
struct diag_mux_ops *ops;
@@ -46,7 +47,9 @@
extern struct diag_md_info diag_md[NUM_DIAG_MD_DEV];
int diag_md_init(void);
+int diag_md_mdm_init(void);
void diag_md_exit(void);
+void diag_md_mdm_exit(void);
void diag_md_open_all(void);
void diag_md_close_all(void);
int diag_md_register(int id, int ctx, struct diag_mux_ops *ops);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index fc18776..9bbfb82 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -673,6 +673,7 @@
struct diag_mask_info *log_mask;
struct diag_mask_info *event_mask;
struct diag_mask_info *build_time_mask;
+ uint8_t set_mask_cmd;
uint8_t msg_mask_tbl_count;
uint8_t bt_msg_mask_tbl_count;
uint16_t event_mask_size;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 0d3389a..a169230 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -969,6 +969,9 @@
poolsize_mdm_dci_write);
diagmem_setsize(POOL_TYPE_QSC_MUX, itemsize_qsc_usb,
poolsize_qsc_usb);
+ diag_md_mdm_init();
+ if (diag_dci_init_remote())
+ return -ENOMEM;
driver->hdlc_encode_buf = kzalloc(DIAG_MAX_HDLC_BUF_SIZE, GFP_KERNEL);
if (!driver->hdlc_encode_buf)
return -ENOMEM;
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 8d47ee38..6f81bfd 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -45,8 +45,11 @@
void diag_cntl_channel_open(struct diagfwd_info *p_info)
{
- if (!p_info)
+ if (!p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid fwd_info structure\n");
return;
+ }
driver->mask_update |= PERIPHERAL_MASK(p_info->peripheral);
queue_work(driver->cntl_wq, &driver->mask_update_work);
diag_notify_md_client(p_info->peripheral, DIAG_STATUS_OPEN);
@@ -56,12 +59,18 @@
{
uint8_t peripheral;
- if (!p_info)
+ if (!p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid fwd_info structure\n");
return;
+ }
peripheral = p_info->peripheral;
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
driver->feature[peripheral].sent_feature_mask = 0;
driver->feature[peripheral].rcvd_feature_mask = 0;
@@ -87,8 +96,11 @@
driver->stm_peripheral = 0;
mutex_unlock(&driver->cntl_lock);
- if (peripheral_mask == 0)
+ if (peripheral_mask == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Empty Peripheral mask\n");
return;
+ }
for (i = 0; i < NUM_PERIPHERALS; i++) {
if (!driver->feature[i].stm_support)
@@ -111,11 +123,18 @@
struct pid *pid_struct;
struct task_struct *result;
- if (peripheral > NUM_PERIPHERALS)
+ if (peripheral > NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
- if (driver->logging_mode != DIAG_MEMORY_DEVICE_MODE)
+ if (driver->logging_mode != DIAG_MEMORY_DEVICE_MODE) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid logging_mode (%d)\n",
+ driver->logging_mode);
return;
+ }
mutex_lock(&driver->md_session_lock);
memset(&info, 0, sizeof(struct siginfo));
@@ -171,8 +190,12 @@
uint32_t pd;
int status = DIAG_STATUS_CLOSED;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < sizeof(*pd_msg))
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < sizeof(*pd_msg)) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, pd_msg_len = %d\n",
+ !buf, peripheral, len, (int)sizeof(*pd_msg));
return;
+ }
pd_msg = (struct diag_ctrl_msg_pd_status *)buf;
pd = pd_msg->pd_id;
@@ -182,8 +205,11 @@
static void enable_stm_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
mutex_lock(&driver->cntl_lock);
driver->feature[peripheral].stm_support = ENABLE_STM;
@@ -195,8 +221,11 @@
static void enable_socket_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_sockets)
driver->feature[peripheral].sockets_enabled = 1;
@@ -206,8 +235,11 @@
static void process_hdlc_encoding_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_apps_hdlc_encoding) {
driver->feature[peripheral].encode_hdlc =
@@ -220,8 +252,11 @@
static void process_upd_header_untagging_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
if (driver->supports_apps_header_untagging) {
driver->feature[peripheral].untag_header =
@@ -247,8 +282,16 @@
* Perform Basic sanity. The len field is the size of the data payload.
* This doesn't include the header size.
*/
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral(%d) command deregistration packet processing started\n",
+ peripheral);
dereg = (struct diag_ctrl_cmd_dereg *)ptr;
ptr += header_len;
@@ -256,8 +299,8 @@
read_len += header_len - (2 * sizeof(uint32_t));
if (dereg->count_entries == 0) {
- pr_debug("diag: In %s, received reg tbl with no entries\n",
- __func__);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: received reg tbl with no entries\n");
return;
}
@@ -276,6 +319,9 @@
pr_err("diag: In %s, reading less than available, read_len: %d, len: %d count: %d\n",
__func__, read_len, len, dereg->count_entries);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral(%d) command deregistration packet processing complete\n",
+ peripheral);
}
static void process_command_registration(uint8_t *buf, uint32_t len,
uint8_t peripheral)
@@ -292,8 +338,15 @@
* Perform Basic sanity. The len field is the size of the data payload.
* This doesn't include the header size.
*/
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: peripheral(%d) command registration packet processing started\n",
+ peripheral);
reg = (struct diag_ctrl_cmd_reg *)ptr;
ptr += header_len;
@@ -301,7 +354,8 @@
read_len += header_len - (2 * sizeof(uint32_t));
if (reg->count_entries == 0) {
- pr_debug("diag: In %s, received reg tbl with no entries\n",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: In %s, received reg tbl with no entries\n",
__func__);
return;
}
@@ -321,6 +375,9 @@
pr_err("diag: In %s, reading less than available, read_len: %d, len: %d count: %d\n",
__func__, read_len, len, reg->count_entries);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: peripheral(%d) command registration packet processing complete\n",
+ peripheral);
}
static void diag_close_transport_work_fn(struct work_struct *work)
@@ -347,8 +404,11 @@
static void process_socket_feature(uint8_t peripheral)
{
- if (peripheral >= NUM_PERIPHERALS)
+ if (peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid peripheral (%d)\n", peripheral);
return;
+ }
mutex_lock(&driver->cntl_lock);
driver->close_transport |= PERIPHERAL_MASK(peripheral);
@@ -379,15 +439,20 @@
uint32_t feature_mask = 0;
uint8_t *ptr = buf;
- if (!buf || peripheral >= NUM_PERIPHERALS || len == 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
header = (struct diag_ctrl_feature_mask *)ptr;
ptr += header_len;
feature_mask_len = header->feature_mask_len;
if (feature_mask_len == 0) {
- pr_debug("diag: In %s, received invalid feature mask from peripheral %d\n",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: In %s, received invalid feature mask from peripheral %d\n",
__func__, peripheral);
return;
}
@@ -400,6 +465,8 @@
diag_cmd_remove_reg_by_proc(peripheral);
driver->feature[peripheral].rcvd_feature_mask = 1;
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Received feature mask for peripheral %d\n", peripheral);
for (i = 0; i < feature_mask_len && read_len < len; i++) {
feature_mask = *(uint8_t *)ptr;
@@ -431,6 +498,10 @@
process_socket_feature(peripheral);
process_log_on_demand_feature(peripheral);
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Peripheral(%d) feature mask is processed\n",
+ peripheral);
}
static void process_last_event_report(uint8_t *buf, uint32_t len,
@@ -442,14 +513,23 @@
uint32_t pkt_len = sizeof(uint32_t) + sizeof(uint16_t);
uint16_t event_size = 0;
- if (!buf || peripheral >= NUM_PERIPHERALS || len != pkt_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len != pkt_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, pkt_len = %d\n",
+ !buf, peripheral, len, pkt_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:started processing last event report for peripheral (%d)\n",
+ peripheral);
mutex_lock(&event_mask.lock);
header = (struct diag_ctrl_last_event_report *)ptr;
event_size = ((header->event_last_id / 8) + 1);
if (event_size >= driver->event_mask_size) {
- pr_debug("diag: In %s, receiving event mask size more that Apps can handle\n",
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: In %s, receiving event mask size more that Apps can handle\n",
__func__);
temp = krealloc(driver->event_mask->ptr, event_size,
GFP_KERNEL);
@@ -467,6 +547,9 @@
driver->last_event_id = header->event_last_id;
err:
mutex_unlock(&event_mask.lock);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: last event report processed for peripheral (%d)\n",
+ peripheral);
}
static void process_log_range_report(uint8_t *buf, uint32_t len,
@@ -480,8 +563,15 @@
struct diag_ctrl_log_range *log_range = NULL;
struct diag_log_mask_t *mask_ptr = NULL;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < 0)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d\n",
+ !buf, peripheral, len);
return;
+ }
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:started processing log range report for peripheral(%d)\n",
+ peripheral);
header = (struct diag_ctrl_log_range_report *)ptr;
ptr += header_len;
@@ -507,6 +597,9 @@
mask_ptr->range = LOG_ITEMS_TO_SIZE(log_range->num_items);
mutex_unlock(&(mask_ptr->lock));
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: log range report processed for peripheral (%d)\n",
+ peripheral);
}
static int update_msg_mask_tbl_entry(struct diag_msg_mask_t *mask,
@@ -514,8 +607,12 @@
{
uint32_t temp_range;
- if (!mask || !range)
+ if (!mask || !range) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid %s\n",
+ (!mask ? "mask" : (!range ? "range" : " ")));
return -EIO;
+ }
if (range->ssid_last < range->ssid_first) {
pr_err("diag: In %s, invalid ssid range, first: %d, last: %d\n",
__func__, range->ssid_first, range->ssid_last);
@@ -547,8 +644,16 @@
uint8_t *temp = NULL;
uint32_t min_len = header_len - sizeof(struct diag_ctrl_pkt_header_t);
- if (!buf || peripheral >= NUM_PERIPHERALS || len < min_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < min_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, min_len = %d\n",
+ !buf, peripheral, len, min_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: started processing ssid range for peripheral (%d)\n",
+ peripheral);
header = (struct diag_ctrl_ssid_range_report *)ptr;
ptr += header_len;
@@ -600,6 +705,9 @@
driver->msg_mask_tbl_count += 1;
}
mutex_unlock(&driver->msg_mask_lock);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: processed ssid range for peripheral(%d)\n",
+ peripheral);
}
static void diag_build_time_mask_update(uint8_t *buf,
@@ -616,8 +724,12 @@
uint32_t *dest_ptr = NULL;
struct diag_msg_mask_t *build_mask = NULL;
- if (!range || !buf)
+ if (!range || !buf) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid %s\n",
+ (!range ? "range" : (!buf ? "buf" : " ")));
return;
+ }
if (range->ssid_last < range->ssid_first) {
pr_err("diag: In %s, invalid ssid range, first: %d, last: %d\n",
@@ -679,8 +791,16 @@
struct diag_ctrl_build_mask_report *header = NULL;
struct diag_ssid_range_t *range = NULL;
- if (!buf || peripheral >= NUM_PERIPHERALS || len < header_len)
+ if (!buf || peripheral >= NUM_PERIPHERALS || len < header_len) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters:(!buf) = %d, peripheral = %d, len = %d, header_len = %d\n",
+ !buf, peripheral, len, header_len);
return;
+ }
+
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: started processing build mask for peripheral(%d)\n",
+ peripheral);
header = (struct diag_ctrl_build_mask_report *)ptr;
ptr += header_len;
@@ -696,6 +816,8 @@
ptr += num_items * sizeof(uint32_t);
read_len += num_items * sizeof(uint32_t);
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: processing build mask complete (%d)\n", peripheral);
}
int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name,
@@ -703,8 +825,12 @@
{
struct diag_id_tbl_t *new_item = NULL;
- if (!process_name || diag_id == 0)
+ if (!process_name || diag_id == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters: !process_name = %d, diag_id = %d\n",
+ !process_name, diag_id);
return -EINVAL;
+ }
new_item = kzalloc(sizeof(struct diag_id_tbl_t), GFP_KERNEL);
if (!new_item)
@@ -734,8 +860,10 @@
struct list_head *temp;
struct diag_id_tbl_t *item = NULL;
- if (!process_name || !diag_id)
+ if (!process_name || !diag_id) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid parameters\n");
return -EINVAL;
+ }
mutex_lock(&driver->diag_id_mutex);
list_for_each_safe(start, temp, &driver->diag_id_list) {
@@ -762,8 +890,12 @@
uint8_t local_diag_id = 0;
uint8_t new_request = 0, i = 0, ch_type = 0;
- if (!buf || len == 0 || peripheral >= NUM_PERIPHERALS)
+ if (!buf || len == 0 || peripheral >= NUM_PERIPHERALS) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: Invalid parameters: !buf = %d, len = %d, peripheral = %d\n",
+ !buf, len, peripheral);
return;
+ }
header = (struct diag_ctrl_diagid *)buf;
process_name = (char *)&header->process_name;
@@ -841,7 +973,7 @@
fwd_info = &peripheral_info[TYPE_DATA][peripheral];
diagfwd_buffers_init(fwd_info);
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
- "diag: diag_id sent = %d to peripheral = %d with diag_id = %d for %s :\n",
+ "diag: diag_id sent = %d to peripheral = %d with diag_id = %d for %s\n",
driver->diag_id_sent[peripheral], peripheral,
ctrl_pkt.diag_id, process_name);
}
@@ -855,8 +987,10 @@
uint8_t *ptr = buf;
struct diag_ctrl_pkt_header_t *ctrl_pkt = NULL;
- if (!buf || len <= 0 || !p_info)
+ if (!buf || len <= 0 || !p_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid parameters\n");
return;
+ }
if (reg_dirty & PERIPHERAL_MASK(p_info->peripheral)) {
pr_err_ratelimited("diag: dropping command registration from peripheral %d\n",
@@ -866,6 +1000,9 @@
while (read_len + header_len < len) {
ctrl_pkt = (struct diag_ctrl_pkt_header_t *)ptr;
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag:peripheral: %d: pkt_id: %d\n",
+ p_info->peripheral, ctrl_pkt->pkt_id);
switch (ctrl_pkt->pkt_id) {
case DIAG_CTRL_MSG_REG:
process_command_registration(ptr, ctrl_pkt->len,
@@ -904,12 +1041,15 @@
p_info->peripheral);
break;
default:
- pr_debug("diag: Control packet %d not supported\n",
- ctrl_pkt->pkt_id);
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: Control packet %d not supported\n",
+ ctrl_pkt->pkt_id);
}
ptr += header_len + ctrl_pkt->len;
read_len += header_len + ctrl_pkt->len;
}
+ DIAG_LOG(DIAG_DEBUG_CONTROL,
+ "diag: control packet processing complete\n");
}
static int diag_compute_real_time(int idx)
@@ -1127,15 +1267,16 @@
for (i = 0; i < DIAG_NUM_PROC; i++) {
temp_real_time = diag_compute_real_time(i);
if (temp_real_time == driver->real_time_mode[i]) {
- pr_debug("diag: did not update real time mode on proc %d, already in the req mode %d",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: did not update real time mode on proc %d, already in the req mode %d\n",
i, temp_real_time);
continue;
}
if (i == DIAG_LOCAL_PROC) {
if (!send_update) {
- pr_debug("diag: In %s, cannot send real time mode pkt since one of the periperhal is in buffering mode\n",
- __func__);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: cannot send real time mode pkt since one of the periperhal is in buffering mode\n");
break;
}
for (j = 0; j < NUM_PERIPHERALS; j++)
@@ -1169,7 +1310,8 @@
temp_real_time = MODE_NONREALTIME;
}
if (temp_real_time == driver->real_time_mode[i]) {
- pr_debug("diag: did not update real time mode on proc %d, already in the req mode %d",
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: did not update real time mode on proc %d, already in the req mode %d\n",
i, temp_real_time);
continue;
}
@@ -1204,8 +1346,8 @@
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n", peripheral);
return err;
}
@@ -1317,8 +1459,9 @@
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
driver->buffering_flag[params->peripheral] = 0;
return -EIO;
}
@@ -1383,8 +1526,9 @@
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
@@ -1413,15 +1557,17 @@
struct diag_ctrl_drain_immediate_v2 ctrl_pkt_v2;
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
@@ -1478,8 +1624,9 @@
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
@@ -1557,15 +1704,17 @@
}
if (!driver->feature[peripheral].peripheral_buffering) {
- pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: peripheral %d doesn't support buffering\n",
+ peripheral);
return -EINVAL;
}
if (!driver->diagfwd_cntl[peripheral] ||
!driver->diagfwd_cntl[peripheral]->ch_open) {
- pr_debug("diag: In %s, control channel is not open, p: %d\n",
- __func__, peripheral);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: control channel is not open, p: %d\n",
+ peripheral);
return -ENODEV;
}
diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index f8c3fde..6f41868 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -434,9 +434,10 @@
do {
if (!(atomic_read(&(read_ch->opened))))
break;
-
+ spin_lock_irqsave(&read_ch->lock, flags);
buf = diagmem_alloc(driver, DIAG_MDM_BUF_SIZE,
mhi_info->mempool);
+ spin_unlock_irqrestore(&read_ch->lock, flags);
if (!buf)
break;
@@ -743,4 +744,3 @@
diagmem_exit(driver, mhi_info->mempool);
}
}
-
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 7225dc2..2022e7b 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -723,6 +723,7 @@
unsigned char *buf, int len)
{
if (!fwd_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid fwd_info\n");
diag_ws_release();
return;
}
@@ -743,8 +744,12 @@
*/
diag_ws_on_copy_fail(DIAG_WS_MUX);
/* Reset the buffer in_busy value after processing the data */
- if (fwd_info->buf_1)
+ if (fwd_info->buf_1) {
atomic_set(&fwd_info->buf_1->in_busy, 0);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
+ }
diagfwd_queue_read(fwd_info);
diagfwd_queue_read(&peripheral_info[TYPE_DATA][fwd_info->peripheral]);
@@ -769,8 +774,12 @@
diag_dci_process_peripheral_data(fwd_info, (void *)buf, len);
/* Reset the buffer in_busy value after processing the data */
- if (fwd_info->buf_1)
+ if (fwd_info->buf_1) {
atomic_set(&fwd_info->buf_1->in_busy, 0);
+ DIAG_LOG(DIAG_DEBUG_DCI,
+ "Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
+ }
diagfwd_queue_read(fwd_info);
}
@@ -1638,13 +1647,15 @@
struct diagfwd_buf_t *temp_buf = NULL;
if (!fwd_info) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: Invalid fwd_info\n");
diag_ws_release();
return;
}
if (!fwd_info->inited || !atomic_read(&fwd_info->opened)) {
- pr_debug("diag: In %s, p: %d, t: %d, inited: %d, opened: %d ch_open: %d\n",
- __func__, fwd_info->peripheral, fwd_info->type,
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: p: %d, t: %d, inited: %d, opened: %d, ch_open: %d\n",
+ fwd_info->peripheral, fwd_info->type,
fwd_info->inited, atomic_read(&fwd_info->opened),
fwd_info->ch_open);
diag_ws_release();
@@ -1680,8 +1691,9 @@
atomic_set(&temp_buf->in_busy, 1);
}
} else {
- pr_debug("diag: In %s, both buffers are empty for p: %d, t: %d\n",
- __func__, fwd_info->peripheral, fwd_info->type);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: both buffers are busy for p: %d, t: %d\n",
+ fwd_info->peripheral, fwd_info->type);
}
if (!read_buf) {
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 9f2e3be..676c910 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -66,7 +66,7 @@
/* Holds the old poweroff function so we can restore it on removal. */
static void (*old_poweroff_func)(void);
-static int set_param_ifnum(const char *val, struct kernel_param *kp)
+static int set_param_ifnum(const char *val, const struct kernel_param *kp)
{
int rv = param_set_int(val, kp);
if (rv)
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index e0a5315..89adeb4 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -1344,7 +1344,7 @@
#define IPMI_MEM_ADDR_SPACE 1
static const char * const addr_space_to_str[] = { "i/o", "mem" };
-static int hotmod_handler(const char *val, struct kernel_param *kp);
+static int hotmod_handler(const char *val, const struct kernel_param *kp);
module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
@@ -1814,7 +1814,7 @@
return info;
}
-static int hotmod_handler(const char *val, struct kernel_param *kp)
+static int hotmod_handler(const char *val, const struct kernel_param *kp)
{
char *str = kstrdup(val, GFP_KERNEL);
int rv;
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 510fc10..f11c1c7 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -409,6 +409,7 @@
msg = ipmi_alloc_smi_msg();
if (!msg) {
ssif_info->ssif_state = SSIF_NORMAL;
+ ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -431,6 +432,7 @@
msg = ipmi_alloc_smi_msg();
if (!msg) {
ssif_info->ssif_state = SSIF_NORMAL;
+ ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 9093110..055d2ce 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -515,7 +515,7 @@
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
- atomic_add(2, &panic_done_count);
+ atomic_add(1, &panic_done_count);
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
@@ -525,7 +525,7 @@
&panic_halt_heartbeat_recv_msg,
1);
if (rv)
- atomic_sub(2, &panic_done_count);
+ atomic_sub(1, &panic_done_count);
}
static struct ipmi_smi_msg panic_halt_smi_msg = {
@@ -549,12 +549,12 @@
/* Wait for the messages to be free. */
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
- atomic_add(2, &panic_done_count);
+ atomic_add(1, &panic_done_count);
rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
&panic_halt_recv_msg,
&send_heartbeat_now);
if (rv) {
- atomic_sub(2, &panic_done_count);
+ atomic_sub(1, &panic_done_count);
printk(KERN_WARNING PFX
"Unable to extend the watchdog timeout.");
} else {
diff --git a/drivers/char/random.c b/drivers/char/random.c
index ee737ef..1b3c731 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -259,6 +259,7 @@
#include <linux/kmemcheck.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
+#include <linux/ratelimit.h>
#include <linux/syscalls.h>
#include <linux/completion.h>
#include <linux/uuid.h>
@@ -434,8 +435,9 @@
* its value (from 0->1->2).
*/
static int crng_init = 0;
-#define crng_ready() (likely(crng_init > 0))
+#define crng_ready() (likely(crng_init > 1))
static int crng_init_cnt = 0;
+static unsigned long crng_global_init_time = 0;
#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE)
static void _extract_crng(struct crng_state *crng,
__u8 out[CHACHA20_BLOCK_SIZE]);
@@ -443,6 +445,16 @@
__u8 tmp[CHACHA20_BLOCK_SIZE], int used);
static void process_random_ready_list(void);
+static struct ratelimit_state unseeded_warning =
+ RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3);
+static struct ratelimit_state urandom_warning =
+ RATELIMIT_STATE_INIT("warn_urandom_randomness", HZ, 3);
+
+static int ratelimit_disable __read_mostly;
+
+module_param_named(ratelimit_disable, ratelimit_disable, int, 0644);
+MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
+
/**********************************************************************
*
* OS independent entropy store. Here are the functions which handle
@@ -741,7 +753,7 @@
static int credit_entropy_bits_safe(struct entropy_store *r, int nbits)
{
- const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1));
+ const int nbits_max = r->poolinfo->poolwords * 32;
if (nbits < 0)
return -EINVAL;
@@ -800,7 +812,7 @@
if (!spin_trylock_irqsave(&primary_crng.lock, flags))
return 0;
- if (crng_ready()) {
+ if (crng_init != 0) {
spin_unlock_irqrestore(&primary_crng.lock, flags);
return 0;
}
@@ -818,6 +830,39 @@
return 1;
}
+#ifdef CONFIG_NUMA
+static void do_numa_crng_init(struct work_struct *work)
+{
+ int i;
+ struct crng_state *crng;
+ struct crng_state **pool;
+
+ pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL);
+ for_each_online_node(i) {
+ crng = kmalloc_node(sizeof(struct crng_state),
+ GFP_KERNEL | __GFP_NOFAIL, i);
+ spin_lock_init(&crng->lock);
+ crng_initialize(crng);
+ pool[i] = crng;
+ }
+ mb();
+ if (cmpxchg(&crng_node_pool, NULL, pool)) {
+ for_each_node(i)
+ kfree(pool[i]);
+ kfree(pool);
+ }
+}
+
+static DECLARE_WORK(numa_crng_init_work, do_numa_crng_init);
+
+static void numa_crng_init(void)
+{
+ schedule_work(&numa_crng_init_work);
+}
+#else
+static void numa_crng_init(void) {}
+#endif
+
static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
{
unsigned long flags;
@@ -836,7 +881,7 @@
_crng_backtrack_protect(&primary_crng, buf.block,
CHACHA20_KEY_SIZE);
}
- spin_lock_irqsave(&primary_crng.lock, flags);
+ spin_lock_irqsave(&crng->lock, flags);
for (i = 0; i < 8; i++) {
unsigned long rv;
if (!arch_get_random_seed_long(&rv) &&
@@ -847,12 +892,25 @@
memzero_explicit(&buf, sizeof(buf));
crng->init_time = jiffies;
if (crng == &primary_crng && crng_init < 2) {
+ numa_crng_init();
crng_init = 2;
process_random_ready_list();
wake_up_interruptible(&crng_init_wait);
pr_notice("random: crng init done\n");
+ if (unseeded_warning.missed) {
+ pr_notice("random: %d get_random_xx warning(s) missed "
+ "due to ratelimiting\n",
+ unseeded_warning.missed);
+ unseeded_warning.missed = 0;
+ }
+ if (urandom_warning.missed) {
+ pr_notice("random: %d urandom warning(s) missed "
+ "due to ratelimiting\n",
+ urandom_warning.missed);
+ urandom_warning.missed = 0;
+ }
}
- spin_unlock_irqrestore(&primary_crng.lock, flags);
+ spin_unlock_irqrestore(&crng->lock, flags);
}
static inline void maybe_reseed_primary_crng(void)
@@ -872,8 +930,9 @@
{
unsigned long v, flags;
- if (crng_init > 1 &&
- time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL))
+ if (crng_ready() &&
+ (time_after(crng_global_init_time, crng->init_time) ||
+ time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)))
crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL);
spin_lock_irqsave(&crng->lock, flags);
if (arch_get_random_long(&v))
@@ -1115,12 +1174,16 @@
static __u32 get_reg(struct fast_pool *f, struct pt_regs *regs)
{
__u32 *ptr = (__u32 *) regs;
+ unsigned int idx;
if (regs == NULL)
return 0;
- if (f->reg_idx >= sizeof(struct pt_regs) / sizeof(__u32))
- f->reg_idx = 0;
- return *(ptr + f->reg_idx++);
+ idx = READ_ONCE(f->reg_idx);
+ if (idx >= sizeof(struct pt_regs) / sizeof(__u32))
+ idx = 0;
+ ptr += idx++;
+ WRITE_ONCE(f->reg_idx, idx);
+ return *ptr;
}
void add_interrupt_randomness(int irq, int irq_flags)
@@ -1149,7 +1212,7 @@
fast_mix(fast_pool);
add_interrupt_bench(cycles);
- if (!crng_ready()) {
+ if (unlikely(crng_init == 0)) {
if ((fast_pool->count >= 64) &&
crng_fast_load((char *) fast_pool->pool,
sizeof(fast_pool->pool))) {
@@ -1655,28 +1718,14 @@
*/
static int rand_initialize(void)
{
-#ifdef CONFIG_NUMA
- int i;
- struct crng_state *crng;
- struct crng_state **pool;
-#endif
-
init_std_data(&input_pool);
init_std_data(&blocking_pool);
crng_initialize(&primary_crng);
-
-#ifdef CONFIG_NUMA
- pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL);
- for_each_online_node(i) {
- crng = kmalloc_node(sizeof(struct crng_state),
- GFP_KERNEL | __GFP_NOFAIL, i);
- spin_lock_init(&crng->lock);
- crng_initialize(crng);
- pool[i] = crng;
+ crng_global_init_time = jiffies;
+ if (ratelimit_disable) {
+ urandom_warning.interval = 0;
+ unseeded_warning.interval = 0;
}
- mb();
- crng_node_pool = pool;
-#endif
return 0;
}
early_initcall(rand_initialize);
@@ -1744,9 +1793,10 @@
if (!crng_ready() && maxwarn > 0) {
maxwarn--;
- printk(KERN_NOTICE "random: %s: uninitialized urandom read "
- "(%zd bytes read)\n",
- current->comm, nbytes);
+ if (__ratelimit(&urandom_warning))
+ printk(KERN_NOTICE "random: %s: uninitialized "
+ "urandom read (%zd bytes read)\n",
+ current->comm, nbytes);
spin_lock_irqsave(&primary_crng.lock, flags);
crng_init_cnt = 0;
spin_unlock_irqrestore(&primary_crng.lock, flags);
@@ -1850,6 +1900,14 @@
input_pool.entropy_count = 0;
blocking_pool.entropy_count = 0;
return 0;
+ case RNDRESEEDCRNG:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (crng_init < 2)
+ return -ENODATA;
+ crng_reseed(&primary_crng, NULL);
+ crng_global_init_time = jiffies - 1;
+ return 0;
default:
return -EINVAL;
}
@@ -2143,7 +2201,7 @@
{
struct entropy_store *poolp = &input_pool;
- if (!crng_ready()) {
+ if (unlikely(crng_init == 0)) {
crng_fast_load(buffer, count);
return;
}
diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c
index 6f060c7..7205e6d 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.c
+++ b/drivers/char/tpm/st33zp24/st33zp24.c
@@ -458,7 +458,7 @@
size_t count)
{
int size = 0;
- int expected;
+ u32 expected;
if (!chip)
return -EBUSY;
@@ -475,7 +475,7 @@
}
expected = be32_to_cpu(*(__be32 *)(buf + 2));
- if (expected > count) {
+ if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index 912ad30..65b8249 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -136,6 +136,12 @@
return -EFAULT;
}
+ if (in_size < 6 ||
+ in_size < be32_to_cpu(*((__be32 *) (priv->data_buffer + 2)))) {
+ mutex_unlock(&priv->buffer_mutex);
+ return -EINVAL;
+ }
+
/* atomic tpm command send and result receive. We only hold the ops
* lock during this period so that the tpm can be unregistered even if
* the char dev is held open.
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index d0ac2d5..830d7e3 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -1078,6 +1078,11 @@
break;
recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
+ if (recd > num_bytes) {
+ total = -EFAULT;
+ break;
+ }
+
memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
dest += recd;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 17896d6..a5780eb 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -668,6 +668,11 @@
if (!rc) {
data_len = be16_to_cpup(
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
+ if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) {
+ rc = -EFAULT;
+ goto out;
+ }
+
data = &buf.data[TPM_HEADER_SIZE + 6];
memcpy(payload->key, data, data_len - 1);
@@ -675,6 +680,7 @@
payload->migratable = data[data_len - 1];
}
+out:
tpm_buf_destroy(&buf);
return rc;
}
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 62ee44e..da69dde 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -437,7 +437,8 @@
static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
int size = 0;
- int expected, status;
+ int status;
+ u32 expected;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
@@ -452,7 +453,7 @@
}
expected = be32_to_cpu(*(__be32 *)(buf + 2));
- if ((size_t) expected > count) {
+ if (((size_t) expected > count) || (expected < TPM_HEADER_SIZE)) {
size = -EIO;
goto out;
}
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index c642877..caa86b1 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -281,7 +281,11 @@
struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
s32 rc;
- int expected, status, burst_count, retries, size = 0;
+ int status;
+ int burst_count;
+ int retries;
+ int size = 0;
+ u32 expected;
if (count < TPM_HEADER_SIZE) {
i2c_nuvoton_ready(chip); /* return to idle */
@@ -323,7 +327,7 @@
* to machine native
*/
expected = be32_to_cpu(*(__be32 *) (buf + 2));
- if (expected > count) {
+ if (expected > count || expected < size) {
dev_err(dev, "%s() expected > count\n", __func__);
size = -EIO;
continue;
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 8022bea..06173d2 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -98,7 +98,7 @@
}
static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *value)
+ const u8 *value)
{
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 4d24ec3..f9aa47e 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -208,7 +208,8 @@
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int size = 0;
- int expected, status;
+ int status;
+ u32 expected;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
@@ -223,7 +224,7 @@
}
expected = be32_to_cpu(*(__be32 *) (buf + 2));
- if (expected > count) {
+ if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
@@ -256,7 +257,7 @@
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int rc, status, burstcnt;
@@ -345,7 +346,7 @@
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int rc;
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index 9191aab..e1c2193 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -98,7 +98,7 @@
int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
u8 *result);
int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *value);
+ const u8 *value);
int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
@@ -128,7 +128,7 @@
}
static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *value)
+ u16 len, const u8 *value)
{
return data->phy_ops->write_bytes(data, addr, len, value);
}
diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
index 3b97b14..01eccb1 100644
--- a/drivers/char/tpm/tpm_tis_spi.c
+++ b/drivers/char/tpm/tpm_tis_spi.c
@@ -47,9 +47,7 @@
struct tpm_tis_spi_phy {
struct tpm_tis_data priv;
struct spi_device *spi_device;
-
- u8 tx_buf[4];
- u8 rx_buf[4];
+ u8 *iobuf;
};
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
@@ -58,7 +56,7 @@
}
static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *buffer, u8 direction)
+ u8 *in, const u8 *out)
{
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
int ret = 0;
@@ -72,14 +70,14 @@
while (len) {
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
- phy->tx_buf[0] = direction | (transfer_len - 1);
- phy->tx_buf[1] = 0xd4;
- phy->tx_buf[2] = addr >> 8;
- phy->tx_buf[3] = addr;
+ phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
+ phy->iobuf[1] = 0xd4;
+ phy->iobuf[2] = addr >> 8;
+ phy->iobuf[3] = addr;
memset(&spi_xfer, 0, sizeof(spi_xfer));
- spi_xfer.tx_buf = phy->tx_buf;
- spi_xfer.rx_buf = phy->rx_buf;
+ spi_xfer.tx_buf = phy->iobuf;
+ spi_xfer.rx_buf = phy->iobuf;
spi_xfer.len = 4;
spi_xfer.cs_change = 1;
@@ -89,9 +87,9 @@
if (ret < 0)
goto exit;
- if ((phy->rx_buf[3] & 0x01) == 0) {
+ if ((phy->iobuf[3] & 0x01) == 0) {
// handle SPI wait states
- phy->tx_buf[0] = 0;
+ phy->iobuf[0] = 0;
for (i = 0; i < TPM_RETRY; i++) {
spi_xfer.len = 1;
@@ -100,7 +98,7 @@
ret = spi_sync_locked(phy->spi_device, &m);
if (ret < 0)
goto exit;
- if (phy->rx_buf[0] & 0x01)
+ if (phy->iobuf[0] & 0x01)
break;
}
@@ -114,12 +112,12 @@
spi_xfer.len = transfer_len;
spi_xfer.delay_usecs = 5;
- if (direction) {
+ if (in) {
spi_xfer.tx_buf = NULL;
- spi_xfer.rx_buf = buffer;
- } else {
- spi_xfer.tx_buf = buffer;
+ } else if (out) {
spi_xfer.rx_buf = NULL;
+ memcpy(phy->iobuf, out, transfer_len);
+ out += transfer_len;
}
spi_message_init(&m);
@@ -128,8 +126,12 @@
if (ret < 0)
goto exit;
+ if (in) {
+ memcpy(in, phy->iobuf, transfer_len);
+ in += transfer_len;
+ }
+
len -= transfer_len;
- buffer += transfer_len;
}
exit:
@@ -140,13 +142,13 @@
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, u8 *result)
{
- return tpm_tis_spi_transfer(data, addr, len, result, 0x80);
+ return tpm_tis_spi_transfer(data, addr, len, result, NULL);
}
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *value)
+ u16 len, const u8 *value)
{
- return tpm_tis_spi_transfer(data, addr, len, value, 0);
+ return tpm_tis_spi_transfer(data, addr, len, NULL, value);
}
static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
@@ -195,6 +197,10 @@
phy->spi_device = dev;
+ phy->iobuf = devm_kmalloc(&dev->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
+ if (!phy->iobuf)
+ return -ENOMEM;
+
return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops,
NULL);
}
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 8f890c1..8c0017d 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1405,7 +1405,6 @@
{
char debugfs_name[16];
struct port *port;
- struct port_buffer *buf;
dev_t devt;
unsigned int nr_added_bufs;
int err;
@@ -1516,8 +1515,6 @@
return 0;
free_inbufs:
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
- free_buf(buf, true);
free_device:
device_destroy(pdrvdata.class, port->dev->devt);
free_cdev:
@@ -1542,34 +1539,14 @@
static void remove_port_data(struct port *port)
{
- struct port_buffer *buf;
-
spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
spin_unlock_irq(&port->inbuf_lock);
- /* Remove buffers we queued up for the Host to send us data in. */
- do {
- spin_lock_irq(&port->inbuf_lock);
- buf = virtqueue_detach_unused_buf(port->in_vq);
- spin_unlock_irq(&port->inbuf_lock);
- if (buf)
- free_buf(buf, true);
- } while (buf);
-
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
spin_unlock_irq(&port->outvq_lock);
-
- /* Free pending buffers from the out-queue. */
- do {
- spin_lock_irq(&port->outvq_lock);
- buf = virtqueue_detach_unused_buf(port->out_vq);
- spin_unlock_irq(&port->outvq_lock);
- if (buf)
- free_buf(buf, true);
- } while (buf);
}
/*
@@ -1794,13 +1771,24 @@
spin_unlock(&portdev->c_ivq_lock);
}
+static void flush_bufs(struct virtqueue *vq, bool can_sleep)
+{
+ struct port_buffer *buf;
+ unsigned int len;
+
+ while ((buf = virtqueue_get_buf(vq, &len)))
+ free_buf(buf, can_sleep);
+}
+
static void out_intr(struct virtqueue *vq)
{
struct port *port;
port = find_port_by_vq(vq->vdev->priv, vq);
- if (!port)
+ if (!port) {
+ flush_bufs(vq, false);
return;
+ }
wake_up_interruptible(&port->waitqueue);
}
@@ -1811,8 +1799,10 @@
unsigned long flags;
port = find_port_by_vq(vq->vdev->priv, vq);
- if (!port)
+ if (!port) {
+ flush_bufs(vq, false);
return;
+ }
spin_lock_irqsave(&port->inbuf_lock, flags);
port->inbuf = get_inbuf(port);
@@ -1987,6 +1977,15 @@
static void remove_vqs(struct ports_device *portdev)
{
+ struct virtqueue *vq;
+
+ virtio_device_for_each_vq(portdev->vdev, vq) {
+ struct port_buffer *buf;
+
+ flush_bufs(vq, true);
+ while ((buf = virtqueue_detach_unused_buf(vq)))
+ free_buf(buf, true);
+ }
portdev->vdev->config->del_vqs(portdev->vdev);
kfree(portdev->in_vqs);
kfree(portdev->out_vqs);
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 4e1cd5a..07c8f70 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -260,13 +260,13 @@
gck->lock = lock;
gck->range = *range;
+ clk_generated_startup(gck);
hw = &gck->hw;
ret = clk_hw_register(NULL, &gck->hw);
if (ret) {
kfree(gck);
hw = ERR_PTR(ret);
- } else
- clk_generated_startup(gck);
+ }
return hw;
}
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 2acaa77..73aab6e 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -401,17 +401,17 @@
static const struct bcm2835_pll_ana_bits bcm2835_ana_default = {
.mask0 = 0,
.set0 = 0,
- .mask1 = (u32)~(A2W_PLL_KI_MASK | A2W_PLL_KP_MASK),
+ .mask1 = A2W_PLL_KI_MASK | A2W_PLL_KP_MASK,
.set1 = (2 << A2W_PLL_KI_SHIFT) | (8 << A2W_PLL_KP_SHIFT),
- .mask3 = (u32)~A2W_PLL_KA_MASK,
+ .mask3 = A2W_PLL_KA_MASK,
.set3 = (2 << A2W_PLL_KA_SHIFT),
.fb_prediv_mask = BIT(14),
};
static const struct bcm2835_pll_ana_bits bcm2835_ana_pllh = {
- .mask0 = (u32)~(A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK),
+ .mask0 = A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK,
.set0 = (2 << A2W_PLLH_KA_SHIFT) | (2 << A2W_PLLH_KI_LOW_SHIFT),
- .mask1 = (u32)~(A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK),
+ .mask1 = A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK,
.set1 = (6 << A2W_PLLH_KP_SHIFT),
.mask3 = 0,
.set3 = 0,
@@ -545,9 +545,7 @@
const struct bcm2835_pll_data *data = pll->data;
spin_lock(&cprman->regs_lock);
- cprman_write(cprman, data->cm_ctrl_reg,
- cprman_read(cprman, data->cm_ctrl_reg) |
- CM_PLL_ANARST);
+ cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST);
cprman_write(cprman, data->a2w_ctrl_reg,
cprman_read(cprman, data->a2w_ctrl_reg) |
A2W_PLL_CTRL_PWRDN);
@@ -566,8 +564,10 @@
~A2W_PLL_CTRL_PWRDN);
/* Take the PLL out of reset. */
+ spin_lock(&cprman->regs_lock);
cprman_write(cprman, data->cm_ctrl_reg,
cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST);
+ spin_unlock(&cprman->regs_lock);
/* Wait for the PLL to lock. */
timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
@@ -581,6 +581,10 @@
cpu_relax();
}
+ cprman_write(cprman, data->a2w_ctrl_reg,
+ cprman_read(cprman, data->a2w_ctrl_reg) |
+ A2W_PLL_CTRL_PRST_DISABLE);
+
return 0;
}
@@ -644,9 +648,11 @@
}
/* Unmask the reference clock from the oscillator. */
+ spin_lock(&cprman->regs_lock);
cprman_write(cprman, A2W_XOSC_CTRL,
cprman_read(cprman, A2W_XOSC_CTRL) |
data->reference_enable_mask);
+ spin_unlock(&cprman->regs_lock);
if (do_ana_setup_first)
bcm2835_pll_write_ana(cprman, data->ana_reg_base, ana);
diff --git a/drivers/clk/bcm/clk-ns2.c b/drivers/clk/bcm/clk-ns2.c
index a564e92..adc1414 100644
--- a/drivers/clk/bcm/clk-ns2.c
+++ b/drivers/clk/bcm/clk-ns2.c
@@ -103,7 +103,7 @@
static const struct iproc_pll_ctrl genpll_sw = {
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL,
- .aon = AON_VAL(0x0, 2, 9, 8),
+ .aon = AON_VAL(0x0, 1, 11, 10),
.reset = RESET_VAL(0x4, 2, 1),
.dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 2, 3),
.ndiv_int = REG_VAL(0x8, 4, 10),
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 5e918e7..95a6e98 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -40,6 +40,10 @@
#define MMCM_REG_FILTER1 0x4e
#define MMCM_REG_FILTER2 0x4f
+#define MMCM_CLKOUT_NOCOUNT BIT(6)
+
+#define MMCM_CLK_DIV_NOCOUNT BIT(12)
+
struct axi_clkgen {
void __iomem *base;
struct clk_hw clk_hw;
@@ -315,12 +319,27 @@
unsigned int reg;
unsigned long long tmp;
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
- dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, ®);
+ if (reg & MMCM_CLKOUT_NOCOUNT) {
+ dout = 1;
+ } else {
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
+ dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ }
+
axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®);
- d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
- m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ if (reg & MMCM_CLK_DIV_NOCOUNT)
+ d = 1;
+ else
+ d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, ®);
+ if (reg & MMCM_CLKOUT_NOCOUNT) {
+ m = 1;
+ } else {
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
+ m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+ }
if (d == 0 || dout == 0)
return 0;
diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c
index 674785d..f0290092 100644
--- a/drivers/clk/clk-conf.c
+++ b/drivers/clk/clk-conf.c
@@ -106,7 +106,7 @@
rc = clk_set_rate(clk, rate);
if (rc < 0)
- pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n",
+ pr_err("clk: couldn't set %s clk rate to %u (%d), current rate: %lu\n",
__clk_get_name(clk), rate, rc,
clk_get_rate(clk));
clk_put(clk);
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
index 96d37175..8ad458b 100644
--- a/drivers/clk/clk-scpi.c
+++ b/drivers/clk/clk-scpi.c
@@ -71,15 +71,15 @@
};
/* find closest match to given frequency in OPP table */
-static int __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
+static long __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
{
int idx;
- u32 fmin = 0, fmax = ~0, ftmp;
+ unsigned long fmin = 0, fmax = ~0, ftmp;
const struct scpi_opp *opp = clk->info->opps;
for (idx = 0; idx < clk->info->count; idx++, opp++) {
ftmp = opp->freq;
- if (ftmp >= (u32)rate) {
+ if (ftmp >= rate) {
if (ftmp <= fmax)
fmax = ftmp;
break;
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
index b051db4..136a860 100644
--- a/drivers/clk/clk-si5351.c
+++ b/drivers/clk/clk-si5351.c
@@ -72,7 +72,7 @@
"xtal", "clkin"
};
static const char * const si5351_pll_names[] = {
- "plla", "pllb", "vxco"
+ "si5351_plla", "si5351_pllb", "si5351_vxco"
};
static const char * const si5351_msynth_names[] = {
"ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 929aef0..0426ff7 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2280,6 +2280,9 @@
int ret;
clk_prepare_lock();
+ /* Always try to update cached phase if possible */
+ if (core->ops->get_phase)
+ core->phase = core->ops->get_phase(core->hw);
ret = core->phase;
clk_prepare_unlock();
@@ -3263,6 +3266,21 @@
core->rate = core->req_rate = rate;
/*
+ * Enable CLK_IS_CRITICAL clocks so newly added critical clocks
+ * don't get accidentally disabled when walking the orphan tree and
+ * reparenting clocks
+ */
+ if (core->flags & CLK_IS_CRITICAL) {
+ unsigned long flags;
+
+ clk_core_prepare(core);
+
+ flags = clk_enable_lock();
+ clk_core_enable(core);
+ clk_enable_unlock(flags);
+ }
+
+ /*
* walk the list of orphan clocks and reparent any that newly finds a
* parent.
*/
@@ -3270,10 +3288,13 @@
struct clk_core *parent = __clk_init_parent(orphan);
/*
- * we could call __clk_set_parent, but that would result in a
- * redundant call to the .set_rate op, if it exists
+ * We need to use __clk_set_parent_before() and _after() to
+ * to properly migrate any prepare/enable count of the orphan
+ * clock. This is important for CLK_IS_CRITICAL clocks, which
+ * are enabled during init but might not have a parent yet.
*/
if (parent) {
+ /* update the clk tree topology */
__clk_set_parent_before(orphan, parent);
__clk_set_parent_after(orphan, parent, NULL);
__clk_recalc_accuracies(orphan);
@@ -3292,16 +3313,6 @@
if (core->ops->init)
core->ops->init(core->hw);
- if (core->flags & CLK_IS_CRITICAL) {
- unsigned long flags;
-
- clk_core_prepare(core);
-
- flags = clk_enable_lock();
- clk_core_enable(core);
- clk_enable_unlock(flags);
- }
-
/*
* enable clocks with the CLK_ENABLE_HAND_OFF flag set
*
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 2f29ee1..5588f75 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -7,9 +7,9 @@
bool
depends on COMMON_CLK_AMLOGIC
help
- Support for the clock controller on AmLogic S805 devices, aka
- meson8b. Say Y if you want peripherals and CPU frequency scaling to
- work.
+ Support for the clock controller on AmLogic S802 (Meson8),
+ S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
+ want peripherals and CPU frequency scaling to work.
config COMMON_CLK_GXBB
bool
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 9d9af44..37e05d6 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -572,7 +572,7 @@
static MESON_GATE(gxbb_periphs, HHI_GCLK_MPEG0, 7);
static MESON_GATE(gxbb_spicc, HHI_GCLK_MPEG0, 8);
static MESON_GATE(gxbb_i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG0, 10);
+static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG0, 10);
static MESON_GATE(gxbb_smart_card, HHI_GCLK_MPEG0, 11);
static MESON_GATE(gxbb_rng0, HHI_GCLK_MPEG0, 12);
static MESON_GATE(gxbb_uart0, HHI_GCLK_MPEG0, 13);
@@ -623,7 +623,7 @@
static MESON_GATE(gxbb_mmc_pclk, HHI_GCLK_MPEG2, 11);
static MESON_GATE(gxbb_dvin, HHI_GCLK_MPEG2, 12);
static MESON_GATE(gxbb_uart2, HHI_GCLK_MPEG2, 15);
-static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG2, 22);
+static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG2, 22);
static MESON_GATE(gxbb_vpu_intr, HHI_GCLK_MPEG2, 25);
static MESON_GATE(gxbb_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
static MESON_GATE(gxbb_clk81_a53, HHI_GCLK_MPEG2, 29);
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 3f1be46..70567958 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -1,5 +1,6 @@
/*
- * AmLogic S805 / Meson8b Clock Controller Driver
+ * AmLogic S802 (Meson8) / S805 (Meson8b) / S812 (Meson8m2) Clock Controller
+ * Driver
*
* Copyright (c) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <carlo@endlessm.com>
@@ -661,7 +662,9 @@
}
static const struct of_device_id meson8b_clkc_match_table[] = {
+ { .compatible = "amlogic,meson8-clkc" },
{ .compatible = "amlogic,meson8b-clkc" },
+ { .compatible = "amlogic,meson8m2-clkc" },
{ }
};
diff --git a/drivers/clk/msm/clock-cpu-8939.c b/drivers/clk/msm/clock-cpu-8939.c
index 5ba6bd0..5b8b8f3 100644
--- a/drivers/clk/msm/clock-cpu-8939.c
+++ b/drivers/clk/msm/clock-cpu-8939.c
@@ -999,7 +999,7 @@
return rc;
}
-late_initcall(clock_cpu_lpm_get_latency);
+late_initcall_sync(clock_cpu_lpm_get_latency);
#define APCS_C0_PLL 0xb116000
#define C0_PLL_MODE 0x0
diff --git a/drivers/clk/msm/clock-cpu-8953.c b/drivers/clk/msm/clock-cpu-8953.c
index 4ba2543..fb393c9 100644
--- a/drivers/clk/msm/clock-cpu-8953.c
+++ b/drivers/clk/msm/clock-cpu-8953.c
@@ -796,11 +796,13 @@
.priority = 1,
};
+static unsigned long pwrcl_boot_rate = 883200000;
+
static int clock_cpu_probe(struct platform_device *pdev)
{
int speed_bin, version, rc, cpu, mux_id;
char prop_name[] = "qcom,speedX-bin-vX-XXX";
- unsigned long ccirate, pwrcl_boot_rate = 883200000;
+ unsigned long ccirate;
get_speed_bin(pdev, &speed_bin, &version);
@@ -952,7 +954,7 @@
#define SRC_DIV 0x1
/* Configure PLL at Low frequency */
-unsigned long pwrcl_early_boot_rate = 652800000;
+static unsigned long pwrcl_early_boot_rate = 652800000;
static int __init cpu_clock_pwr_init(void)
{
@@ -968,9 +970,20 @@
clk_ops_variable_rate = clk_ops_variable_rate_pll_hwfsm;
clk_ops_variable_rate.list_registers = variable_pll_list_registers;
- __variable_rate_pll_init(&apcs_hf_pll.c);
- apcs_hf_pll.c.ops->set_rate(&apcs_hf_pll.c, pwrcl_early_boot_rate);
- clk_ops_variable_rate_pll.enable(&apcs_hf_pll.c);
+ /* Read back the L-val of PLL */
+ regval = readl_relaxed(virt_bases[APCS_C0_PLL_BASE] + APCS_PLL_L_VAL);
+ if (regval) {
+ pr_debug("PLL preconfigured for frequency %ld\n",
+ (19200000UL * regval));
+ pwrcl_boot_rate = (apcs_hf_pll.src_rate * regval);
+ apcs_hf_pll.c.ops->set_rate(&apcs_hf_pll.c, pwrcl_boot_rate);
+ clk_ops_variable_rate_pll_hwfsm.enable(&apcs_hf_pll.c);
+ } else {
+ __variable_rate_pll_init(&apcs_hf_pll.c);
+ apcs_hf_pll.c.ops->set_rate(&apcs_hf_pll.c,
+ pwrcl_early_boot_rate);
+ clk_ops_variable_rate_pll.enable(&apcs_hf_pll.c);
+ }
base = ioremap_nocache(APCS_ALIAS1_CMD_RCGR, SZ_8);
regval = readl_relaxed(base);
diff --git a/drivers/clk/msm/clock-gcc-8952.c b/drivers/clk/msm/clock-gcc-8952.c
index 6d7727f..d471138 100644
--- a/drivers/clk/msm/clock-gcc-8952.c
+++ b/drivers/clk/msm/clock-gcc-8952.c
@@ -216,6 +216,7 @@
.config_reg = (void __iomem *)APCS_C0_PLL_USER_CTL,
.status_reg = (void __iomem *)APCS_C0_PLL_STATUS,
.freq_tbl = apcs_c0_pll_freq,
+ .config_ctl_reg = (void __iomem *)APCS_C0_PLL_CONFIG_CTL,
.masks = {
.vco_mask = BM(29, 28),
.pre_div_mask = BIT(12),
@@ -283,6 +284,7 @@
.config_reg = (void __iomem *)APCS_C1_PLL_USER_CTL,
.status_reg = (void __iomem *)APCS_C1_PLL_STATUS,
.freq_tbl = apcs_c1_pll_freq,
+ .config_ctl_reg = (void __iomem *)APCS_C1_PLL_CONFIG_CTL,
.masks = {
.vco_mask = BM(29, 28),
.pre_div_mask = BIT(12),
@@ -4407,6 +4409,11 @@
if (compat_bin2 || compat_bin4 || compat_bin5)
nbases = APCS_C0_PLL_BASE;
+ if (compat_bin5 || compat_bin6) {
+ a53ss_c0_pll.c.ops = &clk_ops_acpu_pll;
+ a53ss_c1_pll.c.ops = &clk_ops_acpu_pll;
+ }
+
ret = get_mmio_addr(pdev, nbases);
if (ret)
return ret;
diff --git a/drivers/clk/msm/clock-gcc-8953.c b/drivers/clk/msm/clock-gcc-8953.c
index 57adebf..0e8665c 100644
--- a/drivers/clk/msm/clock-gcc-8953.c
+++ b/drivers/clk/msm/clock-gcc-8953.c
@@ -411,6 +411,27 @@
F_END
};
+static struct clk_freq_tbl ftbl_gfx3d_clk_src_sdm632[] = {
+ F_MM( 19200000, FIXED_CLK_SRC, xo, 1, 0, 0),
+ F_MM( 50000000, FIXED_CLK_SRC, gpll0_main_div2_mm, 8, 0, 0),
+ F_MM( 80000000, FIXED_CLK_SRC, gpll0_main_div2_mm, 5, 0, 0),
+ F_MM( 100000000, FIXED_CLK_SRC, gpll0_main_div2_mm, 4, 0, 0),
+ F_MM( 133330000, FIXED_CLK_SRC, gpll0_main_div2_mm, 3, 0, 0),
+ F_MM( 160000000, FIXED_CLK_SRC, gpll0_main_div2_mm, 2.5, 0, 0),
+ F_MM( 200000000, FIXED_CLK_SRC, gpll0_main_div2_mm, 2, 0, 0),
+ F_MM( 216000000, FIXED_CLK_SRC, gpll6_main_div2_gfx, 2.5, 0, 0),
+ F_MM( 266670000, FIXED_CLK_SRC, gpll0, 3, 0, 0),
+ F_MM( 320000000, FIXED_CLK_SRC, gpll0, 2.5, 0, 0),
+ F_MM( 400000000, FIXED_CLK_SRC, gpll0, 2, 0, 0),
+ F_MM( 460800000, FIXED_CLK_SRC, gpll4_out_aux, 2.5, 0, 0),
+ F_MM( 510000000, 1020000000, gpll3, 1, 0, 0),
+ F_MM( 560000000, 1120000000, gpll3, 1, 0, 0),
+ F_MM( 650000000, 1300000000, gpll3, 1, 0, 0),
+ F_MM( 700000000, 1400000000, gpll3, 1, 0, 0),
+ F_MM( 725000000, 1450000000, gpll3, 1, 0, 0),
+
+ F_END
+};
static struct rcg_clk gfx3d_clk_src = {
.cmd_rcgr_reg = GFX3D_CMD_RCGR,
.set_rate = set_rate_hid,
@@ -4104,6 +4125,11 @@
if (compat_bin)
gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_sdm450;
+ compat_bin = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,gcc-gfx-sdm632");
+ if (compat_bin)
+ gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_sdm632;
+
ret = of_get_fmax_vdd_class(pdev, &gcc_oxili_gfx3d_clk.c,
"qcom,gfxfreq-corner");
if (ret) {
diff --git a/drivers/clk/msm/clock-pll.c b/drivers/clk/msm/clock-pll.c
index 26c04e5..381c8db 100644
--- a/drivers/clk/msm/clock-pll.c
+++ b/drivers/clk/msm/clock-pll.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -217,13 +217,46 @@
writel_relaxed(regval, pll_config);
}
+static void pll_wait_for_lock(struct pll_clk *pll)
+{
+ int count;
+ u32 mode = readl_relaxed(PLL_MODE_REG(pll));
+ u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
+ u32 status_reg, user_reg, l_reg, m_reg, n_reg, config_reg;
+
+ /* Wait for pll to lock. */
+ for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
+ if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask)
+ break;
+ udelay(1);
+ }
+
+ if (!(readl_relaxed(PLL_STATUS_REG(pll)) & lockmask)) {
+ mode = readl_relaxed(PLL_MODE_REG(pll));
+ status_reg = readl_relaxed(PLL_STATUS_REG(pll));
+ user_reg = readl_relaxed(PLL_CONFIG_REG(pll));
+ config_reg = readl_relaxed(PLL_CFG_CTL_REG(pll));
+ l_reg = readl_relaxed(PLL_L_REG(pll));
+ m_reg = readl_relaxed(PLL_M_REG(pll));
+ n_reg = readl_relaxed(PLL_N_REG(pll));
+ pr_err("count = %d\n", (int)count);
+ pr_err("mode register is 0x%x\n", mode);
+ pr_err("status register is 0x%x\n", status_reg);
+ pr_err("user control register is 0x%x\n", user_reg);
+ pr_err("config control register is 0x%x\n", config_reg);
+ pr_err("L value register is 0x%x\n", l_reg);
+ pr_err("M value register is 0x%x\n", m_reg);
+ pr_err("N value control register is 0x%x\n", n_reg);
+ panic("PLL %s didn't lock after enabling it!\n",
+ pll->c.dbg_name);
+ }
+}
+
static int sr2_pll_clk_enable(struct clk *c)
{
unsigned long flags;
struct pll_clk *pll = to_pll_clk(c);
- int ret = 0, count;
u32 mode = readl_relaxed(PLL_MODE_REG(pll));
- u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
spin_lock_irqsave(&pll_reg_lock, flags);
@@ -245,15 +278,7 @@
mode |= PLL_RESET_N;
writel_relaxed(mode, PLL_MODE_REG(pll));
- /* Wait for pll to lock. */
- for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
- if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask)
- break;
- udelay(1);
- }
-
- if (!(readl_relaxed(PLL_STATUS_REG(pll)) & lockmask))
- pr_err("PLL %s didn't lock after enabling it!\n", c->dbg_name);
+ pll_wait_for_lock(pll);
/* Enable PLL output. */
mode |= PLL_OUTCTRL;
@@ -263,7 +288,50 @@
mb();
spin_unlock_irqrestore(&pll_reg_lock, flags);
- return ret;
+ return 0;
+}
+
+static int acpu_pll_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode = readl_relaxed(PLL_MODE_REG(pll));
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+
+ spm_event(pll->spm_ctrl.spm_base, pll->spm_ctrl.offset,
+ pll->spm_ctrl.event_bit, false);
+
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ mb();
+ udelay(10);
+
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* PLL H/W requires a 50uSec delay before polling lock_detect. */
+ mb();
+ udelay(50);
+
+ pll_wait_for_lock(pll);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+ return 0;
}
void __variable_rate_pll_init(struct clk *c)
@@ -886,6 +954,15 @@
.list_registers = local_pll_clk_list_registers,
};
+const struct clk_ops clk_ops_acpu_pll = {
+ .enable = acpu_pll_clk_enable,
+ .disable = local_pll_clk_disable,
+ .set_rate = local_pll_clk_set_rate,
+ .round_rate = local_pll_clk_round_rate,
+ .handoff = local_pll_clk_handoff,
+ .list_registers = local_pll_clk_list_registers,
+};
+
const struct clk_ops clk_ops_variable_rate_pll_hwfsm = {
.enable = variable_rate_pll_clk_enable_hwfsm,
.disable = variable_rate_pll_clk_disable_hwfsm,
diff --git a/drivers/clk/msm/mdss/Makefile b/drivers/clk/msm/mdss/Makefile
index 6285714..3b884dc 100644
--- a/drivers/clk/msm/mdss/Makefile
+++ b/drivers/clk/msm/mdss/Makefile
@@ -5,3 +5,5 @@
obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o
obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o
obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-12nm.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-12nm-util.o
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
new file mode 100644
index 0000000..0bdfeeb
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
@@ -0,0 +1,973 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-12nm.h"
+
+#define DSI_PLL_POLL_MAX_READS 15
+#define DSI_PLL_POLL_TIMEOUT_US 1000
+
+int pixel_div_set_div(struct div_clk *clk, int div)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+
+ /* Programming during vco_prepare. Keep this value */
+ pdb->param.pixel_divhf = (div - 1);
+
+ pr_debug("ndx=%d div=%d divhf=%d\n",
+ pll->index, div, pdb->param.pixel_divhf);
+
+ return 0;
+}
+
+int pixel_div_get_div(struct div_clk *clk)
+{
+ u32 div;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SSC9);
+ div &= 0x7f;
+ pr_debug("pixel_div = %d\n", (div+1));
+
+ mdss_pll_resource_enable(pll, false);
+
+ return (div + 1);
+}
+
+int set_post_div_mux_sel(struct mux_clk *clk, int sel)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+
+ /* Programming during vco_prepare. Keep this value */
+ pdb->param.post_div_mux = sel;
+
+ pr_debug("ndx=%d post_div_mux_sel=%d p_div=%d\n",
+ pll->index, sel, (u32) BIT(sel));
+
+ return 0;
+}
+
+int get_post_div_mux_sel(struct mux_clk *clk)
+{
+ u32 sel = 0;
+ u32 vco_cntrl = 0, cpbias_cntrl = 0;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ vco_cntrl = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_PLL_VCO_CTRL);
+ vco_cntrl &= 0x30;
+
+ cpbias_cntrl = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_CHAR_PUMP_BIAS_CTRL);
+ cpbias_cntrl = ((cpbias_cntrl >> 6) & 0x1);
+
+ if (cpbias_cntrl == 0) {
+ if (vco_cntrl == 0x00)
+ sel = 0;
+ else if (vco_cntrl == 0x10)
+ sel = 2;
+ else if (vco_cntrl == 0x20)
+ sel = 3;
+ else if (vco_cntrl == 0x30)
+ sel = 4;
+ } else if (cpbias_cntrl == 1) {
+ if (vco_cntrl == 0x30)
+ sel = 2;
+ else if (vco_cntrl == 0x00)
+ sel = 5;
+ }
+
+ mdss_pll_resource_enable(pll, false);
+
+ return sel;
+}
+
+int set_gp_mux_sel(struct mux_clk *clk, int sel)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+
+ /* Programming during vco_prepare. Keep this value */
+ pdb->param.gp_div_mux = sel;
+
+ pr_debug("ndx=%d gp_div_mux_sel=%d gp_cntrl=%d\n",
+ pll->index, sel, (u32) BIT(sel));
+
+ return 0;
+}
+
+int get_gp_mux_sel(struct mux_clk *clk)
+{
+ u32 sel = 0;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ sel = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_PLL_CTRL);
+ sel = (sel >> 5) & 0x7;
+ pr_debug("gp_cntrl = %d\n", sel);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return sel;
+}
+
+static bool pll_is_pll_locked_12nm(struct mdss_pll_resources *pll,
+ bool is_handoff)
+{
+ u32 status;
+ bool pll_locked;
+
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_atomic((pll->pll_base +
+ DSIPHY_STAT0),
+ status,
+ ((status & BIT(1)) > 0),
+ DSI_PLL_POLL_MAX_READS,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ if (!is_handoff)
+ pr_err("DSI PLL ndx=%d status=%x failed to Lock\n",
+ pll->index, status);
+ pll_locked = false;
+ } else {
+ pll_locked = true;
+ }
+
+ return pll_locked;
+}
+
+int dsi_pll_enable_seq_12nm(struct mdss_pll_resources *pll)
+{
+ int rc = 0;
+ struct dsi_pll_db *pdb;
+ void __iomem *pll_base;
+
+ if (!pll) {
+ pr_err("Invalid PLL resources\n");
+ return -EINVAL;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No priv found\n");
+ return -EINVAL;
+ }
+
+ pll_base = pll->pll_base;
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SYS_CTRL, 0x49);
+ wmb(); /* make sure register committed before enabling branch clocks */
+ udelay(5); /* h/w recommended delay */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SYS_CTRL, 0xc9);
+ wmb(); /* make sure register committed before enabling branch clocks */
+ udelay(50); /* h/w recommended delay */
+
+ if (!pll_is_pll_locked_12nm(pll, false)) {
+ pr_err("DSI PLL ndx=%d lock failed!\n",
+ pll->index);
+ rc = -EINVAL;
+ goto init_lock_err;
+ }
+
+ pr_debug("DSI PLL ndx:%d Locked!\n", pll->index);
+
+init_lock_err:
+ return rc;
+}
+
+static int dsi_pll_enable(struct clk *c)
+{
+ int i, rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ /* Try all enable sequences until one succeeds */
+ for (i = 0; i < vco->pll_en_seq_cnt; i++) {
+ rc = vco->pll_enable_seqs[i](pll);
+ pr_debug("DSI PLL %s after sequence #%d\n",
+ rc ? "unlocked" : "locked", i + 1);
+ if (!rc)
+ break;
+ }
+
+ if (rc)
+ pr_err("ndx=%d DSI PLL failed to lock\n", pll->index);
+ else
+ pll->pll_on = true;
+
+ return rc;
+}
+
+static int dsi_pll_relock(struct mdss_pll_resources *pll)
+{
+ void __iomem *pll_base = pll->pll_base;
+ u32 data = 0;
+ int rc = 0;
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_POWERUP_CTRL);
+ data &= ~BIT(1); /* remove ONPLL_OVR_EN bit */
+ data |= 0x1; /* set ONPLL_OVN to 0x1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_POWERUP_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+
+ if (!pll_is_pll_locked_12nm(pll, false)) {
+ pr_err("DSI PLL ndx=%d lock failed!\n",
+ pll->index);
+ rc = -EINVAL;
+ goto relock_err;
+ }
+ ndelay(50); /* h/w recommended delay */
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_CTRL);
+ data |= 0x01; /* set CLK_SEL bits to 0x1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+ wmb(); /* make sure register committed before enabling branch clocks */
+ pll->pll_on = true;
+relock_err:
+ return rc;
+}
+
+static void dsi_pll_disable(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ void __iomem *pll_base = pll->pll_base;
+ u32 data = 0;
+
+ if (!pll->pll_on &&
+ mdss_pll_resource_enable(pll, true)) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return;
+ }
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_SSC0);
+ data &= ~BIT(6); /* disable GP_CLK_EN */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC0, data);
+ ndelay(500); /* h/w recommended delay */
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_CTRL);
+ data &= ~0x03; /* remove CLK_SEL bits */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+
+ data = MDSS_PLL_REG_R(pll_base, DSIPHY_PLL_POWERUP_CTRL);
+ data &= ~0x1; /* remove ONPLL_OVR bit */
+ data |= BIT(1); /* set ONPLL_OVR_EN to 0x1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_POWERUP_CTRL, data);
+ ndelay(500); /* h/w recommended delay */
+ wmb(); /* make sure register committed before disabling branch clocks */
+ pll->handoff_resources = false;
+
+ mdss_pll_resource_enable(pll, false);
+
+ pll->pll_on = false;
+
+ pr_debug("DSI PLL ndx=%d Disabled\n", pll->index);
+}
+
+static u32 __mdss_dsi_get_hsfreqrange(u64 target_freq)
+{
+ u64 bitclk_rate_mhz = div_u64((target_freq * 2), 1000000);
+
+ if (bitclk_rate_mhz >= 80 && bitclk_rate_mhz < 90)
+ return 0x00;
+ else if (bitclk_rate_mhz >= 90 && bitclk_rate_mhz < 100)
+ return 0x10;
+ else if (bitclk_rate_mhz >= 100 && bitclk_rate_mhz < 110)
+ return 0x20;
+ else if (bitclk_rate_mhz >= 110 && bitclk_rate_mhz < 120)
+ return 0x30;
+ else if (bitclk_rate_mhz >= 120 && bitclk_rate_mhz < 130)
+ return 0x01;
+ else if (bitclk_rate_mhz >= 130 && bitclk_rate_mhz < 140)
+ return 0x11;
+ else if (bitclk_rate_mhz >= 140 && bitclk_rate_mhz < 150)
+ return 0x21;
+ else if (bitclk_rate_mhz >= 150 && bitclk_rate_mhz < 160)
+ return 0x31;
+ else if (bitclk_rate_mhz >= 160 && bitclk_rate_mhz < 170)
+ return 0x02;
+ else if (bitclk_rate_mhz >= 170 && bitclk_rate_mhz < 180)
+ return 0x12;
+ else if (bitclk_rate_mhz >= 180 && bitclk_rate_mhz < 190)
+ return 0x22;
+ else if (bitclk_rate_mhz >= 190 && bitclk_rate_mhz < 205)
+ return 0x32;
+ else if (bitclk_rate_mhz >= 205 && bitclk_rate_mhz < 220)
+ return 0x03;
+ else if (bitclk_rate_mhz >= 220 && bitclk_rate_mhz < 235)
+ return 0x13;
+ else if (bitclk_rate_mhz >= 235 && bitclk_rate_mhz < 250)
+ return 0x23;
+ else if (bitclk_rate_mhz >= 250 && bitclk_rate_mhz < 275)
+ return 0x33;
+ else if (bitclk_rate_mhz >= 275 && bitclk_rate_mhz < 300)
+ return 0x04;
+ else if (bitclk_rate_mhz >= 300 && bitclk_rate_mhz < 325)
+ return 0x14;
+ else if (bitclk_rate_mhz >= 325 && bitclk_rate_mhz < 350)
+ return 0x25;
+ else if (bitclk_rate_mhz >= 350 && bitclk_rate_mhz < 400)
+ return 0x35;
+ else if (bitclk_rate_mhz >= 400 && bitclk_rate_mhz < 450)
+ return 0x05;
+ else if (bitclk_rate_mhz >= 450 && bitclk_rate_mhz < 500)
+ return 0x16;
+ else if (bitclk_rate_mhz >= 500 && bitclk_rate_mhz < 550)
+ return 0x26;
+ else if (bitclk_rate_mhz >= 550 && bitclk_rate_mhz < 600)
+ return 0x37;
+ else if (bitclk_rate_mhz >= 600 && bitclk_rate_mhz < 650)
+ return 0x07;
+ else if (bitclk_rate_mhz >= 650 && bitclk_rate_mhz < 700)
+ return 0x18;
+ else if (bitclk_rate_mhz >= 700 && bitclk_rate_mhz < 750)
+ return 0x28;
+ else if (bitclk_rate_mhz >= 750 && bitclk_rate_mhz < 800)
+ return 0x39;
+ else if (bitclk_rate_mhz >= 800 && bitclk_rate_mhz < 850)
+ return 0x09;
+ else if (bitclk_rate_mhz >= 850 && bitclk_rate_mhz < 900)
+ return 0x19;
+ else if (bitclk_rate_mhz >= 900 && bitclk_rate_mhz < 950)
+ return 0x29;
+ else if (bitclk_rate_mhz >= 950 && bitclk_rate_mhz < 1000)
+ return 0x3a;
+ else if (bitclk_rate_mhz >= 1000 && bitclk_rate_mhz < 1050)
+ return 0x0a;
+ else if (bitclk_rate_mhz >= 1050 && bitclk_rate_mhz < 1100)
+ return 0x1a;
+ else if (bitclk_rate_mhz >= 1100 && bitclk_rate_mhz < 1150)
+ return 0x2a;
+ else if (bitclk_rate_mhz >= 1150 && bitclk_rate_mhz < 1200)
+ return 0x3b;
+ else if (bitclk_rate_mhz >= 1200 && bitclk_rate_mhz < 1250)
+ return 0x0b;
+ else if (bitclk_rate_mhz >= 1250 && bitclk_rate_mhz < 1300)
+ return 0x1b;
+ else if (bitclk_rate_mhz >= 1300 && bitclk_rate_mhz < 1350)
+ return 0x2b;
+ else if (bitclk_rate_mhz >= 1350 && bitclk_rate_mhz < 1400)
+ return 0x3c;
+ else if (bitclk_rate_mhz >= 1400 && bitclk_rate_mhz < 1450)
+ return 0x0c;
+ else if (bitclk_rate_mhz >= 1450 && bitclk_rate_mhz < 1500)
+ return 0x1c;
+ else if (bitclk_rate_mhz >= 1500 && bitclk_rate_mhz < 1550)
+ return 0x2c;
+ else if (bitclk_rate_mhz >= 1550 && bitclk_rate_mhz < 1600)
+ return 0x3d;
+ else if (bitclk_rate_mhz >= 1600 && bitclk_rate_mhz < 1650)
+ return 0x0d;
+ else if (bitclk_rate_mhz >= 1650 && bitclk_rate_mhz < 1700)
+ return 0x1d;
+ else if (bitclk_rate_mhz >= 1700 && bitclk_rate_mhz < 1750)
+ return 0x2e;
+ else if (bitclk_rate_mhz >= 1750 && bitclk_rate_mhz < 1800)
+ return 0x3e;
+ else if (bitclk_rate_mhz >= 1800 && bitclk_rate_mhz < 1850)
+ return 0x0e;
+ else if (bitclk_rate_mhz >= 1850 && bitclk_rate_mhz < 1900)
+ return 0x1e;
+ else if (bitclk_rate_mhz >= 1900 && bitclk_rate_mhz < 1950)
+ return 0x2f;
+ else if (bitclk_rate_mhz >= 1950 && bitclk_rate_mhz < 2000)
+ return 0x3f;
+ else if (bitclk_rate_mhz >= 2000 && bitclk_rate_mhz < 2050)
+ return 0x0f;
+ else if (bitclk_rate_mhz >= 2050 && bitclk_rate_mhz < 2100)
+ return 0x40;
+ else if (bitclk_rate_mhz >= 2100 && bitclk_rate_mhz < 2150)
+ return 0x41;
+ else if (bitclk_rate_mhz >= 2150 && bitclk_rate_mhz < 2200)
+ return 0x42;
+ else if (bitclk_rate_mhz >= 2200 && bitclk_rate_mhz < 2250)
+ return 0x43;
+ else if (bitclk_rate_mhz >= 2250 && bitclk_rate_mhz < 2300)
+ return 0x44;
+ else if (bitclk_rate_mhz >= 2300 && bitclk_rate_mhz < 2350)
+ return 0x45;
+ else if (bitclk_rate_mhz >= 2350 && bitclk_rate_mhz < 2400)
+ return 0x46;
+ else if (bitclk_rate_mhz >= 2400 && bitclk_rate_mhz < 2450)
+ return 0x47;
+ else if (bitclk_rate_mhz >= 2450 && bitclk_rate_mhz < 2500)
+ return 0x48;
+ else
+ return 0x49;
+}
+
+static void __mdss_dsi_get_pll_vco_cntrl(u64 target_freq, u32 post_div_mux,
+ u32 *vco_cntrl, u32 *cpbias_cntrl)
+{
+ u64 target_freq_mhz = div_u64(target_freq, 1000000);
+ u32 p_div = BIT(post_div_mux);
+
+ if (p_div == 1) {
+ *vco_cntrl = 0x00;
+ *cpbias_cntrl = 0;
+ } else if (p_div == 2) {
+ *vco_cntrl = 0x30;
+ *cpbias_cntrl = 1;
+ } else if (p_div == 4) {
+ *vco_cntrl = 0x10;
+ *cpbias_cntrl = 0;
+ } else if (p_div == 8) {
+ *vco_cntrl = 0x20;
+ *cpbias_cntrl = 0;
+ } else if (p_div == 16) {
+ *vco_cntrl = 0x30;
+ *cpbias_cntrl = 0;
+ } else {
+ *vco_cntrl = 0x00;
+ *cpbias_cntrl = 1;
+ }
+
+ if (target_freq_mhz <= 1250 && target_freq_mhz >= 1092)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 1092 && target_freq_mhz >= 950)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 950 && target_freq_mhz >= 712)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 712 && target_freq_mhz >= 546)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 546 && target_freq_mhz >= 475)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 475 && target_freq_mhz >= 356)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 356 && target_freq_mhz >= 273)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 273 && target_freq_mhz >= 237)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 237 && target_freq_mhz >= 178)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 178 && target_freq_mhz >= 136)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 136 && target_freq_mhz >= 118)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 118 && target_freq_mhz >= 89)
+ *vco_cntrl = *vco_cntrl | 1;
+ else if (target_freq_mhz < 89 && target_freq_mhz >= 68)
+ *vco_cntrl = *vco_cntrl | 2;
+ else if (target_freq_mhz < 68 && target_freq_mhz >= 57)
+ *vco_cntrl = *vco_cntrl | 3;
+ else if (target_freq_mhz < 57 && target_freq_mhz >= 44)
+ *vco_cntrl = *vco_cntrl | 1;
+ else
+ *vco_cntrl = *vco_cntrl | 2;
+}
+
+static u32 __mdss_dsi_get_osc_freq_target(u64 target_freq)
+{
+ u64 target_freq_mhz = div_u64(target_freq, 1000000);
+
+ if (target_freq_mhz <= 1000)
+ return 1315;
+ else if (target_freq_mhz > 1000 && target_freq_mhz <= 1500)
+ return 1839;
+ else
+ return 0;
+}
+
+static u64 __mdss_dsi_pll_get_m_div(u64 vco_rate)
+{
+ return div_u64((vco_rate * 4), 19200000);
+}
+
+static u32 __mdss_dsi_get_fsm_ovr_ctrl(u64 target_freq)
+{
+ u64 bitclk_rate_mhz = div_u64((target_freq * 2), 1000000);
+
+ if (bitclk_rate_mhz > 1500 && bitclk_rate_mhz <= 2500)
+ return 0;
+ else
+ return BIT(6);
+}
+
+static void mdss_dsi_pll_12nm_calc_reg(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ struct dsi_pll_param *param = &pdb->param;
+ u64 target_freq = 0;
+
+ target_freq = div_u64(pll->vco_current_rate,
+ BIT(pdb->param.post_div_mux));
+
+ param->hsfreqrange = __mdss_dsi_get_hsfreqrange(target_freq);
+ __mdss_dsi_get_pll_vco_cntrl(target_freq, param->post_div_mux,
+ ¶m->vco_cntrl, ¶m->cpbias_cntrl);
+ param->osc_freq_target = __mdss_dsi_get_osc_freq_target(target_freq);
+ param->m_div = (u32) __mdss_dsi_pll_get_m_div(pll->vco_current_rate);
+ param->fsm_ovr_ctrl = __mdss_dsi_get_fsm_ovr_ctrl(target_freq);
+ param->prop_cntrl = 0x05;
+ param->int_cntrl = 0x00;
+ param->gmp_cntrl = 0x1;
+}
+
+static u32 __mdss_dsi_get_multi_intX100(u64 vco_rate, u32 *rem)
+{
+ u32 reminder = 0;
+ u64 temp = 0;
+ const u32 ref_clk_rate = 19200000, quarterX100 = 25;
+
+ temp = div_u64_rem(vco_rate, ref_clk_rate, &reminder);
+ temp *= 100;
+
+ /*
+ * Multiplication integer needs to be floored in steps of 0.25
+ * Hence multi_intX100 needs to be rounded off in steps of 25
+ */
+ if (reminder < (ref_clk_rate / 4)) {
+ *rem = reminder;
+ return temp;
+ } else if ((reminder >= (ref_clk_rate / 4)) &&
+ reminder < (ref_clk_rate / 2)) {
+ *rem = (reminder - (ref_clk_rate / 4));
+ return (temp + quarterX100);
+ } else if ((reminder >= (ref_clk_rate / 2)) &&
+ (reminder < ((3 * ref_clk_rate) / 4))) {
+ *rem = (reminder - (ref_clk_rate / 2));
+ return (temp + (quarterX100 * 2));
+ }
+
+ *rem = (reminder - ((3 * ref_clk_rate) / 4));
+ return (temp + (quarterX100 * 3));
+}
+
+static u32 __calc_gcd(u32 num1, u32 num2)
+{
+ if (num2 != 0)
+ return __calc_gcd(num2, (num1 % num2));
+ else
+ return num1;
+}
+
+static void mdss_dsi_pll_12nm_calc_ssc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ struct dsi_pll_param *param = &pdb->param;
+ u64 multi_intX100 = 0, temp = 0;
+ u32 temp_rem1 = 0, temp_rem2 = 0;
+ const u64 power_2_17 = 131072, power_2_10 = 1024;
+ const u32 ref_clk_rate = 19200000;
+
+ multi_intX100 = __mdss_dsi_get_multi_intX100(pll->vco_current_rate,
+ &temp_rem1);
+
+ /* Calculation for mpll_ssc_peak_i */
+ temp = (multi_intX100 * pll->ssc_ppm * power_2_17);
+ temp = div_u64(temp, 100); /* 100 div for multi_intX100 */
+ param->mpll_ssc_peak_i =
+ (u32) div_u64(temp, 1000000); /*10^6 for SSC PPM */
+
+ /* Calculation for mpll_stepsize_i */
+ param->mpll_stepsize_i = (u32) div_u64((param->mpll_ssc_peak_i *
+ pll->ssc_freq * power_2_10), ref_clk_rate);
+
+ /* Calculation for mpll_mint_i */
+ param->mpll_mint_i = (u32) (div_u64((multi_intX100 * 4), 100) - 32);
+
+ /* Calculation for mpll_frac_den */
+ param->mpll_frac_den = (u32) div_u64(ref_clk_rate,
+ __calc_gcd((u32)pll->vco_current_rate, ref_clk_rate));
+
+ /* Calculation for mpll_frac_quot_i */
+ temp = (temp_rem1 * power_2_17);
+ param->mpll_frac_quot_i =
+ (u32) div_u64_rem(temp, ref_clk_rate, &temp_rem2);
+
+ /* Calculation for mpll_frac_rem */
+ param->mpll_frac_rem = (u32) div_u64(((u64)temp_rem2 *
+ param->mpll_frac_den), ref_clk_rate);
+
+ pr_debug("mpll_ssc_peak_i=%d mpll_stepsize_i=%d mpll_mint_i=%d\n",
+ param->mpll_ssc_peak_i, param->mpll_stepsize_i,
+ param->mpll_mint_i);
+ pr_debug("mpll_frac_den=%d mpll_frac_quot_i=%d mpll_frac_rem=%d",
+ param->mpll_frac_den, param->mpll_frac_quot_i,
+ param->mpll_frac_rem);
+}
+
+static void pll_db_commit_12nm_ssc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_param *param = &pdb->param;
+ char data = 0;
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC0, 0x27);
+
+ data = (param->mpll_mint_i & 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC7, data);
+
+ data = ((param->mpll_mint_i & 0xff00) >> 8);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC8, data);
+
+ data = (param->mpll_ssc_peak_i & 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC1, data);
+
+ data = ((param->mpll_ssc_peak_i & 0xff00) >> 8);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC2, data);
+
+ data = ((param->mpll_ssc_peak_i & 0xf0000) >> 16);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC3, data);
+
+ data = (param->mpll_stepsize_i & 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC4, data);
+
+ data = ((param->mpll_stepsize_i & 0xff00) >> 8);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC5, data);
+
+ data = ((param->mpll_stepsize_i & 0x1f0000) >> 16);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC6, data);
+
+ data = (param->mpll_frac_quot_i & 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC10, data);
+
+ data = ((param->mpll_frac_quot_i & 0xff00) >> 8);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC11, data);
+
+ data = (param->mpll_frac_rem & 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC12, data);
+
+ data = ((param->mpll_frac_rem & 0xff00) >> 8);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC13, data);
+
+ data = (param->mpll_frac_den & 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC14, data);
+
+ data = ((param->mpll_frac_den & 0xff00) >> 8);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC15, data);
+}
+
+static void pll_db_commit_12nm(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_param *param = &pdb->param;
+ char data = 0;
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CTRL0, 0x01);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, 0x05);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SLEWRATE_DDL_LOOP_CTRL, 0x01);
+
+ data = ((param->hsfreqrange & 0x7f) | BIT(7));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_HS_FREQ_RAN_SEL, data);
+
+ data = ((param->vco_cntrl & 0x3f) | BIT(6));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_CTRL, data);
+
+ data = (param->osc_freq_target & 0x7f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_0, data);
+
+ data = ((param->osc_freq_target & 0xf80) >> 7);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_1, data);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_INPUT_LOOP_DIV_RAT_CTRL, 0x30);
+
+ data = (param->m_div & 0x3f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOOP_DIV_RATIO_0, data);
+
+ data = ((param->m_div & 0xfc0) >> 6);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOOP_DIV_RATIO_1, data);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_INPUT_DIV_PLL_OVR, 0x60);
+
+ data = (param->prop_cntrl & 0x3f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PROP_CHRG_PUMP_CTRL, data);
+
+ data = (param->int_cntrl & 0x3f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_INTEG_CHRG_PUMP_CTRL, data);
+
+ data = ((param->gmp_cntrl & 0x3) << 4);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_GMP_CTRL_DIG_TST, data);
+
+ data = ((param->cpbias_cntrl & 0x1) << 6) | BIT(4);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CHAR_PUMP_BIAS_CTRL, data);
+
+ data = ((param->gp_div_mux & 0x7) << 5) | 0x5;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CTRL, data);
+
+ data = (param->pixel_divhf & 0x7f);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_SSC9, data);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_ANA_PROG_CTRL, 0x03);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_ANA_TST_LOCK_ST_OVR_CTRL, 0x50);
+ MDSS_PLL_REG_W(pll_base,
+ DSIPHY_SLEWRATE_FSM_OVR_CTRL, param->fsm_ovr_ctrl);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PHA_ERR_CTRL_0, 0x01);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PHA_ERR_CTRL_1, 0x00);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOCK_FILTER, 0xff);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_UNLOCK_FILTER, 0x03);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PRO_DLY_RELOCK, 0x0c);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOCK_DET_MODE_SEL, 0x02);
+
+ if (pll->ssc_en)
+ pll_db_commit_12nm_ssc(pll, pdb);
+
+ pr_debug("pll:%d\n", pll->index);
+ wmb(); /* make sure register committed before preparing the clocks */
+}
+
+int pll_vco_set_rate_12nm(struct clk *c, unsigned long rate)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("pll pdb not found\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pr_debug("%s: ndx=%d rate=%lu\n", __func__, pll->index, rate);
+
+ pll->vco_current_rate = rate;
+ pll->vco_ref_clk_rate = vco->ref_clk_rate;
+error:
+ return rc;
+}
+
+static unsigned long pll_vco_get_rate_12nm(struct clk *c)
+{
+ u64 vco_rate = 0;
+ u32 m_div_5_0 = 0, m_div_11_6 = 0, m_div = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ u64 ref_clk = vco->ref_clk_rate;
+ int rc;
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return rc;
+ }
+
+ m_div_5_0 = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_LOOP_DIV_RATIO_0);
+ m_div_5_0 &= 0x3f;
+ pr_debug("m_div_5_0 = 0x%x\n", m_div_5_0);
+
+ m_div_11_6 = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_LOOP_DIV_RATIO_1);
+ m_div_11_6 &= 0x3f;
+ pr_debug("m_div_11_6 = 0x%x\n", m_div_11_6);
+
+ m_div = ((m_div_11_6 << 6) | (m_div_5_0));
+
+ vco_rate = div_u64((ref_clk * m_div), 4);
+
+ pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return (unsigned long)vco_rate;
+}
+
+long pll_vco_round_rate_12nm(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ if (rate < vco->min_rate)
+ rrate = vco->min_rate;
+ if (rate > vco->max_rate)
+ rrate = vco->max_rate;
+
+ return rrate;
+}
+
+enum handoff pll_vco_handoff_12nm(struct clk *c)
+{
+ int rc;
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return HANDOFF_DISABLED_CLK;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return ret;
+ }
+
+ if (pll_is_pll_locked_12nm(pll, true)) {
+ pll->handoff_resources = true;
+ pll->pll_on = true;
+ c->rate = pll_vco_get_rate_12nm(c);
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ mdss_pll_resource_enable(pll, false);
+ }
+
+ return ret;
+}
+
+int pll_vco_prepare_12nm(struct clk *c)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct dsi_pll_db *pdb;
+ u32 data = 0;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No prov found\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("ndx=%d Failed to enable mdss dsi pll resources\n",
+ pll->index);
+ return rc;
+ }
+
+ if ((pll->vco_cached_rate != 0)
+ && (pll->vco_cached_rate == c->rate)) {
+ rc = c->ops->set_rate(c, pll->vco_cached_rate);
+ if (rc) {
+ pr_err("index=%d vco_set_rate failed. rc=%d\n",
+ rc, pll->index);
+ goto error;
+ }
+ }
+
+ /*
+ * For cases where DSI PHY is already enabled like:
+ * 1.) LP-11 during static screen
+ * 2.) ULPS during static screen
+ * 3.) Boot up with cont splash enabled where PHY is programmed in LK
+ * Execute the Re-lock sequence to enable the DSI PLL.
+ */
+ data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SYS_CTRL);
+ if (data & BIT(7)) {
+ rc = dsi_pll_relock(pll);
+ if (rc)
+ goto error;
+ else
+ goto end;
+ }
+
+ mdss_dsi_pll_12nm_calc_reg(pll, pdb);
+ if (pll->ssc_en)
+ mdss_dsi_pll_12nm_calc_ssc(pll, pdb);
+
+ /* commit DSI vco */
+ pll_db_commit_12nm(pll, pdb);
+
+ rc = dsi_pll_enable(c);
+
+error:
+ if (rc) {
+ mdss_pll_resource_enable(pll, false);
+ pr_err("ndx=%d failed to enable dsi pll\n", pll->index);
+ }
+
+end:
+ return rc;
+}
+
+void pll_vco_unprepare_12nm(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return;
+ }
+
+ pll->vco_cached_rate = c->rate;
+ dsi_pll_disable(c);
+}
+
+int pll_vco_enable_12nm(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ u32 data = 0;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ if (!pll->pll_on) {
+ pr_err("DSI PLL not enabled, return\n");
+ return -EINVAL;
+ }
+
+ data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SSC0);
+ data |= BIT(6); /* enable GP_CLK_EN */
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_SSC0, data);
+ wmb(); /* make sure register committed before enabling branch clocks */
+
+ return 0;
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
new file mode 100644
index 0000000..5d2fa9a
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
@@ -0,0 +1,758 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/workqueue.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <dt-bindings/clock/msm-clocks-8952.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-12nm.h"
+
+/*
+ * Clock tree model for generating DSI byte clock and pclk for 12nm DSI PLL
+ *
+ *
+ * +---------------+
+ * +----------| vco_clk |----------+
+ * | +---------------+ |
+ * | |
+ * | |
+ * | |
+ * +---------+---------+----+----+---------+---------+ |
+ * | | | | | | |
+ * | | | | | | |
+ * | | | | | | |
+ * +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ |
+ * | DIV(1)| | DIV(2)| | DIV(4)| | DIV(8)| |DIV(16)| |DIV(32)| |
+ * +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ |
+ * | | | | | | |
+ * | | +---+ +---+ | | |
+ * | +-----------+ | | +-----------+ | |
+ * +-------------------+ | | | | +-------------------+ |
+ * | | | | | | |
+ * +--v-v-v-v-v-v---+ |
+ * \ post_div_mux / |
+ * \ / |
+ * +-----+----+ +---------------------+
+ * | |
+ * +------------------------+ |
+ * | |
+ * +----v----+ +---------+---------+----+----+---------+---------+
+ * | DIV-4 | | | | | | |
+ * +----+----+ | | | | | |
+ * | +---v---+ +---v---+ +---v---+ +---v---+ +---v---+ +---v---+
+ * | | DIV(1)| | DIV(2)| | DIV(4)| | DIV(8)| |DIV(16)| |DIV(32)|
+ * | +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+
+ * | | | | | | |
+ * v | | +---+ +---+ | |
+ * byte_clk_src | +-----------+ | | +-----------+ |
+ * +-------------------+ | | | | +-------------------+
+ * | | | | | |
+ * +--v-v-v-v-v-v---+
+ * \ gp_cntrl_mux /
+ * \ /
+ * +-----+----+
+ * |
+ * |
+ * +-------v-------+
+ * | (DIV + 1) |
+ * | DIV = 0...127 |
+ * +-------+-------+
+ * |
+ * |
+ * v
+ * dsi_pclk input to Clock Controller MND
+ */
+
+static struct dsi_pll_db pll_db[DSI_PLL_NUM];
+
+static struct clk_ops pixel_div_clk_src_ops;
+
+/* Op structures */
+static const struct clk_ops clk_ops_dsi_vco = {
+ .set_rate = pll_vco_set_rate_12nm,
+ .round_rate = pll_vco_round_rate_12nm,
+ .handoff = pll_vco_handoff_12nm,
+ .prepare = pll_vco_prepare_12nm,
+ .unprepare = pll_vco_unprepare_12nm,
+ .enable = pll_vco_enable_12nm,
+};
+
+static struct clk_div_ops pixel_div_ops = {
+ .set_div = pixel_div_set_div,
+ .get_div = pixel_div_get_div,
+};
+
+static struct clk_mux_ops post_div_mux_ops = {
+ .set_mux_sel = set_post_div_mux_sel,
+ .get_mux_sel = get_post_div_mux_sel,
+};
+
+static struct clk_mux_ops gp_div_mux_ops = {
+ .set_mux_sel = set_gp_mux_sel,
+ .get_mux_sel = get_gp_mux_sel,
+};
+
+static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1000000000UL,
+ .max_rate = 2000000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_12nm,
+ .c = {
+ .dbg_name = "dsi0pll_vco_clk_12nm",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi0pll_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1000000000UL,
+ .max_rate = 2000000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_12nm,
+ .c = {
+ .dbg_name = "dsi1pll_vco_clk_12nm",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi1pll_vco_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div1",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div1.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div2",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div2.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div4",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div4.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div8",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div8.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div16",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div16.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_div32",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div32.c),
+ },
+};
+
+static struct mux_clk dsi0pll_post_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_post_div1.c, 0},
+ {&dsi0pll_post_div2.c, 1},
+ {&dsi0pll_post_div4.c, 2},
+ {&dsi0pll_post_div8.c, 3},
+ {&dsi0pll_post_div16.c, 4},
+ {&dsi0pll_post_div32.c, 5},
+ },
+ .ops = &post_div_mux_ops,
+ .c = {
+ .parent = &dsi0pll_post_div1.c,
+ .dbg_name = "dsi0pll_post_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_div_mux.c),
+ }
+};
+
+static struct div_clk dsi1pll_post_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div1",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div1.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div2",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div2.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div4",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div4.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div8",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div8.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div16",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div16.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_div32",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div32.c),
+ },
+};
+
+static struct mux_clk dsi1pll_post_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_post_div1.c, 0},
+ {&dsi1pll_post_div2.c, 1},
+ {&dsi1pll_post_div4.c, 2},
+ {&dsi1pll_post_div8.c, 3},
+ {&dsi1pll_post_div16.c, 4},
+ {&dsi1pll_post_div32.c, 5},
+ },
+ .ops = &post_div_mux_ops,
+ .c = {
+ .parent = &dsi1pll_post_div1.c,
+ .dbg_name = "dsi1pll_post_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_div_mux.c),
+ }
+};
+
+static struct div_clk dsi0pll_gp_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div1",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div1.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div2",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div2.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div4",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div4.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div8",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div8.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div16",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div16.c),
+ },
+};
+
+static struct div_clk dsi0pll_gp_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_gp_div32",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div32.c),
+ },
+};
+
+static struct mux_clk dsi0pll_gp_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_gp_div1.c, 0},
+ {&dsi0pll_gp_div2.c, 1},
+ {&dsi0pll_gp_div4.c, 2},
+ {&dsi0pll_gp_div8.c, 3},
+ {&dsi0pll_gp_div16.c, 4},
+ {&dsi0pll_gp_div32.c, 5},
+ },
+ .ops = &gp_div_mux_ops,
+ .c = {
+ .parent = &dsi0pll_gp_div1.c,
+ .dbg_name = "dsi0pll_gp_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_gp_div_mux.c),
+ }
+};
+
+static struct div_clk dsi1pll_gp_div1 = {
+ .data = {
+ .div = 1,
+ .min_div = 1,
+ .max_div = 1,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div1",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div1.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div2 = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div2",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div2.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div4 = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div4",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div4.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div8 = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div8",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div8.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div16 = {
+ .data = {
+ .div = 16,
+ .min_div = 16,
+ .max_div = 16,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div16",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div16.c),
+ },
+};
+
+static struct div_clk dsi1pll_gp_div32 = {
+ .data = {
+ .div = 32,
+ .min_div = 32,
+ .max_div = 32,
+ },
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_gp_div32",
+ .ops = &clk_ops_slave_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div32.c),
+ },
+};
+
+static struct mux_clk dsi1pll_gp_div_mux = {
+ .num_parents = 6,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_gp_div1.c, 0},
+ {&dsi1pll_gp_div2.c, 1},
+ {&dsi1pll_gp_div4.c, 2},
+ {&dsi1pll_gp_div8.c, 3},
+ {&dsi1pll_gp_div16.c, 4},
+ {&dsi1pll_gp_div32.c, 5},
+ },
+ .ops = &gp_div_mux_ops,
+ .c = {
+ .parent = &dsi1pll_gp_div1.c,
+ .dbg_name = "dsi1pll_gp_div_mux",
+ .ops = &clk_ops_gen_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_gp_div_mux.c),
+ }
+};
+
+static struct div_clk dsi0pll_pixel_clk_src = {
+ .data = {
+ .max_div = 128,
+ .min_div = 1,
+ },
+ .ops = &pixel_div_ops,
+ .c = {
+ .parent = &dsi0pll_gp_div_mux.c,
+ .dbg_name = "dsi0pll_pixel_clk_src",
+ .ops = &pixel_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_pixel_clk_src = {
+ .data = {
+ .max_div = 128,
+ .min_div = 1,
+ },
+ .ops = &pixel_div_ops,
+ .c = {
+ .parent = &dsi1pll_gp_div_mux.c,
+ .dbg_name = "dsi1pll_pixel_clk_src",
+ .ops = &pixel_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi0pll_byte_clk_src = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi0pll_post_div_mux.c,
+ .dbg_name = "dsi0pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi0pll_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_byte_clk_src = {
+ .data = {
+ .div = 4,
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi1pll_post_div_mux.c,
+ .dbg_name = "dsi1pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi1pll_byte_clk_src.c),
+ },
+};
+
+static struct clk_lookup mdss_dsi_pllcc_12nm[] = {
+ CLK_LIST(dsi0pll_vco_clk),
+ CLK_LIST(dsi0pll_byte_clk_src),
+ CLK_LIST(dsi0pll_pixel_clk_src),
+};
+
+static struct clk_lookup mdss_dsi_pllcc_12nm_1[] = {
+ CLK_LIST(dsi1pll_vco_clk),
+ CLK_LIST(dsi1pll_byte_clk_src),
+ CLK_LIST(dsi1pll_pixel_clk_src),
+};
+
+int dsi_pll_clock_register_12nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0, ndx;
+ struct dsi_pll_db *pdb;
+ int const ssc_freq_min = 30000; /* min. recommended freq. value */
+ int const ssc_freq_max = 33000; /* max. recommended freq. value */
+ int const ssc_ppm_max = 5000; /* max. recommended ppm */
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!pll_res || !pll_res->pll_base) {
+ pr_err("Invalid PLL resources\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (pll_res->index >= DSI_PLL_NUM) {
+ pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
+ return -EINVAL;
+ }
+
+ ndx = pll_res->index;
+ pdb = &pll_db[ndx];
+ pll_res->priv = pdb;
+ pdb->pll = pll_res;
+ ndx++;
+ ndx %= DSI_PLL_NUM;
+ pdb->next = &pll_db[ndx];
+
+ /* Set clock source operations */
+
+ /* pixel_clk */
+ pixel_div_clk_src_ops = clk_ops_div;
+ pixel_div_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ if (pll_res->ssc_en) {
+ if (!pll_res->ssc_freq || (pll_res->ssc_freq < ssc_freq_min) ||
+ (pll_res->ssc_freq > ssc_freq_max)) {
+ pll_res->ssc_freq = ssc_freq_min;
+ pr_debug("SSC frequency out of recommended range. Set to default=%d\n",
+ pll_res->ssc_freq);
+ }
+
+ if (!pll_res->ssc_ppm || (pll_res->ssc_ppm > ssc_ppm_max)) {
+ pll_res->ssc_ppm = ssc_ppm_max;
+ pr_debug("SSC PPM out of recommended range. Set to default=%d\n",
+ pll_res->ssc_ppm);
+ }
+ }
+
+ /* Set client data to mux, div and vco clocks. */
+ if (pll_res->index == DSI_PLL_1) {
+ dsi1pll_byte_clk_src.priv = pll_res;
+ dsi1pll_post_div_mux.priv = pll_res;
+ dsi1pll_post_div1.priv = pll_res;
+ dsi1pll_post_div2.priv = pll_res;
+ dsi1pll_post_div4.priv = pll_res;
+ dsi1pll_post_div8.priv = pll_res;
+ dsi1pll_post_div16.priv = pll_res;
+ dsi1pll_post_div32.priv = pll_res;
+ dsi1pll_pixel_clk_src.priv = pll_res;
+ dsi1pll_gp_div_mux.priv = pll_res;
+ dsi1pll_gp_div1.priv = pll_res;
+ dsi1pll_gp_div2.priv = pll_res;
+ dsi1pll_gp_div4.priv = pll_res;
+ dsi1pll_gp_div8.priv = pll_res;
+ dsi1pll_gp_div16.priv = pll_res;
+ dsi1pll_gp_div32.priv = pll_res;
+ dsi1pll_vco_clk.priv = pll_res;
+
+ if (pll_res->target_id == MDSS_PLL_TARGET_SDM439)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_12nm_1,
+ ARRAY_SIZE(mdss_dsi_pllcc_12nm_1));
+ } else {
+ dsi0pll_byte_clk_src.priv = pll_res;
+ dsi0pll_post_div_mux.priv = pll_res;
+ dsi0pll_post_div1.priv = pll_res;
+ dsi0pll_post_div2.priv = pll_res;
+ dsi0pll_post_div4.priv = pll_res;
+ dsi0pll_post_div8.priv = pll_res;
+ dsi0pll_post_div16.priv = pll_res;
+ dsi0pll_post_div32.priv = pll_res;
+ dsi0pll_pixel_clk_src.priv = pll_res;
+ dsi0pll_gp_div_mux.priv = pll_res;
+ dsi0pll_gp_div1.priv = pll_res;
+ dsi0pll_gp_div2.priv = pll_res;
+ dsi0pll_gp_div4.priv = pll_res;
+ dsi0pll_gp_div8.priv = pll_res;
+ dsi0pll_gp_div16.priv = pll_res;
+ dsi0pll_gp_div32.priv = pll_res;
+ dsi0pll_vco_clk.priv = pll_res;
+
+ if (pll_res->target_id == MDSS_PLL_TARGET_SDM439)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_12nm,
+ ARRAY_SIZE(mdss_dsi_pllcc_12nm));
+ }
+
+ if (!rc) {
+ pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
+ pll_res->index);
+ }
+
+ return rc;
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
new file mode 100644
index 0000000..0974717
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MDSS_DSI_PLL_12NM_H
+#define MDSS_DSI_PLL_12NM_H
+
+#define DSIPHY_PLL_POWERUP_CTRL 0x034
+#define DSIPHY_PLL_PROP_CHRG_PUMP_CTRL 0x038
+#define DSIPHY_PLL_INTEG_CHRG_PUMP_CTRL 0x03c
+#define DSIPHY_PLL_ANA_TST_LOCK_ST_OVR_CTRL 0x044
+#define DSIPHY_PLL_VCO_CTRL 0x048
+#define DSIPHY_PLL_GMP_CTRL_DIG_TST 0x04c
+#define DSIPHY_PLL_PHA_ERR_CTRL_0 0x050
+#define DSIPHY_PLL_LOCK_FILTER 0x054
+#define DSIPHY_PLL_UNLOCK_FILTER 0x058
+#define DSIPHY_PLL_INPUT_DIV_PLL_OVR 0x05c
+#define DSIPHY_PLL_LOOP_DIV_RATIO_0 0x060
+#define DSIPHY_PLL_INPUT_LOOP_DIV_RAT_CTRL 0x064
+#define DSIPHY_PLL_PRO_DLY_RELOCK 0x06c
+#define DSIPHY_PLL_CHAR_PUMP_BIAS_CTRL 0x070
+#define DSIPHY_PLL_LOCK_DET_MODE_SEL 0x074
+#define DSIPHY_PLL_ANA_PROG_CTRL 0x07c
+#define DSIPHY_HS_FREQ_RAN_SEL 0x110
+#define DSIPHY_SLEWRATE_FSM_OVR_CTRL 0x280
+#define DSIPHY_SLEWRATE_DDL_LOOP_CTRL 0x28c
+#define DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_0 0x290
+#define DSIPHY_PLL_PHA_ERR_CTRL_1 0x2e4
+#define DSIPHY_PLL_LOOP_DIV_RATIO_1 0x2e8
+#define DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_1 0x328
+#define DSIPHY_SSC0 0x394
+#define DSIPHY_SSC7 0x3b0
+#define DSIPHY_SSC8 0x3b4
+#define DSIPHY_SSC1 0x398
+#define DSIPHY_SSC2 0x39c
+#define DSIPHY_SSC3 0x3a0
+#define DSIPHY_SSC4 0x3a4
+#define DSIPHY_SSC5 0x3a8
+#define DSIPHY_SSC6 0x3ac
+#define DSIPHY_SSC10 0x360
+#define DSIPHY_SSC11 0x364
+#define DSIPHY_SSC12 0x368
+#define DSIPHY_SSC13 0x36c
+#define DSIPHY_SSC14 0x370
+#define DSIPHY_SSC15 0x374
+#define DSIPHY_SSC7 0x3b0
+#define DSIPHY_SSC8 0x3b4
+#define DSIPHY_SSC9 0x3b8
+#define DSIPHY_STAT0 0x3e0
+#define DSIPHY_CTRL0 0x3e8
+#define DSIPHY_SYS_CTRL 0x3f0
+#define DSIPHY_PLL_CTRL 0x3f8
+
+struct dsi_pll_param {
+ u32 hsfreqrange;
+ u32 vco_cntrl;
+ u32 osc_freq_target;
+ u32 m_div;
+ u32 prop_cntrl;
+ u32 int_cntrl;
+ u32 gmp_cntrl;
+ u32 cpbias_cntrl;
+
+ /* mux and dividers */
+ u32 gp_div_mux;
+ u32 post_div_mux;
+ u32 pixel_divhf;
+ u32 fsm_ovr_ctrl;
+
+ /* ssc_params */
+ u32 mpll_ssc_peak_i;
+ u32 mpll_stepsize_i;
+ u32 mpll_mint_i;
+ u32 mpll_frac_den;
+ u32 mpll_frac_quot_i;
+ u32 mpll_frac_rem;
+};
+
+enum {
+ DSI_PLL_0,
+ DSI_PLL_1,
+ DSI_PLL_NUM
+};
+
+struct dsi_pll_db {
+ struct dsi_pll_db *next;
+ struct mdss_pll_resources *pll;
+ struct dsi_pll_param param;
+};
+
+int pll_vco_set_rate_12nm(struct clk *c, unsigned long rate);
+long pll_vco_round_rate_12nm(struct clk *c, unsigned long rate);
+enum handoff pll_vco_handoff_12nm(struct clk *c);
+int pll_vco_prepare_12nm(struct clk *c);
+void pll_vco_unprepare_12nm(struct clk *c);
+int pll_vco_enable_12nm(struct clk *c);
+int pixel_div_set_div(struct div_clk *clk, int div);
+int pixel_div_get_div(struct div_clk *clk);
+int set_post_div_mux_sel(struct mux_clk *clk, int sel);
+int get_post_div_mux_sel(struct mux_clk *clk);
+int set_gp_mux_sel(struct mux_clk *clk, int sel);
+int get_gp_mux_sel(struct mux_clk *clk);
+int dsi_pll_enable_seq_12nm(struct mdss_pll_resources *pll);
+
+#endif /* MDSS_DSI_PLL_12NM_H */
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll.h b/drivers/clk/msm/mdss/mdss-dsi-pll.h
index 4a9bb64..8498259 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll.h
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2015, 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -81,6 +81,8 @@
struct mdss_pll_resources *pll_res);
int dsi_pll_clock_register_8996(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_12nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
int set_byte_mux_sel(struct mux_clk *clk, int sel);
int get_byte_mux_sel(struct mux_clk *clk);
diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c
index 49f3d7b..b8b6883 100644
--- a/drivers/clk/msm/mdss/mdss-pll.c
+++ b/drivers/clk/msm/mdss/mdss-pll.c
@@ -136,6 +136,9 @@
} else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8909")) {
pll_res->pll_interface_type = MDSS_DSI_PLL_LPM;
pll_res->target_id = MDSS_PLL_TARGET_8909;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_sdm439")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_12NM;
+ pll_res->target_id = MDSS_PLL_TARGET_SDM439;
} else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996")) {
pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
pll_res->target_id = MDSS_PLL_TARGET_8996;
@@ -186,6 +189,9 @@
case MDSS_DSI_PLL_8996:
rc = dsi_pll_clock_register_8996(pdev, pll_res);
break;
+ case MDSS_DSI_PLL_12NM:
+ rc = dsi_pll_clock_register_12nm(pdev, pll_res);
+ break;
case MDSS_HDMI_PLL_8996:
rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res);
break;
@@ -403,6 +409,7 @@
{.compatible = "qcom,mdss_dsi_pll_8937"},
{.compatible = "qcom,mdss_dsi_pll_8909"},
{.compatible = "qcom,mdss_dsi_pll_8953"},
+ {.compatible = "qcom,mdss_dsi_pll_sdm439"},
{}
};
diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h
index 1fa5cff..026ac8e 100644
--- a/drivers/clk/msm/mdss/mdss-pll.h
+++ b/drivers/clk/msm/mdss/mdss-pll.h
@@ -31,6 +31,7 @@
enum {
MDSS_DSI_PLL_LPM,
MDSS_DSI_PLL_8996,
+ MDSS_DSI_PLL_12NM,
MDSS_HDMI_PLL_8996,
MDSS_HDMI_PLL_8996_V2,
MDSS_HDMI_PLL_8996_V3,
@@ -44,6 +45,7 @@
MDSS_PLL_TARGET_8937,
MDSS_PLL_TARGET_8953,
MDSS_PLL_TARGET_8909,
+ MDSS_PLL_TARGET_SDM439,
};
struct mdss_pll_resources {
diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c
index 8bccf4e..9ff4ea6 100644
--- a/drivers/clk/mvebu/armada-38x.c
+++ b/drivers/clk/mvebu/armada-38x.c
@@ -46,10 +46,11 @@
}
static const u32 armada_38x_cpu_frequencies[] __initconst = {
- 0, 0, 0, 0,
- 1066 * 1000 * 1000, 0, 0, 0,
+ 666 * 1000 * 1000, 0, 800 * 1000 * 1000, 0,
+ 1066 * 1000 * 1000, 0, 1200 * 1000 * 1000, 0,
1332 * 1000 * 1000, 0, 0, 0,
- 1600 * 1000 * 1000,
+ 1600 * 1000 * 1000, 0, 0, 0,
+ 1866 * 1000 * 1000, 0, 0, 2000 * 1000 * 1000,
};
static u32 __init armada_38x_get_cpu_freq(void __iomem *sar)
@@ -75,11 +76,11 @@
};
static const int armada_38x_cpu_l2_ratios[32][2] __initconst = {
- {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {1, 2}, {0, 1},
+ {1, 2}, {0, 1}, {1, 2}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
- {1, 2}, {0, 1}, {0, 1}, {0, 1},
- {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {1, 2},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
@@ -90,7 +91,7 @@
{1, 2}, {0, 1}, {0, 1}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
- {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {7, 15},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c
index 836c25c..7f9ba03 100644
--- a/drivers/clk/qcom/camcc-sdm845.c
+++ b/drivers/clk/qcom/camcc-sdm845.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -833,6 +833,7 @@
};
static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = {
+ F(8000000, P_CAM_CC_PLL2_OUT_EVEN, 10, 1, 6),
F(19200000, P_BI_TCXO, 1, 0, 0),
F(24000000, P_CAM_CC_PLL2_OUT_EVEN, 10, 1, 2),
F(33333333, P_CAM_CC_PLL0_OUT_EVEN, 2, 1, 9),
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index aaf2324..f759978 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -185,6 +185,7 @@
extern const struct clk_ops clk_pixel_ops;
extern const struct clk_ops clk_gfx3d_ops;
extern const struct clk_ops clk_dp_ops;
+extern const struct clk_ops clk_esc_ops;
extern int clk_rcg2_get_dfs_clock_rate(struct clk_rcg2 *clk,
struct device *dev, u8 rcg_flags);
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 35bcf5a..057f0e1 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -246,12 +246,14 @@
clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f_curr;
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
if (rcg->flags & DFS_ENABLE_RCG)
return rcg->current_freq;
- if (rcg->enable_safe_config && !clk_hw_is_prepared(hw)) {
+ if (rcg->enable_safe_config && (!clk_hw_is_prepared(hw)
+ || !clk_hw_is_enabled(hw))) {
if (!rcg->current_freq)
rcg->current_freq = cxo_f.freq;
return rcg->current_freq;
@@ -271,9 +273,17 @@
mode >>= CFG_MODE_SHIFT;
}
- mask = BIT(rcg->hid_width) - 1;
- hid_div = cfg >> CFG_SRC_DIV_SHIFT;
- hid_div &= mask;
+ if (rcg->enable_safe_config) {
+ f_curr = qcom_find_freq(rcg->freq_tbl, rcg->current_freq);
+ if (!f_curr)
+ return -EINVAL;
+
+ hid_div = f_curr->pre_div;
+ } else {
+ mask = BIT(rcg->hid_width) - 1;
+ hid_div = cfg >> CFG_SRC_DIV_SHIFT;
+ hid_div &= mask;
+ }
return clk_rcg2_calc_rate(parent_rate, m, n, mode, hid_div);
}
@@ -1181,6 +1191,76 @@
};
EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
+static int clk_esc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ unsigned long parent_rate, div;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ struct clk_hw *p;
+ unsigned long rate = req->rate;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ p = req->best_parent_hw;
+ req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate);
+
+ div = ((2 * parent_rate) / rate) - 1;
+ div = min_t(u32, div, mask);
+
+ req->rate = clk_rcg2_calc_rate(parent_rate, 0, 0, 0, div);
+
+ return 0;
+}
+
+static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ struct freq_tbl f = { 0 };
+ unsigned long div;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 cfg;
+
+ div = ((2 * parent_rate) / rate) - 1;
+ div = min_t(u32, div, mask);
+
+ f.pre_div = div;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++) {
+ if (cfg == rcg->parent_map[i].cfg) {
+ f.src = rcg->parent_map[i].src;
+ return clk_rcg2_configure(rcg, &f);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int clk_esc_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ return clk_esc_set_rate(hw, rate, parent_rate);
+}
+
+const struct clk_ops clk_esc_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .determine_rate = clk_esc_determine_rate,
+ .set_rate = clk_esc_set_rate,
+ .set_rate_and_parent = clk_esc_set_rate_and_parent,
+ .list_registers = clk_rcg2_list_registers,
+};
+EXPORT_SYMBOL(clk_esc_ops);
+
/* Common APIs to be used for DFS based RCGR */
static u8 clk_parent_index_pre_div_and_mode(struct clk_hw *hw, u32 offset,
u32 *mode, u32 *pre_div)
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index d426691..316ac39 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2017-2018,
+ * The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -30,7 +31,9 @@
struct qcom_cc {
struct qcom_reset_controller reset;
struct clk_regmap **rclks;
+ struct clk_hw **hwclks;
size_t num_rclks;
+ size_t num_hwclks;
};
const
@@ -182,11 +185,14 @@
struct qcom_cc *cc = data;
unsigned int idx = clkspec->args[0];
- if (idx >= cc->num_rclks) {
+ if (idx >= cc->num_rclks + cc->num_hwclks) {
pr_err("invalid index %u\n", idx);
return ERR_PTR(-EINVAL);
}
+ if (idx < cc->num_hwclks && cc->hwclks[idx])
+ return cc->hwclks[idx];
+
return cc->rclks[idx] ? &cc->rclks[idx]->hw : ERR_PTR(-ENOENT);
}
@@ -199,7 +205,9 @@
struct qcom_cc *cc;
struct gdsc_desc *scd;
size_t num_clks = desc->num_clks;
+ size_t num_hwclks = desc->num_hwclks;
struct clk_regmap **rclks = desc->clks;
+ struct clk_hw **hwclks = desc->hwclks;
cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
if (!cc)
@@ -207,6 +215,17 @@
cc->rclks = rclks;
cc->num_rclks = num_clks;
+ cc->hwclks = hwclks;
+ cc->num_hwclks = num_hwclks;
+
+ for (i = 0; i < num_hwclks; i++) {
+ if (!hwclks[i])
+ continue;
+
+ ret = devm_clk_hw_register(dev, hwclks[i]);
+ if (ret)
+ return ret;
+ }
for (i = 0; i < num_clks; i++) {
if (!rclks[i])
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 5e26763..29c4697 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -28,7 +28,9 @@
struct qcom_cc_desc {
const struct regmap_config *config;
struct clk_regmap **clks;
+ struct clk_hw **hwclks;
size_t num_clks;
+ size_t num_hwclks;
const struct qcom_reset_map *resets;
size_t num_resets;
struct gdsc **gdscs;
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index d4f27d7..0c49fa4 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -328,13 +328,12 @@
.mnd_width = 0,
.hid_width = 5,
.parent_map = disp_cc_parent_map_0,
- .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_esc0_clk_src",
.parent_names = disp_cc_parent_names_0,
.num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_rcg2_ops,
+ .ops = &clk_esc_ops,
VDD_CX_FMAX_MAP1(
MIN, 19200000),
},
@@ -345,13 +344,12 @@
.mnd_width = 0,
.hid_width = 5,
.parent_map = disp_cc_parent_map_0,
- .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_esc1_clk_src",
.parent_names = disp_cc_parent_names_0,
.num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_rcg2_ops,
+ .ops = &clk_esc_ops,
VDD_CX_FMAX_MAP1(
MIN, 19200000),
},
diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c
index 5c4e193..8dd7134 100644
--- a/drivers/clk/qcom/gcc-msm8916.c
+++ b/drivers/clk/qcom/gcc-msm8916.c
@@ -1437,6 +1437,7 @@
static struct clk_rcg2 codec_digcodec_clk_src = {
.cmd_rcgr = 0x1c09c,
+ .mnd_width = 8,
.hid_width = 5,
.parent_map = gcc_xo_gpll1_emclk_sleep_map,
.freq_tbl = ftbl_codec_clk,
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 555b8bd..7ea5d9d 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -35,6 +35,7 @@
#include "reset.h"
#include "clk-alpha-pll.h"
#include "vdd-level-sdm845.h"
+#include "clk-voter.h"
#define GCC_MMSS_MISC 0x09FFC
#define GCC_GPU_MISC 0x71028
@@ -1505,6 +1506,11 @@
},
};
+static DEFINE_CLK_VOTER(ufs_phy_axi_emmc_vote_clk,
+ gcc_aggre_ufs_phy_axi_clk, 0);
+static DEFINE_CLK_VOTER(ufs_phy_axi_ufs_vote_clk,
+ gcc_aggre_ufs_phy_axi_clk, 0);
+
static struct clk_branch gcc_aggre_ufs_phy_axi_hw_ctl_clk = {
.halt_reg = 0x82024,
.clkr = {
@@ -3780,6 +3786,8 @@
[MEASURE_ONLY_CNOC_CLK] = &measure_only_cnoc_clk.hw,
[MEASURE_ONLY_BIMC_CLK] = &measure_only_bimc_clk.hw,
[MEASURE_ONLY_IPA_2X_CLK] = &measure_only_ipa_2x_clk.hw,
+ [UFS_PHY_AXI_EMMC_VOTE_CLK] = &ufs_phy_axi_emmc_vote_clk.hw,
+ [UFS_PHY_AXI_UFS_VOTE_CLK] = &ufs_phy_axi_ufs_vote_clk.hw,
};
static struct clk_regmap *gcc_sdm845_clocks[] = {
@@ -4061,6 +4069,8 @@
.config = &gcc_sdm845_regmap_config,
.clks = gcc_sdm845_clocks,
.num_clks = ARRAY_SIZE(gcc_sdm845_clocks),
+ .hwclks = gcc_sdm845_hws,
+ .num_hwclks = ARRAY_SIZE(gcc_sdm845_hws),
.resets = gcc_sdm845_resets,
.num_resets = ARRAY_SIZE(gcc_sdm845_resets),
};
@@ -4279,9 +4289,8 @@
static int gcc_sdm845_probe(struct platform_device *pdev)
{
- struct clk *clk;
struct regmap *regmap;
- int i, ret = 0;
+ int ret = 0;
regmap = qcom_cc_map(pdev, &gcc_sdm845_desc);
if (IS_ERR(regmap))
@@ -4307,13 +4316,6 @@
if (ret)
return ret;
- /* Register the dummy measurement clocks */
- for (i = 0; i < ARRAY_SIZE(gcc_sdm845_hws); i++) {
- clk = devm_clk_register(&pdev->dev, gcc_sdm845_hws[i]);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
- }
-
ret = qcom_cc_really_probe(pdev, &gcc_sdm845_desc, regmap);
if (ret) {
dev_err(&pdev->dev, "Failed to register GCC clocks\n");
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
index 874c229..7b23db4 100644
--- a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
@@ -174,6 +174,7 @@
unsigned int *val)
{
int rc = 0;
+ u32 data;
struct mdss_pll_resources *rsc = context;
rc = mdss_pll_resource_enable(rsc, true);
@@ -182,7 +183,19 @@
return rc;
}
+ /*
+ * DSI PHY/PLL should be both powered on when reading PLL
+ * registers. Since PHY power has been enabled in DSI PHY
+ * driver, only PLL power is needed to enable here.
+ */
+ data = MDSS_PLL_REG_R(rsc->phy_base, PHY_CMN_CTRL_0);
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data | BIT(5));
+ ndelay(250);
+
*val = MDSS_PLL_REG_R(rsc->pll_base, reg);
+
+ MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_0, data);
+
(void)mdss_pll_resource_enable(rsc, false);
return rc;
diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c
index ca97e11..3b171be 100644
--- a/drivers/clk/qcom/mmcc-msm8996.c
+++ b/drivers/clk/qcom/mmcc-msm8996.c
@@ -2984,7 +2984,7 @@
.cxcs = (unsigned int []){ 0x36ac },
.cxc_count = 1,
.pd = {
- .name = "vfe0",
+ .name = "vfe1",
},
.parent = &camss_gdsc.pd,
.pwrsts = PWRSTS_OFF_ON,
diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c
index 00e6aba..c55d5fe 100644
--- a/drivers/clk/renesas/clk-rcar-gen2.c
+++ b/drivers/clk/renesas/clk-rcar-gen2.c
@@ -271,11 +271,14 @@
unsigned int extal_div;
unsigned int pll1_mult;
unsigned int pll3_mult;
+ unsigned int pll0_mult; /* For R-Car V2H and E2 only */
};
static const struct cpg_pll_config cpg_pll_configs[8] __initconst = {
- { 1, 208, 106 }, { 1, 208, 88 }, { 1, 156, 80 }, { 1, 156, 66 },
- { 2, 240, 122 }, { 2, 240, 102 }, { 2, 208, 106 }, { 2, 208, 88 },
+ { 1, 208, 106, 200 }, { 1, 208, 88, 200 },
+ { 1, 156, 80, 150 }, { 1, 156, 66, 150 },
+ { 2, 240, 122, 230 }, { 2, 240, 102, 230 },
+ { 2, 208, 106, 200 }, { 2, 208, 88, 200 },
};
/* SDHI divisors */
@@ -297,6 +300,12 @@
static u32 cpg_mode __initdata;
+static const char * const pll0_mult_match[] = {
+ "renesas,r8a7792-cpg-clocks",
+ "renesas,r8a7794-cpg-clocks",
+ NULL
+};
+
static struct clk * __init
rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
const struct cpg_pll_config *config,
@@ -317,9 +326,15 @@
* clock implementation and we currently have no need to change
* the multiplier value.
*/
- u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
+ if (of_device_compatible_match(np, pll0_mult_match)) {
+ /* R-Car V2H and E2 do not have PLL0CR */
+ mult = config->pll0_mult;
+ div = 3;
+ } else {
+ u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
+ mult = ((value >> 24) & ((1 << 7) - 1)) + 1;
+ }
parent_name = "main";
- mult = ((value >> 24) & ((1 << 7) - 1)) + 1;
} else if (!strcmp(name, "pll1")) {
parent_name = "main";
mult = config->pll1_mult / 2;
diff --git a/drivers/clk/renesas/clk-sh73a0.c b/drivers/clk/renesas/clk-sh73a0.c
index eea38f6..3892346 100644
--- a/drivers/clk/renesas/clk-sh73a0.c
+++ b/drivers/clk/renesas/clk-sh73a0.c
@@ -46,7 +46,7 @@
unsigned int shift;
};
-static struct div4_clk div4_clks[] = {
+static const struct div4_clk div4_clks[] = {
{ "zg", "pll0", CPG_FRQCRA, 16 },
{ "m3", "pll1", CPG_FRQCRA, 12 },
{ "b", "pll1", CPG_FRQCRA, 8 },
@@ -79,7 +79,7 @@
{
const struct clk_div_table *table = NULL;
unsigned int shift, reg, width;
- const char *parent_name;
+ const char *parent_name = NULL;
unsigned int mult = 1;
unsigned int div = 1;
@@ -135,7 +135,7 @@
shift = 24;
width = 5;
} else {
- struct div4_clk *c;
+ const struct div4_clk *c;
for (c = div4_clks; c->name; c++) {
if (!strcmp(name, c->name)) {
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
index 077fcdc..fe7d9ed 100644
--- a/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -58,6 +58,12 @@
u16 degrees;
u32 delay_num = 0;
+ /* See the comment for rockchip_mmc_set_phase below */
+ if (!rate) {
+ pr_err("%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
@@ -84,6 +90,23 @@
u32 raw_value;
u32 delay;
+ /*
+ * The below calculation is based on the output clock from
+ * MMC host to the card, which expects the phase clock inherits
+ * the clock rate from its parent, namely the output clock
+ * provider of MMC host. However, things may go wrong if
+ * (1) It is orphan.
+ * (2) It is assigned to the wrong parent.
+ *
+ * This check help debug the case (1), which seems to be the
+ * most likely problem we often face and which makes it difficult
+ * for people to debug unstable mmc tuning results.
+ */
+ if (!rate) {
+ pr_err("%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
nineties = degrees / 90;
remainder = (degrees % 90);
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index db6e5a9..53f16ef 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -369,7 +369,7 @@
RK2928_CLKSEL_CON(23), 5, 2, MFLAGS, 0, 6, DFLAGS,
RK2928_CLKGATE_CON(2), 15, GFLAGS),
- COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0,
+ COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0,
RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK2928_CLKGATE_CON(2), 11, GFLAGS),
diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c
index 1b81e28..ed36728 100644
--- a/drivers/clk/samsung/clk-exynos3250.c
+++ b/drivers/clk/samsung/clk-exynos3250.c
@@ -698,7 +698,7 @@
PLL_36XX_RATE(144000000, 96, 2, 3, 0),
PLL_36XX_RATE( 96000000, 128, 2, 4, 0),
PLL_36XX_RATE( 84000000, 112, 2, 4, 0),
- PLL_36XX_RATE( 80000004, 106, 2, 4, 43691),
+ PLL_36XX_RATE( 80000003, 106, 2, 4, 43691),
PLL_36XX_RATE( 73728000, 98, 2, 4, 19923),
PLL_36XX_RATE( 67737598, 270, 3, 5, 62285),
PLL_36XX_RATE( 65535999, 174, 2, 5, 49982),
@@ -734,7 +734,7 @@
PLL_36XX_RATE(148352005, 98, 2, 3, 59070),
PLL_36XX_RATE(108000000, 144, 2, 4, 0),
PLL_36XX_RATE( 74250000, 99, 2, 4, 0),
- PLL_36XX_RATE( 74176002, 98, 3, 4, 59070),
+ PLL_36XX_RATE( 74176002, 98, 2, 4, 59070),
PLL_36XX_RATE( 54054000, 216, 3, 5, 14156),
PLL_36XX_RATE( 54000000, 144, 2, 5, 0),
{ /* sentinel */ }
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index 27a227d..6a0cb8a 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -711,13 +711,13 @@
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
PLL_36XX_RATE(192000000, 64, 2, 2, 0),
- PLL_36XX_RATE(180633600, 90, 3, 2, 20762),
+ PLL_36XX_RATE(180633605, 90, 3, 2, 20762),
PLL_36XX_RATE(180000000, 90, 3, 2, 0),
PLL_36XX_RATE(73728000, 98, 2, 4, 19923),
- PLL_36XX_RATE(67737600, 90, 2, 4, 20762),
+ PLL_36XX_RATE(67737602, 90, 2, 4, 20762),
PLL_36XX_RATE(49152000, 98, 3, 4, 19923),
- PLL_36XX_RATE(45158400, 90, 3, 4, 20762),
- PLL_36XX_RATE(32768000, 131, 3, 5, 4719),
+ PLL_36XX_RATE(45158401, 90, 3, 4, 20762),
+ PLL_36XX_RATE(32768001, 131, 3, 5, 4719),
{ },
};
diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c
index fd1d9bf..8eae175 100644
--- a/drivers/clk/samsung/clk-exynos5260.c
+++ b/drivers/clk/samsung/clk-exynos5260.c
@@ -65,7 +65,7 @@
PLL_36XX_RATE(480000000, 160, 2, 2, 0),
PLL_36XX_RATE(432000000, 144, 2, 2, 0),
PLL_36XX_RATE(400000000, 200, 3, 2, 0),
- PLL_36XX_RATE(394073130, 459, 7, 2, 49282),
+ PLL_36XX_RATE(394073128, 459, 7, 2, 49282),
PLL_36XX_RATE(333000000, 111, 2, 2, 0),
PLL_36XX_RATE(300000000, 100, 2, 2, 0),
PLL_36XX_RATE(266000000, 266, 3, 3, 0),
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 2fe0573..09cdd35 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -725,7 +725,7 @@
PLL_35XX_RATE(800000000U, 400, 6, 1),
PLL_35XX_RATE(733000000U, 733, 12, 1),
PLL_35XX_RATE(700000000U, 175, 3, 1),
- PLL_35XX_RATE(667000000U, 222, 4, 1),
+ PLL_35XX_RATE(666000000U, 222, 4, 1),
PLL_35XX_RATE(633000000U, 211, 4, 1),
PLL_35XX_RATE(600000000U, 500, 5, 2),
PLL_35XX_RATE(552000000U, 460, 5, 2),
@@ -751,12 +751,12 @@
/* AUD_PLL */
static const struct samsung_pll_rate_table exynos5443_aud_pll_rates[] __initconst = {
PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
- PLL_36XX_RATE(393216000U, 197, 3, 2, -25690),
+ PLL_36XX_RATE(393216003U, 197, 3, 2, -25690),
PLL_36XX_RATE(384000000U, 128, 2, 2, 0),
- PLL_36XX_RATE(368640000U, 246, 4, 2, -15729),
- PLL_36XX_RATE(361507200U, 181, 3, 2, -16148),
- PLL_36XX_RATE(338688000U, 113, 2, 2, -6816),
- PLL_36XX_RATE(294912000U, 98, 1, 3, 19923),
+ PLL_36XX_RATE(368639991U, 246, 4, 2, -15729),
+ PLL_36XX_RATE(361507202U, 181, 3, 2, -16148),
+ PLL_36XX_RATE(338687988U, 113, 2, 2, -6816),
+ PLL_36XX_RATE(294912002U, 98, 1, 3, 19923),
PLL_36XX_RATE(288000000U, 96, 1, 3, 0),
PLL_36XX_RATE(252000000U, 84, 1, 3, 0),
{ /* sentinel */ }
diff --git a/drivers/clk/samsung/clk-exynos7.c b/drivers/clk/samsung/clk-exynos7.c
index 5931a41..bbfa57b 100644
--- a/drivers/clk/samsung/clk-exynos7.c
+++ b/drivers/clk/samsung/clk-exynos7.c
@@ -140,7 +140,7 @@
};
static const struct samsung_pll_rate_table pll1460x_24mhz_tbl[] __initconst = {
- PLL_36XX_RATE(491520000, 20, 1, 0, 31457),
+ PLL_36XX_RATE(491519897, 20, 1, 0, 31457),
{},
};
diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
index d7a1e77..5f50037 100644
--- a/drivers/clk/samsung/clk-s3c2410.c
+++ b/drivers/clk/samsung/clk-s3c2410.c
@@ -168,7 +168,7 @@
PLL_35XX_RATE(226000000, 105, 1, 1),
PLL_35XX_RATE(210000000, 132, 2, 1),
/* 2410 common */
- PLL_35XX_RATE(203000000, 161, 3, 1),
+ PLL_35XX_RATE(202800000, 161, 3, 1),
PLL_35XX_RATE(192000000, 88, 1, 1),
PLL_35XX_RATE(186000000, 85, 1, 1),
PLL_35XX_RATE(180000000, 82, 1, 1),
@@ -178,18 +178,18 @@
PLL_35XX_RATE(147000000, 90, 2, 1),
PLL_35XX_RATE(135000000, 82, 2, 1),
PLL_35XX_RATE(124000000, 116, 1, 2),
- PLL_35XX_RATE(118000000, 150, 2, 2),
+ PLL_35XX_RATE(118500000, 150, 2, 2),
PLL_35XX_RATE(113000000, 105, 1, 2),
- PLL_35XX_RATE(101000000, 127, 2, 2),
+ PLL_35XX_RATE(101250000, 127, 2, 2),
PLL_35XX_RATE(90000000, 112, 2, 2),
- PLL_35XX_RATE(85000000, 105, 2, 2),
+ PLL_35XX_RATE(84750000, 105, 2, 2),
PLL_35XX_RATE(79000000, 71, 1, 2),
- PLL_35XX_RATE(68000000, 82, 2, 2),
- PLL_35XX_RATE(56000000, 142, 2, 3),
+ PLL_35XX_RATE(67500000, 82, 2, 2),
+ PLL_35XX_RATE(56250000, 142, 2, 3),
PLL_35XX_RATE(48000000, 120, 2, 3),
- PLL_35XX_RATE(51000000, 161, 3, 3),
+ PLL_35XX_RATE(50700000, 161, 3, 3),
PLL_35XX_RATE(45000000, 82, 1, 3),
- PLL_35XX_RATE(34000000, 82, 2, 3),
+ PLL_35XX_RATE(33750000, 82, 2, 3),
{ /* sentinel */ },
};
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
index 9fe0939..6ea5401 100644
--- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
@@ -750,7 +750,7 @@
.features = CCU_FEATURE_FIXED_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS("out-a",
clk_out_parents,
- &ccu_div_ops,
+ &ccu_mp_ops,
0),
},
};
@@ -771,7 +771,7 @@
.features = CCU_FEATURE_FIXED_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS("out-b",
clk_out_parents,
- &ccu_div_ops,
+ &ccu_mp_ops,
0),
},
};
@@ -792,7 +792,7 @@
.features = CCU_FEATURE_FIXED_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS("out-c",
clk_out_parents,
- &ccu_div_ops,
+ &ccu_mp_ops,
0),
},
};
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index b385536..66d1fc7 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -1145,6 +1145,8 @@
.enable = clk_pllu_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_pll_set_rate,
};
static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cd6d307..91cf857 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -331,6 +331,15 @@
This option enables support for reading the ARM architected timer's
virtual counter in userspace.
+config MSM_TIMER_LEAP
+ bool "ARCH TIMER counter rollover"
+ default n
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a check for least significant 32 bits of
+ counter rollover. On every counter read if least significant
+ 32 bits are set, reread counter.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 597aa57..379080c 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -45,6 +45,15 @@
If in doubt, say N.
+config CPU_FREQ_TIMES
+ bool "CPU frequency time-in-state statistics"
+ default y
+ help
+ This driver exports CPU time-in-state information through procfs file
+ system.
+
+ If in doubt, say N.
+
choice
prompt "Default CPUFreq governor"
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index bf98b28..ba9f9405 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -4,7 +4,10 @@
# CPUfreq stats
obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o
-# CPUfreq governors
+# CPUfreq times
+obj-$(CONFIG_CPU_FREQ_TIMES) += cpufreq_times.o
+
+# CPUfreq governors
obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o
obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o
obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 8059ef9..7279448 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -19,6 +19,7 @@
#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/cpufreq_times.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
@@ -444,6 +445,7 @@
(unsigned long)freqs->new, (unsigned long)freqs->cpu);
trace_cpu_frequency(freqs->new, freqs->cpu);
cpufreq_stats_record_transition(policy, freqs->new);
+ cpufreq_times_record_transition(freqs);
srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_POSTCHANGE, freqs);
if (likely(policy) && likely(policy->cpu == freqs->cpu))
@@ -1373,6 +1375,7 @@
goto out_exit_policy;
cpufreq_stats_create_table(policy);
+ cpufreq_times_create_policy(policy);
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
CPUFREQ_CREATE_POLICY, policy);
@@ -1651,7 +1654,10 @@
if (policy) {
down_read(&policy->rwsem);
- ret_freq = __cpufreq_get(policy);
+
+ if (!policy_is_inactive(policy))
+ ret_freq = __cpufreq_get(policy);
+
up_read(&policy->rwsem);
cpufreq_cpu_put(policy);
@@ -2394,6 +2400,11 @@
down_write(&policy->rwsem);
+ if (policy_is_inactive(policy)) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
pr_debug("updating policy for CPU %u\n", cpu);
memcpy(&new_policy, policy, sizeof(*policy));
new_policy.min = policy->user_policy.min;
diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c
new file mode 100644
index 0000000..6254f45
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_times.c
@@ -0,0 +1,461 @@
+/* drivers/cpufreq/cpufreq_times.c
+ *
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/cpufreq_times.h>
+#include <linux/cputime.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+
+#define UID_HASH_BITS 10
+
+static DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS);
+
+static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */
+static DEFINE_SPINLOCK(uid_lock); /* uid_hash_table */
+
+struct uid_entry {
+ uid_t uid;
+ unsigned int max_state;
+ struct hlist_node hash;
+ struct rcu_head rcu;
+ u64 time_in_state[0];
+};
+
+/**
+ * struct cpu_freqs - per-cpu frequency information
+ * @offset: start of these freqs' stats in task time_in_state array
+ * @max_state: number of entries in freq_table
+ * @last_index: index in freq_table of last frequency switched to
+ * @freq_table: list of available frequencies
+ */
+struct cpu_freqs {
+ unsigned int offset;
+ unsigned int max_state;
+ unsigned int last_index;
+ unsigned int freq_table[0];
+};
+
+static struct cpu_freqs *all_freqs[NR_CPUS];
+
+static unsigned int next_offset;
+
+
+/* Caller must hold rcu_read_lock() */
+static struct uid_entry *find_uid_entry_rcu(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+
+ hash_for_each_possible_rcu(uid_hash_table, uid_entry, hash, uid) {
+ if (uid_entry->uid == uid)
+ return uid_entry;
+ }
+ return NULL;
+}
+
+/* Caller must hold uid lock */
+static struct uid_entry *find_uid_entry_locked(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+
+ hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) {
+ if (uid_entry->uid == uid)
+ return uid_entry;
+ }
+ return NULL;
+}
+
+/* Caller must hold uid lock */
+static struct uid_entry *find_or_register_uid_locked(uid_t uid)
+{
+ struct uid_entry *uid_entry, *temp;
+ unsigned int max_state = READ_ONCE(next_offset);
+ size_t alloc_size = sizeof(*uid_entry) + max_state *
+ sizeof(uid_entry->time_in_state[0]);
+
+ uid_entry = find_uid_entry_locked(uid);
+ if (uid_entry) {
+ if (uid_entry->max_state == max_state)
+ return uid_entry;
+ /* uid_entry->time_in_state is too small to track all freqs, so
+ * expand it.
+ */
+ temp = __krealloc(uid_entry, alloc_size, GFP_ATOMIC);
+ if (!temp)
+ return uid_entry;
+ temp->max_state = max_state;
+ memset(temp->time_in_state + uid_entry->max_state, 0,
+ (max_state - uid_entry->max_state) *
+ sizeof(uid_entry->time_in_state[0]));
+ if (temp != uid_entry) {
+ hlist_replace_rcu(&uid_entry->hash, &temp->hash);
+ kfree_rcu(uid_entry, rcu);
+ }
+ return temp;
+ }
+
+ uid_entry = kzalloc(alloc_size, GFP_ATOMIC);
+ if (!uid_entry)
+ return NULL;
+
+ uid_entry->uid = uid;
+ uid_entry->max_state = max_state;
+
+ hash_add_rcu(uid_hash_table, &uid_entry->hash, uid);
+
+ return uid_entry;
+}
+
+static bool freq_index_invalid(unsigned int index)
+{
+ unsigned int cpu;
+ struct cpu_freqs *freqs;
+
+ for_each_possible_cpu(cpu) {
+ freqs = all_freqs[cpu];
+ if (!freqs || index < freqs->offset ||
+ freqs->offset + freqs->max_state <= index)
+ continue;
+ return freqs->freq_table[index - freqs->offset] ==
+ CPUFREQ_ENTRY_INVALID;
+ }
+ return true;
+}
+
+static int single_uid_time_in_state_show(struct seq_file *m, void *ptr)
+{
+ struct uid_entry *uid_entry;
+ unsigned int i;
+ u64 time;
+ uid_t uid = from_kuid_munged(current_user_ns(), *(kuid_t *)m->private);
+
+ if (uid == overflowuid)
+ return -EINVAL;
+
+ rcu_read_lock();
+
+ uid_entry = find_uid_entry_rcu(uid);
+ if (!uid_entry) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ for (i = 0; i < uid_entry->max_state; ++i) {
+ if (freq_index_invalid(i))
+ continue;
+ time = cputime_to_clock_t(uid_entry->time_in_state[i]);
+ seq_write(m, &time, sizeof(time));
+ }
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static void *uid_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ if (*pos >= HASH_SIZE(uid_hash_table))
+ return NULL;
+
+ return &uid_hash_table[*pos];
+}
+
+static void *uid_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ (*pos)++;
+
+ if (*pos >= HASH_SIZE(uid_hash_table))
+ return NULL;
+
+ return &uid_hash_table[*pos];
+}
+
+static void uid_seq_stop(struct seq_file *seq, void *v) { }
+
+static int uid_time_in_state_seq_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry;
+ struct cpu_freqs *freqs, *last_freqs = NULL;
+ int i, cpu;
+
+ if (v == uid_hash_table) {
+ seq_puts(m, "uid:");
+ for_each_possible_cpu(cpu) {
+ freqs = all_freqs[cpu];
+ if (!freqs || freqs == last_freqs)
+ continue;
+ last_freqs = freqs;
+ for (i = 0; i < freqs->max_state; i++) {
+ if (freqs->freq_table[i] ==
+ CPUFREQ_ENTRY_INVALID)
+ continue;
+ seq_printf(m, " %d", freqs->freq_table[i]);
+ }
+ }
+ seq_putc(m, '\n');
+ }
+
+ rcu_read_lock();
+
+ hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) {
+ if (uid_entry->max_state)
+ seq_printf(m, "%d:", uid_entry->uid);
+ for (i = 0; i < uid_entry->max_state; ++i) {
+ if (freq_index_invalid(i))
+ continue;
+ seq_printf(m, " %lu", (unsigned long)cputime_to_clock_t(
+ uid_entry->time_in_state[i]));
+ }
+ if (uid_entry->max_state)
+ seq_putc(m, '\n');
+ }
+
+ rcu_read_unlock();
+ return 0;
+}
+
+void cpufreq_task_times_init(struct task_struct *p)
+{
+ void *temp;
+ unsigned long flags;
+ unsigned int max_state;
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ p->time_in_state = NULL;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ p->max_state = 0;
+
+ max_state = READ_ONCE(next_offset);
+
+ /* We use one array to avoid multiple allocs per task */
+ temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC);
+ if (!temp)
+ return;
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ p->time_in_state = temp;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ p->max_state = max_state;
+}
+
+/* Caller must hold task_time_in_state_lock */
+static int cpufreq_task_times_realloc_locked(struct task_struct *p)
+{
+ void *temp;
+ unsigned int max_state = READ_ONCE(next_offset);
+
+ temp = krealloc(p->time_in_state, max_state * sizeof(u64), GFP_ATOMIC);
+ if (!temp)
+ return -ENOMEM;
+ p->time_in_state = temp;
+ memset(p->time_in_state + p->max_state, 0,
+ (max_state - p->max_state) * sizeof(u64));
+ p->max_state = max_state;
+ return 0;
+}
+
+void cpufreq_task_times_exit(struct task_struct *p)
+{
+ unsigned long flags;
+ void *temp;
+
+ if (!p->time_in_state)
+ return;
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ temp = p->time_in_state;
+ p->time_in_state = NULL;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ kfree(temp);
+}
+
+int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *p)
+{
+ unsigned int cpu, i;
+ cputime_t cputime;
+ unsigned long flags;
+ struct cpu_freqs *freqs;
+ struct cpu_freqs *last_freqs = NULL;
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ for_each_possible_cpu(cpu) {
+ freqs = all_freqs[cpu];
+ if (!freqs || freqs == last_freqs)
+ continue;
+ last_freqs = freqs;
+
+ seq_printf(m, "cpu%u\n", cpu);
+ for (i = 0; i < freqs->max_state; i++) {
+ if (freqs->freq_table[i] == CPUFREQ_ENTRY_INVALID)
+ continue;
+ cputime = 0;
+ if (freqs->offset + i < p->max_state &&
+ p->time_in_state)
+ cputime = p->time_in_state[freqs->offset + i];
+ seq_printf(m, "%u %lu\n", freqs->freq_table[i],
+ (unsigned long)cputime_to_clock_t(cputime));
+ }
+ }
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ return 0;
+}
+
+void cpufreq_acct_update_power(struct task_struct *p, cputime_t cputime)
+{
+ unsigned long flags;
+ unsigned int state;
+ struct uid_entry *uid_entry;
+ struct cpu_freqs *freqs = all_freqs[task_cpu(p)];
+ uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
+
+ if (!freqs || p->flags & PF_EXITING)
+ return;
+
+ state = freqs->offset + READ_ONCE(freqs->last_index);
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) &&
+ p->time_in_state)
+ p->time_in_state[state] += cputime;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+
+ spin_lock_irqsave(&uid_lock, flags);
+ uid_entry = find_or_register_uid_locked(uid);
+ if (uid_entry && state < uid_entry->max_state)
+ uid_entry->time_in_state[state] += cputime;
+ spin_unlock_irqrestore(&uid_lock, flags);
+}
+
+void cpufreq_times_create_policy(struct cpufreq_policy *policy)
+{
+ int cpu, index;
+ unsigned int count = 0;
+ struct cpufreq_frequency_table *pos, *table;
+ struct cpu_freqs *freqs;
+ void *tmp;
+
+ if (all_freqs[policy->cpu])
+ return;
+
+ table = policy->freq_table;
+ if (!table)
+ return;
+
+ cpufreq_for_each_entry(pos, table)
+ count++;
+
+ tmp = kzalloc(sizeof(*freqs) + sizeof(freqs->freq_table[0]) * count,
+ GFP_KERNEL);
+ if (!tmp)
+ return;
+
+ freqs = tmp;
+ freqs->max_state = count;
+
+ index = cpufreq_frequency_table_get_index(policy, policy->cur);
+ if (index >= 0)
+ WRITE_ONCE(freqs->last_index, index);
+
+ cpufreq_for_each_entry(pos, table)
+ freqs->freq_table[pos - table] = pos->frequency;
+
+ freqs->offset = next_offset;
+ WRITE_ONCE(next_offset, freqs->offset + count);
+ for_each_cpu(cpu, policy->related_cpus)
+ all_freqs[cpu] = freqs;
+}
+
+void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end)
+{
+ struct uid_entry *uid_entry;
+ struct hlist_node *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uid_lock, flags);
+
+ for (; uid_start <= uid_end; uid_start++) {
+ hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp,
+ hash, uid_start) {
+ if (uid_start == uid_entry->uid) {
+ hash_del_rcu(&uid_entry->hash);
+ kfree_rcu(uid_entry, rcu);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&uid_lock, flags);
+}
+
+void cpufreq_times_record_transition(struct cpufreq_freqs *freq)
+{
+ int index;
+ struct cpu_freqs *freqs = all_freqs[freq->cpu];
+ struct cpufreq_policy *policy;
+
+ if (!freqs)
+ return;
+
+ policy = cpufreq_cpu_get(freq->cpu);
+ if (!policy)
+ return;
+
+ index = cpufreq_frequency_table_get_index(policy, freq->new);
+ if (index >= 0)
+ WRITE_ONCE(freqs->last_index, index);
+
+ cpufreq_cpu_put(policy);
+}
+
+static const struct seq_operations uid_time_in_state_seq_ops = {
+ .start = uid_seq_start,
+ .next = uid_seq_next,
+ .stop = uid_seq_stop,
+ .show = uid_time_in_state_seq_show,
+};
+
+static int uid_time_in_state_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &uid_time_in_state_seq_ops);
+}
+
+int single_uid_time_in_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, single_uid_time_in_state_show,
+ &(inode->i_uid));
+}
+
+static const struct file_operations uid_time_in_state_fops = {
+ .open = uid_time_in_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int __init cpufreq_times_init(void)
+{
+ proc_create_data("uid_time_in_state", 0444, NULL,
+ &uid_time_in_state_fops, NULL);
+
+ return 0;
+}
+
+early_initcall(cpufreq_times_init);
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index 6fb3cd2..a1d7fa4 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -599,6 +599,16 @@
if (!spin_trylock(&gpstates->gpstate_lock))
return;
+ /*
+ * If the timer has migrated to the different cpu then bring
+ * it back to one of the policy->cpus
+ */
+ if (!cpumask_test_cpu(raw_smp_processor_id(), policy->cpus)) {
+ gpstates->timer.expires = jiffies + msecs_to_jiffies(1);
+ add_timer_on(&gpstates->timer, cpumask_first(policy->cpus));
+ spin_unlock(&gpstates->gpstate_lock);
+ return;
+ }
gpstates->last_sampled_time += time_diff;
gpstates->elapsed_time += time_diff;
@@ -626,10 +636,8 @@
gpstates->last_gpstate_idx = pstate_to_idx(freq_data.gpstate_id);
gpstates->last_lpstate_idx = pstate_to_idx(freq_data.pstate_id);
+ set_pstate(&freq_data);
spin_unlock(&gpstates->gpstate_lock);
-
- /* Timer may get migrated to a different cpu on cpu hot unplug */
- smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1);
}
/*
diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c
index 7b596fa..6bebc1f 100644
--- a/drivers/cpufreq/s3c24xx-cpufreq.c
+++ b/drivers/cpufreq/s3c24xx-cpufreq.c
@@ -351,7 +351,13 @@
static int s3c_cpufreq_init(struct cpufreq_policy *policy)
{
policy->clk = clk_arm;
- return cpufreq_generic_init(policy, ftab, cpu_cur.info->latency);
+
+ policy->cpuinfo.transition_latency = cpu_cur.info->latency;
+
+ if (ftab)
+ return cpufreq_table_validate_and_show(policy, ftab);
+
+ return 0;
}
static int __init s3c_cpufreq_initclks(void)
diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c
index 86628e2..719c3d9 100644
--- a/drivers/cpufreq/sh-cpufreq.c
+++ b/drivers/cpufreq/sh-cpufreq.c
@@ -30,11 +30,51 @@
static DEFINE_PER_CPU(struct clk, sh_cpuclk);
+struct cpufreq_target {
+ struct cpufreq_policy *policy;
+ unsigned int freq;
+};
+
static unsigned int sh_cpufreq_get(unsigned int cpu)
{
return (clk_get_rate(&per_cpu(sh_cpuclk, cpu)) + 500) / 1000;
}
+static long __sh_cpufreq_target(void *arg)
+{
+ struct cpufreq_target *target = arg;
+ struct cpufreq_policy *policy = target->policy;
+ int cpu = policy->cpu;
+ struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
+ struct cpufreq_freqs freqs;
+ struct device *dev;
+ long freq;
+
+ if (smp_processor_id() != cpu)
+ return -ENODEV;
+
+ dev = get_cpu_device(cpu);
+
+ /* Convert target_freq from kHz to Hz */
+ freq = clk_round_rate(cpuclk, target->freq * 1000);
+
+ if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
+ return -EINVAL;
+
+ dev_dbg(dev, "requested frequency %u Hz\n", target->freq * 1000);
+
+ freqs.old = sh_cpufreq_get(cpu);
+ freqs.new = (freq + 500) / 1000;
+ freqs.flags = 0;
+
+ cpufreq_freq_transition_begin(target->policy, &freqs);
+ clk_set_rate(cpuclk, freq);
+ cpufreq_freq_transition_end(target->policy, &freqs, 0);
+
+ dev_dbg(dev, "set frequency %lu Hz\n", freq);
+ return 0;
+}
+
/*
* Here we notify other drivers of the proposed change and the final change.
*/
@@ -42,40 +82,9 @@
unsigned int target_freq,
unsigned int relation)
{
- unsigned int cpu = policy->cpu;
- struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
- cpumask_t cpus_allowed;
- struct cpufreq_freqs freqs;
- struct device *dev;
- long freq;
+ struct cpufreq_target data = { .policy = policy, .freq = target_freq };
- cpus_allowed = current->cpus_allowed;
- set_cpus_allowed_ptr(current, cpumask_of(cpu));
-
- BUG_ON(smp_processor_id() != cpu);
-
- dev = get_cpu_device(cpu);
-
- /* Convert target_freq from kHz to Hz */
- freq = clk_round_rate(cpuclk, target_freq * 1000);
-
- if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
- return -EINVAL;
-
- dev_dbg(dev, "requested frequency %u Hz\n", target_freq * 1000);
-
- freqs.old = sh_cpufreq_get(cpu);
- freqs.new = (freq + 500) / 1000;
- freqs.flags = 0;
-
- cpufreq_freq_transition_begin(policy, &freqs);
- set_cpus_allowed_ptr(current, &cpus_allowed);
- clk_set_rate(cpuclk, freq);
- cpufreq_freq_transition_end(policy, &freqs, 0);
-
- dev_dbg(dev, "set frequency %lu Hz\n", freq);
-
- return 0;
+ return work_on_cpu(policy->cpu, __sh_cpufreq_target, &data);
}
static int sh_cpufreq_verify(struct cpufreq_policy *policy)
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 21340e0..f521448 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -4,6 +4,7 @@
config ARM_CPUIDLE
bool "Generic ARM/ARM64 CPU idle Driver"
select DT_IDLE_STATES
+ select CPU_IDLE_MULTIPLE_DRIVERS
help
Select this to enable generic cpuidle driver for ARM.
It provides a generic idle driver whose idle states are configured
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index cb3c48a..99ad22e 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -27,4 +27,8 @@
# POWERPC drivers
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
+ifeq ($(CONFIG_MSM_PM_LEGACY), y)
+obj-y += lpm-levels-legacy.o lpm-levels-of-legacy.o lpm-workarounds.o
+else
obj-$(CONFIG_MSM_PM) += lpm-levels.o lpm-levels-of.o
+endif
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index f440d38..f47c545 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
+#include <linux/topology.h>
#include <asm/cpuidle.h>
@@ -44,7 +45,7 @@
return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, idx);
}
-static struct cpuidle_driver arm_idle_driver = {
+static struct cpuidle_driver arm_idle_driver __initdata = {
.name = "arm_idle",
.owner = THIS_MODULE,
/*
@@ -80,30 +81,42 @@
static int __init arm_idle_init(void)
{
int cpu, ret;
- struct cpuidle_driver *drv = &arm_idle_driver;
+ struct cpuidle_driver *drv;
struct cpuidle_device *dev;
- /*
- * Initialize idle states data, starting at index 1.
- * This driver is DT only, if no DT idle states are detected (ret == 0)
- * let the driver initialization fail accordingly since there is no
- * reason to initialize the idle driver if only wfi is supported.
- */
- ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
- if (ret <= 0)
- return ret ? : -ENODEV;
-
- ret = cpuidle_register_driver(drv);
- if (ret) {
- pr_err("Failed to register cpuidle driver\n");
- return ret;
- }
-
- /*
- * Call arch CPU operations in order to initialize
- * idle states suspend back-end specific data
- */
for_each_possible_cpu(cpu) {
+
+ drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL);
+ if (!drv) {
+ ret = -ENOMEM;
+ goto out_fail;
+ }
+
+ drv->cpumask = (struct cpumask *)cpumask_of(cpu);
+
+ /*
+ * Initialize idle states data, starting at index 1. This
+ * driver is DT only, if no DT idle states are detected (ret
+ * == 0) let the driver initialization fail accordingly since
+ * there is no reason to initialize the idle driver if only
+ * wfi is supported.
+ */
+ ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
+ if (ret <= 0) {
+ ret = ret ? : -ENODEV;
+ goto out_kfree_drv;
+ }
+
+ ret = cpuidle_register_driver(drv);
+ if (ret) {
+ pr_err("Failed to register cpuidle driver\n");
+ goto out_kfree_drv;
+ }
+
+ /*
+ * Call arch CPU operations in order to initialize
+ * idle states suspend back-end specific data
+ */
ret = arm_cpuidle_init(cpu);
/*
@@ -115,14 +128,14 @@
if (ret) {
pr_err("CPU %d failed to init idle CPU ops\n", cpu);
- goto out_fail;
+ goto out_unregister_drv;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
ret = -ENOMEM;
- goto out_fail;
+ goto out_unregister_drv;
}
dev->cpu = cpu;
@@ -130,21 +143,28 @@
if (ret) {
pr_err("Failed to register cpuidle device for CPU %d\n",
cpu);
- kfree(dev);
- goto out_fail;
+ goto out_kfree_dev;
}
}
return 0;
+
+out_kfree_dev:
+ kfree(dev);
+out_unregister_drv:
+ cpuidle_unregister_driver(drv);
+out_kfree_drv:
+ kfree(drv);
out_fail:
while (--cpu >= 0) {
dev = per_cpu(cpuidle_devices, cpu);
+ drv = cpuidle_get_cpu_driver(dev);
cpuidle_unregister_device(dev);
+ cpuidle_unregister_driver(drv);
kfree(dev);
+ kfree(drv);
}
- cpuidle_unregister_driver(drv);
-
return ret;
}
device_initcall(arm_idle_init);
diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c
index a5c111b..ea11a33 100644
--- a/drivers/cpuidle/dt_idle_states.c
+++ b/drivers/cpuidle/dt_idle_states.c
@@ -174,8 +174,10 @@
if (!state_node)
break;
- if (!of_device_is_available(state_node))
+ if (!of_device_is_available(state_node)) {
+ of_node_put(state_node);
continue;
+ }
if (!idle_state_valid(state_node, i, cpumask)) {
pr_warn("%s idle state not valid, bailing out\n",
diff --git a/drivers/cpuidle/lpm-levels-legacy.c b/drivers/cpuidle/lpm-levels-legacy.c
new file mode 100644
index 0000000..006a5ef
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-legacy.c
@@ -0,0 +1,1533 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/tick.h>
+#include <linux/suspend.h>
+#include <linux/pm_qos.h>
+#include <linux/of_platform.h>
+#include <linux/smp.h>
+#include <linux/remote_spinlock.h>
+#include <linux/msm_remote_spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/coresight-cti.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/cpu_pm.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm-legacy.h>
+#include <soc/qcom/rpm-notifier.h>
+#include <soc/qcom/event_timer.h>
+#include <soc/qcom/lpm-stats.h>
+#include <soc/qcom/lpm_levels.h>
+#include <soc/qcom/jtag.h>
+#include <asm/cputype.h>
+#include <asm/arch_timer.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include "lpm-levels-legacy.h"
+#include "lpm-workarounds.h"
+#include <trace/events/power.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/trace_msm_low_power.h>
+#if defined(CONFIG_COMMON_CLK)
+#include "../clk/clk.h"
+#elif defined(CONFIG_COMMON_CLK_MSM)
+#include "../../drivers/clk/msm/clock.h"
+#endif /* CONFIG_COMMON_CLK */
+#include <soc/qcom/minidump.h>
+
+#define SCLK_HZ (32768)
+#define SCM_HANDOFF_LOCK_ID "S:7"
+#define PSCI_POWER_STATE(reset) (reset << 30)
+#define PSCI_AFFINITY_LEVEL(lvl) ((lvl & 0x3) << 24)
+static remote_spinlock_t scm_handoff_lock;
+
+enum {
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+ MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+enum debug_event {
+ CPU_ENTER,
+ CPU_EXIT,
+ CLUSTER_ENTER,
+ CLUSTER_EXIT,
+ PRE_PC_CB,
+ CPU_HP_STARTING,
+ CPU_HP_DYING,
+};
+
+struct lpm_debug {
+ cycle_t time;
+ enum debug_event evt;
+ int cpu;
+ uint32_t arg1;
+ uint32_t arg2;
+ uint32_t arg3;
+ uint32_t arg4;
+};
+
+static struct system_pm_ops *sys_pm_ops;
+struct lpm_cluster *lpm_root_node;
+
+static DEFINE_PER_CPU(struct lpm_cluster*, cpu_cluster);
+static bool suspend_in_progress;
+static struct hrtimer lpm_hrtimer;
+static struct lpm_debug *lpm_debug;
+static phys_addr_t lpm_debug_phys;
+
+static const int num_dbg_elements = 0x100;
+
+static void cluster_unprepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t time);
+static void cluster_prepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t time);
+
+static bool menu_select;
+module_param_named(
+ menu_select, menu_select, bool, 0664
+);
+
+static bool print_parsed_dt;
+module_param_named(
+ print_parsed_dt, print_parsed_dt, bool, 0664
+);
+
+static bool sleep_disabled;
+module_param_named(sleep_disabled,
+ sleep_disabled, bool, 0664);
+
+s32 msm_cpuidle_get_deep_idle_latency(void)
+{
+ return 10;
+}
+EXPORT_SYMBOL(msm_cpuidle_get_deep_idle_latency);
+
+uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops)
+{
+ if (sys_pm_ops)
+ return -EUSERS;
+
+ sys_pm_ops = pm_ops;
+
+ return 0;
+}
+
+static uint32_t least_cluster_latency(struct lpm_cluster *cluster,
+ struct latency_level *lat_level)
+{
+ struct list_head *list;
+ struct lpm_cluster_level *level;
+ struct lpm_cluster *n;
+ struct power_params *pwr_params;
+ uint32_t latency = 0;
+ int i;
+
+ if (!cluster->list.next) {
+ for (i = 0; i < cluster->nlevels; i++) {
+ level = &cluster->levels[i];
+ pwr_params = &level->pwr;
+ if (lat_level->reset_level == level->reset_level) {
+ if ((latency > pwr_params->latency_us)
+ || (!latency))
+ latency = pwr_params->latency_us;
+ break;
+ }
+ }
+ } else {
+ list_for_each(list, &cluster->parent->child) {
+ n = list_entry(list, typeof(*n), list);
+ if (lat_level->level_name) {
+ if (strcmp(lat_level->level_name,
+ n->cluster_name))
+ continue;
+ }
+ for (i = 0; i < n->nlevels; i++) {
+ level = &n->levels[i];
+ pwr_params = &level->pwr;
+ if (lat_level->reset_level ==
+ level->reset_level) {
+ if ((latency > pwr_params->latency_us)
+ || (!latency))
+ latency =
+ pwr_params->latency_us;
+ break;
+ }
+ }
+ }
+ }
+ return latency;
+}
+
+static uint32_t least_cpu_latency(struct list_head *child,
+ struct latency_level *lat_level)
+{
+ struct list_head *list;
+ struct lpm_cpu_level *level;
+ struct power_params *pwr_params;
+ struct lpm_cpu *cpu;
+ struct lpm_cluster *n;
+ uint32_t latency = 0;
+ int i;
+
+ list_for_each(list, child) {
+ n = list_entry(list, typeof(*n), list);
+ if (lat_level->level_name) {
+ if (strcmp(lat_level->level_name, n->cluster_name))
+ continue;
+ }
+ cpu = n->cpu;
+ for (i = 0; i < cpu->nlevels; i++) {
+ level = &cpu->levels[i];
+ pwr_params = &level->pwr;
+ if (lat_level->reset_level == level->reset_level) {
+ if ((latency > pwr_params->latency_us)
+ || (!latency))
+ latency = pwr_params->latency_us;
+ break;
+ }
+ }
+ }
+ return latency;
+}
+
+static struct lpm_cluster *cluster_aff_match(struct lpm_cluster *cluster,
+ int affinity_level)
+{
+ struct lpm_cluster *n;
+
+ if ((cluster->aff_level == affinity_level)
+ || ((cluster->cpu) && (affinity_level == 0)))
+ return cluster;
+ else if (!cluster->cpu) {
+ n = list_entry(cluster->child.next, typeof(*n), list);
+ return cluster_aff_match(n, affinity_level);
+ } else
+ return NULL;
+}
+
+int lpm_get_latency(struct latency_level *level, uint32_t *latency)
+{
+ struct lpm_cluster *cluster;
+ uint32_t val;
+
+ if (!lpm_root_node) {
+ pr_err("%s: lpm_probe not completed\n", __func__);
+ return -EAGAIN;
+ }
+
+ if ((level->affinity_level < 0)
+ || (level->affinity_level > lpm_root_node->aff_level)
+ || (level->reset_level < LPM_RESET_LVL_RET)
+ || (level->reset_level > LPM_RESET_LVL_PC)
+ || !latency)
+ return -EINVAL;
+
+ cluster = cluster_aff_match(lpm_root_node, level->affinity_level);
+ if (!cluster) {
+ pr_err("%s:No matching cluster found for affinity_level:%d\n",
+ __func__, level->affinity_level);
+ return -EINVAL;
+ }
+
+ if (level->affinity_level == 0)
+ val = least_cpu_latency(&cluster->parent->child, level);
+ else
+ val = least_cluster_latency(cluster, level);
+
+ if (!val) {
+ pr_err("%s:No mode with affinity_level:%d reset_level:%d\n",
+ __func__, level->affinity_level, level->reset_level);
+ return -EINVAL;
+ }
+
+ *latency = val;
+
+ return 0;
+}
+EXPORT_SYMBOL(lpm_get_latency);
+
+static void update_debug_pc_event(enum debug_event event, uint32_t arg1,
+ uint32_t arg2, uint32_t arg3, uint32_t arg4)
+{
+ struct lpm_debug *dbg;
+ int idx;
+ static DEFINE_SPINLOCK(debug_lock);
+ static int pc_event_index;
+
+ if (!lpm_debug)
+ return;
+
+ spin_lock(&debug_lock);
+ idx = pc_event_index++;
+ dbg = &lpm_debug[idx & (num_dbg_elements - 1)];
+
+ dbg->evt = event;
+ dbg->time = arch_counter_get_cntpct();
+ dbg->cpu = raw_smp_processor_id();
+ dbg->arg1 = arg1;
+ dbg->arg2 = arg2;
+ dbg->arg3 = arg3;
+ dbg->arg4 = arg4;
+ spin_unlock(&debug_lock);
+}
+
+static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h)
+{
+ return HRTIMER_NORESTART;
+}
+
+static void msm_pm_set_timer(uint32_t modified_time_us)
+{
+ u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
+ ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
+
+ lpm_hrtimer.function = lpm_hrtimer_cb;
+ hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED);
+}
+
+int set_l2_mode(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level)
+{
+ int lpm = mode;
+ int rc = 0;
+ bool notify_rpm = level->notify_rpm;
+ struct low_power_ops *cpu_ops = per_cpu(cpu_cluster,
+ smp_processor_id())->lpm_dev;
+
+ if (cpu_ops->tz_flag & MSM_SCM_L2_OFF ||
+ cpu_ops->tz_flag & MSM_SCM_L2_GDHS)
+ coresight_cti_ctx_restore();
+
+ switch (mode) {
+ case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_FASTPC:
+ if (level->no_cache_flush)
+ cpu_ops->tz_flag = MSM_SCM_L2_GDHS;
+ else
+ cpu_ops->tz_flag = MSM_SCM_L2_OFF;
+ coresight_cti_ctx_save();
+ break;
+ case MSM_SPM_MODE_GDHS:
+ cpu_ops->tz_flag = MSM_SCM_L2_GDHS;
+ coresight_cti_ctx_save();
+ break;
+ case MSM_SPM_MODE_CLOCK_GATING:
+ case MSM_SPM_MODE_RETENTION:
+ case MSM_SPM_MODE_DISABLED:
+ cpu_ops->tz_flag = MSM_SCM_L2_ON;
+ break;
+ default:
+ cpu_ops->tz_flag = MSM_SCM_L2_ON;
+ lpm = MSM_SPM_MODE_DISABLED;
+ break;
+ }
+
+ if (lpm_wa_get_skip_l2_spm())
+ rc = msm_spm_config_low_power_mode_addr(ops->spm, lpm,
+ notify_rpm);
+ else
+ rc = msm_spm_config_low_power_mode(ops->spm, lpm, notify_rpm);
+
+ if (rc)
+ pr_err("%s: Failed to set L2 low power mode %d, ERR %d",
+ __func__, lpm, rc);
+
+ return rc;
+}
+
+int set_l3_mode(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level)
+{
+ bool notify_rpm = level->notify_rpm;
+ struct low_power_ops *cpu_ops = per_cpu(cpu_cluster,
+ smp_processor_id())->lpm_dev;
+
+ switch (mode) {
+ case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_POWER_COLLAPSE:
+ case MSM_SPM_MODE_FASTPC:
+ cpu_ops->tz_flag |= MSM_SCM_L3_PC_OFF;
+ break;
+ default:
+ break;
+ }
+ return msm_spm_config_low_power_mode(ops->spm, mode, notify_rpm);
+}
+
+
+int set_system_mode(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level)
+{
+ bool notify_rpm = level->notify_rpm;
+
+ return msm_spm_config_low_power_mode(ops->spm, mode, notify_rpm);
+}
+
+static int set_device_mode(struct lpm_cluster *cluster, int ndevice,
+ struct lpm_cluster_level *level)
+{
+ struct low_power_ops *ops;
+
+ if (use_psci)
+ return 0;
+
+ ops = &cluster->lpm_dev[ndevice];
+ if (ops && ops->set_mode)
+ return ops->set_mode(ops, level->mode[ndevice],
+ level);
+ else
+ return -EINVAL;
+}
+
+static int cpu_power_select(struct cpuidle_device *dev,
+ struct lpm_cpu *cpu)
+{
+ int best_level = 0;
+ uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY,
+ dev->cpu);
+ s64 sleep_us = ktime_to_us(tick_nohz_get_sleep_length());
+ uint32_t modified_time_us = 0;
+ uint32_t next_event_us = 0;
+ int i;
+ uint32_t lvl_latency_us = 0;
+ uint32_t *residency = get_per_cpu_max_residency(dev->cpu);
+
+ if (!cpu)
+ return best_level;
+
+ if ((sleep_disabled && !cpu_isolated(dev->cpu)) || sleep_us < 0)
+ return 0;
+
+ next_event_us = (uint32_t)(ktime_to_us(get_next_event_time(dev->cpu)));
+
+ for (i = 0; i < cpu->nlevels; i++) {
+ struct lpm_cpu_level *level = &cpu->levels[i];
+ struct power_params *pwr_params = &level->pwr;
+ uint32_t next_wakeup_us = (uint32_t)sleep_us;
+ enum msm_pm_sleep_mode mode = level->mode;
+ bool allow;
+
+ allow = lpm_cpu_mode_allow(dev->cpu, i, true);
+
+ if (!allow)
+ continue;
+
+ lvl_latency_us = pwr_params->latency_us;
+
+ if (latency_us < lvl_latency_us)
+ break;
+
+ if (next_event_us) {
+ if (next_event_us < lvl_latency_us)
+ break;
+
+ if (((next_event_us - lvl_latency_us) < sleep_us) ||
+ (next_event_us < sleep_us))
+ next_wakeup_us = next_event_us - lvl_latency_us;
+ }
+
+ best_level = i;
+
+ if (next_event_us && next_event_us < sleep_us &&
+ (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
+ modified_time_us
+ = next_event_us - lvl_latency_us;
+ else
+ modified_time_us = 0;
+
+ if (next_wakeup_us <= residency[i])
+ break;
+ }
+
+ if (modified_time_us)
+ msm_pm_set_timer(modified_time_us);
+
+ trace_cpu_power_select(best_level, sleep_us, latency_us, next_event_us);
+
+ return best_level;
+}
+
+static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
+ struct cpumask *mask, bool from_idle)
+{
+ int cpu;
+ int next_cpu = raw_smp_processor_id();
+ ktime_t next_event;
+ struct cpumask online_cpus_in_cluster;
+
+ next_event.tv64 = KTIME_MAX;
+ if (!from_idle) {
+ if (mask)
+ cpumask_copy(mask, cpumask_of(raw_smp_processor_id()));
+ return ~0ULL;
+ }
+
+ cpumask_and(&online_cpus_in_cluster,
+ &cluster->num_children_in_sync, cpu_online_mask);
+
+ for_each_cpu(cpu, &online_cpus_in_cluster) {
+ ktime_t *next_event_c;
+
+ next_event_c = get_next_event_cpu(cpu);
+ if (next_event_c->tv64 < next_event.tv64) {
+ next_event.tv64 = next_event_c->tv64;
+ next_cpu = cpu;
+ }
+ }
+
+ if (mask)
+ cpumask_copy(mask, cpumask_of(next_cpu));
+
+
+ if (ktime_to_us(next_event) > ktime_to_us(ktime_get()))
+ return ktime_to_us(ktime_sub(next_event, ktime_get()));
+ else
+ return 0;
+}
+
+static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
+{
+ int best_level = -1;
+ int i;
+ struct cpumask mask;
+ uint32_t latency_us = ~0U;
+ uint32_t sleep_us;
+
+ if (!cluster)
+ return -EINVAL;
+
+ sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL, from_idle);
+
+ if (cpumask_and(&mask, cpu_online_mask, &cluster->child_cpus))
+ latency_us = pm_qos_request_for_cpumask(PM_QOS_CPU_DMA_LATENCY,
+ &mask);
+
+ /*
+ * If atleast one of the core in the cluster is online, the cluster
+ * low power modes should be determined by the idle characteristics
+ * even if the last core enters the low power mode as a part of
+ * hotplug.
+ */
+
+ if (!from_idle && num_online_cpus() > 1 &&
+ cpumask_intersects(&cluster->child_cpus, cpu_online_mask))
+ from_idle = true;
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *level = &cluster->levels[i];
+ struct power_params *pwr_params = &level->pwr;
+
+ if (!lpm_cluster_mode_allow(cluster, i, from_idle))
+ continue;
+
+ if (level->last_core_only &&
+ cpumask_weight(cpu_online_mask) > 1)
+ continue;
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &level->num_cpu_votes))
+ continue;
+
+ if (from_idle && latency_us < pwr_params->latency_us)
+ break;
+
+ if (sleep_us < pwr_params->time_overhead_us)
+ break;
+
+ if (suspend_in_progress && from_idle && level->notify_rpm)
+ continue;
+
+ if (level->notify_rpm) {
+ if (!(sys_pm_ops && sys_pm_ops->sleep_allowed))
+ continue;
+ if (!sys_pm_ops->sleep_allowed())
+ continue;
+ }
+
+ best_level = i;
+
+ if (from_idle && sleep_us <= pwr_params->max_residency)
+ break;
+ }
+
+ return best_level;
+}
+
+static void cluster_notify(struct lpm_cluster *cluster,
+ struct lpm_cluster_level *level, bool enter)
+{
+ if (level->is_reset && enter)
+ cpu_cluster_pm_enter(cluster->aff_level);
+ else if (level->is_reset && !enter)
+ cpu_cluster_pm_exit(cluster->aff_level);
+}
+
+static unsigned int get_next_online_cpu(bool from_idle)
+{
+ unsigned int cpu;
+ ktime_t next_event;
+ unsigned int next_cpu = raw_smp_processor_id();
+
+ if (!from_idle)
+ return next_cpu;
+ next_event.tv64 = KTIME_MAX;
+ for_each_online_cpu(cpu) {
+ ktime_t *next_event_c;
+
+ next_event_c = get_next_event_cpu(cpu);
+ if (next_event_c->tv64 < next_event.tv64) {
+ next_event.tv64 = next_event_c->tv64;
+ next_cpu = cpu;
+ }
+ }
+ return next_cpu;
+}
+
+static int cluster_configure(struct lpm_cluster *cluster, int idx,
+ bool from_idle)
+{
+ struct lpm_cluster_level *level = &cluster->levels[idx];
+ struct cpumask cpumask;
+ unsigned int cpu;
+ int ret, i;
+
+ if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
+ || is_IPI_pending(&cluster->num_children_in_sync)) {
+ return -EPERM;
+ }
+
+ if (idx != cluster->default_level) {
+ update_debug_pc_event(CLUSTER_ENTER, idx,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+ trace_cluster_enter(cluster->cluster_name, idx,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+ lpm_stats_cluster_enter(cluster->stats, idx);
+ }
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ ret = set_device_mode(cluster, i, level);
+ if (ret)
+ goto failed_set_mode;
+ }
+
+ if (level->notify_rpm) {
+ struct cpumask *nextcpu;
+
+ cpu = get_next_online_cpu(from_idle);
+ cpumask_copy(&cpumask, cpumask_of(cpu));
+ nextcpu = level->disable_dynamic_routing ? NULL : &cpumask;
+
+ if (sys_pm_ops && sys_pm_ops->enter)
+ if ((sys_pm_ops->enter(nextcpu)))
+ return -EBUSY;
+
+ if (cluster->no_saw_devices && !use_psci)
+ msm_spm_set_rpm_hs(true);
+ }
+
+ /* Notify cluster enter event after successfully config completion */
+ cluster_notify(cluster, level, true);
+
+ cluster->last_level = idx;
+ return 0;
+
+failed_set_mode:
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ int rc = 0;
+
+ level = &cluster->levels[cluster->default_level];
+ rc = set_device_mode(cluster, i, level);
+ WARN_ON(rc);
+ }
+ return ret;
+}
+
+static void cluster_prepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t start_time)
+{
+ int i;
+
+ if (!cluster)
+ return;
+
+ if (cluster->min_child_level > child_idx)
+ return;
+
+ spin_lock(&cluster->sync_lock);
+ cpumask_or(&cluster->num_children_in_sync, cpu,
+ &cluster->num_children_in_sync);
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *lvl = &cluster->levels[i];
+
+ if (child_idx >= lvl->min_child_level)
+ cpumask_or(&lvl->num_cpu_votes, cpu,
+ &lvl->num_cpu_votes);
+ }
+
+ /*
+ * cluster_select() does not make any configuration changes. So its ok
+ * to release the lock here. If a core wakes up for a rude request,
+ * it need not wait for another to finish its cluster selection and
+ * configuration process
+ */
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus))
+ goto failed;
+
+ i = cluster_select(cluster, from_idle);
+
+ if (i < 0)
+ goto failed;
+
+ if (cluster_configure(cluster, i, from_idle))
+ goto failed;
+
+ cluster->stats->sleep_time = start_time;
+ cluster_prepare(cluster->parent, &cluster->num_children_in_sync, i,
+ from_idle, start_time);
+
+ spin_unlock(&cluster->sync_lock);
+
+ if (!use_psci) {
+ struct lpm_cluster_level *level = &cluster->levels[i];
+
+ if (level->notify_rpm)
+ if (sys_pm_ops && sys_pm_ops->update_wakeup)
+ sys_pm_ops->update_wakeup(from_idle);
+ }
+
+ return;
+failed:
+ spin_unlock(&cluster->sync_lock);
+ cluster->stats->sleep_time = 0;
+}
+
+static void cluster_unprepare(struct lpm_cluster *cluster,
+ const struct cpumask *cpu, int child_idx, bool from_idle,
+ int64_t end_time)
+{
+ struct lpm_cluster_level *level;
+ bool first_cpu;
+ int last_level, i, ret;
+
+ if (!cluster)
+ return;
+
+ if (cluster->min_child_level > child_idx)
+ return;
+
+ spin_lock(&cluster->sync_lock);
+ last_level = cluster->default_level;
+ first_cpu = cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus);
+ cpumask_andnot(&cluster->num_children_in_sync,
+ &cluster->num_children_in_sync, cpu);
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *lvl = &cluster->levels[i];
+
+ if (child_idx >= lvl->min_child_level)
+ cpumask_andnot(&lvl->num_cpu_votes,
+ &lvl->num_cpu_votes, cpu);
+ }
+
+ if (!first_cpu || cluster->last_level == cluster->default_level)
+ goto unlock_return;
+
+ if (cluster->stats->sleep_time)
+ cluster->stats->sleep_time = end_time -
+ cluster->stats->sleep_time;
+ lpm_stats_cluster_exit(cluster->stats, cluster->last_level, true);
+
+ level = &cluster->levels[cluster->last_level];
+ if (level->notify_rpm) {
+ if (sys_pm_ops && sys_pm_ops->exit)
+ sys_pm_ops->exit();
+
+ /* If RPM bumps up CX to turbo, unvote CX turbo vote
+ * during exit of rpm assisted power collapse to
+ * reduce the power impact
+ */
+ lpm_wa_cx_unvote_send();
+
+ if (cluster->no_saw_devices && !use_psci)
+ msm_spm_set_rpm_hs(false);
+
+ }
+
+ update_debug_pc_event(CLUSTER_EXIT, cluster->last_level,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+ trace_cluster_exit(cluster->cluster_name, cluster->last_level,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], from_idle);
+
+ last_level = cluster->last_level;
+ cluster->last_level = cluster->default_level;
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ level = &cluster->levels[cluster->default_level];
+ ret = set_device_mode(cluster, i, level);
+
+ WARN_ON(ret);
+
+ }
+
+ cluster_notify(cluster, &cluster->levels[last_level], false);
+ cluster_unprepare(cluster->parent, &cluster->child_cpus,
+ last_level, from_idle, end_time);
+unlock_return:
+ spin_unlock(&cluster->sync_lock);
+}
+
+static inline void cpu_prepare(struct lpm_cluster *cluster, int cpu_index,
+ bool from_idle)
+{
+ struct lpm_cpu_level *cpu_level = &cluster->cpu->levels[cpu_index];
+ bool jtag_save_restore =
+ cluster->cpu->levels[cpu_index].jtag_save_restore;
+
+ /* Use broadcast timer for aggregating sleep mode within a cluster.
+ * A broadcast timer could be used in the following scenarios
+ * 1) The architected timer HW gets reset during certain low power
+ * modes and the core relies on a external(broadcast) timer to wake up
+ * from sleep. This information is passed through device tree.
+ * 2) The CPU low power mode could trigger a system low power mode.
+ * The low power module relies on Broadcast timer to aggregate the
+ * next wakeup within a cluster, in which case, CPU switches over to
+ * use broadcast timer.
+ */
+ if (from_idle && (cpu_level->use_bc_timer ||
+ (cpu_index >= cluster->min_child_level)))
+ tick_broadcast_enter();
+
+ if (from_idle && ((cpu_level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
+ || (cpu_level->mode ==
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)
+ || (cpu_level->is_reset)))
+ cpu_pm_enter();
+
+ /*
+ * Save JTAG registers for 8996v1.0 & 8996v2.x in C4 LPM
+ */
+ if (jtag_save_restore)
+ msm_jtag_save_state();
+}
+
+static inline void cpu_unprepare(struct lpm_cluster *cluster, int cpu_index,
+ bool from_idle)
+{
+ struct lpm_cpu_level *cpu_level = &cluster->cpu->levels[cpu_index];
+ bool jtag_save_restore =
+ cluster->cpu->levels[cpu_index].jtag_save_restore;
+
+ if (from_idle && (cpu_level->use_bc_timer ||
+ (cpu_index >= cluster->min_child_level)))
+ tick_broadcast_exit();
+
+ if (from_idle && ((cpu_level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
+ || (cpu_level->mode ==
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)
+ || cpu_level->is_reset))
+ cpu_pm_exit();
+
+ /*
+ * Restore JTAG registers for 8996v1.0 & 8996v2.x in C4 LPM
+ */
+ if (jtag_save_restore)
+ msm_jtag_restore_state();
+}
+
+#if defined(CONFIG_ARM_PSCI) || !defined(CONFIG_CPU_V7)
+static int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
+{
+ int state_id = 0;
+
+ if (!cluster)
+ return 0;
+
+ spin_lock(&cluster->sync_lock);
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus))
+ goto unlock_and_return;
+
+ state_id |= get_cluster_id(cluster->parent, aff_lvl);
+
+ if (cluster->last_level != cluster->default_level) {
+ struct lpm_cluster_level *level
+ = &cluster->levels[cluster->last_level];
+
+ state_id |= (level->psci_id & cluster->psci_mode_mask)
+ << cluster->psci_mode_shift;
+ (*aff_lvl)++;
+ }
+unlock_and_return:
+ spin_unlock(&cluster->sync_lock);
+ return state_id;
+}
+#endif
+
+#if !defined(CONFIG_CPU_V7)
+asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
+static bool psci_enter_sleep(struct lpm_cluster *cluster,
+ int idx, bool from_idle)
+
+{
+ bool ret;
+ /*
+ * idx = 0 is the default LPM state
+ */
+ if (!idx) {
+ stop_critical_timings();
+ wfi();
+ start_critical_timings();
+ ret = true;
+ } else {
+ int affinity_level = 0;
+ int state_id = get_cluster_id(cluster, &affinity_level);
+ int power_state =
+ PSCI_POWER_STATE(cluster->cpu->levels[idx].is_reset);
+ bool success = false;
+
+ if (cluster->cpu->levels[idx].hyp_psci) {
+ stop_critical_timings();
+ __invoke_psci_fn_smc(0xC4000021, 0, 0, 0);
+ start_critical_timings();
+ return 1;
+ }
+
+ affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
+ state_id |= (power_state | affinity_level
+ | cluster->cpu->levels[idx].psci_id);
+
+ update_debug_pc_event(CPU_ENTER, state_id,
+ 0xdeaffeed, 0xdeaffeed, true);
+ stop_critical_timings();
+ success = !arm_cpuidle_suspend(state_id);
+ start_critical_timings();
+ update_debug_pc_event(CPU_EXIT, state_id,
+ success, 0xdeaffeed, true);
+ ret = success;
+ }
+ return ret;
+}
+#elif defined(CONFIG_ARM_PSCI)
+static bool psci_enter_sleep(struct lpm_cluster *cluster,
+ int idx, bool from_idle)
+{
+ bool ret;
+
+ if (!idx) {
+ stop_critical_timings();
+ wfi();
+ start_critical_timings();
+ ret = true;
+ } else {
+ int affinity_level = 0;
+ int state_id = get_cluster_id(cluster, &affinity_level);
+ int power_state =
+ PSCI_POWER_STATE(cluster->cpu->levels[idx].is_reset);
+ bool success = false;
+
+ affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
+ state_id |= (power_state | affinity_level
+ | cluster->cpu->levels[idx].psci_id);
+
+ update_debug_pc_event(CPU_ENTER, state_id,
+ 0xdeaffeed, 0xdeaffeed, true);
+ stop_critical_timings();
+ success = !arm_cpuidle_suspend(state_id);
+ start_critical_timings();
+ update_debug_pc_event(CPU_EXIT, state_id,
+ success, 0xdeaffeed, true);
+ ret = success;
+ }
+ return ret;
+}
+#else
+static bool psci_enter_sleep(struct lpm_cluster *cluster,
+ int idx, bool from_idle)
+{
+ WARN_ONCE(true, "PSCI cpu_suspend ops not supported\n");
+ return false;
+}
+#endif
+
+static int lpm_cpuidle_select(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
+ int idx;
+
+ if (!cluster)
+ return 0;
+
+ idx = cpu_power_select(dev, cluster->cpu);
+
+ return idx;
+}
+
+static int lpm_cpuidle_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int idx)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
+ bool success = true;
+ const struct cpumask *cpumask = get_cpu_mask(dev->cpu);
+ ktime_t start = ktime_get();
+ int64_t start_time = ktime_to_ns(ktime_get()), end_time;
+
+ if (idx < 0)
+ return -EINVAL;
+
+ cpu_prepare(cluster, idx, true);
+ cluster_prepare(cluster, cpumask, idx, true, ktime_to_ns(ktime_get()));
+
+ trace_cpu_idle_enter(idx);
+ lpm_stats_cpu_enter(idx, start_time);
+
+ if (need_resched())
+ goto exit;
+
+ if (!use_psci) {
+ if (idx > 0)
+ update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
+ 0xdeaffeed, true);
+ success = msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode,
+ true);
+
+ if (idx > 0)
+ update_debug_pc_event(CPU_EXIT, idx, success,
+ 0xdeaffeed, true);
+ } else {
+ success = psci_enter_sleep(cluster, idx, true);
+ }
+
+exit:
+ end_time = ktime_to_ns(ktime_get());
+ lpm_stats_cpu_exit(idx, end_time, success);
+
+ cluster_unprepare(cluster, cpumask, idx, true, end_time);
+ cpu_unprepare(cluster, idx, true);
+
+ trace_cpu_idle_exit(idx, success);
+ dev->last_residency = ktime_us_delta(ktime_get(), start);
+ local_irq_enable();
+
+ return idx;
+}
+
+#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
+static int cpuidle_register_cpu(struct cpuidle_driver *drv,
+ struct cpumask *mask)
+{
+ struct cpuidle_device *device;
+ int cpu, ret;
+
+
+ if (!mask || !drv)
+ return -EINVAL;
+
+ drv->cpumask = mask;
+ ret = cpuidle_register_driver(drv);
+ if (ret) {
+ pr_err("Failed to register cpuidle driver %d\n", ret);
+ goto failed_driver_register;
+ }
+
+ for_each_cpu(cpu, mask) {
+ device = &per_cpu(cpuidle_dev, cpu);
+ device->cpu = cpu;
+
+ ret = cpuidle_register_device(device);
+ if (ret) {
+ pr_err("Failed to register cpuidle driver for cpu:%u\n",
+ cpu);
+ goto failed_driver_register;
+ }
+ }
+ return ret;
+failed_driver_register:
+ for_each_cpu(cpu, mask)
+ cpuidle_unregister_driver(drv);
+ return ret;
+}
+#else
+static int cpuidle_register_cpu(struct cpuidle_driver *drv,
+ struct cpumask *mask)
+{
+ return cpuidle_register(drv, NULL);
+}
+#endif
+
+static struct cpuidle_governor lpm_governor = {
+ .name = "qcom",
+ .rating = 30,
+ .select = lpm_cpuidle_select,
+ .owner = THIS_MODULE,
+};
+
+static int cluster_cpuidle_register(struct lpm_cluster *cl)
+{
+ int i = 0, ret = 0;
+ unsigned int cpu;
+ struct lpm_cluster *p = NULL;
+
+ if (!cl->cpu) {
+ struct lpm_cluster *n;
+
+ list_for_each_entry(n, &cl->child, list) {
+ ret = cluster_cpuidle_register(n);
+ if (ret)
+ break;
+ }
+ return ret;
+ }
+
+ cl->drv = kzalloc(sizeof(*cl->drv), GFP_KERNEL);
+ if (!cl->drv)
+ return -ENOMEM;
+
+ cl->drv->name = "msm_idle";
+
+ for (i = 0; i < cl->cpu->nlevels; i++) {
+ struct cpuidle_state *st = &cl->drv->states[i];
+ struct lpm_cpu_level *cpu_level = &cl->cpu->levels[i];
+
+ snprintf(st->name, CPUIDLE_NAME_LEN, "C%u\n", i);
+ snprintf(st->desc, CPUIDLE_DESC_LEN, "%s", cpu_level->name);
+ st->flags = 0;
+ st->exit_latency = cpu_level->pwr.latency_us;
+ st->power_usage = cpu_level->pwr.ss_power;
+ st->target_residency = 0;
+ st->enter = lpm_cpuidle_enter;
+ }
+
+ cl->drv->state_count = cl->cpu->nlevels;
+ cl->drv->safe_state_index = 0;
+ for_each_cpu(cpu, &cl->child_cpus)
+ per_cpu(cpu_cluster, cpu) = cl;
+
+ for_each_possible_cpu(cpu) {
+ if (cpu_online(cpu))
+ continue;
+ p = per_cpu(cpu_cluster, cpu);
+ while (p) {
+ int j;
+
+ spin_lock(&p->sync_lock);
+ cpumask_set_cpu(cpu, &p->num_children_in_sync);
+ for (j = 0; j < p->nlevels; j++)
+ cpumask_copy(&p->levels[j].num_cpu_votes,
+ &p->num_children_in_sync);
+ spin_unlock(&p->sync_lock);
+ p = p->parent;
+ }
+ }
+ ret = cpuidle_register_cpu(cl->drv, &cl->child_cpus);
+
+ if (ret) {
+ kfree(cl->drv);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * init_lpm - initializes the governor
+ */
+static int __init init_lpm(void)
+{
+ return cpuidle_register_governor(&lpm_governor);
+}
+
+postcore_initcall(init_lpm);
+
+static void register_cpu_lpm_stats(struct lpm_cpu *cpu,
+ struct lpm_cluster *parent)
+{
+ const char **level_name;
+ int i;
+
+ level_name = kcalloc(cpu->nlevels, sizeof(*level_name), GFP_KERNEL);
+
+ if (!level_name)
+ return;
+
+ for (i = 0; i < cpu->nlevels; i++)
+ level_name[i] = cpu->levels[i].name;
+
+ lpm_stats_config_level("cpu", level_name, cpu->nlevels,
+ parent->stats, &parent->child_cpus);
+
+ kfree(level_name);
+}
+
+static void register_cluster_lpm_stats(struct lpm_cluster *cl,
+ struct lpm_cluster *parent)
+{
+ const char **level_name;
+ int i;
+ struct lpm_cluster *child;
+
+ if (!cl)
+ return;
+
+ level_name = kcalloc(cl->nlevels, sizeof(*level_name), GFP_KERNEL);
+
+ if (!level_name)
+ return;
+
+ for (i = 0; i < cl->nlevels; i++)
+ level_name[i] = cl->levels[i].level_name;
+
+ cl->stats = lpm_stats_config_level(cl->cluster_name, level_name,
+ cl->nlevels, parent ? parent->stats : NULL, NULL);
+
+ kfree(level_name);
+
+ if (cl->cpu) {
+ register_cpu_lpm_stats(cl->cpu, cl);
+ return;
+ }
+
+ list_for_each_entry(child, &cl->child, list)
+ register_cluster_lpm_stats(child, cl);
+}
+
+static int lpm_suspend_prepare(void)
+{
+ suspend_in_progress = true;
+ lpm_stats_suspend_enter();
+
+ return 0;
+}
+
+static void lpm_suspend_wake(void)
+{
+ suspend_in_progress = false;
+ lpm_stats_suspend_exit();
+}
+
+static int lpm_suspend_enter(suspend_state_t state)
+{
+ int cpu = raw_smp_processor_id();
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+ struct lpm_cpu *lpm_cpu = cluster->cpu;
+ const struct cpumask *cpumask = get_cpu_mask(cpu);
+ int idx;
+
+ for (idx = lpm_cpu->nlevels - 1; idx >= 0; idx--) {
+
+ if (lpm_cpu_mode_allow(cpu, idx, false))
+ break;
+ }
+ if (idx < 0) {
+ pr_err("Failed suspend\n");
+ return 0;
+ }
+ cpu_prepare(cluster, idx, false);
+ cluster_prepare(cluster, cpumask, idx, false, 0);
+ if (idx > 0)
+ update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
+ 0xdeaffeed, false);
+
+ /*
+ * Print the clocks which are enabled during system suspend
+ * This debug information is useful to know which are the
+ * clocks that are enabled and preventing the system level
+ * LPMs(XO and Vmin).
+ */
+ clock_debug_print_enabled(true);
+
+ if (!use_psci)
+ msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, false);
+ else
+ psci_enter_sleep(cluster, idx, true);
+
+ if (idx > 0)
+ update_debug_pc_event(CPU_EXIT, idx, true, 0xdeaffeed,
+ false);
+
+ cluster_unprepare(cluster, cpumask, idx, false, 0);
+ cpu_unprepare(cluster, idx, false);
+ return 0;
+}
+
+static int lpm_dying_cpu(unsigned int cpu)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+
+ update_debug_pc_event(CPU_HP_DYING, cpu,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], false);
+ cluster_prepare(cluster, get_cpu_mask(cpu), NR_LPM_LEVELS, false, 0);
+ return 0;
+}
+
+static int lpm_starting_cpu(unsigned int cpu)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+
+ update_debug_pc_event(CPU_HP_STARTING, cpu,
+ cluster->num_children_in_sync.bits[0],
+ cluster->child_cpus.bits[0], false);
+ cluster_unprepare(cluster, get_cpu_mask(cpu), NR_LPM_LEVELS, false, 0);
+ return 0;
+}
+
+static const struct platform_suspend_ops lpm_suspend_ops = {
+ .enter = lpm_suspend_enter,
+ .valid = suspend_valid_only_mem,
+ .prepare_late = lpm_suspend_prepare,
+ .wake = lpm_suspend_wake,
+};
+
+static int lpm_probe(struct platform_device *pdev)
+{
+ int ret;
+ int size;
+ struct kobject *module_kobj = NULL;
+ struct md_region md_entry;
+
+ get_online_cpus();
+ lpm_root_node = lpm_of_parse_cluster(pdev);
+
+ if (IS_ERR_OR_NULL(lpm_root_node)) {
+ pr_err("%s(): Failed to probe low power modes\n", __func__);
+ put_online_cpus();
+ return PTR_ERR(lpm_root_node);
+ }
+
+ if (print_parsed_dt)
+ cluster_dt_walkthrough(lpm_root_node);
+
+ /*
+ * Register hotplug notifier before broadcast time to ensure there
+ * to prevent race where a broadcast timer might not be setup on for a
+ * core. BUG in existing code but no known issues possibly because of
+ * how late lpm_levels gets initialized.
+ */
+ suspend_set_ops(&lpm_suspend_ops);
+ hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ ret = remote_spin_lock_init(&scm_handoff_lock, SCM_HANDOFF_LOCK_ID);
+ if (ret) {
+ pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
+ __func__, ret);
+ put_online_cpus();
+ return ret;
+ }
+ size = num_dbg_elements * sizeof(struct lpm_debug);
+ lpm_debug = dma_alloc_coherent(&pdev->dev, size,
+ &lpm_debug_phys, GFP_KERNEL);
+ register_cluster_lpm_stats(lpm_root_node, NULL);
+
+ ret = cluster_cpuidle_register(lpm_root_node);
+ put_online_cpus();
+ if (ret) {
+ pr_err("%s()Failed to register with cpuidle framework\n",
+ __func__);
+ goto failed;
+ }
+ ret = cpuhp_setup_state(CPUHP_AP_QCOM_SLEEP_STARTING,
+ "AP_QCOM_SLEEP_STARTING",
+ lpm_starting_cpu, lpm_dying_cpu);
+ if (ret)
+ goto failed;
+
+ module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+ if (!module_kobj) {
+ pr_err("%s: cannot find kobject for module %s\n",
+ __func__, KBUILD_MODNAME);
+ ret = -ENOENT;
+ goto failed;
+ }
+
+ ret = create_cluster_lvl_nodes(lpm_root_node, module_kobj);
+ if (ret) {
+ pr_err("%s(): Failed to create cluster level nodes\n",
+ __func__);
+ goto failed;
+ }
+
+ /* Add lpm_debug to Minidump*/
+ strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name));
+ md_entry.virt_addr = (uintptr_t)lpm_debug;
+ md_entry.phys_addr = lpm_debug_phys;
+ md_entry.size = size;
+ if (msm_minidump_add_region(&md_entry))
+ pr_info("Failed to add lpm_debug in Minidump\n");
+
+ return 0;
+failed:
+ free_cluster_node(lpm_root_node);
+ lpm_root_node = NULL;
+ return ret;
+}
+
+static const struct of_device_id lpm_mtch_tbl[] = {
+ {.compatible = "qcom,lpm-levels"},
+ {},
+};
+
+static struct platform_driver lpm_driver = {
+ .probe = lpm_probe,
+ .driver = {
+ .name = "lpm-levels",
+ .owner = THIS_MODULE,
+ .of_match_table = lpm_mtch_tbl,
+ },
+};
+
+static int __init lpm_levels_module_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&lpm_driver);
+ if (rc) {
+ pr_info("Error registering %s\n", lpm_driver.driver.name);
+ goto fail;
+ }
+
+fail:
+ return rc;
+}
+late_initcall(lpm_levels_module_init);
+
+enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu)
+{
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+ enum msm_pm_l2_scm_flag retflag = MSM_SCM_L2_ON;
+
+ /*
+ * No need to acquire the lock if probe isn't completed yet
+ * In the event of the hotplug happening before lpm probe, we want to
+ * flush the cache to make sure that L2 is flushed. In particular, this
+ * could cause incoherencies for a cluster architecture. This wouldn't
+ * affect the idle case as the idle driver wouldn't be registered
+ * before the probe function
+ */
+ if (!cluster)
+ return MSM_SCM_L2_OFF;
+
+ /*
+ * Assumes L2 only. What/How parameters gets passed into TZ will
+ * determine how this function reports this info back in msm-pm.c
+ */
+ spin_lock(&cluster->sync_lock);
+
+ if (!cluster->lpm_dev) {
+ retflag = MSM_SCM_L2_OFF;
+ goto unlock_and_return;
+ }
+
+ if (!cpumask_equal(&cluster->num_children_in_sync,
+ &cluster->child_cpus))
+ goto unlock_and_return;
+
+ if (cluster->lpm_dev)
+ retflag = cluster->lpm_dev->tz_flag;
+ /*
+ * The scm_handoff_lock will be release by the secure monitor.
+ * It is used to serialize power-collapses from this point on,
+ * so that both Linux and the secure context have a consistent
+ * view regarding the number of running cpus (cpu_count).
+ *
+ * It must be acquired before releasing the cluster lock.
+ */
+unlock_and_return:
+ update_debug_pc_event(PRE_PC_CB, retflag, 0xdeadbeef, 0xdeadbeef,
+ 0xdeadbeef);
+ trace_pre_pc_cb(retflag);
+ remote_spin_lock_rlock_id(&scm_handoff_lock,
+ REMOTE_SPINLOCK_TID_START + cpu);
+ spin_unlock(&cluster->sync_lock);
+ return retflag;
+}
+
+/**
+ * lpm_cpu_hotplug_enter(): Called by dying CPU to terminate in low power mode
+ *
+ * @cpu: cpuid of the dying CPU
+ *
+ * Called from platform_cpu_kill() to terminate hotplug in a low power mode
+ */
+void lpm_cpu_hotplug_enter(unsigned int cpu)
+{
+ enum msm_pm_sleep_mode mode = MSM_PM_SLEEP_MODE_NR;
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+ int i;
+ int idx = -1;
+
+ /*
+ * If lpm isn't probed yet, try to put cpu into the one of the modes
+ * available
+ */
+ if (!cluster) {
+ if (msm_spm_is_mode_avail(
+ MSM_SPM_MODE_POWER_COLLAPSE)){
+ mode = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
+ } else if (msm_spm_is_mode_avail(
+ MSM_SPM_MODE_FASTPC)) {
+ mode = MSM_PM_SLEEP_MODE_FASTPC;
+ } else if (msm_spm_is_mode_avail(
+ MSM_SPM_MODE_RETENTION)) {
+ mode = MSM_PM_SLEEP_MODE_RETENTION;
+ } else {
+ pr_err("No mode avail for cpu%d hotplug\n", cpu);
+ WARN_ON(1);
+ return;
+ }
+ } else {
+ struct lpm_cpu *lpm_cpu;
+ uint32_t ss_pwr = ~0U;
+
+ lpm_cpu = cluster->cpu;
+ for (i = 0; i < lpm_cpu->nlevels; i++) {
+ if (ss_pwr < lpm_cpu->levels[i].pwr.ss_power)
+ continue;
+ ss_pwr = lpm_cpu->levels[i].pwr.ss_power;
+ idx = i;
+ mode = lpm_cpu->levels[i].mode;
+ }
+
+ if (mode == MSM_PM_SLEEP_MODE_NR)
+ return;
+
+ WARN_ON(idx < 0);
+ cluster_prepare(cluster, get_cpu_mask(cpu), idx, false, 0);
+ }
+
+ msm_cpu_pm_enter_sleep(mode, false);
+}
diff --git a/drivers/cpuidle/lpm-levels-legacy.h b/drivers/cpuidle/lpm-levels-legacy.h
new file mode 100644
index 0000000..4a07355
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-legacy.h
@@ -0,0 +1,152 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <soc/qcom/pm-legacy.h>
+#include <soc/qcom/spm.h>
+
+#define NR_LPM_LEVELS 8
+
+extern bool use_psci;
+
+struct lpm_lookup_table {
+ uint32_t modes;
+ const char *mode_name;
+};
+
+struct power_params {
+ uint32_t latency_us; /* Enter + Exit latency */
+ uint32_t ss_power; /* Steady state power */
+ uint32_t energy_overhead; /* Enter + exit over head */
+ uint32_t time_overhead_us; /* Enter + exit overhead */
+ uint32_t residencies[NR_LPM_LEVELS];
+ uint32_t max_residency;
+};
+
+struct lpm_cpu_level {
+ const char *name;
+ enum msm_pm_sleep_mode mode;
+ bool use_bc_timer;
+ struct power_params pwr;
+ unsigned int psci_id;
+ bool is_reset;
+ bool jtag_save_restore;
+ bool hyp_psci;
+ int reset_level;
+};
+
+struct lpm_cpu {
+ struct lpm_cpu_level levels[NR_LPM_LEVELS];
+ int nlevels;
+ unsigned int psci_mode_shift;
+ unsigned int psci_mode_mask;
+ struct lpm_cluster *parent;
+};
+
+struct lpm_level_avail {
+ bool idle_enabled;
+ bool suspend_enabled;
+ struct kobject *kobj;
+ struct kobj_attribute idle_enabled_attr;
+ struct kobj_attribute suspend_enabled_attr;
+ void *data;
+ int idx;
+ bool cpu_node;
+};
+
+struct lpm_cluster_level {
+ const char *level_name;
+ int *mode; /* SPM mode to enter */
+ int min_child_level;
+ struct cpumask num_cpu_votes;
+ struct power_params pwr;
+ bool notify_rpm;
+ bool disable_dynamic_routing;
+ bool sync_level;
+ bool last_core_only;
+ struct lpm_level_avail available;
+ unsigned int psci_id;
+ bool is_reset;
+ int reset_level;
+ bool no_cache_flush;
+};
+
+struct low_power_ops {
+ struct msm_spm_device *spm;
+ int (*set_mode)(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level);
+ enum msm_pm_l2_scm_flag tz_flag;
+};
+
+struct lpm_cluster {
+ struct list_head list;
+ struct list_head child;
+ const char *cluster_name;
+ const char **name;
+ unsigned long aff_level; /* Affinity level of the node */
+ struct low_power_ops *lpm_dev;
+ int ndevices;
+ struct lpm_cluster_level levels[NR_LPM_LEVELS];
+ int nlevels;
+ enum msm_pm_l2_scm_flag l2_flag;
+ int min_child_level;
+ int default_level;
+ int last_level;
+ struct lpm_cpu *cpu;
+ struct cpuidle_driver *drv;
+ spinlock_t sync_lock;
+ struct cpumask child_cpus;
+ struct cpumask num_children_in_sync;
+ struct lpm_cluster *parent;
+ struct lpm_stats *stats;
+ unsigned int psci_mode_shift;
+ unsigned int psci_mode_mask;
+ bool no_saw_devices;
+};
+
+int set_l2_mode(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level);
+int set_system_mode(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level);
+int set_l3_mode(struct low_power_ops *ops, int mode,
+ struct lpm_cluster_level *level);
+void lpm_suspend_wake_time(uint64_t wakeup_time);
+
+struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
+void free_cluster_node(struct lpm_cluster *cluster);
+void cluster_dt_walkthrough(struct lpm_cluster *cluster);
+
+int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj);
+bool lpm_cpu_mode_allow(unsigned int cpu,
+ unsigned int mode, bool from_idle);
+bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
+ unsigned int mode, bool from_idle);
+uint32_t *get_per_cpu_max_residency(int cpu);
+extern struct lpm_cluster *lpm_root_node;
+
+#ifdef CONFIG_SMP
+extern DEFINE_PER_CPU(bool, pending_ipi);
+static inline bool is_IPI_pending(const struct cpumask *mask)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, mask) {
+ if per_cpu(pending_ipi, cpu)
+ return true;
+ }
+ return false;
+}
+#else
+static inline bool is_IPI_pending(const struct cpumask *mask)
+{
+ return false;
+}
+#endif
diff --git a/drivers/cpuidle/lpm-levels-of-legacy.c b/drivers/cpuidle/lpm-levels-of-legacy.c
new file mode 100644
index 0000000..bf74124
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-of-legacy.c
@@ -0,0 +1,1006 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include "lpm-levels-legacy.h"
+
+bool use_psci;
+enum lpm_type {
+ IDLE = 0,
+ SUSPEND,
+ LPM_TYPE_NR
+};
+
+struct lpm_type_str {
+ enum lpm_type type;
+ char *str;
+};
+
+static const struct lpm_type_str lpm_types[] = {
+ {IDLE, "idle_enabled"},
+ {SUSPEND, "suspend_enabled"},
+};
+
+static DEFINE_PER_CPU(uint32_t *, max_residency);
+static struct lpm_level_avail *cpu_level_available[NR_CPUS];
+static struct platform_device *lpm_pdev;
+
+static void *get_enabled_ptr(struct kobj_attribute *attr,
+ struct lpm_level_avail *avail)
+{
+ void *arg = NULL;
+
+ if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+ arg = (void *) &avail->idle_enabled;
+ else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+ arg = (void *) &avail->suspend_enabled;
+
+ return arg;
+}
+
+static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj,
+ struct kobj_attribute *attr)
+{
+ struct lpm_level_avail *avail = NULL;
+
+ if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+ avail = container_of(attr, struct lpm_level_avail,
+ idle_enabled_attr);
+ else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+ avail = container_of(attr, struct lpm_level_avail,
+ suspend_enabled_attr);
+
+ return avail;
+}
+
+static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id,
+ bool probe_time)
+{
+ int i, j;
+ bool mode_avail;
+ uint32_t *residency = per_cpu(max_residency, cpu_id);
+
+ for (i = 0; i < cpu->nlevels; i++) {
+ struct power_params *pwr = &cpu->levels[i].pwr;
+
+ mode_avail = probe_time ||
+ lpm_cpu_mode_allow(cpu_id, i, true);
+
+ if (!mode_avail) {
+ residency[i] = 0;
+ continue;
+ }
+
+ residency[i] = ~0;
+ for (j = i + 1; j < cpu->nlevels; j++) {
+ mode_avail = probe_time ||
+ lpm_cpu_mode_allow(cpu_id, j, true);
+
+ if (mode_avail &&
+ (residency[i] > pwr->residencies[j]) &&
+ (pwr->residencies[j] != 0))
+ residency[i] = pwr->residencies[j];
+ }
+ }
+}
+
+static void set_optimum_cluster_residency(struct lpm_cluster *cluster,
+ bool probe_time)
+{
+ int i, j;
+ bool mode_avail;
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct power_params *pwr = &cluster->levels[i].pwr;
+
+ mode_avail = probe_time ||
+ lpm_cluster_mode_allow(cluster, i,
+ true);
+
+ if (!mode_avail) {
+ pwr->max_residency = 0;
+ continue;
+ }
+
+ pwr->max_residency = ~0;
+ for (j = i+1; j < cluster->nlevels; j++) {
+ mode_avail = probe_time ||
+ lpm_cluster_mode_allow(cluster, j,
+ true);
+ if (mode_avail &&
+ (pwr->max_residency > pwr->residencies[j]) &&
+ (pwr->residencies[j] != 0))
+ pwr->max_residency = pwr->residencies[j];
+ }
+ }
+}
+
+uint32_t *get_per_cpu_max_residency(int cpu)
+{
+ return per_cpu(max_residency, cpu);
+}
+
+static ssize_t lpm_enable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int ret = 0;
+ struct kernel_param kp;
+
+ kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr));
+ ret = param_get_bool(buf, &kp);
+ if (ret > 0) {
+ strlcat(buf, "\n", PAGE_SIZE);
+ ret++;
+ }
+
+ return ret;
+}
+
+static ssize_t lpm_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t len)
+{
+ int ret = 0;
+ struct kernel_param kp;
+ struct lpm_level_avail *avail;
+
+ avail = get_avail_ptr(kobj, attr);
+ if (WARN_ON(!avail))
+ return -EINVAL;
+
+ kp.arg = get_enabled_ptr(attr, avail);
+ ret = param_set_bool(buf, &kp);
+
+ if (avail->cpu_node)
+ set_optimum_cpu_residency(avail->data, avail->idx, false);
+ else
+ set_optimum_cluster_residency(avail->data, false);
+
+ return ret ? ret : len;
+}
+
+static int create_lvl_avail_nodes(const char *name,
+ struct kobject *parent, struct lpm_level_avail *avail,
+ void *data, int index, bool cpu_node)
+{
+ struct attribute_group *attr_group = NULL;
+ struct attribute **attr = NULL;
+ struct kobject *kobj = NULL;
+ int ret = 0;
+
+ kobj = kobject_create_and_add(name, parent);
+ if (!kobj)
+ return -ENOMEM;
+
+ attr_group = devm_kzalloc(&lpm_pdev->dev, sizeof(*attr_group),
+ GFP_KERNEL);
+ if (!attr_group) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ attr = devm_kzalloc(&lpm_pdev->dev,
+ sizeof(*attr) * (LPM_TYPE_NR + 1), GFP_KERNEL);
+ if (!attr) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ sysfs_attr_init(&avail->idle_enabled_attr.attr);
+ avail->idle_enabled_attr.attr.name = lpm_types[IDLE].str;
+ avail->idle_enabled_attr.attr.mode = 0644;
+ avail->idle_enabled_attr.show = lpm_enable_show;
+ avail->idle_enabled_attr.store = lpm_enable_store;
+
+ sysfs_attr_init(&avail->suspend_enabled_attr.attr);
+ avail->suspend_enabled_attr.attr.name = lpm_types[SUSPEND].str;
+ avail->suspend_enabled_attr.attr.mode = 0644;
+ avail->suspend_enabled_attr.show = lpm_enable_show;
+ avail->suspend_enabled_attr.store = lpm_enable_store;
+
+ attr[0] = &avail->idle_enabled_attr.attr;
+ attr[1] = &avail->suspend_enabled_attr.attr;
+ attr[2] = NULL;
+ attr_group->attrs = attr;
+
+ ret = sysfs_create_group(kobj, attr_group);
+ if (ret) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ avail->idle_enabled = true;
+ avail->suspend_enabled = true;
+ avail->kobj = kobj;
+ avail->data = data;
+ avail->idx = index;
+ avail->cpu_node = cpu_node;
+
+ return ret;
+
+failed:
+ kobject_put(kobj);
+ return ret;
+}
+
+static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
+{
+ int cpu;
+ int i, cpu_idx;
+ struct kobject **cpu_kobj = NULL;
+ struct lpm_level_avail *level_list = NULL;
+ char cpu_name[20] = {0};
+ int ret = 0;
+
+ cpu_kobj = devm_kzalloc(&lpm_pdev->dev, sizeof(*cpu_kobj) *
+ cpumask_weight(&p->child_cpus), GFP_KERNEL);
+ if (!cpu_kobj)
+ return -ENOMEM;
+
+ cpu_idx = 0;
+ for_each_cpu(cpu, &p->child_cpus) {
+ snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu);
+ cpu_kobj[cpu_idx] = kobject_create_and_add(cpu_name, parent);
+ if (!cpu_kobj[cpu_idx]) {
+ ret = -ENOMEM;
+ goto release_kobj;
+ }
+
+ level_list = devm_kzalloc(&lpm_pdev->dev,
+ p->cpu->nlevels * sizeof(*level_list),
+ GFP_KERNEL);
+ if (!level_list) {
+ ret = -ENOMEM;
+ goto release_kobj;
+ }
+
+ for (i = 0; i < p->cpu->nlevels; i++) {
+
+ ret = create_lvl_avail_nodes(p->cpu->levels[i].name,
+ cpu_kobj[cpu_idx], &level_list[i],
+ (void *)p->cpu, cpu, true);
+ if (ret)
+ goto release_kobj;
+ }
+
+ cpu_level_available[cpu] = level_list;
+ cpu_idx++;
+ }
+
+ return ret;
+
+release_kobj:
+ for (i = 0; i < cpumask_weight(&p->child_cpus); i++)
+ kobject_put(cpu_kobj[i]);
+
+ return ret;
+}
+
+int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj)
+{
+ int ret = 0;
+ struct lpm_cluster *child = NULL;
+ int i;
+ struct kobject *cluster_kobj = NULL;
+
+ if (!p)
+ return -ENODEV;
+
+ cluster_kobj = kobject_create_and_add(p->cluster_name, kobj);
+ if (!cluster_kobj)
+ return -ENOMEM;
+
+ for (i = 0; i < p->nlevels; i++) {
+ ret = create_lvl_avail_nodes(p->levels[i].level_name,
+ cluster_kobj, &p->levels[i].available,
+ (void *)p, 0, false);
+ if (ret)
+ return ret;
+ }
+
+ list_for_each_entry(child, &p->child, list) {
+ ret = create_cluster_lvl_nodes(child, cluster_kobj);
+ if (ret)
+ return ret;
+ }
+
+ if (p->cpu) {
+ ret = create_cpu_lvl_nodes(p, cluster_kobj);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+bool lpm_cpu_mode_allow(unsigned int cpu,
+ unsigned int index, bool from_idle)
+{
+ struct lpm_level_avail *avail = cpu_level_available[cpu];
+
+ if (!lpm_pdev || !avail)
+ return !from_idle;
+
+ return !!(from_idle ? avail[index].idle_enabled :
+ avail[index].suspend_enabled);
+}
+
+bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
+ unsigned int mode, bool from_idle)
+{
+ struct lpm_level_avail *avail = &cluster->levels[mode].available;
+
+ if (!lpm_pdev || !avail)
+ return false;
+
+ return !!(from_idle ? avail->idle_enabled :
+ avail->suspend_enabled);
+}
+
+static int parse_legacy_cluster_params(struct device_node *node,
+ struct lpm_cluster *c)
+{
+ int i;
+ char *key;
+ int ret;
+ struct lpm_match {
+ char *devname;
+ int (*set_mode)(struct low_power_ops *, int,
+ struct lpm_cluster_level *);
+ };
+ struct lpm_match match_tbl[] = {
+ {"l2", set_l2_mode},
+ {"cci", set_system_mode},
+ {"l3", set_l3_mode},
+ {"cbf", set_system_mode},
+ };
+
+
+ key = "qcom,spm-device-names";
+ c->ndevices = of_property_count_strings(node, key);
+
+ if (c->ndevices < 0) {
+ pr_info("%s(): Ignoring cluster params\n", __func__);
+ c->no_saw_devices = true;
+ c->ndevices = 0;
+ return 0;
+ }
+
+ c->name = devm_kzalloc(&lpm_pdev->dev, c->ndevices * sizeof(*c->name),
+ GFP_KERNEL);
+ c->lpm_dev = devm_kzalloc(&lpm_pdev->dev,
+ c->ndevices * sizeof(*c->lpm_dev),
+ GFP_KERNEL);
+ if (!c->name || !c->lpm_dev) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ for (i = 0; i < c->ndevices; i++) {
+ char device_name[20];
+ int j;
+
+ ret = of_property_read_string_index(node, key, i, &c->name[i]);
+ if (ret)
+ goto failed;
+ snprintf(device_name, sizeof(device_name), "%s-%s",
+ c->cluster_name, c->name[i]);
+
+ c->lpm_dev[i].spm = msm_spm_get_device_by_name(device_name);
+
+ if (IS_ERR_OR_NULL(c->lpm_dev[i].spm)) {
+ pr_err("Failed to get spm device by name:%s\n",
+ device_name);
+ ret = PTR_ERR(c->lpm_dev[i].spm);
+ goto failed;
+ }
+ for (j = 0; j < ARRAY_SIZE(match_tbl); j++) {
+ if (!strcmp(c->name[i], match_tbl[j].devname))
+ c->lpm_dev[i].set_mode = match_tbl[j].set_mode;
+ }
+
+ if (!c->lpm_dev[i].set_mode) {
+ ret = -ENODEV;
+ goto failed;
+ }
+ }
+
+ key = "qcom,default-level";
+ if (of_property_read_u32(node, key, &c->default_level))
+ c->default_level = 0;
+ return 0;
+failed:
+ pr_err("%s(): Failed reading %s\n", __func__, key);
+ return ret;
+}
+
+static int parse_cluster_params(struct device_node *node,
+ struct lpm_cluster *c)
+{
+ char *key;
+ int ret;
+
+ key = "label";
+ ret = of_property_read_string(node, key, &c->cluster_name);
+ if (ret) {
+ pr_err("%s(): Cannot read required param %s\n", __func__, key);
+ return ret;
+ }
+
+ if (use_psci) {
+ key = "qcom,psci-mode-shift";
+ ret = of_property_read_u32(node, key,
+ &c->psci_mode_shift);
+ if (ret) {
+ pr_err("%s(): Failed to read param: %s\n",
+ __func__, key);
+ return ret;
+ }
+
+ key = "qcom,psci-mode-mask";
+ ret = of_property_read_u32(node, key,
+ &c->psci_mode_mask);
+ if (ret) {
+ pr_err("%s(): Failed to read param: %s\n",
+ __func__, key);
+ return ret;
+ }
+
+ /* Set ndevice to 1 as default */
+ c->ndevices = 1;
+
+ return 0;
+ } else
+ return parse_legacy_cluster_params(node, c);
+}
+
+static int parse_lpm_mode(const char *str)
+{
+ int i;
+ struct lpm_lookup_table mode_lookup[] = {
+ {MSM_SPM_MODE_POWER_COLLAPSE, "pc"},
+ {MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE, "spc"},
+ {MSM_SPM_MODE_FASTPC, "fpc"},
+ {MSM_SPM_MODE_GDHS, "gdhs"},
+ {MSM_SPM_MODE_RETENTION, "retention"},
+ {MSM_SPM_MODE_CLOCK_GATING, "wfi"},
+ {MSM_SPM_MODE_DISABLED, "active"}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(mode_lookup); i++)
+ if (!strcmp(str, mode_lookup[i].mode_name))
+ return mode_lookup[i].modes;
+ return -EINVAL;
+}
+
+static int parse_power_params(struct device_node *node,
+ struct power_params *pwr)
+{
+ char *key;
+ int ret;
+
+ key = "qcom,latency-us";
+ ret = of_property_read_u32(node, key, &pwr->latency_us);
+ if (ret)
+ goto fail;
+
+ key = "qcom,ss-power";
+ ret = of_property_read_u32(node, key, &pwr->ss_power);
+ if (ret)
+ goto fail;
+
+ key = "qcom,energy-overhead";
+ ret = of_property_read_u32(node, key, &pwr->energy_overhead);
+ if (ret)
+ goto fail;
+
+ key = "qcom,time-overhead";
+ ret = of_property_read_u32(node, key, &pwr->time_overhead_us);
+ if (ret)
+ goto fail;
+
+fail:
+ if (ret)
+ pr_err("%s(): %s Error reading %s\n", __func__, node->name,
+ key);
+ return ret;
+}
+
+static int parse_cluster_level(struct device_node *node,
+ struct lpm_cluster *cluster)
+{
+ int i = 0;
+ struct lpm_cluster_level *level = &cluster->levels[cluster->nlevels];
+ int ret = -ENOMEM;
+ char *key;
+
+ key = "label";
+ ret = of_property_read_string(node, key, &level->level_name);
+ if (ret)
+ goto failed;
+
+ if (use_psci) {
+ char *k = "qcom,psci-mode";
+
+ ret = of_property_read_u32(node, k, &level->psci_id);
+ if (ret)
+ goto failed;
+
+ level->is_reset = of_property_read_bool(node, "qcom,is-reset");
+ } else if (!cluster->no_saw_devices) {
+ key = "no saw-devices";
+
+ level->mode = devm_kzalloc(&lpm_pdev->dev,
+ cluster->ndevices * sizeof(*level->mode),
+ GFP_KERNEL);
+ if (!level->mode) {
+ pr_err("Memory allocation failed\n");
+ goto failed;
+ }
+
+ for (i = 0; i < cluster->ndevices; i++) {
+ const char *spm_mode;
+ char key[25] = {0};
+
+ snprintf(key, 25, "qcom,spm-%s-mode", cluster->name[i]);
+ ret = of_property_read_string(node, key, &spm_mode);
+ if (ret)
+ goto failed;
+
+ level->mode[i] = parse_lpm_mode(spm_mode);
+
+ if (level->mode[i] < 0)
+ goto failed;
+
+ if (level->mode[i] == MSM_SPM_MODE_POWER_COLLAPSE
+ || level->mode[i] ==
+ MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE)
+ level->is_reset |= true;
+ }
+ }
+
+ key = "label";
+ ret = of_property_read_string(node, key, &level->level_name);
+ if (ret)
+ goto failed;
+
+ if (cluster->nlevels != cluster->default_level) {
+ key = "min child idx";
+ ret = of_property_read_u32(node, "qcom,min-child-idx",
+ &level->min_child_level);
+ if (ret)
+ goto failed;
+
+ if (cluster->min_child_level > level->min_child_level)
+ cluster->min_child_level = level->min_child_level;
+ }
+
+ level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm");
+ level->disable_dynamic_routing = of_property_read_bool(node,
+ "qcom,disable-dynamic-int-routing");
+ level->last_core_only = of_property_read_bool(node,
+ "qcom,last-core-only");
+ level->no_cache_flush = of_property_read_bool(node,
+ "qcom,no-cache-flush");
+
+ key = "parse_power_params";
+ ret = parse_power_params(node, &level->pwr);
+ if (ret)
+ goto failed;
+
+ key = "qcom,reset-level";
+ ret = of_property_read_u32(node, key, &level->reset_level);
+ if (ret == -EINVAL)
+ level->reset_level = LPM_RESET_LVL_NONE;
+ else if (ret)
+ goto failed;
+
+ cluster->nlevels++;
+ return 0;
+failed:
+ pr_err("Failed %s() key = %s ret = %d\n", __func__, key, ret);
+ return ret;
+}
+
+static int parse_cpu_spm_mode(const char *mode_name)
+{
+ struct lpm_lookup_table pm_sm_lookup[] = {
+ {MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+ "wfi"},
+ {MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+ "standalone_pc"},
+ {MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+ "pc"},
+ {MSM_PM_SLEEP_MODE_RETENTION,
+ "retention"},
+ {MSM_PM_SLEEP_MODE_FASTPC,
+ "fpc"},
+ };
+ int i;
+ int ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(pm_sm_lookup); i++) {
+ if (!strcmp(mode_name, pm_sm_lookup[i].mode_name)) {
+ ret = pm_sm_lookup[i].modes;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int parse_cpu_mode(struct device_node *n, struct lpm_cpu_level *l)
+{
+ char *key;
+ int ret;
+
+ key = "qcom,spm-cpu-mode";
+ ret = of_property_read_string(n, key, &l->name);
+ if (ret) {
+ pr_err("Failed %s %d\n", n->name, __LINE__);
+ return ret;
+ }
+
+ if (use_psci) {
+ key = "qcom,psci-cpu-mode";
+
+ ret = of_property_read_u32(n, key, &l->psci_id);
+ if (ret) {
+ pr_err("Failed reading %s on device %s\n", key,
+ n->name);
+ return ret;
+ }
+ key = "qcom,hyp-psci";
+
+ l->hyp_psci = of_property_read_bool(n, key);
+ } else {
+ l->mode = parse_cpu_spm_mode(l->name);
+
+ if (l->mode < 0)
+ return l->mode;
+ }
+ return 0;
+
+}
+
+static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask)
+{
+ struct device_node *cpu_node;
+ int cpu;
+ int idx = 0;
+
+ cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
+ if (!cpu_node) {
+ pr_info("%s: No CPU phandle, assuming single cluster\n",
+ node->full_name);
+ /*
+ * Not all targets have the cpu node populated in the device
+ * tree. If cpu node is not populated assume all possible
+ * nodes belong to this cluster
+ */
+ cpumask_copy(mask, cpu_possible_mask);
+ return 0;
+ }
+
+ while (cpu_node) {
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+ cpumask_set_cpu(cpu, mask);
+ break;
+ }
+ }
+ of_node_put(cpu_node);
+ cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
+ }
+
+ return 0;
+}
+
+static int calculate_residency(struct power_params *base_pwr,
+ struct power_params *next_pwr)
+{
+ int32_t residency = (int32_t)(next_pwr->energy_overhead -
+ base_pwr->energy_overhead) -
+ ((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us)
+ - (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us));
+
+ residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power);
+
+ if (residency < 0) {
+ pr_err("%s: residency < 0 for LPM\n",
+ __func__);
+ return next_pwr->time_overhead_us;
+ }
+
+ return residency < next_pwr->time_overhead_us ?
+ next_pwr->time_overhead_us : residency;
+}
+
+static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
+{
+ struct device_node *n;
+ int ret = -ENOMEM;
+ int i, j;
+ char *key;
+
+ c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL);
+ if (!c->cpu)
+ return ret;
+
+ c->cpu->parent = c;
+ if (use_psci) {
+
+ key = "qcom,psci-mode-shift";
+
+ ret = of_property_read_u32(node, key, &c->cpu->psci_mode_shift);
+ if (ret) {
+ pr_err("Failed reading %s on device %s\n", key,
+ node->name);
+ return ret;
+ }
+ key = "qcom,psci-mode-mask";
+
+ ret = of_property_read_u32(node, key, &c->cpu->psci_mode_mask);
+ if (ret) {
+ pr_err("Failed reading %s on device %s\n", key,
+ node->name);
+ return ret;
+ }
+ }
+ for_each_child_of_node(node, n) {
+ struct lpm_cpu_level *l = &c->cpu->levels[c->cpu->nlevels];
+
+ c->cpu->nlevels++;
+
+ ret = parse_cpu_mode(n, l);
+ if (ret < 0) {
+ pr_info("Failed %s\n", l->name);
+ goto failed;
+ }
+
+ ret = parse_power_params(n, &l->pwr);
+ if (ret)
+ goto failed;
+
+ key = "qcom,use-broadcast-timer";
+ l->use_bc_timer = of_property_read_bool(n, key);
+
+ l->is_reset = of_property_read_bool(n, "qcom,is-reset");
+
+ key = "qcom,jtag-save-restore";
+ l->jtag_save_restore = of_property_read_bool(n, key);
+
+ key = "qcom,reset-level";
+ ret = of_property_read_u32(n, key, &l->reset_level);
+ if (ret == -EINVAL)
+ l->reset_level = LPM_RESET_LVL_NONE;
+ else if (ret)
+ goto failed;
+ of_node_put(n);
+ }
+ for (i = 0; i < c->cpu->nlevels; i++) {
+ for (j = 0; j < c->cpu->nlevels; j++) {
+ if (i >= j) {
+ c->cpu->levels[i].pwr.residencies[j] = 0;
+ continue;
+ }
+
+ c->cpu->levels[i].pwr.residencies[j] =
+ calculate_residency(&c->cpu->levels[i].pwr,
+ &c->cpu->levels[j].pwr);
+
+ pr_err("%s: idx %d %u\n", __func__, j,
+ c->cpu->levels[i].pwr.residencies[j]);
+ }
+ }
+
+ return 0;
+failed:
+ of_node_put(n);
+ pr_err("%s(): Failed with error code:%d\n", __func__, ret);
+ return ret;
+}
+
+void free_cluster_node(struct lpm_cluster *cluster)
+{
+ struct lpm_cluster *cl, *m;
+
+ list_for_each_entry_safe(cl, m, &cluster->child, list) {
+ list_del(&cl->list);
+ free_cluster_node(cl);
+ };
+
+ cluster->ndevices = 0;
+}
+
+/*
+ * TODO:
+ * Expects a CPU or a cluster only. This ensures that affinity
+ * level of a cluster is consistent with reference to its
+ * child nodes.
+ */
+static struct lpm_cluster *parse_cluster(struct device_node *node,
+ struct lpm_cluster *parent)
+{
+ struct lpm_cluster *c;
+ struct device_node *n;
+ char *key;
+ int ret = 0;
+ int i, j;
+
+ c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return ERR_PTR(-ENOMEM);
+
+ ret = parse_cluster_params(node, c);
+
+ if (ret)
+ goto failed_parse_params;
+
+ INIT_LIST_HEAD(&c->child);
+ c->parent = parent;
+ spin_lock_init(&c->sync_lock);
+ c->min_child_level = NR_LPM_LEVELS;
+
+ for_each_child_of_node(node, n) {
+
+ if (!n->name)
+ continue;
+ key = "qcom,pm-cluster-level";
+ if (!of_node_cmp(n->name, key)) {
+ if (parse_cluster_level(n, c))
+ goto failed_parse_cluster;
+ continue;
+ }
+
+ key = "qcom,pm-cluster";
+ if (!of_node_cmp(n->name, key)) {
+ struct lpm_cluster *child;
+
+ if (c->no_saw_devices)
+ pr_info("%s: SAW device not provided.\n",
+ __func__);
+
+ child = parse_cluster(n, c);
+ if (!child)
+ goto failed_parse_cluster;
+
+ of_node_put(n);
+ list_add(&child->list, &c->child);
+ cpumask_or(&c->child_cpus, &c->child_cpus,
+ &child->child_cpus);
+ c->aff_level = child->aff_level + 1;
+ continue;
+ }
+
+ key = "qcom,pm-cpu";
+ if (!of_node_cmp(n->name, key)) {
+ /*
+ * Parse the the cpu node only if a pm-cpu node
+ * is available, though the mask is defined @ the
+ * cluster level
+ */
+ if (get_cpumask_for_node(node, &c->child_cpus))
+ goto failed_parse_cluster;
+
+ if (parse_cpu_levels(n, c))
+ goto failed_parse_cluster;
+
+ c->aff_level = 1;
+
+ for_each_cpu(i, &c->child_cpus) {
+ per_cpu(max_residency, i) = devm_kzalloc(
+ &lpm_pdev->dev,
+ sizeof(uint32_t) * c->cpu->nlevels,
+ GFP_KERNEL);
+ if (!per_cpu(max_residency, i))
+ return ERR_PTR(-ENOMEM);
+ set_optimum_cpu_residency(c->cpu, i, true);
+ }
+ }
+ }
+
+ if (cpumask_intersects(&c->child_cpus, cpu_online_mask))
+ c->last_level = c->default_level;
+ else
+ c->last_level = c->nlevels-1;
+
+ for (i = 0; i < c->nlevels; i++) {
+ for (j = 0; j < c->nlevels; j++) {
+ if (i >= j) {
+ c->levels[i].pwr.residencies[j] = 0;
+ continue;
+ }
+ c->levels[i].pwr.residencies[j] = calculate_residency(
+ &c->levels[i].pwr, &c->levels[j].pwr);
+ }
+ }
+ set_optimum_cluster_residency(c, true);
+ return c;
+
+failed_parse_cluster:
+ pr_err("Failed parse cluster:%s\n", key);
+ if (parent)
+ list_del(&c->list);
+ free_cluster_node(c);
+failed_parse_params:
+ pr_err("Failed parse params\n");
+ return NULL;
+}
+struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
+{
+ struct device_node *top = NULL;
+ struct lpm_cluster *c;
+
+ use_psci = of_property_read_bool(pdev->dev.of_node, "qcom,use-psci");
+
+ top = of_find_node_by_name(pdev->dev.of_node, "qcom,pm-cluster");
+ if (!top) {
+ pr_err("Failed to find root node\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ lpm_pdev = pdev;
+ c = parse_cluster(top, NULL);
+ of_node_put(top);
+ return c;
+}
+
+void cluster_dt_walkthrough(struct lpm_cluster *cluster)
+{
+ struct list_head *list;
+ int i, j;
+ static int id;
+ char str[10] = {0};
+
+ if (!cluster)
+ return;
+
+ for (i = 0; i < id; i++)
+ snprintf(str+i, 10 - i, "\t");
+ pr_info("%d\n", __LINE__);
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct lpm_cluster_level *l = &cluster->levels[i];
+
+ pr_info("%d ndevices:%d\n", __LINE__, cluster->ndevices);
+ for (j = 0; j < cluster->ndevices; j++)
+ pr_info("%sDevice: %pk id:%pk\n", str,
+ &cluster->name[j], &l->mode[i]);
+ }
+
+ if (cluster->cpu) {
+ pr_info("%d\n", __LINE__);
+ for (j = 0; j < cluster->cpu->nlevels; j++)
+ pr_info("%s\tCPU mode: %s id:%d\n", str,
+ cluster->cpu->levels[j].name,
+ cluster->cpu->levels[j].mode);
+ }
+
+ id++;
+
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ pr_info("%d\n", __LINE__);
+ n = list_entry(list, typeof(*n), list);
+ cluster_dt_walkthrough(n);
+ }
+ id--;
+}
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index ad0be45..1d1d7e7 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -474,7 +474,7 @@
/* Set default_level to 0 as default */
c->default_level = 0;
- return ret;
+ return 0;
fail:
pr_err("Failed to read key: %s ret: %d\n", key, ret);
@@ -614,6 +614,7 @@
break;
}
}
+ of_node_put(cpu_node);
cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
}
@@ -651,13 +652,16 @@
cpu->nlevels++;
ret = parse_cpu_mode(n, l);
- if (ret)
+ if (ret) {
+ of_node_put(n);
return ret;
+ }
ret = parse_power_params(n, &l->pwr);
- if (ret)
+ if (ret) {
+ of_node_put(n);
return ret;
-
+ }
key = "qcom,use-broadcast-timer";
l->use_bc_timer = of_property_read_bool(n, key);
@@ -670,6 +674,7 @@
l->reset_level = LPM_RESET_LVL_NONE;
else if (ret)
return ret;
+ of_node_put(n);
}
for (i = 0; i < cpu->nlevels; i++) {
@@ -708,7 +713,7 @@
static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
{
- int ret, i;
+ int ret;
char *key;
struct lpm_cpu *cpu;
@@ -724,12 +729,12 @@
key = "qcom,psci-mode-shift";
ret = of_property_read_u32(node, key, &cpu->psci_mode_shift);
if (ret)
- goto failed_parse_params;
+ goto failed;
key = "qcom,psci-mode-mask";
ret = of_property_read_u32(node, key, &cpu->psci_mode_mask);
if (ret)
- goto failed_parse_params;
+ goto failed;
key = "qcom,disable-prediction";
cpu->lpm_prediction = !(of_property_read_bool(node, key));
@@ -757,20 +762,14 @@
key = "parse_cpu";
ret = parse_cpu(node, cpu);
if (ret)
- goto failed_parse_cpu;
+ goto failed;
cpumask_or(&c->child_cpus, &c->child_cpus, &cpu->related_cpus);
list_add(&cpu->list, &c->cpu);
return ret;
-failed_parse_cpu:
- for (i = 0; i < cpu->nlevels; i++) {
- kfree(cpu->levels[i].name);
- cpu->levels[i].name = NULL;
- }
-
-failed_parse_params:
+failed:
pr_err("Failed to read key: %s node: %s\n", key, node->name);
return ret;
}
@@ -785,15 +784,8 @@
free_cluster_node(cl);
};
- list_for_each_entry_safe(cpu, n, &cluster->cpu, list) {
- int i;
-
+ list_for_each_entry_safe(cpu, n, &cluster->cpu, list)
list_del(&cpu->list);
- for (i = 0; i < cpu->nlevels; i++) {
- kfree(cpu->levels[i].name);
- cpu->levels[i].name = NULL;
- }
- }
}
/*
@@ -833,8 +825,11 @@
key = "qcom,pm-cluster-level";
if (!of_node_cmp(n->name, key)) {
- if (parse_cluster_level(n, c))
+ if (parse_cluster_level(n, c)) {
+ of_node_put(n);
goto failed_parse_cluster;
+ }
+ of_node_put(n);
continue;
}
@@ -843,22 +838,28 @@
struct lpm_cluster *child;
child = parse_cluster(n, c);
- if (!child)
+ if (!child) {
+ of_node_put(n);
goto failed_parse_cluster;
+ }
list_add(&child->list, &c->child);
cpumask_or(&c->child_cpus, &c->child_cpus,
&child->child_cpus);
c->aff_level = child->aff_level + 1;
+ of_node_put(n);
continue;
}
key = "qcom,pm-cpu";
if (!of_node_cmp(n->name, key)) {
- if (parse_cpu_levels(n, c))
+ if (parse_cpu_levels(n, c)) {
+ of_node_put(n);
goto failed_parse_cluster;
+ }
c->aff_level = 1;
+ of_node_put(n);
}
}
@@ -886,13 +887,13 @@
list_del(&c->list);
free_cluster_node(c);
failed_parse_params:
- c->parent = NULL;
pr_err("Failed parse params\n");
return NULL;
}
struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
{
struct device_node *top = NULL;
+ struct lpm_cluster *c;
top = of_find_node_by_name(pdev->dev.of_node, "qcom,pm-cluster");
if (!top) {
@@ -901,7 +902,9 @@
}
lpm_pdev = pdev;
- return parse_cluster(top, NULL);
+ c = parse_cluster(top, NULL);
+ of_node_put(top);
+ return c;
}
void cluster_dt_walkthrough(struct lpm_cluster *cluster)
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 9ae640d..463589a 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -341,6 +341,11 @@
{
unsigned int cpu = raw_smp_processor_id();
struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu);
+ ktime_t time_rem;
+
+ time_rem = hrtimer_get_remaining(cpu_histtimer);
+ if (ktime_to_us(time_rem) <= 0)
+ return;
hrtimer_try_to_cancel(cpu_histtimer);
}
@@ -386,11 +391,21 @@
{
int cpu = raw_smp_processor_id();
struct lpm_cluster *cluster = per_cpu(cpu_lpm, cpu)->parent;
+ ktime_t time_rem;
- hrtimer_try_to_cancel(&cluster->histtimer);
+ time_rem = hrtimer_get_remaining(&cluster->histtimer);
+ if (ktime_to_us(time_rem) > 0)
+ hrtimer_try_to_cancel(&cluster->histtimer);
- if (cluster->parent)
+ if (cluster->parent) {
+ time_rem = hrtimer_get_remaining(
+ &cluster->parent->histtimer);
+
+ if (ktime_to_us(time_rem) <= 0)
+ return;
+
hrtimer_try_to_cancel(&cluster->parent->histtimer);
+ }
}
static enum hrtimer_restart clusttimer_fn(struct hrtimer *h)
@@ -1394,11 +1409,11 @@
dev->last_residency = ktime_us_delta(ktime_get(), start);
update_history(dev, idx);
trace_cpu_idle_exit(idx, success);
- local_irq_enable();
if (lpm_prediction && cpu->lpm_prediction) {
histtimer_cancel();
clusttimer_cancel();
}
+ local_irq_enable();
return idx;
}
@@ -1783,12 +1798,10 @@
#endif
rc = platform_driver_register(&lpm_driver);
- if (rc) {
- pr_info("Error registering %s\n", lpm_driver.driver.name);
- goto fail;
- }
+ if (rc)
+ pr_info("Error registering %s rc=%d\n", lpm_driver.driver.name,
+ rc);
-fail:
return rc;
}
late_initcall(lpm_levels_module_init);
diff --git a/drivers/cpuidle/lpm-workarounds.c b/drivers/cpuidle/lpm-workarounds.c
new file mode 100644
index 0000000..657e2b9
--- /dev/null
+++ b/drivers/cpuidle/lpm-workarounds.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <lpm-workarounds.h>
+
+static struct regulator *lpm_cx_reg;
+static struct work_struct dummy_vote_work;
+static struct workqueue_struct *lpm_wa_wq;
+static bool lpm_wa_cx_turbo_unvote;
+static bool skip_l2_spm;
+
+/* While exiting from RPM assisted power collapse on some targets like MSM8939
+ * the CX is bumped to turbo mode by RPM. To reduce the power impact, APSS
+ * low power driver need to remove the CX turbo vote.
+ */
+static void send_dummy_cx_vote(struct work_struct *w)
+{
+ if (lpm_cx_reg) {
+ regulator_set_voltage(lpm_cx_reg,
+ RPM_REGULATOR_CORNER_SUPER_TURBO,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+
+ regulator_set_voltage(lpm_cx_reg,
+ RPM_REGULATOR_CORNER_NONE,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
+}
+
+/*
+ * lpm_wa_cx_unvote_send(): Unvote for CX turbo mode
+ */
+void lpm_wa_cx_unvote_send(void)
+{
+ if (lpm_wa_cx_turbo_unvote)
+ queue_work(lpm_wa_wq, &dummy_vote_work);
+}
+EXPORT_SYMBOL(lpm_wa_cx_unvote_send);
+
+static int lpm_wa_cx_unvote_init(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ lpm_cx_reg = devm_regulator_get(&pdev->dev, "lpm-cx");
+ if (IS_ERR(lpm_cx_reg)) {
+ ret = PTR_ERR(lpm_cx_reg);
+ if (ret != -EPROBE_DEFER)
+ pr_err("Unable to get the CX regulator\n");
+ return ret;
+ }
+
+ INIT_WORK(&dummy_vote_work, send_dummy_cx_vote);
+
+ lpm_wa_wq = alloc_workqueue("lpm-wa",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
+
+ return ret;
+}
+
+static int lpm_wa_cx_unvote_exit(void)
+{
+ if (lpm_wa_wq)
+ destroy_workqueue(lpm_wa_wq);
+
+ return 0;
+}
+
+bool lpm_wa_get_skip_l2_spm(void)
+{
+ return skip_l2_spm;
+}
+EXPORT_SYMBOL(lpm_wa_get_skip_l2_spm);
+
+static int lpm_wa_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ lpm_wa_cx_turbo_unvote = of_property_read_bool(pdev->dev.of_node,
+ "qcom,lpm-wa-cx-turbo-unvote");
+ if (lpm_wa_cx_turbo_unvote) {
+ ret = lpm_wa_cx_unvote_init(pdev);
+ if (ret) {
+ pr_err("%s: Failed to initialize lpm_wa_cx_unvote (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ skip_l2_spm = of_property_read_bool(pdev->dev.of_node,
+ "qcom,lpm-wa-skip-l2-spm");
+
+ return ret;
+}
+
+static int lpm_wa_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (lpm_wa_cx_turbo_unvote)
+ ret = lpm_wa_cx_unvote_exit();
+
+ return ret;
+}
+
+static const struct of_device_id lpm_wa_mtch_tbl[] = {
+ {.compatible = "qcom,lpm-workarounds"},
+ {},
+};
+
+static struct platform_driver lpm_wa_driver = {
+ .probe = lpm_wa_probe,
+ .remove = lpm_wa_remove,
+ .driver = {
+ .name = "lpm-workarounds",
+ .owner = THIS_MODULE,
+ .of_match_table = lpm_wa_mtch_tbl,
+ },
+};
+
+static int __init lpm_wa_module_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&lpm_wa_driver);
+ if (ret)
+ pr_info("Error registering %s\n", lpm_wa_driver.driver.name);
+
+ return ret;
+}
+late_initcall(lpm_wa_module_init);
diff --git a/drivers/cpuidle/lpm-workarounds.h b/drivers/cpuidle/lpm-workarounds.h
new file mode 100644
index 0000000..a290dcb
--- /dev/null
+++ b/drivers/cpuidle/lpm-workarounds.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LPM_WA_H
+#define __LPM_WA_H
+
+void lpm_wa_cx_unvote_send(void);
+bool lpm_wa_get_skip_l2_spm(void);
+
+#endif /* __LPM_WA_H */
diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c
index 3aa75aa..199d573 100644
--- a/drivers/crypto/msm/ice.c
+++ b/drivers/crypto/msm/ice.c
@@ -77,6 +77,8 @@
#define QCOM_ICE_ENCRYPT 0x1
#define QCOM_ICE_DECRYPT 0x2
#define QCOM_SECT_LEN_IN_BYTE 512
+#define QCOM_UD_FOOTER_SIZE 0x4000
+#define QCOM_UD_FOOTER_SECS (QCOM_UD_FOOTER_SIZE / QCOM_SECT_LEN_IN_BYTE)
struct ice_clk_info {
struct list_head list;
@@ -127,8 +129,6 @@
};
static int ice_fde_flag;
-static unsigned long userdata_start;
-static unsigned long userdata_end;
static struct ice_crypto_setting ice_data;
static int qti_ice_setting_config(struct request *req,
@@ -152,22 +152,29 @@
return -EPERM;
}
+ if (!setting)
+ return -EINVAL;
+
if ((short)(crypto_data->key_index) >= 0) {
memcpy(&setting->crypto_data, crypto_data,
sizeof(setting->crypto_data));
- if (rq_data_dir(req) == WRITE &&
- (ice_fde_flag & QCOM_ICE_ENCRYPT))
- setting->encr_bypass = false;
- else if (rq_data_dir(req) == READ &&
- (ice_fde_flag & QCOM_ICE_DECRYPT))
- setting->decr_bypass = false;
- else {
+ switch (rq_data_dir(req)) {
+ case WRITE:
+ if (!ice_fde_flag || (ice_fde_flag & QCOM_ICE_ENCRYPT))
+ setting->encr_bypass = false;
+ break;
+ case READ:
+ if (!ice_fde_flag || (ice_fde_flag & QCOM_ICE_DECRYPT))
+ setting->decr_bypass = false;
+ break;
+ default:
/* Should I say BUG_ON */
setting->encr_bypass = true;
setting->decr_bypass = true;
- pr_debug("%s direction unknown", __func__);
+ pr_debug("%s(): direction unknown\n", __func__);
+ break;
}
}
@@ -181,26 +188,6 @@
}
EXPORT_SYMBOL(qcom_ice_set_fde_flag);
-int qcom_ice_set_fde_conf(sector_t s_sector, sector_t size,
- int index, int mode)
-{
- userdata_start = s_sector;
- userdata_end = s_sector + size;
- if (INT_MAX - s_sector < size) {
- WARN_ON(1);
- return -EINVAL;
- }
- ice_data.key_index = index;
- ice_data.algo_mode = mode;
- ice_data.key_size = ICE_CRYPTO_KEY_SIZE_256;
- ice_data.key_mode = ICE_CRYPTO_USE_LUT_SW_KEY;
-
- pr_debug("%s sector info set start %lu end %lu\n", __func__,
- userdata_start, userdata_end);
- return 0;
-}
-EXPORT_SYMBOL(qcom_ice_set_fde_conf);
-
static int qcom_ice_enable_clocks(struct ice_device *, bool);
#ifdef CONFIG_MSM_BUS_SCALING
@@ -1483,12 +1470,15 @@
struct request *req,
struct ice_data_setting *setting, bool async)
{
+ struct ice_crypto_setting *crypto_data;
struct ice_crypto_setting pfk_crypto_data = {0};
int ret = 0;
bool is_pfe = false;
sector_t data_size;
+ union map_info *info;
+ unsigned long sec_end = 0;
- if (!pdev || !req || !setting) {
+ if (!pdev || !req) {
pr_err("%s: Invalid params passed\n", __func__);
return -EINVAL;
}
@@ -1507,6 +1497,7 @@
/* It is not an error to have a request with no bio */
return 0;
}
+ //pr_err("%s bio is %pK\n", __func__, req->bio);
ret = pfk_load_key_start(req->bio, &pfk_crypto_data, &is_pfe, async);
if (is_pfe) {
@@ -1521,22 +1512,46 @@
&pfk_crypto_data, setting);
}
- if (ice_fde_flag == 0)
- return 0;
-
- if ((req->__sector >= userdata_start) &&
- (req->__sector < userdata_end)) {
- /*
- * Ugly hack to address non-block-size aligned userdata end address in
- * eMMC based devices.
- */
- data_size = req->__data_len/QCOM_SECT_LEN_IN_BYTE;
-
- if ((req->__sector + data_size) > userdata_end)
- return 0;
- else
+ if (!ice_fde_flag) {
+ if (bio_flagged(req->bio, BIO_INLINECRYPT)) {
+ info = dm_get_rq_mapinfo(req);
+ if (!info) {
+ pr_debug("%s info not available in request\n",
+ __func__);
+ return 0;
+ }
+ crypto_data = (struct ice_crypto_setting *)info->ptr;
+ if (!crypto_data) {
+ pr_err("%s crypto_data not available in req\n",
+ __func__);
+ return -EINVAL;
+ }
return qti_ice_setting_config(req, pdev,
- &ice_data, setting);
+ crypto_data, setting);
+ }
+ return 0;
+ }
+
+ if (req->part && req->part->info && req->part->info->volname[0]) {
+ if (!strcmp(req->part->info->volname, "userdata")) {
+ sec_end = req->part->start_sect + req->part->nr_sects -
+ QCOM_UD_FOOTER_SECS;
+ if ((req->__sector >= req->part->start_sect) &&
+ (req->__sector < sec_end)) {
+ /*
+ * Ugly hack to address non-block-size aligned
+ * userdata end address in eMMC based devices.
+ */
+ data_size = req->__data_len /
+ QCOM_SECT_LEN_IN_BYTE;
+
+ if ((req->__sector + data_size) > sec_end)
+ return 0;
+ else
+ return qti_ice_setting_config(req, pdev,
+ &ice_data, setting);
+ }
+ }
}
/*
@@ -1664,7 +1679,7 @@
list_for_each_entry(ice_dev, &ice_devices, list) {
if (!strcmp(ice_dev->ice_instance_type, storage_type)) {
- pr_info("%s: found ice device %p\n", __func__, ice_dev);
+ pr_debug("%s: ice device %pK\n", __func__, ice_dev);
return ice_dev;
}
}
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index d0b16e5..d8305dd 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -750,7 +750,10 @@
if (final)
new_len = DIV_ROUND_UP(new_len, bs) * bs;
else
- new_len = new_len / bs * bs;
+ new_len = (new_len - 1) / bs * bs;
+
+ if (nbytes != new_len)
+ list_ok = false;
while (nbytes > 0 && sg_tmp) {
n++;
@@ -846,6 +849,8 @@
xmit_len = DIV_ROUND_UP(xmit_len, bs) * bs;
else
xmit_len = xmit_len / bs * bs;
+ } else if (!final) {
+ xmit_len -= bs;
}
hash_later = rctx->total - xmit_len;
@@ -873,14 +878,21 @@
}
if (hash_later) {
- if (req->nbytes) {
- scatterwalk_map_and_copy(rctx->buffer, req->src,
- req->nbytes - hash_later,
- hash_later, 0);
- } else {
+ int offset = 0;
+
+ if (hash_later > req->nbytes) {
memcpy(rctx->buffer, rctx->buffer + xmit_len,
- hash_later);
+ hash_later - req->nbytes);
+ offset = hash_later - req->nbytes;
}
+
+ if (req->nbytes) {
+ scatterwalk_map_and_copy(rctx->buffer + offset,
+ req->src,
+ offset + req->nbytes -
+ hash_later, hash_later, 0);
+ }
+
rctx->bufcnt = hash_later;
} else {
rctx->bufcnt = 0;
@@ -1130,7 +1142,7 @@
ctx = ahash_request_ctx(req);
err = omap_sham_prepare_request(req, ctx->op == OP_UPDATE);
- if (err)
+ if (err || !ctx->total)
goto err1;
dev_dbg(dd->dev, "handling new req, op: %lu, nbytes: %d\n",
@@ -1189,11 +1201,10 @@
if (!req->nbytes)
return 0;
- if (ctx->total + req->nbytes < ctx->buflen) {
+ if (ctx->bufcnt + req->nbytes <= ctx->buflen) {
scatterwalk_map_and_copy(ctx->buffer + ctx->bufcnt, req->src,
0, req->nbytes, 0);
ctx->bufcnt += req->nbytes;
- ctx->total += req->nbytes;
return 0;
}
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-core.c b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
index 3ac6c6c..16bb660 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-core.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
@@ -422,6 +422,7 @@
module_platform_driver(sun4i_ss_driver);
+MODULE_ALIAS("platform:sun4i-ss");
MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>");
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 42c060c..7c71722 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -1116,10 +1116,10 @@
return count;
}
-int talitos_sg_map(struct device *dev, struct scatterlist *src,
- unsigned int len, struct talitos_edesc *edesc,
- struct talitos_ptr *ptr,
- int sg_count, unsigned int offset, int tbl_off)
+static int talitos_sg_map_ext(struct device *dev, struct scatterlist *src,
+ unsigned int len, struct talitos_edesc *edesc,
+ struct talitos_ptr *ptr, int sg_count,
+ unsigned int offset, int tbl_off, int elen)
{
struct talitos_private *priv = dev_get_drvdata(dev);
bool is_sec1 = has_ftr_sec1(priv);
@@ -1130,7 +1130,7 @@
}
to_talitos_ptr_len(ptr, len, is_sec1);
- to_talitos_ptr_ext_set(ptr, 0, is_sec1);
+ to_talitos_ptr_ext_set(ptr, elen, is_sec1);
if (sg_count == 1) {
to_talitos_ptr(ptr, sg_dma_address(src) + offset, is_sec1);
@@ -1140,7 +1140,7 @@
to_talitos_ptr(ptr, edesc->dma_link_tbl + offset, is_sec1);
return sg_count;
}
- sg_count = sg_to_link_tbl_offset(src, sg_count, offset, len,
+ sg_count = sg_to_link_tbl_offset(src, sg_count, offset, len + elen,
&edesc->link_tbl[tbl_off]);
if (sg_count == 1) {
/* Only one segment now, so no link tbl needed*/
@@ -1154,6 +1154,15 @@
return sg_count;
}
+static int talitos_sg_map(struct device *dev, struct scatterlist *src,
+ unsigned int len, struct talitos_edesc *edesc,
+ struct talitos_ptr *ptr, int sg_count,
+ unsigned int offset, int tbl_off)
+{
+ return talitos_sg_map_ext(dev, src, len, edesc, ptr, sg_count, offset,
+ tbl_off, 0);
+}
+
/*
* fill in and submit ipsec_esp descriptor
*/
@@ -1171,7 +1180,7 @@
unsigned int ivsize = crypto_aead_ivsize(aead);
int tbl_off = 0;
int sg_count, ret;
- int sg_link_tbl_len;
+ int elen = 0;
bool sync_needed = false;
struct talitos_private *priv = dev_get_drvdata(dev);
bool is_sec1 = has_ftr_sec1(priv);
@@ -1225,20 +1234,12 @@
* extent is bytes of HMAC postpended to ciphertext,
* typically 12 for ipsec
*/
- to_talitos_ptr_len(&desc->ptr[4], cryptlen, is_sec1);
- to_talitos_ptr_ext_set(&desc->ptr[4], 0, is_sec1);
+ if ((desc->hdr & DESC_HDR_TYPE_IPSEC_ESP) &&
+ (desc->hdr & DESC_HDR_MODE1_MDEU_CICV))
+ elen = authsize;
- sg_link_tbl_len = cryptlen;
-
- if (desc->hdr & DESC_HDR_TYPE_IPSEC_ESP) {
- to_talitos_ptr_ext_set(&desc->ptr[4], authsize, is_sec1);
-
- if (edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV)
- sg_link_tbl_len += authsize;
- }
-
- ret = talitos_sg_map(dev, areq->src, sg_link_tbl_len, edesc,
- &desc->ptr[4], sg_count, areq->assoclen, tbl_off);
+ ret = talitos_sg_map_ext(dev, areq->src, cryptlen, edesc, &desc->ptr[4],
+ sg_count, areq->assoclen, tbl_off, elen);
if (ret > 1) {
tbl_off += ret;
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c
index 40be374..473b44c 100644
--- a/drivers/dax/dax.c
+++ b/drivers/dax/dax.c
@@ -453,9 +453,21 @@
return rc;
}
+static int dax_dev_split(struct vm_area_struct *vma, unsigned long addr)
+{
+ struct file *filp = vma->vm_file;
+ struct dax_dev *dax_dev = filp->private_data;
+ struct dax_region *dax_region = dax_dev->region;
+
+ if (!IS_ALIGNED(addr, dax_region->align))
+ return -EINVAL;
+ return 0;
+}
+
static const struct vm_operations_struct dax_dev_vm_ops = {
.fault = dax_dev_fault,
.pmd_fault = dax_dev_pmd_fault,
+ .split = dax_dev_split,
};
static int dax_mmap(struct file *filp, struct vm_area_struct *vma)
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index 1dca479..fe249e7 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -82,7 +82,7 @@
{
ktime_t ts;
unsigned int diff;
- unsigned long freq = 0;
+ uint64_t freq = 0;
ts = ktime_get();
diff = ktime_to_us(ktime_sub(ts, cpustats->prev_ts));
@@ -420,6 +420,7 @@
.driver = {
.name = "arm-memlat-mon",
.of_match_table = memlat_match_table,
+ .suppress_bind_attrs = true,
},
};
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 33e16261..d2aaffb 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -1128,6 +1128,7 @@
.driver = {
.name = "bimc-bwmon",
.of_match_table = bimc_bwmon_match_table,
+ .suppress_bind_attrs = true,
},
};
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 3357aaf..2298de2 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -969,7 +969,8 @@
if (df->governor == governor) {
ret = 0;
goto out;
- } else if (df->governor->immutable || governor->immutable) {
+ } else if ((df->governor && df->governor->immutable) ||
+ governor->immutable) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
index 7053bb4..1936383 100644
--- a/drivers/dma-buf/sync_file.c
+++ b/drivers/dma-buf/sync_file.c
@@ -305,7 +305,8 @@
poll_wait(file, &sync_file->wq, wait);
- if (!test_and_set_bit(POLL_ENABLED, &sync_file->flags)) {
+ if (list_empty(&sync_file->cb.node) &&
+ !test_and_set_bit(POLL_ENABLED, &sync_file->flags)) {
if (fence_add_callback(sync_file->fence, &sync_file->cb,
fence_check_cb_func) < 0)
wake_up_all(&sync_file->wq);
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index b7d7f2d..ee7b48d 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -1473,10 +1473,10 @@
for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) {
check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
rmb();
- initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
- rmb();
cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
rmb();
+ initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
+ rmb();
cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
rmb();
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 6775f2c..c756886 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -863,11 +863,11 @@
}
}
-static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma)
+static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks)
{
int i;
- for (i = 0; i < DMAMUX_NR; i++)
+ for (i = 0; i < nr_clocks; i++)
clk_disable_unprepare(fsl_edma->muxclk[i]);
}
@@ -904,25 +904,25 @@
res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(fsl_edma->muxbase[i]))
+ if (IS_ERR(fsl_edma->muxbase[i])) {
+ /* on error: disable all previously enabled clks */
+ fsl_disable_clocks(fsl_edma, i);
return PTR_ERR(fsl_edma->muxbase[i]);
+ }
sprintf(clkname, "dmamux%d", i);
fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname);
if (IS_ERR(fsl_edma->muxclk[i])) {
dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
+ /* on error: disable all previously enabled clks */
+ fsl_disable_clocks(fsl_edma, i);
return PTR_ERR(fsl_edma->muxclk[i]);
}
ret = clk_prepare_enable(fsl_edma->muxclk[i]);
- if (ret) {
- /* disable only clks which were enabled on error */
- for (; i >= 0; i--)
- clk_disable_unprepare(fsl_edma->muxclk[i]);
-
- dev_err(&pdev->dev, "DMAMUX clk block failed.\n");
- return ret;
- }
+ if (ret)
+ /* on error: disable all previously enabled clks */
+ fsl_disable_clocks(fsl_edma, i);
}
@@ -976,7 +976,7 @@
if (ret) {
dev_err(&pdev->dev,
"Can't register Freescale eDMA engine. (%d)\n", ret);
- fsl_disable_clocks(fsl_edma);
+ fsl_disable_clocks(fsl_edma, DMAMUX_NR);
return ret;
}
@@ -985,7 +985,7 @@
dev_err(&pdev->dev,
"Can't register Freescale eDMA of_dma. (%d)\n", ret);
dma_async_device_unregister(&fsl_edma->dma_dev);
- fsl_disable_clocks(fsl_edma);
+ fsl_disable_clocks(fsl_edma, DMAMUX_NR);
return ret;
}
@@ -1015,7 +1015,7 @@
fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
of_dma_controller_free(np);
dma_async_device_unregister(&fsl_edma->dma_dev);
- fsl_disable_clocks(fsl_edma);
+ fsl_disable_clocks(fsl_edma, DMAMUX_NR);
return 0;
}
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index d1651a5..b9c2972 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -937,6 +937,21 @@
return 0;
}
+static int sdma_disable_channel_with_delay(struct dma_chan *chan)
+{
+ sdma_disable_channel(chan);
+
+ /*
+ * According to NXP R&D team a delay of one BD SDMA cost time
+ * (maximum is 1ms) should be added after disable of the channel
+ * bit, to ensure SDMA core has really been stopped after SDMA
+ * clients call .device_terminate_all.
+ */
+ mdelay(1);
+
+ return 0;
+}
+
static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
{
struct sdma_engine *sdma = sdmac->sdma;
@@ -1740,19 +1755,26 @@
if (IS_ERR(sdma->clk_ahb))
return PTR_ERR(sdma->clk_ahb);
- clk_prepare(sdma->clk_ipg);
- clk_prepare(sdma->clk_ahb);
+ ret = clk_prepare(sdma->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare(sdma->clk_ahb);
+ if (ret)
+ goto err_clk;
ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, "sdma",
sdma);
if (ret)
- return ret;
+ goto err_irq;
sdma->irq = irq;
sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
- if (!sdma->script_addrs)
- return -ENOMEM;
+ if (!sdma->script_addrs) {
+ ret = -ENOMEM;
+ goto err_irq;
+ }
/* initially no scripts available */
saddr_arr = (s32 *)sdma->script_addrs;
@@ -1828,7 +1850,7 @@
sdma->dma_device.device_prep_slave_sg = sdma_prep_slave_sg;
sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic;
sdma->dma_device.device_config = sdma_config;
- sdma->dma_device.device_terminate_all = sdma_disable_channel;
+ sdma->dma_device.device_terminate_all = sdma_disable_channel_with_delay;
sdma->dma_device.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
sdma->dma_device.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
sdma->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
@@ -1867,6 +1889,10 @@
dma_async_device_unregister(&sdma->dma_device);
err_init:
kfree(sdma->script_addrs);
+err_irq:
+ clk_unprepare(sdma->clk_ahb);
+err_clk:
+ clk_unprepare(sdma->clk_ipg);
return ret;
}
@@ -1878,6 +1904,8 @@
devm_free_irq(&pdev->dev, sdma->irq, sdma);
dma_async_device_unregister(&sdma->dma_device);
kfree(sdma->script_addrs);
+ clk_unprepare(sdma->clk_ahb);
+ clk_unprepare(sdma->clk_ipg);
/* Kill the tasklet */
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
struct sdma_channel *sdmac = &sdma->channel[i];
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index 43e88d8..8c3c588 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -54,7 +54,15 @@
static inline void ti_am335x_xbar_write(void __iomem *iomem, int event, u8 val)
{
- writeb_relaxed(val, iomem + event);
+ /*
+ * TPCC_EVT_MUX_60_63 register layout is different than the
+ * rest, in the sense, that event 63 is mapped to lowest byte
+ * and event 60 is mapped to highest, handle it separately.
+ */
+ if (event >= 60 && event <= 63)
+ writeb_relaxed(val, iomem + (63 - event % 4));
+ else
+ writeb_relaxed(val, iomem + event);
}
static void ti_am335x_xbar_free(struct device *dev, void *route_data)
diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index 6d221e5..2265805 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -933,7 +933,8 @@
if (!chan)
return;
- devm_free_irq(chan->zdev->dev, chan->irq, chan);
+ if (chan->irq)
+ devm_free_irq(chan->zdev->dev, chan->irq, chan);
tasklet_kill(&chan->tasklet);
list_del(&chan->common.device_node);
clk_disable_unprepare(chan->clk_apb);
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 58d3e2b..61262a7 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -1020,13 +1020,23 @@
return ret;
}
+static int socfpga_is_a10(void)
+{
+ return of_machine_is_compatible("altr,socfpga-arria10");
+}
+
static int validate_parent_available(struct device_node *np);
static const struct of_device_id altr_edac_a10_device_of_match[];
static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
{
int irq;
- struct device_node *child, *np = of_find_compatible_node(NULL, NULL,
- "altr,socfpga-a10-ecc-manager");
+ struct device_node *child, *np;
+
+ if (!socfpga_is_a10())
+ return -ENODEV;
+
+ np = of_find_compatible_node(NULL, NULL,
+ "altr,socfpga-a10-ecc-manager");
if (!np) {
edac_printk(KERN_ERR, EDAC_DEVICE, "ECC Manager not found\n");
return -ENODEV;
@@ -1542,8 +1552,12 @@
static int __init socfpga_init_sdmmc_ecc(void)
{
int rc = -ENODEV;
- struct device_node *child = of_find_compatible_node(NULL, NULL,
- "altr,socfpga-sdmmc-ecc");
+ struct device_node *child;
+
+ if (!socfpga_is_a10())
+ return -ENODEV;
+
+ child = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc");
if (!child) {
edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n");
return -ENODEV;
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 4e0f8e7..bea71fb 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -50,7 +50,7 @@
return edac_mc_poll_msec;
}
-static int edac_set_poll_msec(const char *val, struct kernel_param *kp)
+static int edac_set_poll_msec(const char *val, const struct kernel_param *kp)
{
unsigned long l;
int ret;
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index 5f8543b..b0d3284 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -19,7 +19,8 @@
#ifdef CONFIG_EDAC_DEBUG
-static int edac_set_debug_level(const char *buf, struct kernel_param *kp)
+static int edac_set_debug_level(const char *buf,
+ const struct kernel_param *kp)
{
unsigned long val;
int ret;
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index cb9b857..61c19b8 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -759,7 +759,7 @@
/* Non-ECC RAM? */
printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__);
res = -ENODEV;
- goto err2;
+ goto err;
}
edac_dbg(3, "init mci\n");
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index 2e093c3..2b108fa 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -277,9 +277,8 @@
if (info->vbus_gpiod)
enable_irq(info->vbus_irq);
- if (!device_may_wakeup(dev))
- queue_delayed_work(system_power_efficient_wq,
- &info->wq_detcable, 0);
+ queue_delayed_work(system_power_efficient_wq,
+ &info->wq_detcable, 0);
return ret;
}
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 219556c..cf2ed6c 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -19,7 +19,8 @@
KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
-D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \
- $(call cc-option,-fno-stack-protector)
+ $(call cc-option,-fno-stack-protector) \
+ $(DISABLE_LTO)
GCOV_PROFILE := n
KASAN_SANITIZE := n
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 3d50bae..1f81e63 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -59,7 +59,10 @@
return cpu == resident_cpu;
}
-struct psci_operations psci_ops;
+struct psci_operations psci_ops = {
+ .conduit = PSCI_CONDUIT_NONE,
+ .smccc_version = SMCCC_VERSION_1_0,
+};
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
unsigned long, unsigned long);
@@ -210,6 +213,22 @@
0, 0, 0);
}
+static void set_conduit(enum psci_conduit conduit)
+{
+ switch (conduit) {
+ case PSCI_CONDUIT_HVC:
+ invoke_psci_fn = __invoke_psci_fn_hvc;
+ break;
+ case PSCI_CONDUIT_SMC:
+ invoke_psci_fn = __invoke_psci_fn_smc;
+ break;
+ default:
+ WARN(1, "Unexpected PSCI conduit %d\n", conduit);
+ }
+
+ psci_ops.conduit = conduit;
+}
+
static int get_set_conduit_method(struct device_node *np)
{
const char *method;
@@ -222,9 +241,9 @@
}
if (!strcmp("hvc", method)) {
- invoke_psci_fn = __invoke_psci_fn_hvc;
+ set_conduit(PSCI_CONDUIT_HVC);
} else if (!strcmp("smc", method)) {
- invoke_psci_fn = __invoke_psci_fn_smc;
+ set_conduit(PSCI_CONDUIT_SMC);
} else {
pr_warn("invalid \"method\" property: %s\n", method);
return -EINVAL;
@@ -495,6 +514,31 @@
pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);
}
+static void __init psci_init_smccc(void)
+{
+ u32 ver = ARM_SMCCC_VERSION_1_0;
+ int feature;
+
+ feature = psci_features(ARM_SMCCC_VERSION_FUNC_ID);
+
+ if (feature != PSCI_RET_NOT_SUPPORTED) {
+ u32 ret;
+ ret = invoke_psci_fn(ARM_SMCCC_VERSION_FUNC_ID, 0, 0, 0);
+ if (ret == ARM_SMCCC_VERSION_1_1) {
+ psci_ops.smccc_version = SMCCC_VERSION_1_1;
+ ver = ret;
+ }
+ }
+
+ /*
+ * Conveniently, the SMCCC and PSCI versions are encoded the
+ * same way. No, this isn't accidental.
+ */
+ pr_info("SMC Calling Convention v%d.%d\n",
+ PSCI_VERSION_MAJOR(ver), PSCI_VERSION_MINOR(ver));
+
+}
+
static void __init psci_0_2_set_functions(void)
{
pr_info("Using standard PSCI v0.2 function IDs\n");
@@ -543,6 +587,7 @@
psci_init_migrate();
if (PSCI_VERSION_MAJOR(ver) >= 1) {
+ psci_init_smccc();
psci_init_cpu_suspend();
psci_init_system_suspend();
}
@@ -656,9 +701,9 @@
pr_info("probing for conduit method from ACPI.\n");
if (acpi_psci_use_hvc())
- invoke_psci_fn = __invoke_psci_fn_hvc;
+ set_conduit(PSCI_CONDUIT_HVC);
else
- invoke_psci_fn = __invoke_psci_fn_smc;
+ set_conduit(PSCI_CONDUIT_SMC);
return psci_probe();
}
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 03a5925..a9daf71 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -256,7 +256,7 @@
if (set)
reg |= bit;
else
- reg &= bit;
+ reg &= ~bit;
iowrite32(reg, addr);
spin_unlock_irqrestore(&gpio->lock, flags);
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 7c446d1..1d87b07 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -90,8 +90,18 @@
{
int reg;
- if (gpio == 94)
- return GPIOPANELCTL;
+ if (gpio >= CRYSTALCOVE_GPIO_NUM) {
+ /*
+ * Virtual GPIO called from ACPI, for now we only support
+ * the panel ctl.
+ */
+ switch (gpio) {
+ case 0x5e:
+ return GPIOPANELCTL;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
if (reg_type == CTRL_IN) {
if (gpio < 8)
@@ -130,36 +140,36 @@
static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
+ int reg = to_reg(gpio, CTRL_OUT);
- if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ if (reg < 0)
return 0;
- return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
- CTLO_INPUT_SET);
+ return regmap_write(cg->regmap, reg, CTLO_INPUT_SET);
}
static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio,
int value)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
+ int reg = to_reg(gpio, CTRL_OUT);
- if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ if (reg < 0)
return 0;
- return regmap_write(cg->regmap, to_reg(gpio, CTRL_OUT),
- CTLO_OUTPUT_SET | value);
+ return regmap_write(cg->regmap, reg, CTLO_OUTPUT_SET | value);
}
static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
- int ret;
unsigned int val;
+ int ret, reg = to_reg(gpio, CTRL_IN);
- if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ if (reg < 0)
return 0;
- ret = regmap_read(cg->regmap, to_reg(gpio, CTRL_IN), &val);
+ ret = regmap_read(cg->regmap, reg, &val);
if (ret)
return ret;
@@ -170,14 +180,15 @@
unsigned gpio, int value)
{
struct crystalcove_gpio *cg = gpiochip_get_data(chip);
+ int reg = to_reg(gpio, CTRL_OUT);
- if (gpio > CRYSTALCOVE_VGPIO_NUM)
+ if (reg < 0)
return;
if (value)
- regmap_update_bits(cg->regmap, to_reg(gpio, CTRL_OUT), 1, 1);
+ regmap_update_bits(cg->regmap, reg, 1, 1);
else
- regmap_update_bits(cg->regmap, to_reg(gpio, CTRL_OUT), 1, 0);
+ regmap_update_bits(cg->regmap, reg, 1, 0);
}
static int crystalcove_irq_type(struct irq_data *data, unsigned type)
@@ -185,6 +196,9 @@
struct crystalcove_gpio *cg =
gpiochip_get_data(irq_data_get_irq_chip_data(data));
+ if (data->hwirq >= CRYSTALCOVE_GPIO_NUM)
+ return 0;
+
switch (type) {
case IRQ_TYPE_NONE:
cg->intcnt_value = CTLI_INTCNT_DIS;
@@ -235,8 +249,10 @@
struct crystalcove_gpio *cg =
gpiochip_get_data(irq_data_get_irq_chip_data(data));
- cg->set_irq_mask = false;
- cg->update |= UPDATE_IRQ_MASK;
+ if (data->hwirq < CRYSTALCOVE_GPIO_NUM) {
+ cg->set_irq_mask = false;
+ cg->update |= UPDATE_IRQ_MASK;
+ }
}
static void crystalcove_irq_mask(struct irq_data *data)
@@ -244,8 +260,10 @@
struct crystalcove_gpio *cg =
gpiochip_get_data(irq_data_get_irq_chip_data(data));
- cg->set_irq_mask = true;
- cg->update |= UPDATE_IRQ_MASK;
+ if (data->hwirq < CRYSTALCOVE_GPIO_NUM) {
+ cg->set_irq_mask = true;
+ cg->update |= UPDATE_IRQ_MASK;
+ }
}
static struct irq_chip crystalcove_irqchip = {
diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c
index d0ddba7..5ef4980 100644
--- a/drivers/gpio/gpio-wcove.c
+++ b/drivers/gpio/gpio-wcove.c
@@ -51,6 +51,8 @@
#define GROUP1_NR_IRQS 6
#define IRQ_MASK_BASE 0x4e19
#define IRQ_STATUS_BASE 0x4e0b
+#define GPIO_IRQ0_MASK GENMASK(6, 0)
+#define GPIO_IRQ1_MASK GENMASK(5, 0)
#define UPDATE_IRQ_TYPE BIT(0)
#define UPDATE_IRQ_MASK BIT(1)
@@ -310,7 +312,7 @@
return IRQ_NONE;
}
- pending = p[0] | (p[1] << 8);
+ pending = (p[0] & GPIO_IRQ0_MASK) | ((p[1] & GPIO_IRQ1_MASK) << 7);
if (!pending)
return IRQ_NONE;
@@ -318,7 +320,7 @@
while (pending) {
/* One iteration is for all pending bits */
for_each_set_bit(gpio, (const unsigned long *)&pending,
- GROUP0_NR_IRQS) {
+ WCOVE_GPIO_NUM) {
offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0;
mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) :
BIT(gpio);
@@ -334,7 +336,7 @@
break;
}
- pending = p[0] | (p[1] << 8);
+ pending = (p[0] & GPIO_IRQ0_MASK) | ((p[1] & GPIO_IRQ1_MASK) << 7);
}
return IRQ_HANDLED;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f3c3680..56b2419 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -425,7 +425,7 @@
struct gpiohandle_request handlereq;
struct linehandle_state *lh;
struct file *file;
- int fd, i, ret;
+ int fd, i, count = 0, ret;
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
return -EFAULT;
@@ -471,6 +471,7 @@
if (ret)
goto out_free_descs;
lh->descs[i] = desc;
+ count = i;
if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
@@ -537,7 +538,7 @@
out_put_unused_fd:
put_unused_fd(fd);
out_free_descs:
- for (; i >= 0; i--)
+ for (i = 0; i < count; i++)
gpiod_free(lh->descs[i]);
kfree(lh->label);
out_free_lh:
@@ -794,7 +795,7 @@
desc = &gdev->descs[offset];
ret = gpiod_request(desc, le->label);
if (ret)
- goto out_free_desc;
+ goto out_free_label;
le->desc = desc;
le->eflags = eflags;
@@ -3231,7 +3232,8 @@
return desc;
}
- status = gpiod_request(desc, con_id);
+ /* If a connection label was passed use that, else use the device name as label */
+ status = gpiod_request(desc, con_id ? con_id : dev_name(dev));
if (status < 0)
return ERR_PTR(status);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
index 5796539..648ecf6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
@@ -540,6 +540,9 @@
size_t size;
u32 retry = 3;
+ if (amdgpu_acpi_pcie_notify_device_ready(adev))
+ return -EINVAL;
+
/* Get the device handle */
handle = ACPI_HANDLE(&adev->pdev->dev);
if (!handle)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
index 6c343a9..0217f5d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
@@ -14,6 +14,16 @@
#include "amd_acpi.h"
+#define AMDGPU_PX_QUIRK_FORCE_ATPX (1 << 0)
+
+struct amdgpu_px_quirk {
+ u32 chip_vendor;
+ u32 chip_device;
+ u32 subsys_vendor;
+ u32 subsys_device;
+ u32 px_quirk_flags;
+};
+
struct amdgpu_atpx_functions {
bool px_params;
bool power_cntl;
@@ -35,6 +45,7 @@
static struct amdgpu_atpx_priv {
bool atpx_detected;
bool bridge_pm_usable;
+ unsigned int quirks;
/* handle for device - and atpx */
acpi_handle dhandle;
acpi_handle other_handle;
@@ -205,13 +216,19 @@
atpx->is_hybrid = false;
if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) {
- printk("ATPX Hybrid Graphics\n");
- /*
- * Disable legacy PM methods only when pcie port PM is usable,
- * otherwise the device might fail to power off or power on.
- */
- atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
- atpx->is_hybrid = true;
+ if (amdgpu_atpx_priv.quirks & AMDGPU_PX_QUIRK_FORCE_ATPX) {
+ printk("ATPX Hybrid Graphics, forcing to ATPX\n");
+ atpx->functions.power_cntl = true;
+ atpx->is_hybrid = false;
+ } else {
+ printk("ATPX Hybrid Graphics\n");
+ /*
+ * Disable legacy PM methods only when pcie port PM is usable,
+ * otherwise the device might fail to power off or power on.
+ */
+ atpx->functions.power_cntl = !amdgpu_atpx_priv.bridge_pm_usable;
+ atpx->is_hybrid = true;
+ }
}
atpx->dgpu_req_power_for_displays = false;
@@ -547,6 +564,32 @@
.get_client_id = amdgpu_atpx_get_client_id,
};
+static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = {
+ /* HG _PR3 doesn't seem to work on this A+A weston board */
+ { 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0x1002, 0x67DF, 0x1028, 0x0774, AMDGPU_PX_QUIRK_FORCE_ATPX },
+ { 0, 0, 0, 0, 0 },
+};
+
+static void amdgpu_atpx_get_quirks(struct pci_dev *pdev)
+{
+ const struct amdgpu_px_quirk *p = amdgpu_px_quirk_list;
+
+ /* Apply PX quirks */
+ while (p && p->chip_device != 0) {
+ if (pdev->vendor == p->chip_vendor &&
+ pdev->device == p->chip_device &&
+ pdev->subsystem_vendor == p->subsys_vendor &&
+ pdev->subsystem_device == p->subsys_device) {
+ amdgpu_atpx_priv.quirks |= p->px_quirk_flags;
+ break;
+ }
+ ++p;
+ }
+}
+
/**
* amdgpu_atpx_detect - detect whether we have PX
*
@@ -570,6 +613,7 @@
parent_pdev = pci_upstream_bridge(pdev);
d3_supported |= parent_pdev && parent_pdev->bridge_d3;
+ amdgpu_atpx_get_quirks(pdev);
}
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
@@ -579,6 +623,7 @@
parent_pdev = pci_upstream_bridge(pdev);
d3_supported |= parent_pdev && parent_pdev->bridge_d3;
+ amdgpu_atpx_get_quirks(pdev);
}
if (has_atpx && vga_count == 2) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
index c02db01f6..fe011c7 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
@@ -201,8 +201,10 @@
for (i = 0; i < list->num_entries; i++) {
unsigned priority = list->array[i].priority;
- list_add_tail(&list->array[i].tv.head,
- &bucket[priority]);
+ if (!list->array[i].robj->parent)
+ list_add_tail(&list->array[i].tv.head,
+ &bucket[priority]);
+
list->array[i].user_pages = NULL;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
index 086aa5c..e9311eb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
@@ -69,25 +69,18 @@
/* don't do anything if sink is not display port, i.e.,
* passive dp->(dvi|hdmi) adaptor
*/
- if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
- int saved_dpms = connector->dpms;
- /* Only turn off the display if it's physically disconnected */
- if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) {
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
- } else if (amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) {
- /* Don't try to start link training before we
- * have the dpcd */
- if (amdgpu_atombios_dp_get_dpcd(amdgpu_connector))
- return;
+ if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT &&
+ amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd) &&
+ amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) {
+ /* Don't start link training before we have the DPCD */
+ if (amdgpu_atombios_dp_get_dpcd(amdgpu_connector))
+ return;
- /* set it to OFF so that drm_helper_connector_dpms()
- * won't return immediately since the current state
- * is ON at this point.
- */
- connector->dpms = DRM_MODE_DPMS_OFF;
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
- }
- connector->dpms = saved_dpms;
+ /* Turn the connector off and back on immediately, which
+ * will trigger link training
+ */
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
}
}
}
@@ -739,9 +732,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (encoder) {
struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
@@ -760,8 +755,12 @@
/* check acpi lid status ??? */
amdgpu_connector_update_scratch_regs(connector, ret);
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
+
return ret;
}
@@ -871,9 +870,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
encoder = amdgpu_connector_best_single_encoder(connector);
if (!encoder)
@@ -927,8 +928,10 @@
amdgpu_connector_update_scratch_regs(connector, ret);
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -991,9 +994,11 @@
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false, broken_edid = false;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) {
ret = connector->status;
@@ -1118,8 +1123,10 @@
amdgpu_connector_update_scratch_regs(connector, ret);
exit:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -1362,9 +1369,11 @@
struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector);
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) {
ret = connector->status;
@@ -1432,8 +1441,10 @@
amdgpu_connector_update_scratch_regs(connector, ret);
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index cb505f6..c801624 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -519,7 +519,7 @@
INIT_LIST_HEAD(&duplicates);
amdgpu_vm_get_pd_bo(&fpriv->vm, &p->validated, &p->vm_pd);
- if (p->uf_entry.robj)
+ if (p->uf_entry.robj && !p->uf_entry.robj->parent)
list_add(&p->uf_entry.tv.head, &p->validated);
if (need_mmap_lock)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index ce9797b..d0c3e56 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1678,8 +1678,6 @@
* ignore it */
vga_client_register(adev->pdev, adev, NULL, amdgpu_vga_set_decode);
- if (amdgpu_runtime_pm == 1)
- runtime = true;
if (amdgpu_device_is_px(ddev))
runtime = true;
vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime);
@@ -2239,7 +2237,7 @@
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
- if (!ring)
+ if (!ring || !ring->sched.thread)
continue;
kthread_park(ring->sched.thread);
amd_sched_hw_job_reset(&ring->sched);
@@ -2328,7 +2326,8 @@
}
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
- if (!ring)
+
+ if (!ring || !ring->sched.thread)
continue;
amd_sched_job_recovery(&ring->sched);
@@ -2337,7 +2336,7 @@
} else {
dev_err(adev->dev, "asic resume failed (%d).\n", r);
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
- if (adev->rings[i]) {
+ if (adev->rings[i] && adev->rings[i]->sched.thread) {
kthread_unpark(adev->rings[i]->sched.thread);
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 083e2b4..15a2d8f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -533,6 +533,12 @@
return ERR_PTR(-ENOENT);
}
+ /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
+ if (obj->import_attach) {
+ DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
+ return ERR_PTR(-EINVAL);
+ }
+
amdgpu_fb = kzalloc(sizeof(*amdgpu_fb), GFP_KERNEL);
if (amdgpu_fb == NULL) {
drm_gem_object_unreference_unlocked(obj);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index a7ea9a3..d5e4748 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -36,8 +36,6 @@
struct amdgpu_bo *robj = gem_to_amdgpu_bo(gobj);
if (robj) {
- if (robj->gem_base.import_attach)
- drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg);
amdgpu_mn_unregister(robj);
amdgpu_bo_unref(&robj);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index f3efb1c..5afe727 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -94,6 +94,8 @@
amdgpu_update_memory_usage(bo->adev, &bo->tbo.mem, NULL);
+ if (bo->gem_base.import_attach)
+ drm_prime_gem_destroy(&bo->gem_base, bo->tbo.sg);
drm_gem_object_release(&bo->gem_base);
amdgpu_bo_unref(&bo->parent);
if (!list_empty(&bo->shadow_list)) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index e3281ca..5caf517 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -273,12 +273,15 @@
if (adev->uvd.vcpu_bo == NULL)
return 0;
- for (i = 0; i < adev->uvd.max_handles; ++i)
- if (atomic_read(&adev->uvd.handles[i]))
- break;
+ /* only valid for physical mode */
+ if (adev->asic_type < CHIP_POLARIS10) {
+ for (i = 0; i < adev->uvd.max_handles; ++i)
+ if (atomic_read(&adev->uvd.handles[i]))
+ break;
- if (i == AMDGPU_MAX_UVD_HANDLES)
- return 0;
+ if (i == adev->uvd.max_handles)
+ return 0;
+ }
cancel_delayed_work_sync(&adev->uvd.idle_work);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
index 71116da..e040a89 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
@@ -4475,34 +4475,8 @@
case CHIP_KAVERI:
adev->gfx.config.max_shader_engines = 1;
adev->gfx.config.max_tile_pipes = 4;
- if ((adev->pdev->device == 0x1304) ||
- (adev->pdev->device == 0x1305) ||
- (adev->pdev->device == 0x130C) ||
- (adev->pdev->device == 0x130F) ||
- (adev->pdev->device == 0x1310) ||
- (adev->pdev->device == 0x1311) ||
- (adev->pdev->device == 0x131C)) {
- adev->gfx.config.max_cu_per_sh = 8;
- adev->gfx.config.max_backends_per_se = 2;
- } else if ((adev->pdev->device == 0x1309) ||
- (adev->pdev->device == 0x130A) ||
- (adev->pdev->device == 0x130D) ||
- (adev->pdev->device == 0x1313) ||
- (adev->pdev->device == 0x131D)) {
- adev->gfx.config.max_cu_per_sh = 6;
- adev->gfx.config.max_backends_per_se = 2;
- } else if ((adev->pdev->device == 0x1306) ||
- (adev->pdev->device == 0x1307) ||
- (adev->pdev->device == 0x130B) ||
- (adev->pdev->device == 0x130E) ||
- (adev->pdev->device == 0x1315) ||
- (adev->pdev->device == 0x131B)) {
- adev->gfx.config.max_cu_per_sh = 4;
- adev->gfx.config.max_backends_per_se = 1;
- } else {
- adev->gfx.config.max_cu_per_sh = 3;
- adev->gfx.config.max_backends_per_se = 1;
- }
+ adev->gfx.config.max_cu_per_sh = 8;
+ adev->gfx.config.max_backends_per_se = 2;
adev->gfx.config.max_sh_per_se = 1;
adev->gfx.config.max_texture_channel_caches = 4;
adev->gfx.config.max_gprs = 256;
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index a88d365..564362e 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -1484,10 +1484,11 @@
static const u32 vgpr_init_regs[] =
{
mmCOMPUTE_STATIC_THREAD_MGMT_SE0, 0xffffffff,
- mmCOMPUTE_RESOURCE_LIMITS, 0,
+ mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, /* CU_GROUP_COUNT=1 */
mmCOMPUTE_NUM_THREAD_X, 256*4,
mmCOMPUTE_NUM_THREAD_Y, 1,
mmCOMPUTE_NUM_THREAD_Z, 1,
+ mmCOMPUTE_PGM_RSRC1, 0x100004f, /* VGPRS=15 (64 logical VGPRs), SGPRS=1 (16 SGPRs), BULKY=1 */
mmCOMPUTE_PGM_RSRC2, 20,
mmCOMPUTE_USER_DATA_0, 0xedcedc00,
mmCOMPUTE_USER_DATA_1, 0xedcedc01,
@@ -1504,10 +1505,11 @@
static const u32 sgpr1_init_regs[] =
{
mmCOMPUTE_STATIC_THREAD_MGMT_SE0, 0x0f,
- mmCOMPUTE_RESOURCE_LIMITS, 0x1000000,
+ mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, /* CU_GROUP_COUNT=1 */
mmCOMPUTE_NUM_THREAD_X, 256*5,
mmCOMPUTE_NUM_THREAD_Y, 1,
mmCOMPUTE_NUM_THREAD_Z, 1,
+ mmCOMPUTE_PGM_RSRC1, 0x240, /* SGPRS=9 (80 GPRS) */
mmCOMPUTE_PGM_RSRC2, 20,
mmCOMPUTE_USER_DATA_0, 0xedcedc00,
mmCOMPUTE_USER_DATA_1, 0xedcedc01,
@@ -1528,6 +1530,7 @@
mmCOMPUTE_NUM_THREAD_X, 256*5,
mmCOMPUTE_NUM_THREAD_Y, 1,
mmCOMPUTE_NUM_THREAD_Z, 1,
+ mmCOMPUTE_PGM_RSRC1, 0x240, /* SGPRS=9 (80 GPRS) */
mmCOMPUTE_PGM_RSRC2, 20,
mmCOMPUTE_USER_DATA_0, 0xedcedc00,
mmCOMPUTE_USER_DATA_1, 0xedcedc01,
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
index 4cb347e..3fa8320 100644
--- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
@@ -3507,6 +3507,11 @@
max_sclk = 75000;
max_mclk = 80000;
}
+ if ((adev->pdev->revision == 0xC3) ||
+ (adev->pdev->device == 0x6665)) {
+ max_sclk = 60000;
+ max_mclk = 80000;
+ }
} else if (adev->asic_type == CHIP_OLAND) {
if ((adev->pdev->revision == 0xC7) ||
(adev->pdev->revision == 0x80) ||
@@ -6444,9 +6449,9 @@
{
u32 lane_width;
u32 new_lane_width =
- (amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
u32 current_lane_width =
- (amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
if (new_lane_width != current_lane_width) {
amdgpu_set_pcie_lanes(adev, new_lane_width);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index ef7c8de..171480b 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -317,7 +317,8 @@
/* init process apertures*/
process->is_32bit_user_mode = in_compat_syscall();
- if (kfd_init_apertures(process) != 0)
+ err = kfd_init_apertures(process);
+ if (err != 0)
goto err_init_apretures;
return process;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index 1e50647..8c6e47c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -519,11 +519,17 @@
return ret;
}
+static void kfd_topology_kobj_release(struct kobject *kobj)
+{
+ kfree(kobj);
+}
+
static const struct sysfs_ops sysprops_ops = {
.show = sysprops_show,
};
static struct kobj_type sysprops_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &sysprops_ops,
};
@@ -559,6 +565,7 @@
};
static struct kobj_type iolink_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &iolink_ops,
};
@@ -586,6 +593,7 @@
};
static struct kobj_type mem_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &mem_ops,
};
@@ -625,6 +633,7 @@
};
static struct kobj_type cache_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &cache_ops,
};
@@ -747,6 +756,7 @@
};
static struct kobj_type node_type = {
+ .release = kfd_topology_kobj_release,
.sysfs_ops = &node_ops,
};
diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c
index afec232..cfd80bc 100644
--- a/drivers/gpu/drm/bridge/dumb-vga-dac.c
+++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c
@@ -53,7 +53,9 @@
}
drm_mode_connector_update_edid_property(connector, edid);
- return drm_add_edid_modes(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ return ret;
fallback:
/*
diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
index a7b2a75..cdb5358 100644
--- a/drivers/gpu/drm/drm_dp_dual_mode_helper.c
+++ b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
@@ -322,19 +322,44 @@
{
uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
ssize_t ret;
+ int retry;
if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
return 0;
- ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
- &tmds_oen, sizeof(tmds_oen));
- if (ret) {
- DRM_DEBUG_KMS("Failed to %s TMDS output buffers\n",
- enable ? "enable" : "disable");
- return ret;
+ /*
+ * LSPCON adapters in low-power state may ignore the first write, so
+ * read back and verify the written value a few times.
+ */
+ for (retry = 0; retry < 3; retry++) {
+ uint8_t tmp;
+
+ ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
+ &tmds_oen, sizeof(tmds_oen));
+ if (ret) {
+ DRM_DEBUG_KMS("Failed to %s TMDS output buffers (%d attempts)\n",
+ enable ? "enable" : "disable",
+ retry + 1);
+ return ret;
+ }
+
+ ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
+ &tmp, sizeof(tmp));
+ if (ret) {
+ DRM_DEBUG_KMS("I2C read failed during TMDS output buffer %s (%d attempts)\n",
+ enable ? "enabling" : "disabling",
+ retry + 1);
+ return ret;
+ }
+
+ if (tmp == tmds_oen)
+ return 0;
}
- return 0;
+ DRM_DEBUG_KMS("I2C write value mismatch during TMDS output buffer %s\n",
+ enable ? "enabling" : "disabling");
+
+ return -EIO;
}
EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 4e5ba7e..03a9f20 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -115,6 +115,9 @@
/* AEO model 0 reports 8 bpc, but is a 6 bpc panel */
{ "AEO", 0, EDID_QUIRK_FORCE_6BPC },
+ /* CPT panel of Asus UX303LA reports 8 bpc, but is a 6 bpc panel */
+ { "CPT", 0x17df, EDID_QUIRK_FORCE_6BPC },
+
/* Belinea 10 15 55 */
{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
@@ -3806,8 +3809,7 @@
* @edid: EDID to parse
*
* Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
- * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to
- * fill in.
+ * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
*/
void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
{
@@ -3888,6 +3890,12 @@
}
eld[5] |= total_sad_count << 4;
+ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+ connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP;
+ else
+ eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;
+
eld[DRM_ELD_BASELINE_ELD_LEN] =
DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 48a6167..00c815a 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -1202,9 +1202,9 @@
if (atomic_dec_and_test(&vblank->refcount)) {
if (drm_vblank_offdelay == 0)
return;
- else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0)
+ else if (drm_vblank_offdelay < 0)
vblank_disable_fn((unsigned long)vblank);
- else
+ else if (!dev->vblank_disable_immediate)
mod_timer(&vblank->disable_timer,
jiffies + ((drm_vblank_offdelay * HZ)/1000));
}
@@ -1819,6 +1819,16 @@
wake_up(&vblank->queue);
drm_handle_vblank_events(dev, pipe);
+ /* With instant-off, we defer disabling the interrupt until after
+ * we finish processing the following vblank. The disable has to
+ * be last (after drm_handle_vblank_events) so that the timestamp
+ * is always accurate.
+ */
+ if (dev->vblank_disable_immediate &&
+ drm_vblank_offdelay > 0 &&
+ !atomic_read(&vblank->refcount))
+ vblank_disable_fn((unsigned long)vblank);
+
spin_unlock_irqrestore(&dev->event_lock, irqflags);
return true;
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 276474d..d7822be 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -461,6 +461,26 @@
}
/**
+ * drm_kms_helper_is_poll_worker - is %current task an output poll worker?
+ *
+ * Determine if %current task is an output poll worker. This can be used
+ * to select distinct code paths for output polling versus other contexts.
+ *
+ * One use case is to avoid a deadlock between the output poll worker and
+ * the autosuspend worker wherein the latter waits for polling to finish
+ * upon calling drm_kms_helper_poll_disable(), while the former waits for
+ * runtime suspend to finish upon calling pm_runtime_get_sync() in a
+ * connector ->detect hook.
+ */
+bool drm_kms_helper_is_poll_worker(void)
+{
+ struct work_struct *work = current_work();
+
+ return work && work->func == output_poll_execute;
+}
+EXPORT_SYMBOL(drm_kms_helper_is_poll_worker);
+
+/**
* drm_kms_helper_poll_disable - disable output polling
* @dev: drm_device
*
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 7513e76..bae62cf 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1703,6 +1703,8 @@
if (IS_BROXTON(dev_priv) ||
!(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload))
intel_power_domains_init_hw(dev_priv, true);
+ else
+ intel_display_set_init_power(dev_priv, true);
enable_rpm_wakeref_asserts(dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 36a665f..e23748c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3681,7 +3681,11 @@
struct intel_display_error_state *error);
int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val);
-int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u32 mbox, u32 val);
+int sandybridge_pcode_write_timeout(struct drm_i915_private *dev_priv, u32 mbox,
+ u32 val, int timeout_us);
+#define sandybridge_pcode_write(dev_priv, mbox, val) \
+ sandybridge_pcode_write_timeout(dev_priv, mbox, val, 500)
+
int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
u32 reply_mask, u32 reply, int timeout_base_ms);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index ce32303..c185625 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -6012,8 +6012,8 @@
/* Inform power controller of upcoming frequency change */
mutex_lock(&dev_priv->rps.hw_lock);
- ret = sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ,
- 0x80000000);
+ ret = sandybridge_pcode_write_timeout(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ,
+ 0x80000000, 2000);
mutex_unlock(&dev_priv->rps.hw_lock);
if (ret) {
@@ -6044,8 +6044,9 @@
I915_WRITE(CDCLK_CTL, val);
mutex_lock(&dev_priv->rps.hw_lock);
- ret = sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ,
- DIV_ROUND_UP(cdclk, 25000));
+ ret = sandybridge_pcode_write_timeout(dev_priv,
+ HSW_PCODE_DE_WRITE_FREQ_REQ,
+ DIV_ROUND_UP(cdclk, 25000), 2000);
mutex_unlock(&dev_priv->rps.hw_lock);
if (ret) {
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 13c3061..0c935de 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1452,12 +1452,20 @@
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct edid *edid;
bool connected = false;
+ struct i2c_adapter *i2c;
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
- edid = drm_get_edid(connector,
- intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus));
+ i2c = intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus);
+
+ edid = drm_get_edid(connector, i2c);
+
+ if (!edid && !intel_gmbus_is_forced_bit(i2c)) {
+ DRM_DEBUG_KMS("HDMI GMBUS EDID read failed, retry using GPIO bit-banging\n");
+ intel_gmbus_force_bit(i2c, true);
+ edid = drm_get_edid(connector, i2c);
+ intel_gmbus_force_bit(i2c, false);
+ }
intel_hdmi_dp_dual_mode_detect(connector, edid != NULL);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index e1d47d5..3517c0e 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -321,7 +321,8 @@
I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
POSTING_READ(lvds_encoder->reg);
- if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000))
+
+ if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 5000))
DRM_ERROR("timed out waiting for panel to power on\n");
intel_panel_enable_backlight(intel_connector);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 49de476..05427d2 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -7913,8 +7913,8 @@
return 0;
}
-int sandybridge_pcode_write(struct drm_i915_private *dev_priv,
- u32 mbox, u32 val)
+int sandybridge_pcode_write_timeout(struct drm_i915_private *dev_priv,
+ u32 mbox, u32 val, int timeout_us)
{
int status;
@@ -7935,7 +7935,7 @@
if (intel_wait_for_register_fw(dev_priv,
GEN6_PCODE_MAILBOX, GEN6_PCODE_READY, 0,
- 500)) {
+ timeout_us)) {
DRM_ERROR("timeout waiting for pcode write (%d) to finish\n", mbox);
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 5e73dfa..bfe98b5 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -772,8 +772,6 @@
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
- misc_val = dp_read(catalog, io_data, DP_MISC1_MISC0);
- misc_val |= cc;
misc_val |= (tb << 5);
misc_val |= BIT(0); /* Configure clock to synchronous mode */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 3e5cdc6..1b92261 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1182,7 +1182,8 @@
dp->hdcp.ops->off(dp->hdcp.data);
}
- if (dp->usbpd->hpd_high && dp->usbpd->alt_mode_cfg_done) {
+ if (dp->usbpd->hpd_high && !dp_display_is_sink_count_zero(dp) &&
+ dp->usbpd->alt_mode_cfg_done) {
if (dp->audio_supported)
dp->audio->off(dp->audio);
@@ -1235,7 +1236,8 @@
* any notification from driver. Initialize post_open callback to notify
* DP connection once framework restarts.
*/
- if (dp->usbpd->hpd_high && dp->usbpd->alt_mode_cfg_done)
+ if (dp->usbpd->hpd_high && !dp_display_is_sink_count_zero(dp) &&
+ dp->usbpd->alt_mode_cfg_done)
dp_display->post_open = dp_display_post_open;
dp->power_on = false;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index d083e72..a457070 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -214,6 +214,7 @@
phy->ops.ulps_ops.is_lanes_in_ulps =
dsi_phy_hw_v3_0_is_lanes_in_ulps;
phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v3_0;
+ phy->ops.clamp_ctrl = dsi_phy_hw_v3_0_clamp_ctrl;
phy->ops.phy_lane_reset = dsi_phy_hw_v3_0_lane_reset;
phy->ops.toggle_resync_fifo = dsi_phy_hw_v3_0_toggle_resync_fifo;
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 0e2db430..9a923aa 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -102,12 +102,14 @@
bool dsi_phy_hw_v3_0_is_lanes_in_ulps(u32 lanes, u32 ulps_lanes);
int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
u32 *timing_val, u32 size);
+void dsi_phy_hw_v3_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable);
int dsi_phy_hw_v3_0_lane_reset(struct dsi_phy_hw *phy);
void dsi_phy_hw_v3_0_toggle_resync_fifo(struct dsi_phy_hw *phy);
/* DSI controller common ops */
u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
-void dsi_ctrl_hw_cmn_debug_bus(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_debug_bus(struct dsi_ctrl_hw *ctrl, u32 *entries,
+ u32 size);
void dsi_ctrl_hw_cmn_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints);
void dsi_ctrl_hw_cmn_enable_status_interrupts(struct dsi_ctrl_hw *ctrl,
u32 ints);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
index d89760e..bdc60d2 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
@@ -43,6 +43,13 @@
DSI_LINK_CLK_MAX,
};
+enum dsi_link_clk_op_type {
+ DSI_LINK_CLK_SET_RATE = BIT(0),
+ DSI_LINK_CLK_PREPARE = BIT(1),
+ DSI_LINK_CLK_ENABLE = BIT(2),
+ DSI_LINK_CLK_START = BIT(0) | BIT(1) | BIT(2),
+};
+
enum dsi_clk_type {
DSI_CORE_CLK = BIT(0),
DSI_LINK_CLK = BIT(1),
@@ -50,6 +57,12 @@
DSI_CLKS_MAX = BIT(2),
};
+enum dsi_lclk_type {
+ DSI_LINK_NONE = 0,
+ DSI_LINK_LP_CLK = BIT(0),
+ DSI_LINK_HS_CLK = BIT(1),
+};
+
struct dsi_clk_ctrl_info {
enum dsi_clk_type clk_type;
enum dsi_clk_state clk_state;
@@ -82,23 +95,29 @@
};
/**
- * struct dsi_link_clk_info - Link clock information for DSI hardware.
- * @byte_clk: Handle to DSI byte clock.
- * @pixel_clk: Handle to DSI pixel clock.
- * @esc_clk: Handle to DSI escape clock.
+ * struct dsi_link_hs_clk_info - Set of high speed link clocks for DSI HW
+ * @byte_clk: Handle to DSI byte_clk.
+ * @pixel_clk: Handle to DSI pixel_clk.
* @byte_intf_clk: Handle to DSI byte intf. clock.
*/
-struct dsi_link_clk_info {
+struct dsi_link_hs_clk_info {
struct clk *byte_clk;
struct clk *pixel_clk;
- struct clk *esc_clk;
struct clk *byte_intf_clk;
};
/**
+ * struct dsi_link_lp_clk_info - Set of low power link clocks for DSI HW.
+ * @esc_clk: Handle to DSI escape clock.
+ */
+struct dsi_link_lp_clk_info {
+ struct clk *esc_clk;
+};
+
+/**
* struct link_clk_freq - Clock frequency information for Link clocks
- * @byte_clk_rate: Frequency of DSI byte clock in KHz.
- * @pixel_clk_rate: Frequency of DSI pixel clock in KHz.
+ * @byte_clk_rate: Frequency of DSI byte_clk in KHz.
+ * @pixel_clk_rate: Frequency of DSI pixel_clk in KHz.
* @esc_clk_rate: Frequency of DSI escape clock in KHz.
*/
struct link_clk_freq {
@@ -111,48 +130,56 @@
* typedef *pre_clockoff_cb() - Callback before clock is turned off
* @priv: private data pointer.
* @clk_type: clock which is being turned off.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
typedef int (*pre_clockoff_cb)(void *priv,
enum dsi_clk_type clk_type,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state new_state);
/**
* typedef *post_clockoff_cb() - Callback after clock is turned off
* @priv: private data pointer.
* @clk_type: clock which was turned off.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
typedef int (*post_clockoff_cb)(void *priv,
enum dsi_clk_type clk_type,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state);
/**
* typedef *post_clockon_cb() - Callback after clock is turned on
* @priv: private data pointer.
* @clk_type: clock which was turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
typedef int (*post_clockon_cb)(void *priv,
enum dsi_clk_type clk_type,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state);
/**
* typedef *pre_clockon_cb() - Callback before clock is turned on
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
+ * @l_type: specifies if the clock is HS or LP type.Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
typedef int (*pre_clockon_cb)(void *priv,
enum dsi_clk_type clk_type,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state new_state);
@@ -160,7 +187,8 @@
* struct dsi_clk_info - clock information for DSI hardware.
* @name: client name.
* @c_clks[MAX_DSI_CTRL] array of core clock configurations
- * @l_clks[MAX_DSI_CTRL] array of link clock configurations
+ * @l_lp_clks[MAX_DSI_CTRL] array of low power(esc) clock configurations
+ * @l_hs_clks[MAX_DSI_CTRL] array of high speed clock configurations
* @bus_handle[MAX_DSI_CTRL] array of bus handles
* @ctrl_index[MAX_DSI_CTRL] array of DSI controller indexes mapped
* to core and link clock configurations
@@ -175,7 +203,8 @@
struct dsi_clk_info {
char name[MAX_STRING_LEN];
struct dsi_core_clk_info c_clks[MAX_DSI_CTRL];
- struct dsi_link_clk_info l_clks[MAX_DSI_CTRL];
+ struct dsi_link_lp_clk_info l_lp_clks[MAX_DSI_CTRL];
+ struct dsi_link_hs_clk_info l_hs_clks[MAX_DSI_CTRL];
u32 bus_handle[MAX_DSI_CTRL];
u32 ctrl_index[MAX_DSI_CTRL];
pre_clockoff_cb pre_clkoff_cb;
@@ -189,8 +218,8 @@
/**
* struct dsi_clk_link_set - Pair of clock handles to describe link clocks
- * @byte_clk: Handle to DSi byte clock.
- * @pixel_clk: Handle to DSI pixel clock.
+ * @byte_clk: Handle to DSi byte_clk.
+ * @pixel_clk: Handle to DSI pixel_clk.
*/
struct dsi_clk_link_set {
struct clk *byte_clk;
@@ -263,10 +292,10 @@
/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel_clk
* @client: DSI clock client pointer.
- * @pixel_clk: Pixel clock rate in Hz.
- * @index: Index of the DSI controller.
+ * @pixel_clk: Pixel_clk rate in Hz.
+ * @index: Index of the DSI controller.
* return: error code in case of failure or 0 for success.
*/
int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
index 434d383..2e3828e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
@@ -24,7 +24,8 @@
};
struct dsi_link_clks {
- struct dsi_link_clk_info clks;
+ struct dsi_link_hs_clk_info hs_clks;
+ struct dsi_link_lp_clk_info lp_clks;
struct link_clk_freq freq;
};
@@ -124,7 +125,7 @@
struct dsi_clk_mngr *mngr;
mngr = c->mngr;
- rc = clk_set_rate(mngr->link_clks[index].clks.pixel_clk, pixel_clk);
+ rc = clk_set_rate(mngr->link_clks[index].hs_clks.pixel_clk, pixel_clk);
if (rc)
pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
else
@@ -147,7 +148,7 @@
struct dsi_clk_mngr *mngr;
mngr = c->mngr;
- rc = clk_set_rate(mngr->link_clks[index].clks.byte_clk, byte_clk);
+ rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_clk, byte_clk);
if (rc)
pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
else
@@ -285,38 +286,39 @@
return rc;
}
-static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks, int index)
+static int dsi_link_hs_clk_set_rate(struct dsi_link_hs_clk_info *link_hs_clks,
+ int index)
{
int rc = 0;
struct dsi_clk_mngr *mngr;
+ struct dsi_link_clks *l_clks;
if (index >= MAX_DSI_CTRL) {
pr_err("Invalid DSI ctrl index\n");
return -EINVAL;
}
+ l_clks = container_of(link_hs_clks, struct dsi_link_clks, hs_clks);
mngr = container_of(l_clks, struct dsi_clk_mngr, link_clks[index]);
- if (mngr->is_cont_splash_enabled)
- return 0;
+
/*
* In an ideal world, cont_splash_enabled should not be required inside
* the clock manager. But, in the current driver cont_splash_enabled
* flag is set inside mdp driver and there is no interface event
* associated with this flag setting.
*/
- rc = clk_set_rate(l_clks->clks.esc_clk, l_clks->freq.esc_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
- goto error;
- }
+ if (mngr->is_cont_splash_enabled)
+ return 0;
- rc = clk_set_rate(l_clks->clks.byte_clk, l_clks->freq.byte_clk_rate);
+ rc = clk_set_rate(link_hs_clks->byte_clk,
+ l_clks->freq.byte_clk_rate);
if (rc) {
pr_err("clk_set_rate failed for byte_clk rc = %d\n", rc);
goto error;
}
- rc = clk_set_rate(l_clks->clks.pixel_clk, l_clks->freq.pix_clk_rate);
+ rc = clk_set_rate(link_hs_clks->pixel_clk,
+ l_clks->freq.pix_clk_rate);
if (rc) {
pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
goto error;
@@ -327,8 +329,8 @@
* For DPHY: byte_intf_clk_rate = byte_clk_rate / 2
* todo: this needs to be revisited when support for CPHY is added
*/
- if (l_clks->clks.byte_intf_clk) {
- rc = clk_set_rate(l_clks->clks.byte_intf_clk,
+ if (link_hs_clks->byte_intf_clk) {
+ rc = clk_set_rate(link_hs_clks->byte_intf_clk,
(l_clks->freq.byte_clk_rate / 2));
if (rc) {
pr_err("set_rate failed for byte_intf_clk rc = %d\n",
@@ -340,30 +342,24 @@
return rc;
}
-static int dsi_link_clk_prepare(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_prepare(struct dsi_link_hs_clk_info *link_hs_clks)
{
int rc = 0;
- rc = clk_prepare(l_clks->clks.esc_clk);
- if (rc) {
- pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
- goto esc_clk_err;
- }
-
- rc = clk_prepare(l_clks->clks.byte_clk);
+ rc = clk_prepare(link_hs_clks->byte_clk);
if (rc) {
pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
goto byte_clk_err;
}
- rc = clk_prepare(l_clks->clks.pixel_clk);
+ rc = clk_prepare(link_hs_clks->pixel_clk);
if (rc) {
pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
goto pixel_clk_err;
}
- if (l_clks->clks.byte_intf_clk) {
- rc = clk_prepare(l_clks->clks.byte_intf_clk);
+ if (link_hs_clks->byte_intf_clk) {
+ rc = clk_prepare(link_hs_clks->byte_intf_clk);
if (rc) {
pr_err("Failed to prepare dsi byte intf clk, rc=%d\n",
rc);
@@ -374,48 +370,39 @@
return rc;
byte_intf_clk_err:
- clk_unprepare(l_clks->clks.pixel_clk);
+ clk_unprepare(link_hs_clks->pixel_clk);
pixel_clk_err:
- clk_unprepare(l_clks->clks.byte_clk);
+ clk_unprepare(link_hs_clks->byte_clk);
byte_clk_err:
- clk_unprepare(l_clks->clks.esc_clk);
-esc_clk_err:
return rc;
}
-static void dsi_link_clk_unprepare(struct dsi_link_clks *l_clks)
+static void dsi_link_hs_clk_unprepare(struct dsi_link_hs_clk_info *link_hs_clks)
{
- if (l_clks->clks.byte_intf_clk)
- clk_unprepare(l_clks->clks.byte_intf_clk);
- clk_unprepare(l_clks->clks.pixel_clk);
- clk_unprepare(l_clks->clks.byte_clk);
- clk_unprepare(l_clks->clks.esc_clk);
+ if (link_hs_clks->byte_intf_clk)
+ clk_unprepare(link_hs_clks->byte_intf_clk);
+ clk_unprepare(link_hs_clks->pixel_clk);
+ clk_unprepare(link_hs_clks->byte_clk);
}
-static int dsi_link_clk_enable(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_enable(struct dsi_link_hs_clk_info *link_hs_clks)
{
int rc = 0;
- rc = clk_enable(l_clks->clks.esc_clk);
- if (rc) {
- pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
- goto esc_clk_err;
- }
-
- rc = clk_enable(l_clks->clks.byte_clk);
+ rc = clk_enable(link_hs_clks->byte_clk);
if (rc) {
pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
goto byte_clk_err;
}
- rc = clk_enable(l_clks->clks.pixel_clk);
+ rc = clk_enable(link_hs_clks->pixel_clk);
if (rc) {
pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
goto pixel_clk_err;
}
- if (l_clks->clks.byte_intf_clk) {
- rc = clk_enable(l_clks->clks.byte_intf_clk);
+ if (link_hs_clks->byte_intf_clk) {
+ rc = clk_enable(link_hs_clks->byte_intf_clk);
if (rc) {
pr_err("Failed to enable dsi byte intf clk, rc=%d\n",
rc);
@@ -426,28 +413,26 @@
return rc;
byte_intf_clk_err:
- clk_disable(l_clks->clks.pixel_clk);
+ clk_disable(link_hs_clks->pixel_clk);
pixel_clk_err:
- clk_disable(l_clks->clks.byte_clk);
+ clk_disable(link_hs_clks->byte_clk);
byte_clk_err:
- clk_disable(l_clks->clks.esc_clk);
-esc_clk_err:
return rc;
}
-static void dsi_link_clk_disable(struct dsi_link_clks *l_clks)
+static void dsi_link_hs_clk_disable(struct dsi_link_hs_clk_info *link_hs_clks)
{
- if (l_clks->clks.byte_intf_clk)
- clk_disable(l_clks->clks.byte_intf_clk);
- clk_disable(l_clks->clks.esc_clk);
- clk_disable(l_clks->clks.pixel_clk);
- clk_disable(l_clks->clks.byte_clk);
+ if (link_hs_clks->byte_intf_clk)
+ clk_disable(link_hs_clks->byte_intf_clk);
+ clk_disable(link_hs_clks->pixel_clk);
+ clk_disable(link_hs_clks->byte_clk);
}
/**
* dsi_link_clk_start() - enable dsi link clocks
*/
-static int dsi_link_clk_start(struct dsi_link_clks *clks, int index)
+static int dsi_link_hs_clk_start(struct dsi_link_hs_clk_info *link_hs_clks,
+ enum dsi_link_clk_op_type op_type, int index)
{
int rc = 0;
@@ -456,28 +441,34 @@
return -EINVAL;
}
- rc = dsi_link_clk_set_rate(clks, index);
- if (rc) {
- pr_err("failed to set clk rates, rc = %d\n", rc);
- goto error;
+ if (op_type & DSI_LINK_CLK_SET_RATE) {
+ rc = dsi_link_hs_clk_set_rate(link_hs_clks, index);
+ if (rc) {
+ pr_err("failed to set HS clk rates, rc = %d\n", rc);
+ goto error;
+ }
}
- rc = dsi_link_clk_prepare(clks);
- if (rc) {
- pr_err("failed to prepare link clks, rc = %d\n", rc);
- goto error;
+ if (op_type & DSI_LINK_CLK_PREPARE) {
+ rc = dsi_link_hs_clk_prepare(link_hs_clks);
+ if (rc) {
+ pr_err("failed to prepare link HS clks, rc = %d\n", rc);
+ goto error;
+ }
}
- rc = dsi_link_clk_enable(clks);
- if (rc) {
- pr_err("failed to enable link clks, rc = %d\n", rc);
- goto error_unprepare;
+ if (op_type & DSI_LINK_CLK_ENABLE) {
+ rc = dsi_link_hs_clk_enable(link_hs_clks);
+ if (rc) {
+ pr_err("failed to enable link HS clks, rc = %d\n", rc);
+ goto error_unprepare;
+ }
}
- pr_debug("Link clocks are enabled\n");
+ pr_debug("HS Link clocks are enabled\n");
return rc;
error_unprepare:
- dsi_link_clk_unprepare(clks);
+ dsi_link_hs_clk_unprepare(link_hs_clks);
error:
return rc;
}
@@ -485,13 +476,69 @@
/**
* dsi_link_clk_stop() - Stop DSI link clocks.
*/
-int dsi_link_clk_stop(struct dsi_link_clks *clks)
+static int dsi_link_hs_clk_stop(struct dsi_link_hs_clk_info *link_hs_clks)
{
- dsi_link_clk_disable(clks);
- dsi_link_clk_unprepare(clks);
+ struct dsi_link_clks *l_clks;
- pr_debug("Link clocks disabled\n");
+ l_clks = container_of(link_hs_clks, struct dsi_link_clks, hs_clks);
+ dsi_link_hs_clk_disable(link_hs_clks);
+ dsi_link_hs_clk_unprepare(link_hs_clks);
+
+ pr_debug("HS Link clocks disabled\n");
+
+ return 0;
+}
+
+static int dsi_link_lp_clk_start(struct dsi_link_lp_clk_info *link_lp_clks)
+{
+ int rc = 0;
+ struct dsi_clk_mngr *mngr;
+ struct dsi_link_clks *l_clks;
+
+ l_clks = container_of(link_lp_clks, struct dsi_link_clks, lp_clks);
+
+ mngr = container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
+ if (!mngr)
+ return -EINVAL;
+ /*
+ * In an ideal world, cont_splash_enabled should not be required inside
+ * the clock manager. But, in the current driver cont_splash_enabled
+ * flag is set inside mdp driver and there is no interface event
+ * associated with this flag setting. Also, set rate for clock need not
+ * be called for every enable call. It should be done only once when
+ * coming out of suspend.
+ */
+ if (mngr->is_cont_splash_enabled)
+ goto prepare;
+
+ rc = clk_set_rate(link_lp_clks->esc_clk, l_clks->freq.esc_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
+ goto error;
+ }
+
+prepare:
+ rc = clk_prepare_enable(link_lp_clks->esc_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi esc clk\n");
+ clk_unprepare(l_clks->lp_clks.esc_clk);
+ }
+error:
+ pr_debug("LP Link clocks are enabled\n");
+ return rc;
+}
+
+static int dsi_link_lp_clk_stop(
+ struct dsi_link_lp_clk_info *link_lp_clks)
+{
+ struct dsi_link_clks *l_clks;
+
+ l_clks = container_of(link_lp_clks, struct dsi_link_clks, lp_clks);
+
+ clk_disable_unprepare(l_clks->lp_clks.esc_clk);
+
+ pr_debug("LP Link clocks are disabled\n");
return 0;
}
@@ -556,7 +603,7 @@
}
static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
- u32 ctrl_count, u32 master_ndx)
+ enum dsi_lclk_type l_type, u32 ctrl_count, u32 master_ndx)
{
int rc = 0;
int i;
@@ -570,27 +617,56 @@
m_clks = &clks[master_ndx];
- rc = dsi_link_clk_start(m_clks, master_ndx);
- if (rc) {
- pr_err("failed to turn on master clocks, rc=%d\n", rc);
- goto error;
+ if (l_type & DSI_LINK_LP_CLK) {
+ rc = dsi_link_lp_clk_start(&m_clks->lp_clks);
+ if (rc) {
+ pr_err("failed to turn on master lp link clocks, rc=%d\n",
+ rc);
+ goto error;
+ }
}
- /* Turn on rest of the core clocks */
+ if (l_type & DSI_LINK_HS_CLK) {
+ rc = dsi_link_hs_clk_start(&m_clks->hs_clks,
+ DSI_LINK_CLK_START, master_ndx);
+ if (rc) {
+ pr_err("failed to turn on master hs link clocks, rc=%d\n",
+ rc);
+ goto error;
+ }
+ }
+
for (i = 0; i < ctrl_count; i++) {
clk = &clks[i];
if (!clk || (clk == m_clks))
continue;
- rc = dsi_link_clk_start(clk, i);
- if (rc) {
- pr_err("failed to turn on clocks, rc=%d\n", rc);
- goto error_disable_master;
+ if (l_type & DSI_LINK_LP_CLK) {
+ rc = dsi_link_lp_clk_start(&clk->lp_clks);
+ if (rc) {
+ pr_err("failed to turn on lp link clocks, rc=%d\n",
+ rc);
+ goto error_disable_master;
+ }
+ }
+
+ if (l_type & DSI_LINK_HS_CLK) {
+ rc = dsi_link_hs_clk_start(&clk->hs_clks,
+ DSI_LINK_CLK_START, i);
+ if (rc) {
+ pr_err("failed to turn on hs link clocks, rc=%d\n",
+ rc);
+ goto error_disable_master;
+ }
}
}
return rc;
+
error_disable_master:
- (void)dsi_link_clk_stop(m_clks);
+ if (l_type == DSI_LINK_LP_CLK)
+ (void)dsi_link_lp_clk_stop(&m_clks->lp_clks);
+ else if (l_type == DSI_LINK_HS_CLK)
+ (void)dsi_link_hs_clk_stop(&m_clks->hs_clks);
error:
return rc;
}
@@ -646,7 +722,7 @@
}
static int dsi_display_link_clk_disable(struct dsi_link_clks *clks,
- u32 ctrl_count, u32 master_ndx)
+ enum dsi_lclk_type l_type, u32 ctrl_count, u32 master_ndx)
{
int rc = 0;
int i;
@@ -667,15 +743,100 @@
if (!clk || (clk == m_clks))
continue;
- rc = dsi_link_clk_stop(clk);
- if (rc)
- pr_err("failed to turn off clocks, rc=%d\n", rc);
+ if (l_type & DSI_LINK_LP_CLK) {
+ rc = dsi_link_lp_clk_stop(&clk->lp_clks);
+ if (rc)
+ pr_err("failed to turn off lp link clocks, rc=%d\n",
+ rc);
+ }
+
+ if (l_type & DSI_LINK_HS_CLK) {
+ rc = dsi_link_hs_clk_stop(&clk->hs_clks);
+ if (rc)
+ pr_err("failed to turn off hs link clocks, rc=%d\n",
+ rc);
+ }
}
- rc = dsi_link_clk_stop(m_clks);
- if (rc)
- pr_err("failed to turn off master clocks, rc=%d\n", rc);
+ if (l_type & DSI_LINK_LP_CLK) {
+ rc = dsi_link_lp_clk_stop(&m_clks->lp_clks);
+ if (rc)
+ pr_err("failed to turn off master lp link clocks, rc=%d\n",
+ rc);
+ }
+ if (l_type & DSI_LINK_HS_CLK) {
+ rc = dsi_link_hs_clk_stop(&m_clks->hs_clks);
+ if (rc)
+ pr_err("failed to turn off master hs link clocks, rc=%d\n",
+ rc);
+ }
+
+ return rc;
+}
+
+static int dsi_clk_update_link_clk_state(struct dsi_link_clks *l_clks,
+ enum dsi_lclk_type l_type, u32 l_state, bool enable)
+{
+ int rc = 0;
+ struct dsi_clk_mngr *mngr;
+
+ mngr = container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
+ if (!mngr)
+ return -EINVAL;
+
+ if (enable) {
+ if (mngr->pre_clkon_cb) {
+ rc = mngr->pre_clkon_cb(mngr->priv_data, DSI_LINK_CLK,
+ l_type, l_state);
+ if (rc) {
+ pr_err("pre link clk on cb failed for type %d\n",
+ l_type);
+ goto error;
+ }
+ }
+ rc = dsi_display_link_clk_enable(l_clks, l_type,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to start link clk type %d rc=%d\n",
+ l_type, rc);
+ goto error;
+ }
+
+ if (mngr->post_clkon_cb) {
+ rc = mngr->post_clkon_cb(mngr->priv_data, DSI_LINK_CLK,
+ l_type, l_state);
+ if (rc) {
+ pr_err("post link clk on cb failed for type %d\n",
+ l_type);
+ goto error;
+ }
+ }
+ } else {
+ if (mngr->pre_clkoff_cb) {
+ rc = mngr->pre_clkoff_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_type, l_state);
+ if (rc)
+ pr_err("pre link clk off cb failed\n");
+ }
+
+ rc = dsi_display_link_clk_disable(l_clks, l_type,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to stop link clk type %d, rc = %d\n",
+ l_type, rc);
+ goto error;
+ }
+
+ if (mngr->post_clkoff_cb) {
+ rc = mngr->post_clkoff_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_type, l_state);
+ if (rc)
+ pr_err("post link clk off cb failed\n");
+ }
+ }
+
+error:
return rc;
}
@@ -710,6 +871,7 @@
if (mngr->core_clk_state == DSI_CLK_OFF) {
rc = mngr->pre_clkon_cb(mngr->priv_data,
DSI_CORE_CLK,
+ DSI_LINK_NONE,
DSI_CLK_ON);
if (rc) {
pr_err("failed to turn on MDP FS rc= %d\n", rc);
@@ -726,6 +888,7 @@
if (mngr->post_clkon_cb) {
rc = mngr->post_clkon_cb(mngr->priv_data,
DSI_CORE_CLK,
+ DSI_LINK_NONE,
DSI_CLK_ON);
if (rc)
pr_err("post clk on cb failed, rc = %d\n", rc);
@@ -735,25 +898,15 @@
if (l_clks) {
if (l_state == DSI_CLK_ON) {
- if (mngr->pre_clkon_cb) {
- rc = mngr->pre_clkon_cb(mngr->priv_data,
- DSI_LINK_CLK, l_state);
- if (rc)
- pr_err("pre link clk on cb failed\n");
- }
- rc = dsi_display_link_clk_enable(l_clks,
- mngr->dsi_ctrl_count, mngr->master_ndx);
- if (rc) {
- pr_err("failed to start link clk rc= %d\n", rc);
+ rc = dsi_clk_update_link_clk_state(l_clks,
+ DSI_LINK_LP_CLK, l_state, true);
+ if (rc)
goto error;
- }
- if (mngr->post_clkon_cb) {
- rc = mngr->post_clkon_cb(mngr->priv_data,
- DSI_LINK_CLK,
- l_state);
- if (rc)
- pr_err("post link clk on cb failed\n");
- }
+
+ rc = dsi_clk_update_link_clk_state(l_clks,
+ DSI_LINK_HS_CLK, l_state, true);
+ if (rc)
+ goto error;
} else {
/*
* Two conditions that need to be checked for Link
@@ -784,36 +937,26 @@
}
rc = dsi_display_link_clk_enable(l_clks,
+ (DSI_LINK_LP_CLK & DSI_LINK_HS_CLK),
mngr->dsi_ctrl_count, mngr->master_ndx);
if (rc) {
- pr_err("Link clks did not start\n");
+ pr_err("LP Link clks did not start\n");
goto error;
}
l_c_on = true;
pr_debug("ECG: core and Link_on\n");
}
- if (mngr->pre_clkoff_cb) {
- rc = mngr->pre_clkoff_cb(mngr->priv_data,
- DSI_LINK_CLK, l_state);
- if (rc)
- pr_err("pre link clk off cb failed\n");
- }
-
- rc = dsi_display_link_clk_disable(l_clks,
- mngr->dsi_ctrl_count, mngr->master_ndx);
- if (rc) {
- pr_err("failed to stop link clk, rc = %d\n",
- rc);
+ rc = dsi_clk_update_link_clk_state(l_clks,
+ DSI_LINK_HS_CLK, l_state, false);
+ if (rc)
goto error;
- }
- if (mngr->post_clkoff_cb) {
- rc = mngr->post_clkoff_cb(mngr->priv_data,
- DSI_LINK_CLK, l_state);
- if (rc)
- pr_err("post link clk off cb failed\n");
- }
+ rc = dsi_clk_update_link_clk_state(l_clks,
+ DSI_LINK_LP_CLK, l_state, false);
+ if (rc)
+ goto error;
+
/*
* This check is to save unnecessary clock state
* change when going from EARLY_GATE to OFF. In the
@@ -872,6 +1015,7 @@
if (mngr->pre_clkoff_cb) {
rc = mngr->pre_clkoff_cb(mngr->priv_data,
DSI_CORE_CLK,
+ DSI_LINK_NONE,
c_state);
if (rc)
pr_err("pre core clk off cb failed\n");
@@ -888,6 +1032,7 @@
if (mngr->post_clkoff_cb) {
rc = mngr->post_clkoff_cb(mngr->priv_data,
DSI_CORE_CLK,
+ DSI_LINK_NONE,
DSI_CLK_OFF);
if (rc)
pr_err("post clkoff cb fail, rc = %d\n",
@@ -1095,7 +1240,8 @@
}
rc = dsi_display_link_clk_disable(l_clks,
- mngr->dsi_ctrl_count, mngr->master_ndx);
+ (DSI_LINK_LP_CLK | DSI_LINK_HS_CLK),
+ mngr->dsi_ctrl_count, mngr->master_ndx);
if (rc) {
pr_err("%s, failed to stop link clk, rc = %d\n",
__func__, rc);
@@ -1103,7 +1249,8 @@
}
rc = dsi_display_link_clk_enable(l_clks,
- mngr->dsi_ctrl_count, mngr->master_ndx);
+ (DSI_LINK_LP_CLK | DSI_LINK_HS_CLK),
+ mngr->dsi_ctrl_count, mngr->master_ndx);
if (rc) {
pr_err("%s, failed to start link clk rc= %d\n",
__func__, rc);
@@ -1267,8 +1414,10 @@
for (i = 0; i < mngr->dsi_ctrl_count; i++) {
memcpy(&mngr->core_clks[i].clks, &info->c_clks[i],
sizeof(struct dsi_core_clk_info));
- memcpy(&mngr->link_clks[i].clks, &info->l_clks[i],
- sizeof(struct dsi_link_clk_info));
+ memcpy(&mngr->link_clks[i].hs_clks, &info->l_hs_clks[i],
+ sizeof(struct dsi_link_hs_clk_info));
+ memcpy(&mngr->link_clks[i].lp_clks, &info->l_lp_clks[i],
+ sizeof(struct dsi_link_lp_clk_info));
mngr->core_clks[i].bus_handle = info->bus_handle[i];
mngr->ctrl_index[i] = info->ctrl_index[i];
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 93e364de..92e08e0 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -243,6 +243,8 @@
dsi_ctrl->cell_index);
sde_dbg_reg_register_base(dbg_name, dsi_ctrl->hw.base,
msm_iomap_size(dsi_ctrl->pdev, "dsi_ctrl"));
+ sde_dbg_reg_register_dump_range(dbg_name, dbg_name, 0,
+ msm_iomap_size(dsi_ctrl->pdev, "dsi_ctrl"), 0);
error_remove_dir:
debugfs_remove(dir);
error:
@@ -272,6 +274,8 @@
int rc = 0;
struct dsi_ctrl_state_info *state = &dsi_ctrl->current_state;
+ SDE_EVT32(dsi_ctrl->cell_index, op);
+
switch (op) {
case DSI_CTRL_OP_POWER_STATE_CHANGE:
if (state->power_state == op_state) {
@@ -461,7 +465,7 @@
}
ctrl->hw.base = ptr;
- pr_debug("[%s] map dsi_ctrl registers to %p\n", ctrl->name,
+ pr_debug("[%s] map dsi_ctrl registers to %pK\n", ctrl->name,
ctrl->hw.base);
switch (ctrl->version) {
@@ -498,7 +502,8 @@
static int dsi_ctrl_clocks_deinit(struct dsi_ctrl *ctrl)
{
struct dsi_core_clk_info *core = &ctrl->clk_info.core_clks;
- struct dsi_link_clk_info *link = &ctrl->clk_info.link_clks;
+ struct dsi_link_lp_clk_info *lp_link = &ctrl->clk_info.lp_link_clks;
+ struct dsi_link_hs_clk_info *hs_link = &ctrl->clk_info.hs_link_clks;
struct dsi_clk_link_set *rcg = &ctrl->clk_info.rcg_clks;
if (core->mdp_core_clk)
@@ -514,16 +519,17 @@
memset(core, 0x0, sizeof(*core));
- if (link->byte_clk)
- devm_clk_put(&ctrl->pdev->dev, link->byte_clk);
- if (link->pixel_clk)
- devm_clk_put(&ctrl->pdev->dev, link->pixel_clk);
- if (link->esc_clk)
- devm_clk_put(&ctrl->pdev->dev, link->esc_clk);
- if (link->byte_intf_clk)
- devm_clk_put(&ctrl->pdev->dev, link->byte_intf_clk);
+ if (hs_link->byte_clk)
+ devm_clk_put(&ctrl->pdev->dev, hs_link->byte_clk);
+ if (hs_link->pixel_clk)
+ devm_clk_put(&ctrl->pdev->dev, hs_link->pixel_clk);
+ if (lp_link->esc_clk)
+ devm_clk_put(&ctrl->pdev->dev, lp_link->esc_clk);
+ if (hs_link->byte_intf_clk)
+ devm_clk_put(&ctrl->pdev->dev, hs_link->byte_intf_clk);
- memset(link, 0x0, sizeof(*link));
+ memset(hs_link, 0x0, sizeof(*hs_link));
+ memset(lp_link, 0x0, sizeof(*lp_link));
if (rcg->byte_clk)
devm_clk_put(&ctrl->pdev->dev, rcg->byte_clk);
@@ -540,7 +546,8 @@
{
int rc = 0;
struct dsi_core_clk_info *core = &ctrl->clk_info.core_clks;
- struct dsi_link_clk_info *link = &ctrl->clk_info.link_clks;
+ struct dsi_link_lp_clk_info *lp_link = &ctrl->clk_info.lp_link_clks;
+ struct dsi_link_hs_clk_info *hs_link = &ctrl->clk_info.hs_link_clks;
struct dsi_clk_link_set *rcg = &ctrl->clk_info.rcg_clks;
core->mdp_core_clk = devm_clk_get(&pdev->dev, "mdp_core_clk");
@@ -573,30 +580,30 @@
pr_debug("can't get mnoc clock, rc=%d\n", rc);
}
- link->byte_clk = devm_clk_get(&pdev->dev, "byte_clk");
- if (IS_ERR(link->byte_clk)) {
- rc = PTR_ERR(link->byte_clk);
+ hs_link->byte_clk = devm_clk_get(&pdev->dev, "byte_clk");
+ if (IS_ERR(hs_link->byte_clk)) {
+ rc = PTR_ERR(hs_link->byte_clk);
pr_err("failed to get byte_clk, rc=%d\n", rc);
goto fail;
}
- link->pixel_clk = devm_clk_get(&pdev->dev, "pixel_clk");
- if (IS_ERR(link->pixel_clk)) {
- rc = PTR_ERR(link->pixel_clk);
+ hs_link->pixel_clk = devm_clk_get(&pdev->dev, "pixel_clk");
+ if (IS_ERR(hs_link->pixel_clk)) {
+ rc = PTR_ERR(hs_link->pixel_clk);
pr_err("failed to get pixel_clk, rc=%d\n", rc);
goto fail;
}
- link->esc_clk = devm_clk_get(&pdev->dev, "esc_clk");
- if (IS_ERR(link->esc_clk)) {
- rc = PTR_ERR(link->esc_clk);
+ lp_link->esc_clk = devm_clk_get(&pdev->dev, "esc_clk");
+ if (IS_ERR(lp_link->esc_clk)) {
+ rc = PTR_ERR(lp_link->esc_clk);
pr_err("failed to get esc_clk, rc=%d\n", rc);
goto fail;
}
- link->byte_intf_clk = devm_clk_get(&pdev->dev, "byte_intf_clk");
- if (IS_ERR(link->byte_intf_clk)) {
- link->byte_intf_clk = NULL;
+ hs_link->byte_intf_clk = devm_clk_get(&pdev->dev, "byte_intf_clk");
+ if (IS_ERR(hs_link->byte_intf_clk)) {
+ hs_link->byte_intf_clk = NULL;
pr_debug("can't find byte intf clk, rc=%d\n", rc);
}
@@ -1133,6 +1140,11 @@
}
kickoff:
+ /* check if custom dma scheduling line needed */
+ if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) &&
+ (flags & DSI_CTRL_CMD_CUSTOM_DMA_SCHED))
+ line_no = dsi_ctrl->host_config.u.video_engine.dma_sched_line;
+
timing = &(dsi_ctrl->host_config.video_timing);
if (timing)
line_no += timing->v_back_porch + timing->v_sync_width +
@@ -1330,13 +1342,20 @@
u32 current_read_len = 0, total_bytes_read = 0;
bool short_resp = false;
bool read_done = false;
- u32 dlen, diff, rlen = msg->rx_len;
+ u32 dlen, diff, rlen;
unsigned char *buff;
char cmd;
struct dsi_cmd_desc *of_cmd;
+ if (!msg) {
+ pr_err("Invalid msg\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
of_cmd = container_of(msg, struct dsi_cmd_desc, msg);
+ rlen = msg->rx_len;
if (msg->rx_len <= 2) {
short_resp = true;
rd_pkt_size = msg->rx_len;
@@ -1373,9 +1392,9 @@
* wait before reading rdbk_data register, if any delay is
* required after sending the read command.
*/
- if (of_cmd && of_cmd->post_wait_ms)
- usleep_range(of_cmd->post_wait_ms * 1000,
- ((of_cmd->post_wait_ms * 1000) + 10));
+ if (msg->wait_ms)
+ usleep_range(msg->wait_ms * 1000,
+ ((msg->wait_ms * 1000) + 10));
dlen = dsi_ctrl->hw.ops.get_cmd_read_data(&dsi_ctrl->hw,
buff, total_bytes_read,
@@ -1783,11 +1802,14 @@
#if defined(CONFIG_DEBUG_FS)
-void dsi_ctrl_debug_dump(void)
+void dsi_ctrl_debug_dump(u32 *entries, u32 size)
{
struct list_head *pos, *tmp;
struct dsi_ctrl *ctrl = NULL;
+ if (!entries || !size)
+ return;
+
mutex_lock(&dsi_ctrl_list_lock);
list_for_each_safe(pos, tmp, &dsi_ctrl_list) {
struct dsi_ctrl_list_item *n;
@@ -1795,7 +1817,7 @@
n = list_entry(pos, struct dsi_ctrl_list_item, list);
ctrl = n->ctrl;
pr_err("dsi ctrl:%d\n", ctrl->cell_index);
- ctrl->hw.ops.debug_bus(&ctrl->hw);
+ ctrl->hw.ops.debug_bus(&ctrl->hw, entries, size);
}
mutex_unlock(&dsi_ctrl_list_lock);
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 4f85cda..6ac7dd7 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -38,6 +38,8 @@
* @DSI_CTRL_CMD_LAST_COMMAND: Trigger the DMA cmd transfer if this is last
* command in the batch.
* @DSI_CTRL_CMD_NON_EMBEDDED_MODE:Trasfer cmd packets in non embedded mode.
+ * @DSI_CTRL_CMD_CUSTOM_DMA_SCHED: Use the dma scheduling line number defined in
+ * display panel dtsi file instead of default.
*/
#define DSI_CTRL_CMD_READ 0x1
#define DSI_CTRL_CMD_BROADCAST 0x2
@@ -47,6 +49,7 @@
#define DSI_CTRL_CMD_FETCH_MEMORY 0x20
#define DSI_CTRL_CMD_LAST_COMMAND 0x40
#define DSI_CTRL_CMD_NON_EMBEDDED_MODE 0x80
+#define DSI_CTRL_CMD_CUSTOM_DMA_SCHED 0x100
/* DSI embedded mode fifo size
* If the command is greater than 256 bytes it is sent in non-embedded mode.
@@ -57,6 +60,18 @@
#define DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE 64
/**
+ * enum dsi_channel_id - defines dsi channel id.
+ * @DSI_CTRL_LEFT: DSI 0 channel
+ * @DSI_CTRL_RIGHT: DSI 1 channel
+ * @DSI_CTRL_MAX: Maximum value.
+ */
+enum dsi_channel_id {
+ DSI_CTRL_LEFT = 0,
+ DSI_CTRL_RIGHT,
+ DSI_CTRL_MAX,
+};
+
+/**
* enum dsi_power_state - defines power states for dsi controller.
* @DSI_CTRL_POWER_VREG_OFF: Digital and analog supplies for DSI controller
turned off
@@ -97,7 +112,8 @@
/**
* struct dsi_ctrl_clk_info - clock information for DSI controller
* @core_clks: Core clocks needed to access DSI controller registers.
- * @link_clks: Link clocks required to transmit data over DSI link.
+ * @hs_link_clks: Clocks required to transmit high speed data over DSI
+ * @lp_link_clks: Clocks required to perform low power ops over DSI
* @rcg_clks: Root clock generation clocks generated in MMSS_CC. The
* output of the PLL is set as parent for these root
* clocks. These clocks are specific to controller
@@ -111,7 +127,8 @@
struct dsi_ctrl_clk_info {
/* Clocks parsed from DT */
struct dsi_core_clk_info core_clks;
- struct dsi_link_clk_info link_clks;
+ struct dsi_link_hs_clk_info hs_link_clks;
+ struct dsi_link_lp_clk_info lp_link_clks;
struct dsi_clk_link_set rcg_clks;
/* Clocks set by DSI Manager */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index d6fab59..73aab3f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -457,8 +457,10 @@
/**
* debug_bus() - get dsi debug bus status.
* @ctrl: Pointer to the controller host hardware.
+ * @entries: Array of dsi debug bus control values.
+ * @size: Size of dsi debug bus control array.
*/
- void (*debug_bus)(struct dsi_ctrl_hw *ctrl);
+ void (*debug_bus)(struct dsi_ctrl_hw *ctrl, u32 *entries, u32 size);
/**
* soft_reset() - perform a soft reset on DSI controller
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
index c1af52f..6dde454 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -459,18 +459,20 @@
pr_debug("[DSI_%d] Video engine setup done\n", ctrl->index);
}
-void dsi_ctrl_hw_cmn_debug_bus(struct dsi_ctrl_hw *ctrl)
+void dsi_ctrl_hw_cmn_debug_bus(struct dsi_ctrl_hw *ctrl, u32 *entries, u32 size)
{
- u32 reg = 0;
+ u32 reg = 0, i = 0;
- DSI_W32(ctrl, DSI_DEBUG_BUS_CTL, 0x181);
-
- /* make sure that debug test point is enabled */
- wmb();
- reg = DSI_R32(ctrl, DSI_DEBUG_BUS_STATUS);
-
- pr_err("[DSI_%d] debug bus status:0x%x\n", ctrl->index, reg);
+ for (i = 0; i < size; i++) {
+ DSI_W32(ctrl, DSI_DEBUG_BUS_CTL, entries[i]);
+ /* make sure that debug test point is enabled */
+ wmb();
+ reg = DSI_R32(ctrl, DSI_DEBUG_BUS_STATUS);
+ pr_err("[DSI_%d] debug bus ctrl: 0x%x status:0x%x\n",
+ ctrl->index, entries[i], reg);
+ }
}
+
/**
* cmd_engine_setup() - setup dsi host controller for command mode
* @ctrl: Pointer to the controller host hardware.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index d45f849..6540182 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -438,6 +438,8 @@
* @bllp_lp11_en: Enter low power stop mode (LP-11) during BLLP.
* @traffic_mode: Traffic mode for video stream.
* @vc_id: Virtual channel identifier.
+ * @dma_sched_line: Line number, after vactive end, at which command dma
+ * needs to be triggered.
*/
struct dsi_video_engine_cfg {
bool last_line_interleave_en;
@@ -449,6 +451,7 @@
bool bllp_lp11_en;
enum dsi_video_traffic_mode traffic_mode;
u32 vc_id;
+ u32 dma_sched_line;
};
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 199833d..75bbbfd 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -559,14 +559,12 @@
if (dsi_ctrl_validate_host_state(ctrl->ctrl))
return 1;
- /* acquire panel_lock to make sure no commands are in progress */
- dsi_panel_acquire_panel_lock(panel);
-
config = &(panel->esd_config);
lenp = config->status_valid_params ?: config->status_cmds_rlen;
count = config->status_cmd.count;
cmds = config->status_cmd.cmds;
- flags |= (DSI_CTRL_CMD_FETCH_MEMORY | DSI_CTRL_CMD_READ);
+ flags |= (DSI_CTRL_CMD_FETCH_MEMORY | DSI_CTRL_CMD_READ |
+ DSI_CTRL_CMD_CUSTOM_DMA_SCHED);
for (i = 0; i < count; ++i) {
memset(config->status_buf, 0x0, SZ_4K);
@@ -579,7 +577,7 @@
rc = dsi_ctrl_cmd_transfer(ctrl->ctrl, &cmds[i].msg, flags);
if (rc <= 0) {
pr_err("rx cmd transfer failed rc=%d\n", rc);
- goto error;
+ return rc;
}
memcpy(config->return_buf + start,
@@ -587,9 +585,6 @@
start += lenp[i];
}
-error:
- /* release panel_lock */
- dsi_panel_release_panel_lock(panel);
return rc;
}
@@ -619,12 +614,20 @@
static int dsi_display_status_reg_read(struct dsi_display *display)
{
- int rc = 0, i;
+ int rc = 0, i, cmd_channel_idx = DSI_CTRL_LEFT;
struct dsi_display_ctrl *m_ctrl, *ctrl;
pr_debug(" ++\n");
- m_ctrl = &display->ctrl[display->cmd_master_idx];
+ /*
+ * Check the Panel DSI command channel.
+ * If the cmd_channel is set, then we should
+ * choose the right DSI(DSI1) controller to send command,
+ * else we choose the left(DSI0) controller.
+ */
+ if (display->panel->esd_config.cmd_channel)
+ cmd_channel_idx = DSI_CTRL_RIGHT;
+ m_ctrl = &display->ctrl[cmd_channel_idx];
if (display->tx_cmd_buf == NULL) {
rc = dsi_host_alloc_cmd_tx_buffer(display);
@@ -708,17 +711,19 @@
u32 status_mode;
int rc = 0x1;
- if (dsi_display == NULL)
+ if (!dsi_display || !dsi_display->panel)
return -EINVAL;
- mutex_lock(&dsi_display->display_lock);
-
panel = dsi_display->panel;
+
+ dsi_panel_acquire_panel_lock(panel);
+
if (!panel->panel_initialized) {
pr_debug("Panel not initialized\n");
- mutex_unlock(&dsi_display->display_lock);
+ dsi_panel_release_panel_lock(panel);
return rc;
}
+ SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
if (te_check_override && gpio_is_valid(dsi_display->disp_te_gpio))
status_mode = ESD_MODE_PANEL_TE;
@@ -741,7 +746,8 @@
dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
DSI_ALL_CLKS, DSI_CLK_OFF);
- mutex_unlock(&dsi_display->display_lock);
+ dsi_panel_release_panel_lock(panel);
+ SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
return rc;
}
@@ -757,7 +763,7 @@
cmd->msg.channel = cmd_buf[2];
cmd->msg.flags = cmd_buf[3];
cmd->msg.ctrl = 0;
- cmd->post_wait_ms = cmd_buf[4];
+ cmd->post_wait_ms = cmd->msg.wait_ms = cmd_buf[4];
cmd->msg.tx_len = ((cmd_buf[5] << 8) | (cmd_buf[6]));
if (cmd->msg.tx_len > payload_len) {
@@ -1581,9 +1587,19 @@
m_ctrl = &display->ctrl[display->cmd_master_idx];
ulps_enabled = display->ulps_enabled;
+ /*
+ * Clamp control can be either through the DSI controller or
+ * the DSI PHY depending on hardware variation
+ */
rc = dsi_ctrl_set_clamp_state(m_ctrl->ctrl, enable, ulps_enabled);
if (rc) {
- pr_err("DSI Clamp state change(%d) failed\n", enable);
+ pr_err("DSI ctrl clamp state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ rc = dsi_phy_set_clamp_state(m_ctrl->phy, enable);
+ if (rc) {
+ pr_err("DSI phy clamp state change(%d) failed\n", enable);
return rc;
}
@@ -1597,7 +1613,18 @@
pr_err("DSI Clamp state change(%d) failed\n", enable);
return rc;
}
+
+ rc = dsi_phy_set_clamp_state(ctrl->phy, enable);
+ if (rc) {
+ pr_err("DSI phy clamp state change(%d) failed\n",
+ enable);
+ return rc;
+ }
+
+ pr_debug("Clamps %s for ctrl%d\n",
+ enable ? "enabled" : "disabled", i);
}
+
display->clamp_enabled = enable;
return 0;
}
@@ -3002,12 +3029,14 @@
int dsi_pre_clkoff_cb(void *priv,
enum dsi_clk_type clk,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state new_state)
{
int rc = 0;
struct dsi_display *display = priv;
- if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF)) {
+ if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF) &&
+ (l_type && DSI_LINK_LP_CLK)) {
/*
* If ULPS feature is enabled, enter ULPS first.
* However, when blanking the panel, we should enter ULPS
@@ -3050,6 +3079,8 @@
pr_err("%s: failed to disable ulps. rc=%d\n",
__func__, rc);
}
+ /* dsi will not be able to serve irqs from here on */
+ dsi_display_ctrl_irq_update(display, false);
}
return rc;
@@ -3057,13 +3088,14 @@
int dsi_post_clkon_cb(void *priv,
enum dsi_clk_type clk,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state)
{
int rc = 0;
struct dsi_display *display = priv;
bool mmss_clamp = false;
- if (clk & DSI_CORE_CLK) {
+ if ((clk & DSI_LINK_CLK) && (l_type & DSI_LINK_LP_CLK)) {
mmss_clamp = display->clamp_enabled;
/*
* controller setup is needed if coming out of idle
@@ -3072,6 +3104,13 @@
if (mmss_clamp)
dsi_display_ctrl_setup(display);
+ /*
+ * Phy setup is needed if coming out of idle
+ * power collapse with clamps enabled.
+ */
+ if (display->phy_idle_power_off || mmss_clamp)
+ dsi_display_phy_idle_on(display, mmss_clamp);
+
if (display->ulps_enabled && mmss_clamp) {
/*
* ULPS Entry Request. This is needed if the lanes were
@@ -3110,17 +3149,11 @@
goto error;
}
- /*
- * Phy setup is needed if coming out of idle
- * power collapse with clamps enabled.
- */
- if (display->phy_idle_power_off || mmss_clamp)
- dsi_display_phy_idle_on(display, mmss_clamp);
-
/* enable dsi to serve irqs */
dsi_display_ctrl_irq_update(display, true);
}
- if (clk & DSI_LINK_CLK) {
+
+ if ((clk & DSI_LINK_CLK) && (l_type & DSI_LINK_HS_CLK)) {
/*
* Toggle the resync FIFO everytime clock changes, except
* when cont-splash screen transition is going on.
@@ -3145,6 +3178,7 @@
int dsi_post_clkoff_cb(void *priv,
enum dsi_clk_type clk_type,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state curr_state)
{
int rc = 0;
@@ -3157,9 +3191,6 @@
if ((clk_type & DSI_CORE_CLK) &&
(curr_state == DSI_CLK_OFF)) {
- /* dsi will not be able to serve irqs from here */
- dsi_display_ctrl_irq_update(display, false);
-
rc = dsi_display_phy_power_off(display);
if (rc)
pr_err("[%s] failed to power off PHY, rc=%d\n",
@@ -3175,6 +3206,7 @@
int dsi_pre_clkon_cb(void *priv,
enum dsi_clk_type clk_type,
+ enum dsi_lclk_type l_type,
enum dsi_clk_state new_state)
{
int rc = 0;
@@ -3643,6 +3675,7 @@
/* For split DSI, update the clock master first */
pr_debug("configuring seamless dynamic fps\n\n");
+ SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
m_ctrl = &display->ctrl[display->clk_master_idx];
rc = dsi_ctrl_async_timing_update(m_ctrl->ctrl, timing);
@@ -3677,6 +3710,7 @@
panel_mode->dsi_mode_flags = 0;
error:
+ SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
return rc;
}
@@ -4391,10 +4425,16 @@
goto error_ctrl_deinit;
}
- memcpy(&info.c_clks[i], &display_ctrl->ctrl->clk_info.core_clks,
- sizeof(struct dsi_core_clk_info));
- memcpy(&info.l_clks[i], &display_ctrl->ctrl->clk_info.link_clks,
- sizeof(struct dsi_link_clk_info));
+ memcpy(&info.c_clks[i],
+ (&display_ctrl->ctrl->clk_info.core_clks),
+ sizeof(struct dsi_core_clk_info));
+ memcpy(&info.l_hs_clks[i],
+ (&display_ctrl->ctrl->clk_info.hs_link_clks),
+ sizeof(struct dsi_link_hs_clk_info));
+ memcpy(&info.l_lp_clks[i],
+ (&display_ctrl->ctrl->clk_info.lp_link_clks),
+ sizeof(struct dsi_link_lp_clk_info));
+
info.c_clks[i].phandle = &priv->phandle;
info.bus_handle[i] =
display_ctrl->ctrl->axi_bus_info.bus_handle;
@@ -5792,6 +5832,7 @@
return -EINVAL;
}
+ SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&display->display_lock);
mode = display->panel->cur_mode;
@@ -5926,6 +5967,7 @@
(void)dsi_panel_post_unprepare(display->panel);
error:
mutex_unlock(&display->display_lock);
+ SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
return rc;
}
@@ -6149,6 +6191,7 @@
pr_err("no valid mode set for the display");
return -EINVAL;
}
+ SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
/* Engine states and panel states are populated during splash
* resource init and hence we return early
@@ -6234,6 +6277,7 @@
(void)dsi_panel_disable(display->panel);
error:
mutex_unlock(&display->display_lock);
+ SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
return rc;
}
@@ -6296,6 +6340,7 @@
return -EINVAL;
}
+ SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&display->display_lock);
rc = dsi_display_wake_up(display);
@@ -6324,6 +6369,7 @@
display->name, rc);
mutex_unlock(&display->display_lock);
+ SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
return rc;
}
@@ -6353,6 +6399,7 @@
return -EINVAL;
}
+ SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
mutex_lock(&display->display_lock);
rc = dsi_display_wake_up(display);
@@ -6406,6 +6453,7 @@
display->name, rc);
mutex_unlock(&display->display_lock);
+ SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
return rc;
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 6b1c029..5612016 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -497,12 +497,14 @@
* dsi_pre_clkoff_cb() - Callback before clock is turned off
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
int dsi_pre_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
- enum dsi_clk_state new_state);
+ enum dsi_lclk_type l_type,
+ enum dsi_clk_state new_state);
/**
* dsi_display_update_pps() - update PPS buffer.
@@ -519,35 +521,40 @@
* dsi_post_clkoff_cb() - Callback after clock is turned off
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
int dsi_post_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
- enum dsi_clk_state curr_state);
+ enum dsi_lclk_type l_type,
+ enum dsi_clk_state curr_state);
/**
* dsi_post_clkon_cb() - Callback after clock is turned on
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @curr_state: current state for the clock.
*
* @return: error code.
*/
int dsi_post_clkon_cb(void *priv, enum dsi_clk_type clk_type,
- enum dsi_clk_state curr_state);
-
+ enum dsi_lclk_type l_type,
+ enum dsi_clk_state curr_state);
/**
* dsi_pre_clkon_cb() - Callback before clock is turned on
* @priv: private data pointer.
* @clk_type: clock which is being turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
* @new_state: next state for the clock.
*
* @return: error code.
*/
int dsi_pre_clkon_cb(void *priv, enum dsi_clk_type clk_type,
- enum dsi_clk_state new_state);
+ enum dsi_lclk_type l_type,
+ enum dsi_clk_state new_state);
/**
* dsi_display_unprepare() - power off display hardware.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index 5b47865..6b5bfb4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -186,6 +186,7 @@
{
int rc = 0;
struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
+ struct dsi_display *display;
if (!bridge) {
pr_err("Invalid params\n");
@@ -197,11 +198,15 @@
pr_debug("[%d] seamless enable\n", c_bridge->id);
return;
}
+ display = c_bridge->display;
- rc = dsi_display_post_enable(c_bridge->display);
+ rc = dsi_display_post_enable(display);
if (rc)
pr_err("[%d] DSI display post enabled failed, rc=%d\n",
c_bridge->id, rc);
+
+ if (display && display->drm_conn)
+ sde_connector_helper_bridge_enable(display->drm_conn);
}
static void dsi_bridge_disable(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index e62c65e..dab85f4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1217,6 +1217,7 @@
const char *traffic_mode;
u32 vc_id = 0;
u32 val = 0;
+ u32 line_no = 0;
rc = of_property_read_u32(of_node, "qcom,mdss-dsi-h-sync-pulse", &val);
if (rc) {
@@ -1279,6 +1280,17 @@
cfg->vc_id = vc_id;
}
+ rc = of_property_read_u32(of_node, "qcom,mdss-dsi-dma-schedule-line",
+ &line_no);
+ if (rc) {
+ pr_debug("[%s] set default dma scheduling line no\n", name);
+ cfg->dma_sched_line = 0x1;
+ /* do not fail since we have default value */
+ rc = 0;
+ } else {
+ cfg->dma_sched_line = line_no;
+ }
+
error:
return rc;
}
@@ -1524,7 +1536,7 @@
cmd[i].msg.channel = data[2];
cmd[i].msg.flags |= (data[3] == 1 ? MIPI_DSI_MSG_REQ_ACK : 0);
cmd[i].msg.ctrl = 0;
- cmd[i].post_wait_ms = data[4];
+ cmd[i].post_wait_ms = cmd[i].msg.wait_ms = data[4];
cmd[i].msg.tx_len = ((data[5] << 8) | (data[6]));
size = cmd[i].msg.tx_len * sizeof(u8);
@@ -1759,6 +1771,9 @@
panel->sync_broadcast_en = of_property_read_bool(of_node,
"qcom,cmd-sync-wait-broadcast");
+
+ panel->lp11_init = of_property_read_bool(of_node,
+ "qcom,mdss-dsi-lp11-init");
return 0;
}
@@ -2761,6 +2776,9 @@
esd_config->groups * status_len);
}
+ esd_config->cmd_channel = of_property_read_bool(of_node,
+ "qcom,mdss-dsi-panel-cmds-only-by-right");
+
return 0;
error4:
@@ -3499,6 +3517,7 @@
set->cmds[0].msg.tx_buf = caset;
set->cmds[0].msg.rx_len = 0;
set->cmds[0].msg.rx_buf = 0;
+ set->cmds[0].msg.wait_ms = 0;
set->cmds[0].last_command = 0;
set->cmds[0].post_wait_ms = 0;
@@ -3510,6 +3529,7 @@
set->cmds[1].msg.tx_buf = paset;
set->cmds[1].msg.rx_len = 0;
set->cmds[1].msg.rx_buf = 0;
+ set->cmds[1].msg.wait_ms = 0;
set->cmds[1].last_command = 1;
set->cmds[1].post_wait_ms = 0;
@@ -3734,14 +3754,6 @@
goto error;
}
- if (panel->lp11_init) {
- rc = dsi_panel_power_off(panel);
- if (rc) {
- pr_err("[%s] panel power_Off failed, rc=%d\n",
- panel->name, rc);
- goto error;
- }
- }
error:
mutex_unlock(&panel->panel_lock);
return rc;
@@ -3761,13 +3773,11 @@
mutex_lock(&panel->panel_lock);
- if (!panel->lp11_init) {
- rc = dsi_panel_power_off(panel);
- if (rc) {
- pr_err("[%s] panel power_Off failed, rc=%d\n",
- panel->name, rc);
- goto error;
- }
+ rc = dsi_panel_power_off(panel);
+ if (rc) {
+ pr_err("[%s] panel power_Off failed, rc=%d\n",
+ panel->name, rc);
+ goto error;
}
error:
mutex_unlock(&panel->panel_lock);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index f8b65ab..c0ecb7f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -129,6 +129,7 @@
struct drm_panel_esd_config {
bool esd_enabled;
+ bool cmd_channel;
enum esd_check_status_mode status_mode;
struct dsi_panel_cmd_set status_cmd;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 6a7a84c..3d6711f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -107,7 +107,8 @@
phy->hw.base = ptr;
- pr_debug("[%s] map dsi_phy registers to %p\n", phy->name, phy->hw.base);
+ pr_debug("[%s] map dsi_phy registers to %pK\n",
+ phy->name, phy->hw.base);
return rc;
}
@@ -564,6 +565,8 @@
snprintf(dbg_name, DSI_DEBUG_NAME_LEN, "dsi%d_phy", dsi_phy->index);
sde_dbg_reg_register_base(dbg_name, dsi_phy->hw.base,
msm_iomap_size(dsi_phy->pdev, "dsi_phy"));
+ sde_dbg_reg_register_dump_range(dbg_name, dbg_name, 0,
+ msm_iomap_size(dsi_phy->pdev, "dsi_phy"), 0);
return 0;
}
@@ -902,6 +905,26 @@
}
/**
+ * dsi_phy_set_clamp_state() - configure clamps for DSI lanes
+ * @phy: DSI PHY handle.
+ * @enable: boolean to specify clamp enable/disable.
+ *
+ * Return: error code.
+ */
+int dsi_phy_set_clamp_state(struct msm_dsi_phy *phy, bool enable)
+{
+ if (!phy)
+ return -EINVAL;
+
+ pr_debug("[%s] enable=%d\n", phy->name, enable);
+
+ if (phy->hw.ops.clamp_ctrl)
+ phy->hw.ops.clamp_ctrl(&phy->hw, enable);
+
+ return 0;
+}
+
+/**
* dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
* @phy: DSI PHY handle
* @enable: boolean to specify PHY enable/disable.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index 56d5ee3..4163411 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -218,6 +218,15 @@
int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable);
/**
+ * dsi_phy_set_clamp_state() - configure clamps for DSI lanes
+ * @phy: DSI PHY handle.
+ * @enable: boolean to specify clamp enable/disable.
+ *
+ * Return: error code.
+ */
+int dsi_phy_set_clamp_state(struct msm_dsi_phy *phy, bool enable);
+
+/**
* dsi_phy_set_clk_freq() - set DSI PHY clock frequency setting
* @phy: DSI PHY handle
* @clk_freq: link clock frequency
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
index e31899d4..d24a613 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -234,6 +234,14 @@
u32 *timing, u32 size);
/**
+ * clamp_ctrl() - configure clamps for DSI lanes
+ * @phy: DSI PHY handle.
+ * @enable: boolean to specify clamp enable/disable.
+ * Return: error code.
+ */
+ void (*clamp_ctrl)(struct dsi_phy_hw *phy, bool enable);
+
+ /**
* phy_lane_reset() - Reset dsi phy lanes in case of error.
* @phy: Pointer to DSI PHY hardware object.
* Return: error code.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
index b078231..5015806 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
@@ -196,10 +196,31 @@
DSI_W32(phy, DSIPHY_LNX_OFFSET_BOT_CTRL(i), 0x0);
DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]);
}
+}
- /* Toggle BIT 0 to release freeze I/0 */
- DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(3), 0x05);
- DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(3), 0x04);
+void dsi_phy_hw_v3_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable)
+{
+ u32 reg;
+
+ pr_debug("enable=%s\n", enable ? "true" : "false");
+
+ /*
+ * DSI PHY lane clamps, also referred to as PHY FreezeIO is
+ * enalbed by default as part of the initialization sequnce.
+ * This would get triggered anytime the chip FreezeIO is asserted.
+ */
+ if (enable)
+ return;
+
+ /*
+ * Toggle BIT 0 to exlplictly release PHY freeze I/0 to disable
+ * the clamps.
+ */
+ reg = DSI_R32(phy, DSIPHY_LNX_TX_DCTRL(3));
+ DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(3), reg | BIT(0));
+ wmb(); /* Ensure that the freezeio bit is toggled */
+ DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(3), reg & ~BIT(0));
+ wmb(); /* Ensure that the freezeio bit is toggled */
}
/**
diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c
index 0940e84..2c9d116 100644
--- a/drivers/gpu/drm/msm/edp/edp.c
+++ b/drivers/gpu/drm/msm/edp/edp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015,2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -54,7 +54,7 @@
ret = -ENOMEM;
goto fail;
}
- DBG("eDP probed=%p", edp);
+ DBG("eDP probed=%pK", edp);
edp->pdev = pdev;
platform_set_drvdata(pdev, edp);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index 95bdc36..e445098 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2014 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -25,6 +25,8 @@
#include "msm_fence.h"
#include "sde_trace.h"
+#define MULTIPLE_CONN_DETECTED(x) (x > 1)
+
struct msm_commit {
struct drm_device *dev;
struct drm_atomic_state *state;
@@ -111,6 +113,66 @@
kfree(c);
}
+static inline bool _msm_seamless_for_crtc(struct drm_atomic_state *state,
+ struct drm_crtc_state *crtc_state, bool enable)
+{
+ struct drm_connector *connector = NULL;
+ struct drm_connector_state *conn_state = NULL;
+ int i = 0;
+ int conn_cnt = 0;
+
+ if (msm_is_mode_seamless(&crtc_state->mode) ||
+ msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode))
+ return true;
+
+ if (msm_is_mode_seamless_dms(&crtc_state->adjusted_mode) && !enable)
+ return true;
+
+ if (!crtc_state->mode_changed && crtc_state->connectors_changed) {
+ for_each_connector_in_state(state, connector, conn_state, i) {
+ if ((conn_state->crtc == crtc_state->crtc) ||
+ (connector->state->crtc ==
+ crtc_state->crtc))
+ conn_cnt++;
+
+ if (MULTIPLE_CONN_DETECTED(conn_cnt))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static inline bool _msm_seamless_for_conn(struct drm_connector *connector,
+ struct drm_connector_state *old_conn_state, bool enable)
+{
+ if (!old_conn_state || !old_conn_state->crtc)
+ return false;
+
+ if (!old_conn_state->crtc->state->mode_changed &&
+ !old_conn_state->crtc->state->active_changed &&
+ old_conn_state->crtc->state->connectors_changed) {
+ if (old_conn_state->crtc == connector->state->crtc)
+ return true;
+ }
+
+ if (enable)
+ return false;
+
+ if (msm_is_mode_seamless(&connector->encoder->crtc->state->mode))
+ return true;
+
+ if (msm_is_mode_seamless_vrr(
+ &connector->encoder->crtc->state->adjusted_mode))
+ return true;
+
+ if (msm_is_mode_seamless_dms(
+ &connector->encoder->crtc->state->adjusted_mode))
+ return true;
+
+ return false;
+}
+
static void msm_atomic_wait_for_commit_done(
struct drm_device *dev,
struct drm_atomic_state *old_state)
@@ -174,14 +236,7 @@
if (WARN_ON(!encoder))
continue;
- if (msm_is_mode_seamless(
- &connector->encoder->crtc->state->mode) ||
- msm_is_mode_seamless_vrr(
- &connector->encoder->crtc->state->adjusted_mode))
- continue;
-
- if (msm_is_mode_seamless_dms(
- &connector->encoder->crtc->state->adjusted_mode))
+ if (_msm_seamless_for_conn(connector, old_conn_state, false))
continue;
funcs = encoder->helper_private;
@@ -223,11 +278,7 @@
if (!old_crtc_state->active)
continue;
- if (msm_is_mode_seamless(&crtc->state->mode) ||
- msm_is_mode_seamless_vrr(&crtc->state->adjusted_mode))
- continue;
-
- if (msm_is_mode_seamless_dms(&crtc->state->adjusted_mode))
+ if (_msm_seamless_for_crtc(old_state, crtc->state, false))
continue;
funcs = crtc->helper_private;
@@ -286,8 +337,14 @@
mode = &new_crtc_state->mode;
adjusted_mode = &new_crtc_state->adjusted_mode;
- if (!new_crtc_state->mode_changed)
+ if (!new_crtc_state->mode_changed &&
+ new_crtc_state->connectors_changed) {
+ if (_msm_seamless_for_conn(connector,
+ old_conn_state, false))
+ continue;
+ } else if (!new_crtc_state->mode_changed) {
continue;
+ }
DRM_DEBUG_ATOMIC("modeset on [ENCODER:%d:%s]\n",
encoder->base.id, encoder->name);
@@ -365,8 +422,7 @@
if (!crtc->state->active)
continue;
- if (msm_is_mode_seamless(&crtc->state->mode) ||
- msm_is_mode_seamless_vrr(&crtc->state->adjusted_mode))
+ if (_msm_seamless_for_crtc(old_state, crtc->state, true))
continue;
funcs = crtc->helper_private;
@@ -397,18 +453,24 @@
connector->state->crtc->state))
continue;
+ if (_msm_seamless_for_conn(connector, old_conn_state, true))
+ continue;
+
encoder = connector->state->best_encoder;
funcs = encoder->helper_private;
DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n",
encoder->base.id, encoder->name);
- blank = MSM_DRM_BLANK_UNBLANK;
- notifier_data.data = ␣
- notifier_data.id =
- connector->state->crtc->index;
- msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK,
+ if (connector->state->crtc->state->active_changed) {
+ blank = MSM_DRM_BLANK_UNBLANK;
+ notifier_data.data = ␣
+ notifier_data.id =
+ connector->state->crtc->index;
+ DRM_DEBUG_ATOMIC("Notify early unblank\n");
+ msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK,
¬ifier_data);
+ }
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call enable hooks twice.
@@ -444,14 +506,20 @@
connector->state->crtc->state))
continue;
+ if (_msm_seamless_for_conn(connector, old_conn_state, true))
+ continue;
+
encoder = connector->state->best_encoder;
DRM_DEBUG_ATOMIC("bridge enable enabling [ENCODER:%d:%s]\n",
encoder->base.id, encoder->name);
drm_bridge_enable(encoder->bridge);
- msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK,
+ if (connector->state->crtc->state->active_changed) {
+ DRM_DEBUG_ATOMIC("Notify unblank\n");
+ msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK,
¬ifier_data);
+ }
}
SDE_ATRACE_END("msm_enable");
}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 19df8be..9eb62fe 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -156,7 +156,8 @@
}
if (reglog)
- printk(KERN_DEBUG "IO:region %s %p %08lx\n", dbgname, ptr, size);
+ dev_dbg(&pdev->dev, "IO:region %s %pK %08lx\n",
+ dbgname, ptr, size);
return ptr;
}
@@ -187,7 +188,7 @@
void msm_writel(u32 data, void __iomem *addr)
{
if (reglog)
- printk(KERN_DEBUG "IO:W %p %08x\n", addr, data);
+ pr_debug("IO:W %pK %08x\n", addr, data);
writel(data, addr);
}
@@ -196,7 +197,7 @@
u32 val = readl(addr);
if (reglog)
- printk(KERN_ERR "IO:R %p %08x\n", addr, val);
+ pr_err("IO:R %pK %08x\n", addr, val);
return val;
}
@@ -947,6 +948,14 @@
struct msm_kms *kms = priv->kms;
int i;
+ /* check for splash status before triggering cleanup
+ * if we end up here with splash status ON i.e before first
+ * commit then ignore the last close call
+ */
+ if (kms && kms->funcs && kms->funcs->check_for_splash
+ && kms->funcs->check_for_splash(kms))
+ return;
+
/*
* clean up vblank disable immediately as this is the last close.
*/
@@ -1016,7 +1025,7 @@
if (!kms)
return -ENXIO;
- DBG("dev=%p, crtc=%u", dev, pipe);
+ DBG("dev=%pK, crtc=%u", dev, pipe);
return vblank_ctrl_queue_work(priv, pipe, true);
}
@@ -1027,7 +1036,7 @@
if (!kms)
return;
- DBG("dev=%p, crtc=%u", dev, pipe);
+ DBG("dev=%pK, crtc=%u", dev, pipe);
vblank_ctrl_queue_work(priv, pipe, false);
}
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index ce4197b..fcdddb3 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -155,6 +155,7 @@
CRTC_PROP_SECURITY_LEVEL,
CRTC_PROP_IDLE_TIMEOUT,
CRTC_PROP_DEST_SCALER,
+ CRTC_PROP_CAPTURE_OUTPUT,
CRTC_PROP_ENABLE_SUI_ENHANCEMENT,
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
index e8bf244..a1c9d82 100644
--- a/drivers/gpu/drm/msm/msm_fb.c
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -68,7 +68,7 @@
msm_fb = to_msm_framebuffer(fb);
n = drm_format_num_planes(fb->pixel_format);
- DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
+ DBG("destroy: FB ID: %d (%pK)", fb->base.id, fb);
drm_framebuffer_cleanup(fb);
@@ -336,7 +336,7 @@
unsigned int hsub, vsub;
bool is_modified = false;
- DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
+ DBG("create framebuffer: dev=%pK, mode_cmd=%pK (%dx%d@%4.4s)",
dev, mode_cmd, mode_cmd->width, mode_cmd->height,
(char *)&mode_cmd->pixel_format);
@@ -420,7 +420,7 @@
goto fail;
}
- DBG("create: FB ID: %d (%p)", fb->base.id, fb);
+ DBG("create: FB ID: %d (%pK)", fb->base.id, fb);
return fb;
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index ffd4a33..5b886d0 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -142,7 +142,7 @@
goto fail_unlock;
}
- DBG("fbi=%p, dev=%p", fbi, dev);
+ DBG("fbi=%pK, dev=%pK", fbi, dev);
fbdev->fb = fb;
helper->fb = fb;
@@ -167,7 +167,7 @@
fbi->fix.smem_start = paddr;
fbi->fix.smem_len = fbdev->bo->size;
- DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
+ DBG("par=%pK, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index d9aad88..ddd4607 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -96,13 +96,16 @@
return p;
}
+ msm_obj->pages = p;
+
msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
if (IS_ERR(msm_obj->sgt)) {
- dev_err(dev->dev, "failed to allocate sgt\n");
- return ERR_CAST(msm_obj->sgt);
- }
+ void *ptr = ERR_CAST(msm_obj->sgt);
- msm_obj->pages = p;
+ dev_err(dev->dev, "failed to allocate sgt\n");
+ msm_obj->sgt = NULL;
+ return ptr;
+ }
/*
* Make sure to flush the CPU cache for newly allocated memory
@@ -121,7 +124,9 @@
struct msm_gem_object *msm_obj = to_msm_bo(obj);
if (msm_obj->pages) {
- sg_free_table(msm_obj->sgt);
+ if (msm_obj->sgt)
+ sg_free_table(msm_obj->sgt);
+
kfree(msm_obj->sgt);
if (use_pages(obj))
@@ -248,7 +253,7 @@
pfn = page_to_pfn(pages[pgoff]);
- VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
+ VERB("Inserting %pK pfn %lx, pa %lx", vmf->virtual_address,
pfn, pfn << PAGE_SHIFT);
ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address,
@@ -423,7 +428,7 @@
if (!ret && domain) {
*iova = domain->iova;
- if (aspace && aspace->domain_attached)
+ if (aspace && !msm_obj->in_active_list)
msm_gem_add_obj_to_aspace_active_list(aspace, obj);
} else {
obj_remove_domain(domain);
@@ -794,7 +799,7 @@
break;
}
- seq_printf(m, "%08x: %c %2d (%2d) %08llx %p %zu%s\n",
+ seq_printf(m, "%08x: %c %2d (%2d) %08llx %pK %zu%s\n",
msm_obj->flags, is_active(msm_obj) ? 'A' : 'I',
obj->name, obj->refcount.refcount.counter,
@@ -963,6 +968,7 @@
INIT_LIST_HEAD(&msm_obj->domains);
INIT_LIST_HEAD(&msm_obj->iova_list);
msm_obj->aspace = NULL;
+ msm_obj->in_active_list = false;
list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 8521bea..ba01ffb 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -124,6 +124,7 @@
struct list_head iova_list;
struct msm_gem_address_space *aspace;
+ bool in_active_list;
};
#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index d02228a..e5b1cc37 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -71,6 +71,7 @@
{
WARN_ON(!mutex_is_locked(&aspace->dev->struct_mutex));
list_move_tail(&msm_obj->iova_list, &aspace->active_list);
+ msm_obj->in_active_list = true;
}
static void smmu_aspace_remove_from_active(
@@ -84,6 +85,7 @@
list_for_each_entry_safe(msm_obj, next, &aspace->active_list,
iova_list) {
if (msm_obj == obj) {
+ msm_obj->in_active_list = false;
list_del(&msm_obj->iova_list);
break;
}
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index db9e7ee..e99ff9c 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -106,6 +106,8 @@
unsigned int domain);
/* handle continuous splash */
int (*cont_splash_config)(struct msm_kms *kms);
+ /* check for continuous splash status */
+ bool (*check_for_splash)(struct msm_kms *kms);
};
struct msm_kms {
diff --git a/drivers/gpu/drm/msm/sde/sde_ad4.h b/drivers/gpu/drm/msm/sde/sde_ad4.h
index bf08360..b254d7d 100644
--- a/drivers/gpu/drm/msm/sde/sde_ad4.h
+++ b/drivers/gpu/drm/msm/sde/sde_ad4.h
@@ -52,6 +52,7 @@
AD_IPC_SUSPEND,
AD_IPC_RESUME,
AD_IPC_RESET,
+ AD_VSYNC_UPDATE,
AD_PROPMAX,
};
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 0f55b19..47ff024 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -88,6 +88,7 @@
enum ad_property ad_prop);
static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg);
+static void sde_cp_update_ad_vsync_prop(struct sde_crtc *sde_crtc, u32 val);
#define setup_dspp_prop_install_funcs(func) \
do { \
@@ -138,6 +139,7 @@
SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS,
SDE_CP_CRTC_DSPP_AD_BACKLIGHT,
SDE_CP_CRTC_DSPP_AD_STRENGTH,
+ SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT,
SDE_CP_CRTC_DSPP_MAX,
/* DSPP features end */
@@ -407,6 +409,7 @@
if (IS_ERR(sde_crtc->hist_blob))
sde_crtc->hist_blob = NULL;
+ sde_crtc->ad_vsync_count = 0;
mutex_init(&sde_crtc->crtc_cp_lock);
INIT_LIST_HEAD(&sde_crtc->active_list);
INIT_LIST_HEAD(&sde_crtc->dirty_list);
@@ -789,6 +792,9 @@
ad_cfg.prop = AD_MODE;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_INIT:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -798,6 +804,9 @@
ad_cfg.prop = AD_INIT;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_CFG:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -807,6 +816,9 @@
ad_cfg.prop = AD_CFG;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_INPUT:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -816,6 +828,9 @@
ad_cfg.prop = AD_INPUT;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -825,6 +840,9 @@
ad_cfg.prop = AD_ASSERTIVE;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_BACKLIGHT:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -834,6 +852,9 @@
ad_cfg.prop = AD_BACKLIGHT;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
case SDE_CP_CRTC_DSPP_AD_STRENGTH:
if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -843,6 +864,9 @@
ad_cfg.prop = AD_STRENGTH;
ad_cfg.hw_cfg = &hw_cfg;
hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+ sde_crtc->ad_vsync_count = 0;
+ sde_cp_update_ad_vsync_prop(sde_crtc,
+ sde_crtc->ad_vsync_count);
break;
default:
ret = -EINVAL;
@@ -924,10 +948,15 @@
DRM_DEBUG_DRIVER("Dirty list is empty\n");
goto exit;
}
- sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
set_dspp_flush = true;
}
+ if (!list_empty(&sde_crtc->ad_active)) {
+ sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
+ sde_cp_ad_set_prop(sde_crtc, AD_VSYNC_UPDATE);
+ sde_cp_update_ad_vsync_prop(sde_crtc, sde_crtc->ad_vsync_count);
+ }
+
list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list,
dirty_list) {
sde_dspp_feature = crtc_feature_map[prop_node->feature];
@@ -1449,6 +1478,9 @@
"SDE_DSPP_AD_V4_BACKLIGHT",
SDE_CP_CRTC_DSPP_AD_BACKLIGHT, 0, (BIT(16) - 1),
0);
+ sde_cp_crtc_install_range_property(crtc,
+ "SDE_DSPP_AD_V4_VSYNC_COUNT",
+ SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT, 0, U32_MAX, 0);
break;
default:
DRM_ERROR("version %d not supported\n", version);
@@ -1867,6 +1899,11 @@
hw_cfg.displayh = num_mixers * hw_lm->cfg.out_width;
hw_cfg.displayv = hw_lm->cfg.out_height;
hw_cfg.mixer_info = hw_lm;
+
+ if (ad_prop == AD_VSYNC_UPDATE) {
+ hw_cfg.payload = &sde_crtc->ad_vsync_count;
+ hw_cfg.len = sizeof(sde_crtc->ad_vsync_count);
+ }
ad_cfg.prop = ad_prop;
ad_cfg.hw_cfg = &hw_cfg;
ret = hw_dspp->ops.validate_ad(hw_dspp, (u32 *)&ad_prop);
@@ -2118,3 +2155,35 @@
exit:
return ret;
}
+
+void sde_cp_update_ad_vsync_count(struct drm_crtc *crtc, u32 val)
+{
+ struct sde_crtc *sde_crtc;
+
+ if (!crtc) {
+ DRM_ERROR("invalid crtc %pK\n", crtc);
+ return;
+ }
+
+ sde_crtc = to_sde_crtc(crtc);
+ if (!sde_crtc) {
+ DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+ return;
+ }
+
+ sde_crtc->ad_vsync_count = val;
+ sde_cp_update_ad_vsync_prop(sde_crtc, val);
+}
+
+static void sde_cp_update_ad_vsync_prop(struct sde_crtc *sde_crtc, u32 val)
+{
+ struct sde_cp_node *prop_node = NULL;
+
+ list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
+ if (prop_node->feature == SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT) {
+ prop_node->prop_val = val;
+ pr_debug("AD vsync count updated to %d\n", val);
+ return;
+ }
+ }
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index 7eb1738..620db26 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -146,4 +146,11 @@
*/
int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
struct sde_irq_callback *hist_irq);
+
+/**
+ * sde_cp_update_ad_vsync_count: Api to update AD vsync count
+ * @crtc: Pointer to crtc.
+ * @val: vsync count value
+ */
+void sde_cp_update_ad_vsync_count(struct drm_crtc *crtc, u32 val);
#endif /*_SDE_COLOR_PROCESSING_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d2f8d12..ab1d86b 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -623,6 +623,7 @@
void sde_connector_helper_bridge_disable(struct drm_connector *connector)
{
int rc;
+ struct sde_connector *c_conn = NULL;
if (!connector)
return;
@@ -633,6 +634,34 @@
connector->base.id, rc);
SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR);
}
+
+ /* Disable ESD thread */
+ sde_connector_schedule_status_work(connector, false);
+
+ c_conn = to_sde_connector(connector);
+ if (c_conn->panel_dead) {
+ c_conn->bl_device->props.power = FB_BLANK_POWERDOWN;
+ c_conn->bl_device->props.state |= BL_CORE_FBBLANK;
+ backlight_update_status(c_conn->bl_device);
+ }
+}
+
+void sde_connector_helper_bridge_enable(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn = NULL;
+
+ if (!connector)
+ return;
+
+ c_conn = to_sde_connector(connector);
+
+ /* Special handling for ESD recovery case */
+ if (c_conn->panel_dead) {
+ c_conn->bl_device->props.power = FB_BLANK_UNBLANK;
+ c_conn->bl_device->props.state &= ~BL_CORE_FBBLANK;
+ backlight_update_status(c_conn->bl_device);
+ c_conn->panel_dead = false;
+ }
}
int sde_connector_clk_ctrl(struct drm_connector *connector, bool enable)
@@ -1207,7 +1236,7 @@
}
void sde_connector_complete_commit(struct drm_connector *connector,
- ktime_t ts)
+ ktime_t ts, enum sde_fence_event fence_event)
{
if (!connector) {
SDE_ERROR("invalid connector\n");
@@ -1215,7 +1244,8 @@
}
/* signal connector's retire fence */
- sde_fence_signal(&to_sde_connector(connector)->retire_fence, ts, false);
+ sde_fence_signal(&to_sde_connector(connector)->retire_fence,
+ ts, fence_event);
}
void sde_connector_commit_reset(struct drm_connector *connector, ktime_t ts)
@@ -1226,7 +1256,8 @@
}
/* signal connector's retire fence */
- sde_fence_signal(&to_sde_connector(connector)->retire_fence, ts, true);
+ sde_fence_signal(&to_sde_connector(connector)->retire_fence,
+ ts, SDE_FENCE_RESET_TIMELINE);
}
static void sde_connector_update_hdr_props(struct drm_connector *connector)
@@ -1734,15 +1765,23 @@
static void _sde_connector_report_panel_dead(struct sde_connector *conn)
{
struct drm_event event;
- bool panel_dead = true;
if (!conn)
return;
+ /* Panel dead notification can come:
+ * 1) ESD thread
+ * 2) Commit thread (if TE stops coming)
+ * So such case, avoid failure notification twice.
+ */
+ if (conn->panel_dead)
+ return;
+
+ conn->panel_dead = true;
event.type = DRM_EVENT_PANEL_DEAD;
event.length = sizeof(bool);
msm_mode_object_event_notify(&conn->base.base,
- conn->base.dev, &event, (u8 *)&panel_dead);
+ conn->base.dev, &event, (u8 *)&conn->panel_dead);
sde_encoder_display_failure_notification(conn->encoder);
SDE_EVT32(SDE_EVTLOG_ERROR);
SDE_ERROR("esd check failed report PANEL_DEAD conn_id: %d enc_id: %d\n",
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index c6f348e..51dc92d 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -324,6 +324,8 @@
* @status_work: work object to perform status checks
* @force_panel_dead: variable to trigger forced ESD recovery
* @esd_status_interval: variable to change ESD check interval in millisec
+ * @panel_dead: Flag to indicate if panel has gone bad
+ * @esd_status_check: Flag to indicate if ESD thread is scheduled or not
* @bl_scale_dirty: Flag to indicate PP BL scale value(s) is changed
* @bl_scale: BL scale value for ABA feature
* @bl_scale_ad: BL scale value for AD feature
@@ -365,7 +367,7 @@
struct delayed_work status_work;
u32 force_panel_dead;
u32 esd_status_interval;
-
+ bool panel_dead;
bool esd_status_check;
bool bl_scale_dirty;
@@ -583,8 +585,10 @@
* sde_connector_complete_commit - signal completion of current commit
* @connector: Pointer to drm connector object
* @ts: timestamp to be updated in the fence signalling
+ * @fence_event: enum value to indicate nature of fence event
*/
-void sde_connector_complete_commit(struct drm_connector *connector, ktime_t ts);
+void sde_connector_complete_commit(struct drm_connector *connector,
+ ktime_t ts, enum sde_fence_event fence_event);
/**
* sde_connector_commit_reset - reset the completion signal
@@ -763,6 +767,12 @@
void sde_connector_helper_bridge_disable(struct drm_connector *connector);
/**
+ * sde_connector_helper_bridge_enable - helper function for drm bridge enable
+ * @connector: Pointer to DRM connector object
+ */
+void sde_connector_helper_bridge_enable(struct drm_connector *connector);
+
+/**
* sde_connector_get_panel_vfp - helper to get panel vfp
* @connector: pointer to drm connector
* @h_active: panel width
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index ce2997d..34a3d5f 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -79,6 +79,12 @@
#define MISR_BUFF_SIZE 256
+/*
+ * Time period for fps calculation in micro seconds.
+ * Default value is set to 1 sec.
+ */
+#define CRTC_TIME_PERIOD_CALC_FPS_US 1000000
+
static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
struct msm_drm_private *priv;
@@ -125,6 +131,37 @@
enable);
}
+/*
+ * sde_crtc_calc_fps() - Calculates fps value.
+ * @sde_crtc : CRTC structure
+ *
+ * This function is called at frame done. It counts the number
+ * of frames done for every 1 sec. Stores the value in measured_fps.
+ * measured_fps value is 10 times the calculated fps value.
+ * For example, measured_fps= 594 for calculated fps of 59.4
+ */
+static void sde_crtc_calc_fps(struct sde_crtc *sde_crtc)
+{
+ ktime_t current_time_us;
+ u64 fps, diff_us;
+
+ current_time_us = ktime_get();
+ diff_us = (u64)ktime_us_delta(current_time_us,
+ sde_crtc->fps_info.last_sampled_time_us);
+ sde_crtc->fps_info.frame_count++;
+
+ if (diff_us >= CRTC_TIME_PERIOD_CALC_FPS_US) {
+ fps = ((u64)sde_crtc->fps_info.frame_count) * 10000000;
+ do_div(fps, diff_us);
+ sde_crtc->fps_info.measured_fps = (unsigned int)fps;
+ SDE_DEBUG(" FPS for crtc%d is %d.%d\n",
+ sde_crtc->base.base.id, (unsigned int)fps/10,
+ (unsigned int)fps%10);
+ sde_crtc->fps_info.last_sampled_time_us = current_time_us;
+ sde_crtc->fps_info.frame_count = 0;
+ }
+}
+
/**
* _sde_crtc_rp_to_crtc - get crtc from resource pool object
* @rp: Pointer to resource pool
@@ -601,6 +638,50 @@
return;
}
+static int _sde_debugfs_fps_status_show(struct seq_file *s, void *data)
+{
+ struct sde_crtc *sde_crtc;
+ unsigned int fps_int, fps_float;
+ ktime_t current_time_us;
+ u64 fps, diff_us;
+
+ if (!s || !s->private) {
+ SDE_ERROR("invalid input param(s)\n");
+ return -EAGAIN;
+ }
+
+ sde_crtc = s->private;
+
+ current_time_us = ktime_get();
+ diff_us = (u64)ktime_us_delta(current_time_us,
+ sde_crtc->fps_info.last_sampled_time_us);
+
+ if (diff_us >= CRTC_TIME_PERIOD_CALC_FPS_US) {
+ fps = ((u64)sde_crtc->fps_info.frame_count) * 10000000;
+ do_div(fps, diff_us);
+ sde_crtc->fps_info.measured_fps = (unsigned int)fps;
+ sde_crtc->fps_info.last_sampled_time_us = current_time_us;
+ sde_crtc->fps_info.frame_count = 0;
+ SDE_DEBUG("Measured FPS for crtc%d is %d.%d\n",
+ sde_crtc->base.base.id, (unsigned int)fps/10,
+ (unsigned int)fps%10);
+ }
+
+ fps_int = (unsigned int) sde_crtc->fps_info.measured_fps;
+ fps_float = do_div(fps_int, 10);
+
+ seq_printf(s, "fps: %d.%d\n", fps_int, fps_float);
+
+ return 0;
+}
+
+
+static int _sde_debugfs_fps_status(struct inode *inode, struct file *file)
+{
+ return single_open(file, _sde_debugfs_fps_status_show,
+ inode->i_private);
+}
+
static ssize_t vsync_event_show(struct device *device,
struct device_attribute *attr, char *buf)
{
@@ -936,7 +1017,9 @@
struct sde_crtc *sde_crtc;
struct sde_crtc_state *crtc_state;
struct sde_rect *crtc_roi;
- int i, num_attached_conns = 0;
+ struct msm_mode_info mode_info;
+ int i = 0;
+ int rc;
bool is_crtc_roi_dirty;
bool is_any_conn_roi_dirty;
@@ -958,13 +1041,14 @@
if (!conn_state || conn_state->crtc != crtc)
continue;
- if (num_attached_conns) {
- SDE_ERROR(
- "crtc%d: unsupported: roi on crtc w/ >1 connectors\n",
- DRMID(crtc));
+ rc = sde_connector_get_mode_info(conn_state, &mode_info);
+ if (rc) {
+ SDE_ERROR("failed to get mode info\n");
return -EINVAL;
}
- ++num_attached_conns;
+
+ if (!mode_info.roi_caps.enabled)
+ continue;
sde_conn = to_sde_connector(conn_state->connector);
sde_conn_state = to_sde_connector_state(conn_state);
@@ -1285,13 +1369,6 @@
sde_crtc = to_sde_crtc(crtc);
sde_crtc_state = to_sde_crtc_state(state);
- if (hweight_long(state->connector_mask) != 1) {
- SDE_ERROR("invalid connector count(%d) for crtc: %d\n",
- (int)hweight_long(state->connector_mask),
- crtc->base.id);
- return -EINVAL;
- }
-
/*
* check connector array cached at modeset time since incoming atomic
* state may not include any connectors if they aren't modified
@@ -1307,42 +1384,41 @@
SDE_ERROR("failed to get mode info\n");
return -EINVAL;
}
- break;
- }
- if (!mode_info.roi_caps.enabled)
- return 0;
+ if (!mode_info.roi_caps.enabled)
+ continue;
- if (sde_crtc_state->user_roi_list.num_rects >
- mode_info.roi_caps.num_roi) {
- SDE_ERROR("roi count is more than supported limit, %d > %d\n",
- sde_crtc_state->user_roi_list.num_rects,
- mode_info.roi_caps.num_roi);
- return -E2BIG;
- }
+ if (sde_crtc_state->user_roi_list.num_rects >
+ mode_info.roi_caps.num_roi) {
+ SDE_ERROR("roi count is exceeding limit, %d > %d\n",
+ sde_crtc_state->user_roi_list.num_rects,
+ mode_info.roi_caps.num_roi);
+ return -E2BIG;
+ }
- rc = _sde_crtc_set_crtc_roi(crtc, state);
- if (rc)
- return rc;
+ rc = _sde_crtc_set_crtc_roi(crtc, state);
+ if (rc)
+ return rc;
- rc = _sde_crtc_check_autorefresh(crtc, state);
- if (rc)
- return rc;
+ rc = _sde_crtc_check_autorefresh(crtc, state);
+ if (rc)
+ return rc;
- for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
- rc = _sde_crtc_set_lm_roi(crtc, state, lm_idx);
+ for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
+ rc = _sde_crtc_set_lm_roi(crtc, state, lm_idx);
+ if (rc)
+ return rc;
+ }
+
+ rc = _sde_crtc_check_rois_centered_and_symmetric(crtc, state);
+ if (rc)
+ return rc;
+
+ rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state);
if (rc)
return rc;
}
- rc = _sde_crtc_check_rois_centered_and_symmetric(crtc, state);
- if (rc)
- return rc;
-
- rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state);
- if (rc)
- return rc;
-
return 0;
}
@@ -2116,15 +2192,62 @@
}
}
+static void sde_crtc_frame_event_cb(void *data, u32 event)
+{
+ struct drm_crtc *crtc = (struct drm_crtc *)data;
+ struct sde_crtc *sde_crtc;
+ struct msm_drm_private *priv;
+ struct sde_crtc_frame_event *fevent;
+ struct sde_crtc_frame_event_cb_data *cb_data;
+ unsigned long flags;
+ u32 crtc_id;
+
+ cb_data = (struct sde_crtc_frame_event_cb_data *)data;
+ if (!data) {
+ SDE_ERROR("invalid parameters\n");
+ return;
+ }
+
+ crtc = cb_data->crtc;
+ if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
+ SDE_ERROR("invalid parameters\n");
+ return;
+ }
+ sde_crtc = to_sde_crtc(crtc);
+ priv = crtc->dev->dev_private;
+ crtc_id = drm_crtc_index(crtc);
+
+ SDE_DEBUG("crtc%d\n", crtc->base.id);
+ SDE_EVT32_VERBOSE(DRMID(crtc), event);
+
+ spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+ fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
+ struct sde_crtc_frame_event, list);
+ if (fevent)
+ list_del_init(&fevent->list);
+ spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
+ if (!fevent) {
+ SDE_ERROR("crtc%d event %d overflow\n",
+ crtc->base.id, event);
+ SDE_EVT32(DRMID(crtc), event);
+ return;
+ }
+
+ fevent->event = event;
+ fevent->crtc = crtc;
+ fevent->connector = cb_data->connector;
+ fevent->ts = ktime_get();
+ kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
+}
+
void sde_crtc_prepare_commit(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct sde_crtc *sde_crtc;
struct sde_crtc_state *cstate;
struct drm_connector *conn;
- struct sde_crtc_retire_event *retire_event = NULL;
- unsigned long flags;
- int i;
+ struct drm_encoder *encoder;
if (!crtc || !crtc->state) {
SDE_ERROR("invalid crtc\n");
@@ -2141,31 +2264,17 @@
drm_for_each_connector(conn, crtc->dev)
if (conn->state && conn->state->crtc == crtc &&
cstate->num_connectors < MAX_CONNECTORS) {
+ encoder = conn->state->best_encoder;
+ if (encoder)
+ sde_encoder_register_frame_event_callback(
+ encoder,
+ sde_crtc_frame_event_cb,
+ crtc);
+
cstate->connectors[cstate->num_connectors++] = conn;
sde_connector_prepare_fence(conn);
}
- for (i = 0; i < SDE_CRTC_FRAME_EVENT_SIZE; i++) {
- retire_event = &sde_crtc->retire_events[i];
- if (list_empty(&retire_event->list))
- break;
- retire_event = NULL;
- }
-
- if (retire_event) {
- retire_event->num_connectors = cstate->num_connectors;
- for (i = 0; i < cstate->num_connectors; i++)
- retire_event->connectors[i] = cstate->connectors[i];
-
- spin_lock_irqsave(&sde_crtc->spin_lock, flags);
- list_add_tail(&retire_event->list,
- &sde_crtc->retire_event_list);
- spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
- } else {
- SDE_ERROR("crtc%d retire event overflow\n", crtc->base.id);
- SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
- }
-
/* prepare main output fence */
sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -2216,9 +2325,16 @@
return INTF_MODE_NONE;
}
- drm_for_each_encoder(encoder, crtc->dev)
- if (encoder->crtc == crtc)
- return sde_encoder_get_intf_mode(encoder);
+ drm_for_each_encoder(encoder, crtc->dev) {
+ if (encoder->crtc != crtc)
+ continue;
+
+ /* continue if copy encoder is encountered */
+ if (sde_encoder_in_clone_mode(encoder))
+ continue;
+
+ return sde_encoder_get_intf_mode(encoder);
+ }
return INTF_MODE_NONE;
}
@@ -2243,38 +2359,16 @@
SDE_EVT32_VERBOSE(DRMID(crtc));
}
-static void _sde_crtc_retire_event(struct drm_crtc *crtc, ktime_t ts)
+static void _sde_crtc_retire_event(struct drm_connector *connector,
+ ktime_t ts, bool is_error)
{
- struct sde_crtc_retire_event *retire_event;
- struct sde_crtc *sde_crtc;
- unsigned long flags;
- int i;
-
- if (!crtc) {
+ if (!connector) {
SDE_ERROR("invalid param\n");
return;
}
- sde_crtc = to_sde_crtc(crtc);
- spin_lock_irqsave(&sde_crtc->spin_lock, flags);
- retire_event = list_first_entry_or_null(&sde_crtc->retire_event_list,
- struct sde_crtc_retire_event, list);
- if (retire_event)
- list_del_init(&retire_event->list);
- spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
-
- if (!retire_event) {
- SDE_ERROR("crtc%d retire event without kickoff\n",
- crtc->base.id);
- SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
- return;
- }
-
SDE_ATRACE_BEGIN("signal_retire_fence");
- for (i = 0; (i < retire_event->num_connectors) &&
- retire_event->connectors[i]; ++i)
- sde_connector_complete_commit(
- retire_event->connectors[i], ts);
+ sde_connector_complete_commit(connector, ts, is_error);
SDE_ATRACE_END("signal_retire_fence");
}
@@ -2286,7 +2380,7 @@
struct sde_crtc *sde_crtc;
struct sde_kms *sde_kms;
unsigned long flags;
- bool frame_done = false;
+ bool in_clone_mode = false;
if (!work) {
SDE_ERROR("invalid work handle\n");
@@ -2315,10 +2409,11 @@
SDE_EVT32_VERBOSE(DRMID(crtc), fevent->event, SDE_EVTLOG_FUNC_ENTRY);
- if (fevent->event & (SDE_ENCODER_FRAME_EVENT_DONE
- | SDE_ENCODER_FRAME_EVENT_ERROR
- | SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
+ in_clone_mode = sde_encoder_in_clone_mode(fevent->connector->encoder);
+ if (!in_clone_mode && (fevent->event & (SDE_ENCODER_FRAME_EVENT_ERROR
+ | SDE_ENCODER_FRAME_EVENT_PANEL_DEAD
+ | SDE_ENCODER_FRAME_EVENT_DONE))) {
if (atomic_read(&sde_crtc->frame_pending) < 1) {
/* this should not happen */
SDE_ERROR("crtc%d ts:%lld invalid frame_pending:%d\n",
@@ -2339,75 +2434,32 @@
SDE_EVT32_VERBOSE(DRMID(crtc), fevent->event,
SDE_EVTLOG_FUNC_CASE3);
}
-
- if (fevent->event & (SDE_ENCODER_FRAME_EVENT_DONE
- | SDE_ENCODER_FRAME_EVENT_ERROR))
- frame_done = true;
}
if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE) {
SDE_ATRACE_BEGIN("signal_release_fence");
- sde_fence_signal(&sde_crtc->output_fence, fevent->ts, false);
+ sde_fence_signal(&sde_crtc->output_fence, fevent->ts,
+ (fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR)
+ ? SDE_FENCE_SIGNAL_ERROR : SDE_FENCE_SIGNAL);
SDE_ATRACE_END("signal_release_fence");
}
if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE)
/* this api should be called without spin_lock */
- _sde_crtc_retire_event(crtc, fevent->ts);
+ _sde_crtc_retire_event(fevent->connector, fevent->ts,
+ (fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR)
+ ? SDE_FENCE_SIGNAL_ERROR : SDE_FENCE_SIGNAL);
if (fevent->event & SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)
SDE_ERROR("crtc%d ts:%lld received panel dead event\n",
crtc->base.id, ktime_to_ns(fevent->ts));
- if (frame_done)
- complete_all(&sde_crtc->frame_done_comp);
-
spin_lock_irqsave(&sde_crtc->spin_lock, flags);
list_add_tail(&fevent->list, &sde_crtc->frame_event_list);
spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
SDE_ATRACE_END("crtc_frame_event");
}
-static void sde_crtc_frame_event_cb(void *data, u32 event)
-{
- struct drm_crtc *crtc = (struct drm_crtc *)data;
- struct sde_crtc *sde_crtc;
- struct msm_drm_private *priv;
- struct sde_crtc_frame_event *fevent;
- unsigned long flags;
- u32 crtc_id;
-
- if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
- SDE_ERROR("invalid parameters\n");
- return;
- }
- sde_crtc = to_sde_crtc(crtc);
- priv = crtc->dev->dev_private;
- crtc_id = drm_crtc_index(crtc);
-
- SDE_DEBUG("crtc%d\n", crtc->base.id);
- SDE_EVT32_VERBOSE(DRMID(crtc), event);
-
- spin_lock_irqsave(&sde_crtc->spin_lock, flags);
- fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
- struct sde_crtc_frame_event, list);
- if (fevent)
- list_del_init(&fevent->list);
- spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
-
- if (!fevent) {
- SDE_ERROR("crtc%d event %d overflow\n",
- crtc->base.id, event);
- SDE_EVT32(DRMID(crtc), event);
- return;
- }
-
- fevent->event = event;
- fevent->crtc = crtc;
- fevent->ts = ktime_get();
- kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
-}
-
void sde_crtc_complete_commit(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
@@ -2440,6 +2492,23 @@
}
/**
+ * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings
+ * @cstate: Pointer to sde crtc state
+ */
+static void _sde_crtc_clear_dim_layers_v1(struct sde_crtc_state *cstate)
+{
+ u32 i;
+
+ if (!cstate)
+ return;
+
+ for (i = 0; i < cstate->num_dim_layers; i++)
+ memset(&cstate->dim_layer[i], 0, sizeof(cstate->dim_layer[i]));
+
+ cstate->num_dim_layers = 0;
+}
+
+/**
* _sde_crtc_set_dim_layer_v1 - copy dim layer settings from userspace
* @cstate: Pointer to sde crtc state
* @user_ptr: User ptr for sde_drm_dim_layer_v1 struct
@@ -2459,6 +2528,8 @@
dim_layer = cstate->dim_layer;
if (!usr_ptr) {
+ /* usr_ptr is null when setting the default property value */
+ _sde_crtc_clear_dim_layers_v1(cstate);
SDE_DEBUG("dim_layer data removed\n");
return;
}
@@ -2958,6 +3029,10 @@
if (enc->crtc != crtc)
continue;
+ /* avoid overwriting mixers info from a copy encoder */
+ if (sde_encoder_in_clone_mode(enc))
+ continue;
+
_sde_crtc_setup_mixer_for_encoder(crtc, enc);
}
@@ -3099,7 +3174,7 @@
* apply color processing properties only if
* smmu state is attached,
*/
- if (!sde_kms_is_secure_session_inprogress(sde_kms))
+ if (!sde_kms_is_secure_session_inprogress(sde_kms) && sde_crtc->enabled)
sde_cp_crtc_apply_properties(crtc);
/*
@@ -3258,10 +3333,10 @@
&cstate->property_state);
}
-static int _sde_crtc_wait_for_frame_done(struct drm_crtc *crtc)
+static int _sde_crtc_flush_event_thread(struct drm_crtc *crtc)
{
struct sde_crtc *sde_crtc;
- int ret, rc = 0, i;
+ int i;
if (!crtc) {
SDE_ERROR("invalid argument\n");
@@ -3285,17 +3360,9 @@
kthread_flush_work(&sde_crtc->frame_events[i].work);
}
- ret = wait_for_completion_timeout(&sde_crtc->frame_done_comp,
- msecs_to_jiffies(SDE_FRAME_DONE_TIMEOUT));
- if (!ret) {
- SDE_ERROR("frame done completion wait timed out, ret:%d\n",
- ret);
- SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FATAL);
- rc = -ETIMEDOUT;
- }
SDE_EVT32_VERBOSE(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);
- return rc;
+ return 0;
}
static int _sde_crtc_commit_kickoff_rot(struct drm_crtc *crtc,
@@ -3622,7 +3689,6 @@
struct sde_kms *sde_kms;
struct sde_crtc_state *cstate;
bool is_error, reset_req;
- int ret;
if (!crtc) {
SDE_ERROR("invalid argument\n");
@@ -3681,20 +3747,10 @@
}
sde_crtc->reset_request = reset_req;
- /* wait for frame_event_done completion */
- SDE_ATRACE_BEGIN("wait_for_frame_done_event");
- ret = _sde_crtc_wait_for_frame_done(crtc);
- SDE_ATRACE_END("wait_for_frame_done_event");
- if (ret) {
- SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
- crtc->base.id,
- atomic_read(&sde_crtc->frame_pending));
-
- is_error = true;
-
- /* force offline rotation mode since the commit has no pipes */
- cstate->sbuf_cfg.rot_op_mode = SDE_CTL_ROT_OP_MODE_OFFLINE;
- }
+ SDE_ATRACE_BEGIN("flush_event_thread");
+ _sde_crtc_flush_event_thread(crtc);
+ SDE_ATRACE_END("flush_event_thread");
+ sde_crtc_calc_fps(sde_crtc);
if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
/* acquire bandwidth and other resources */
@@ -3733,7 +3789,6 @@
sde_encoder_kickoff(encoder, false);
}
- reinit_completion(&sde_crtc->frame_done_comp);
SDE_ATRACE_END("crtc_commit");
return;
}
@@ -4125,6 +4180,7 @@
event.type = DRM_EVENT_CRTC_POWER;
event.length = sizeof(u32);
sde_cp_crtc_suspend(crtc);
+ sde_cp_update_ad_vsync_count(crtc, 0);
power_on = 0;
msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
(u8 *)&power_on);
@@ -4133,11 +4189,7 @@
if (cstate->num_ds_enabled)
sde_crtc->ds_reconfig = true;
- /* wait for frame_event_done completion */
- if (_sde_crtc_wait_for_frame_done(crtc))
- SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
- crtc->base.id,
- atomic_read(&sde_crtc->frame_pending));
+ _sde_crtc_flush_event_thread(crtc);
SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
sde_crtc->vblank_requested,
@@ -4152,6 +4204,8 @@
sde_crtc->enabled = false;
if (atomic_read(&sde_crtc->frame_pending)) {
+ SDE_ERROR("crtc%d frame_pending%d\n", crtc->base.id,
+ atomic_read(&sde_crtc->frame_pending));
SDE_EVT32(DRMID(crtc), atomic_read(&sde_crtc->frame_pending),
SDE_EVTLOG_FUNC_CASE2);
sde_core_perf_crtc_release_bw(crtc);
@@ -4191,7 +4245,8 @@
* reset the fence timeline if crtc will not be enabled for this commit
*/
if (!crtc->state->active || !crtc->state->enable) {
- sde_fence_signal(&sde_crtc->output_fence, ktime_get(), true);
+ sde_fence_signal(&sde_crtc->output_fence,
+ ktime_get(), SDE_FENCE_RESET_TIMELINE);
for (i = 0; i < cstate->num_connectors; ++i)
sde_connector_commit_reset(cstate->connectors[i],
ktime_get());
@@ -4256,7 +4311,7 @@
if (encoder->crtc != crtc)
continue;
sde_encoder_register_frame_event_callback(encoder,
- sde_crtc_frame_event_cb, (void *)crtc);
+ sde_crtc_frame_event_cb, crtc);
}
if (!sde_crtc->enabled && !sde_crtc->suspend &&
@@ -4644,8 +4699,6 @@
for (i = 1; i < SSPP_MAX; i++) {
if (pipe_staged[i]) {
- sde_plane_clear_multirect(pipe_staged[i]);
-
if (is_sde_plane_virtual(pipe_staged[i]->plane)) {
SDE_ERROR(
"r1 only virt plane:%d not supported\n",
@@ -4653,6 +4706,7 @@
rc = -EINVAL;
goto end;
}
+ sde_plane_clear_multirect(pipe_staged[i]);
}
}
@@ -4844,7 +4898,7 @@
{
struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
- SDE_DEBUG("%s: cancel: %p\n", sde_crtc->name, file);
+ SDE_DEBUG("%s: cancel: %pK\n", sde_crtc->name, file);
_sde_crtc_complete_flip(crtc, file);
}
@@ -4915,6 +4969,11 @@
{SDE_DRM_SEC_ONLY, "sec_only"},
};
+ static const struct drm_prop_enum_list e_cwb_data_points[] = {
+ {CAPTURE_MIXER_OUT, "capture_mixer_out"},
+ {CAPTURE_DSPP_OUT, "capture_pp_out"},
+ };
+
SDE_DEBUG("\n");
if (!crtc || !catalog) {
@@ -4994,6 +5053,12 @@
"enable_sui_enhancement", 0, 0, U64_MAX, 0,
CRTC_PROP_ENABLE_SUI_ENHANCEMENT);
+ if (catalog->has_cwb_support)
+ msm_property_install_enum(&sde_crtc->property_info,
+ "capture_mode", 0, 0, e_cwb_data_points,
+ ARRAY_SIZE(e_cwb_data_points),
+ CRTC_PROP_CAPTURE_OUTPUT);
+
msm_property_install_blob(&sde_crtc->property_info, "capabilities",
DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
@@ -5677,6 +5742,73 @@
}
DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_crtc_debugfs_state);
+static int _sde_debugfs_fence_status_show(struct seq_file *s, void *data)
+{
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+ struct drm_connector *conn;
+ struct drm_mode_object *drm_obj;
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *cstate;
+ struct sde_fence_context *ctx;
+
+ if (!s || !s->private)
+ return -EINVAL;
+
+ sde_crtc = s->private;
+ crtc = &sde_crtc->base;
+ cstate = to_sde_crtc_state(crtc->state);
+
+ /* Dump input fence info */
+ seq_puts(s, "===Input fence===\n");
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ struct sde_plane_state *pstate;
+ struct fence *fence;
+
+ pstate = to_sde_plane_state(plane->state);
+ if (!pstate)
+ continue;
+
+ seq_printf(s, "plane:%u stage:%d\n", plane->base.id,
+ pstate->stage);
+
+ fence = pstate->input_fence;
+ if (fence)
+ sde_fence_list_dump(fence, &s);
+ }
+
+ /* Dump release fence info */
+ seq_puts(s, "\n");
+ seq_puts(s, "===Release fence===\n");
+ ctx = &sde_crtc->output_fence;
+ drm_obj = &crtc->base;
+ sde_debugfs_timeline_dump(ctx, drm_obj, &s);
+ seq_puts(s, "\n");
+
+ /* Dump retire fence info */
+ seq_puts(s, "===Retire fence===\n");
+ drm_for_each_connector(conn, crtc->dev)
+ if (conn->state && conn->state->crtc == crtc &&
+ cstate->num_connectors < MAX_CONNECTORS) {
+ struct sde_connector *c_conn;
+
+ c_conn = to_sde_connector(conn);
+ ctx = &c_conn->retire_fence;
+ drm_obj = &conn->base;
+ sde_debugfs_timeline_dump(ctx, drm_obj, &s);
+ }
+
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+static int _sde_debugfs_fence_status(struct inode *inode, struct file *file)
+{
+ return single_open(file, _sde_debugfs_fence_status_show,
+ inode->i_private);
+}
+
static int _sde_crtc_init_debugfs(struct drm_crtc *crtc)
{
struct sde_crtc *sde_crtc;
@@ -5693,6 +5825,14 @@
.read = _sde_crtc_misr_read,
.write = _sde_crtc_misr_setup,
};
+ static const struct file_operations debugfs_fence_fops = {
+ .open = _sde_debugfs_fence_status,
+ .read = seq_read,
+ };
+ static const struct file_operations debugfs_fps_fops = {
+ .open = _sde_debugfs_fps_status,
+ .read = seq_read,
+ };
if (!crtc)
return -EINVAL;
@@ -5717,6 +5857,10 @@
&sde_crtc_debugfs_state_fops);
debugfs_create_file("misr_data", 0600, sde_crtc->debugfs_root,
sde_crtc, &debugfs_misr_fops);
+ debugfs_create_file("fence_status", 0400, sde_crtc->debugfs_root,
+ sde_crtc, &debugfs_fence_fops);
+ debugfs_create_file("fps", 0400, sde_crtc->debugfs_root,
+ sde_crtc, &debugfs_fps_fops);
return 0;
}
@@ -5867,10 +6011,6 @@
list_add_tail(&sde_crtc->event_cache[i].list,
&sde_crtc->event_free_list);
- INIT_LIST_HEAD(&sde_crtc->retire_event_list);
- for (i = 0; i < ARRAY_SIZE(sde_crtc->retire_events); i++)
- INIT_LIST_HEAD(&sde_crtc->retire_events[i].list);
-
return rc;
}
@@ -5924,7 +6064,6 @@
mutex_init(&sde_crtc->rp_lock);
INIT_LIST_HEAD(&sde_crtc->rp_head);
- init_completion(&sde_crtc->frame_done_comp);
sde_crtc->enabled = false;
INIT_LIST_HEAD(&sde_crtc->frame_event_list);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 64f3821..99177b1 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -31,7 +31,8 @@
#define SDE_CRTC_NAME_SIZE 12
/* define the maximum number of in-flight frame events */
-#define SDE_CRTC_FRAME_EVENT_SIZE 4
+/* Expand it to 2x for handling atleast 2 connectors safely */
+#define SDE_CRTC_FRAME_EVENT_SIZE (4 * 2)
/**
* enum sde_crtc_client_type: crtc client type
@@ -48,6 +49,16 @@
};
/**
+ * enum sde_crtc_output_capture_point
+ * @MIXER_OUT : capture mixer output
+ * @DSPP_OUT : capture output of dspp
+ */
+enum sde_crtc_output_capture_point {
+ CAPTURE_MIXER_OUT,
+ CAPTURE_DSPP_OUT
+};
+
+/**
* @connectors : Currently associated drm connectors for retire event
* @num_connectors: Number of associated drm connectors for retire event
* @list: event list
@@ -81,9 +92,20 @@
};
/**
+ * struct sde_crtc_frame_event_cb_data : info of drm objects of a frame event
+ * @crtc: pointer to drm crtc object registered for frame event
+ * @connector: pointer to drm connector which is source of frame event
+ */
+struct sde_crtc_frame_event_cb_data {
+ struct drm_crtc *crtc;
+ struct drm_connector *connector;
+};
+
+/**
* struct sde_crtc_frame_event: stores crtc frame event for crtc processing
* @work: base work structure
* @crtc: Pointer to crtc handling this event
+ * @connector: pointer to drm connector which is source of frame event
* @list: event list
* @ts: timestamp at queue entry
* @event: event identifier
@@ -91,6 +113,7 @@
struct sde_crtc_frame_event {
struct kthread_work work;
struct drm_crtc *crtc;
+ struct drm_connector *connector;
struct list_head list;
ktime_t ts;
u32 event;
@@ -113,6 +136,12 @@
void *usr;
};
+struct sde_crtc_fps_info {
+ u32 frame_count;
+ ktime_t last_sampled_time_us;
+ u32 measured_fps;
+};
+
/*
* Maximum number of free event structures to cache
*/
@@ -155,14 +184,12 @@
* @dirty_list : list of color processing features are dirty
* @ad_dirty: list containing ad properties that are dirty
* @ad_active: list containing ad properties that are active
+ * @ad_vsync_count : count of vblank since last reset for AD
* @crtc_lock : crtc lock around create, destroy and access.
* @frame_pending : Whether or not an update is pending
* @frame_events : static allocation of in-flight frame events
* @frame_event_list : available frame event list
* @spin_lock : spin lock for frame event, transaction status, etc...
- * @retire_events : static allocation of retire fence connector
- * @retire_event_list : available retire fence connector list
- * @frame_done_comp : for frame_event_done synchronization
* @event_thread : Pointer to event handler thread
* @event_worker : Event worker queue
* @event_cache : Local cache of event worker structures
@@ -210,6 +237,7 @@
u64 play_count;
ktime_t vblank_cb_time;
ktime_t vblank_last_cb_time;
+ struct sde_crtc_fps_info fps_info;
struct device *sysfs_dev;
struct kernfs_node *vsync_event_sf;
bool vblank_requested;
@@ -224,6 +252,7 @@
struct list_head ad_dirty;
struct list_head ad_active;
struct list_head user_event_list;
+ u32 ad_vsync_count;
struct mutex crtc_lock;
struct mutex crtc_cp_lock;
@@ -232,9 +261,6 @@
struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
struct list_head frame_event_list;
spinlock_t spin_lock;
- struct sde_crtc_retire_event retire_events[SDE_CRTC_FRAME_EVENT_SIZE];
- struct list_head retire_event_list;
- struct completion frame_done_comp;
/* for handling internal event thread */
struct sde_crtc_event event_cache[SDE_CRTC_MAX_EVENT_COUNT];
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 3021496..f9a59b0 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -80,6 +80,11 @@
/* Maximum number of VSYNC wait attempts for RSC state transition */
#define MAX_RSC_WAIT 5
+#define TOPOLOGY_DUALPIPE_MERGE_MODE(x) \
+ (((x) == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) || \
+ ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || \
+ ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC))
+
/**
* enum sde_enc_rc_events - events for resource control state machine
* @SDE_ENC_RC_EVENT_KICKOFF:
@@ -215,6 +220,7 @@
struct sde_encoder_virt {
struct drm_encoder base;
spinlock_t enc_spinlock;
+ struct mutex vblank_ctl_lock;
uint32_t bus_scaling_client;
uint32_t display_num_of_h_tiles;
@@ -234,7 +240,7 @@
struct mutex enc_lock;
DECLARE_BITMAP(frame_busy_mask, MAX_PHYS_ENCODERS_PER_VIRTUAL);
void (*crtc_frame_event_cb)(void *, u32 event);
- void *crtc_frame_event_cb_data;
+ struct sde_crtc_frame_event_cb_data crtc_frame_event_cb_data;
struct timer_list vsync_event_timer;
@@ -416,6 +422,14 @@
return false;
}
+int sde_encoder_in_clone_mode(struct drm_encoder *drm_enc)
+{
+ struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+
+ return sde_enc && sde_enc->cur_master &&
+ sde_enc->cur_master->in_clone_mode;
+}
+
static inline int _sde_encoder_power_enable(struct sde_encoder_virt *sde_enc,
bool enable)
{
@@ -724,8 +738,8 @@
mutex_destroy(&sde_enc->enc_lock);
if (sde_enc->input_handler) {
- input_unregister_handler(sde_enc->input_handler);
kfree(sde_enc->input_handler);
+ sde_enc->input_handler = NULL;
}
kfree(sde_enc);
@@ -1475,12 +1489,13 @@
vsync_cfg.pp_count = sde_enc->num_phys_encs;
vsync_cfg.frame_rate = mode_info.frame_rate;
+ vsync_cfg.vsync_source =
+ sde_enc->cur_master->hw_pp->caps->te_source;
if (is_dummy)
vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_1;
else if (disp_info->is_te_using_watchdog_timer)
vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0;
- else
- vsync_cfg.vsync_source = SDE_VSYNC0_SOURCE_GPIO;
+
vsync_cfg.is_dummy = is_dummy;
hw_mdptop->ops.setup_vsync_source(hw_mdptop, &vsync_cfg);
@@ -1586,11 +1601,16 @@
* only primary command mode panel can request CMD state.
* all other panels/displays can request for VID state including
* secondary command mode panel.
+ * Clone mode encoder can request CLK STATE only.
*/
- rsc_state = enable ?
- (((disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) &&
- disp_info->is_primary) ? SDE_RSC_CMD_STATE :
- SDE_RSC_VID_STATE) : SDE_RSC_IDLE_STATE;
+ if (sde_encoder_in_clone_mode(drm_enc))
+ rsc_state = enable ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE;
+ else
+ rsc_state = enable ?
+ (((disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
+ && disp_info->is_primary) ? SDE_RSC_CMD_STATE :
+ SDE_RSC_VID_STATE) : SDE_RSC_IDLE_STATE;
+
prefill_lines = config ? mode_info.prefill_lines +
config->inline_rotate_prefill : mode_info.prefill_lines;
@@ -2301,10 +2321,17 @@
_sde_encoder_resource_control_rsc_update(drm_enc, true);
_sde_encoder_resource_control_helper(drm_enc, true);
+ /*
+ * In some cases, commit comes with slight delay
+ * (> 80 ms)after early wake up, prevent clock switch
+ * off to avoid jank in next update. So, increase the
+ * command mode idle timeout sufficiently to prevent
+ * such case.
+ */
kthread_mod_delayed_work(&disp_thread->worker,
- &sde_enc->delayed_off_work,
- msecs_to_jiffies(
- IDLE_POWERCOLLAPSE_DURATION));
+ &sde_enc->delayed_off_work,
+ msecs_to_jiffies(
+ IDLE_POWERCOLLAPSE_IN_EARLY_WAKEUP));
sde_enc->rc_state = SDE_ENC_RC_STATE_ON;
}
@@ -2484,6 +2511,109 @@
}
}
+static int _sde_encoder_input_connect(struct input_handler *handler,
+ struct input_dev *dev, const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int rc = 0;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = handler->name;
+
+ rc = input_register_handle(handle);
+ if (rc) {
+ pr_err("failed to register input handle\n");
+ goto error;
+ }
+
+ rc = input_open_device(handle);
+ if (rc) {
+ pr_err("failed to open input device\n");
+ goto error_unregister;
+ }
+
+ return 0;
+
+error_unregister:
+ input_unregister_handle(handle);
+
+error:
+ kfree(handle);
+
+ return rc;
+}
+
+static void _sde_encoder_input_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+/**
+ * Structure for specifying event parameters on which to receive callbacks.
+ * This structure will trigger a callback in case of a touch event (specified by
+ * EV_ABS) where there is a change in X and Y coordinates,
+ */
+static const struct input_device_id sde_input_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_ABS) },
+ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+ BIT_MASK(ABS_MT_POSITION_X) |
+ BIT_MASK(ABS_MT_POSITION_Y) },
+ },
+ { },
+};
+
+static int _sde_encoder_input_handler_register(
+ struct input_handler *input_handler)
+{
+ int rc = 0;
+
+ rc = input_register_handler(input_handler);
+ if (rc) {
+ pr_err("input_register_handler failed, rc= %d\n", rc);
+ kfree(input_handler);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int _sde_encoder_input_handler(
+ struct sde_encoder_virt *sde_enc)
+{
+ struct input_handler *input_handler = NULL;
+ int rc = 0;
+
+ if (sde_enc->input_handler) {
+ SDE_ERROR_ENC(sde_enc,
+ "input_handle is active. unexpected\n");
+ return -EINVAL;
+ }
+
+ input_handler = kzalloc(sizeof(*sde_enc->input_handler), GFP_KERNEL);
+ if (!input_handler)
+ return -ENOMEM;
+
+ input_handler->event = sde_encoder_input_event_handler;
+ input_handler->connect = _sde_encoder_input_connect;
+ input_handler->disconnect = _sde_encoder_input_disconnect;
+ input_handler->name = "sde";
+ input_handler->id_table = sde_input_ids;
+ input_handler->private = sde_enc;
+
+ sde_enc->input_handler = input_handler;
+
+ return rc;
+}
+
static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
{
struct sde_encoder_virt *sde_enc = NULL;
@@ -2558,12 +2688,14 @@
struct msm_compression_info *comp_info = NULL;
struct drm_display_mode *cur_mode = NULL;
struct msm_mode_info mode_info;
+ struct msm_display_info *disp_info;
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
return;
}
sde_enc = to_sde_encoder_virt(drm_enc);
+ disp_info = &sde_enc->disp_info;
if (!sde_kms_power_resource_is_enabled(drm_enc->dev)) {
SDE_ERROR("power resource is not enabled\n");
@@ -2601,6 +2733,14 @@
return;
}
+ if (sde_enc->input_handler) {
+ ret = _sde_encoder_input_handler_register(
+ sde_enc->input_handler);
+ if (ret)
+ SDE_ERROR(
+ "input handler registration failed, rc = %d\n", ret);
+ }
+
ret = sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_KICKOFF);
if (ret) {
SDE_ERROR_ENC(sde_enc, "sde resource control failed: %d\n",
@@ -2648,7 +2788,6 @@
struct sde_encoder_virt *sde_enc = NULL;
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
- struct drm_connector *drm_conn = NULL;
enum sde_intf_mode intf_mode;
int i = 0;
@@ -2677,13 +2816,14 @@
SDE_EVT32(DRMID(drm_enc));
- /* Disable ESD thread */
- drm_conn = sde_enc->cur_master->connector;
- sde_connector_schedule_status_work(drm_conn, false);
-
/* wait for idle */
sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
+ kthread_flush_work(&sde_enc->input_event_work);
+
+ if (sde_enc->input_handler)
+ input_unregister_handler(sde_enc->input_handler);
+
/*
* For primary command mode encoders, execute the resource control
* pre-stop operations before the physical encoders are disabled, to
@@ -2838,8 +2978,8 @@
}
void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
- void (*frame_event_cb)(void *, u32 event),
- void *frame_event_cb_data)
+ void (*frame_event_cb)(void *, u32 event),
+ struct drm_crtc *crtc)
{
struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
unsigned long lock_flags;
@@ -2856,7 +2996,7 @@
spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
sde_enc->crtc_frame_event_cb = frame_event_cb;
- sde_enc->crtc_frame_event_cb_data = frame_event_cb_data;
+ sde_enc->crtc_frame_event_cb_data.crtc = crtc;
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
}
@@ -2867,6 +3007,9 @@
struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
unsigned int i;
+ sde_enc->crtc_frame_event_cb_data.connector =
+ sde_enc->cur_master->connector;
+
if (event & (SDE_ENCODER_FRAME_EVENT_DONE
| SDE_ENCODER_FRAME_EVENT_ERROR
| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {
@@ -2895,13 +3038,13 @@
if (sde_enc->crtc_frame_event_cb)
sde_enc->crtc_frame_event_cb(
- sde_enc->crtc_frame_event_cb_data,
+ &sde_enc->crtc_frame_event_cb_data,
event);
}
} else {
if (sde_enc->crtc_frame_event_cb)
sde_enc->crtc_frame_event_cb(
- sde_enc->crtc_frame_event_cb_data, event);
+ &sde_enc->crtc_frame_event_cb_data, event);
}
}
@@ -3010,6 +3153,10 @@
return;
}
+ /* avoid ctrl start for encoder in clone mode */
+ if (phys->in_clone_mode)
+ return;
+
ctl = phys->hw_ctl;
if (phys->split_role == ENC_ROLE_SKIP) {
SDE_DEBUG_ENC(to_sde_encoder_virt(phys->parent),
@@ -3402,22 +3549,55 @@
static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys)
{
void *dither_cfg;
- int ret = 0;
+ int ret = 0, rc, i = 0;
size_t len = 0;
enum sde_rm_topology_name topology;
+ struct drm_encoder *drm_enc;
+ struct msm_mode_info mode_info;
+ struct msm_display_dsc_info *dsc = NULL;
+ struct sde_encoder_virt *sde_enc;
+ struct sde_hw_pingpong *hw_pp;
if (!phys || !phys->connector || !phys->hw_pp ||
- !phys->hw_pp->ops.setup_dither)
+ !phys->hw_pp->ops.setup_dither || !phys->parent)
return;
+
topology = sde_connector_get_topology_name(phys->connector);
if ((topology == SDE_RM_TOPOLOGY_PPSPLIT) &&
(phys->split_role == ENC_ROLE_SLAVE))
return;
+ drm_enc = phys->parent;
+ sde_enc = to_sde_encoder_virt(drm_enc);
+ rc = _sde_encoder_get_mode_info(&sde_enc->base, &mode_info);
+ if (rc) {
+ SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+ return;
+ }
+
+ dsc = &mode_info.comp_info.dsc_info;
+ /* disable dither for 10 bpp or 10bpc dsc config */
+ if (dsc->bpp == 10 || dsc->bpc == 10) {
+ phys->hw_pp->ops.setup_dither(phys->hw_pp, NULL, 0);
+ return;
+ }
+
ret = sde_connector_get_dither_cfg(phys->connector,
- phys->connector->state, &dither_cfg, &len);
- if (!ret)
+ phys->connector->state, &dither_cfg, &len);
+ if (ret)
+ return;
+
+ if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) {
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ hw_pp = sde_enc->hw_pp[i];
+ if (hw_pp) {
+ phys->hw_pp->ops.setup_dither(hw_pp, dither_cfg,
+ len);
+ }
+ }
+ } else {
phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len);
+ }
}
static u32 _sde_encoder_calculate_linetime(struct sde_encoder_virt *sde_enc,
@@ -3572,102 +3752,6 @@
SDE_ENC_RC_EVENT_EARLY_WAKEUP);
}
-static int _sde_encoder_input_connect(struct input_handler *handler,
- struct input_dev *dev, const struct input_device_id *id)
-{
- struct input_handle *handle;
- int rc = 0;
-
- handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (!handle)
- return -ENOMEM;
-
- handle->dev = dev;
- handle->handler = handler;
- handle->name = handler->name;
-
- rc = input_register_handle(handle);
- if (rc) {
- pr_err("failed to register input handle\n");
- goto error;
- }
-
- rc = input_open_device(handle);
- if (rc) {
- pr_err("failed to open input device\n");
- goto error_unregister;
- }
-
- return 0;
-
-error_unregister:
- input_unregister_handle(handle);
-
-error:
- kfree(handle);
-
- return rc;
-}
-
-static void _sde_encoder_input_disconnect(struct input_handle *handle)
-{
- input_close_device(handle);
- input_unregister_handle(handle);
- kfree(handle);
-}
-
-/**
- * Structure for specifying event parameters on which to receive callbacks.
- * This structure will trigger a callback in case of a touch event (specified by
- * EV_ABS) where there is a change in X and Y coordinates,
- */
-static const struct input_device_id sde_input_ids[] = {
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
- .evbit = { BIT_MASK(EV_ABS) },
- .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
- BIT_MASK(ABS_MT_POSITION_X) |
- BIT_MASK(ABS_MT_POSITION_Y) },
- },
- { },
-};
-
-static int _sde_encoder_input_handler(
- struct sde_encoder_virt *sde_enc)
-{
- struct input_handler *input_handler = NULL;
- int rc = 0;
-
- if (sde_enc->input_handler) {
- SDE_ERROR_ENC(sde_enc,
- "input_handle is active. unexpected\n");
- return -EINVAL;
- }
-
- input_handler = kzalloc(sizeof(*sde_enc->input_handler), GFP_KERNEL);
- if (!input_handler)
- return -ENOMEM;
-
- input_handler->event = sde_encoder_input_event_handler;
- input_handler->connect = _sde_encoder_input_connect;
- input_handler->disconnect = _sde_encoder_input_disconnect;
- input_handler->name = "sde";
- input_handler->id_table = sde_input_ids;
- input_handler->private = sde_enc;
-
- rc = input_register_handler(input_handler);
- if (rc) {
- SDE_ERROR_ENC(sde_enc,
- "input_register_handler failed, rc= %d\n", rc);
- kfree(input_handler);
- return rc;
- }
-
- sde_enc->input_handler = input_handler;
-
- return rc;
-}
-
static void sde_encoder_vsync_event_work_handler(struct kthread_work *work)
{
struct sde_encoder_virt *sde_enc = container_of(work,
@@ -4368,6 +4452,7 @@
phys_params.parent = &sde_enc->base;
phys_params.parent_ops = parent_ops;
phys_params.enc_spinlock = &sde_enc->enc_spinlock;
+ phys_params.vblank_ctl_lock = &sde_enc->vblank_ctl_lock;
SDE_DEBUG("\n");
@@ -4510,6 +4595,7 @@
sde_enc->cur_master = NULL;
spin_lock_init(&sde_enc->enc_spinlock);
+ mutex_init(&sde_enc->vblank_ctl_lock);
drm_enc = &sde_enc->base;
drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
@@ -4815,7 +4901,7 @@
int sde_encoder_display_failure_notification(struct drm_encoder *enc)
{
- struct msm_drm_thread *disp_thread = NULL;
+ struct msm_drm_thread *event_thread = NULL;
struct msm_drm_private *priv = NULL;
struct sde_encoder_virt *sde_enc = NULL;
@@ -4827,7 +4913,7 @@
priv = enc->dev->dev_private;
sde_enc = to_sde_encoder_virt(enc);
if (!sde_enc->crtc || (sde_enc->crtc->index
- >= ARRAY_SIZE(priv->disp_thread))) {
+ >= ARRAY_SIZE(priv->event_thread))) {
SDE_DEBUG_ENC(sde_enc,
"invalid cached CRTC: %d or crtc index: %d\n",
sde_enc->crtc == NULL,
@@ -4837,11 +4923,12 @@
SDE_EVT32_VERBOSE(DRMID(enc));
- disp_thread = &priv->disp_thread[sde_enc->crtc->index];
+ event_thread = &priv->event_thread[sde_enc->crtc->index];
- kthread_queue_work(&disp_thread->worker,
- &sde_enc->esd_trigger_work);
+ kthread_queue_work(&event_thread->worker,
+ &sde_enc->esd_trigger_work);
kthread_flush_work(&sde_enc->esd_trigger_work);
+
/**
* panel may stop generating te signal (vsync) during esd failure. rsc
* hardware may hang without vsync. Avoid rsc hang by generating the
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 0e8e9dd..42b9e58 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -31,6 +31,7 @@
#define SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE BIT(4)
#define IDLE_POWERCOLLAPSE_DURATION (66 - 16/2)
+#define IDLE_POWERCOLLAPSE_IN_EARLY_WAKEUP (200 - 16/2)
/**
* Encoder functions and data types
@@ -97,10 +98,10 @@
* will be called after the request is complete, or other events.
* @encoder: encoder pointer
* @cb: callback pointer, provide NULL to deregister
- * @data: user data provided to callback
+ * @crtc: pointer to drm_crtc object interested in frame events
*/
void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
- void (*cb)(void *, u32), void *data);
+ void (*cb)(void *, u32), struct drm_crtc *crtc);
/**
* sde_encoder_get_rsc_client - gets the rsc client state for primary
@@ -242,14 +243,18 @@
* esd timeout or other display failure notification. This event flows from
* dsi, sde_connector to sde_encoder.
*
- * This api must not be called from crtc_commit (display) thread because it
- * requests the flush work on same thread. It is called from esd check thread
- * based on current design.
- *
* TODO: manage the event at sde_kms level for forward processing.
* @drm_enc: Pointer to drm encoder structure
* @Return: true if successful in updating the encoder structure
*/
int sde_encoder_display_failure_notification(struct drm_encoder *enc);
+/**
+ * sde_encoder_in_clone_mode - checks if underlying phys encoder is in clone
+ * mode or independent display mode. ref@ WB in Concurrent writeback mode.
+ * @drm_enc: Pointer to drm encoder structure
+ * @Return: true if successful in updating the encoder structure
+ */
+int sde_encoder_in_clone_mode(struct drm_encoder *enc);
+
#endif /* __SDE_ENCODER_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 9557d41..4e9430e 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -194,6 +194,9 @@
* @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
* @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
* @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel
+ * @INTR_IDX_WB_DONE: Writeback done interrupt for WB
+ * @INTR_IDX_PP2_OVFL: Pingpong overflow interrupt on PP2 for Concurrent WB
+ * @INTR_IDX_PP2_OVFL: Pingpong overflow interrupt on PP3 for Concurrent WB
* @INTR_IDX_AUTOREFRESH_DONE: Autorefresh done for cmd mode panel meaning
* autorefresh has triggered a double buffer flip
*/
@@ -204,6 +207,9 @@
INTR_IDX_CTL_START,
INTR_IDX_RDPTR,
INTR_IDX_AUTOREFRESH_DONE,
+ INTR_IDX_WB_DONE,
+ INTR_IDX_PP2_OVFL,
+ INTR_IDX_PP3_OVFL,
INTR_IDX_MAX,
};
@@ -263,6 +269,7 @@
* @irq: IRQ tracking structures
* @cont_splash_single_flush Variable to check if single flush is enabled.
* @cont_splash_settings Variable to store continuous splash settings.
+ * @in_clone_mode Indicates if encoder is in clone mode ref@CWB
* @vfp_cached: cached vertical front porch to be used for
* programming ROT and MDP fetch start
*/
@@ -284,6 +291,7 @@
enum msm_display_compression_type comp_type;
spinlock_t *enc_spinlock;
enum sde_enc_enable_state enable_state;
+ struct mutex *vblank_ctl_lock;
atomic_t vblank_refcount;
atomic_t vsync_cnt;
atomic_t underrun_cnt;
@@ -294,6 +302,7 @@
struct sde_encoder_irq irq[INTR_IDX_MAX];
u32 cont_splash_single_flush;
bool cont_splash_settings;
+ bool in_clone_mode;
int vfp_cached;
};
@@ -367,7 +376,6 @@
* writeback specific operations
* @base: Baseclass physical encoder structure
* @hw_wb: Hardware interface to the wb registers
- * @irq_idx: IRQ interface lookup index
* @wbdone_timeout: Timeout value for writeback done in msec
* @bypass_irqreg: Bypass irq register/unregister if non-zero
* @wbdone_complete: for wbdone irq synchronization
@@ -391,8 +399,6 @@
struct sde_encoder_phys_wb {
struct sde_encoder_phys base;
struct sde_hw_wb *hw_wb;
- int irq_idx;
- struct sde_irq_callback irq_cb;
u32 wbdone_timeout;
u32 bypass_irqreg;
struct completion wbdone_complete;
@@ -434,6 +440,7 @@
enum sde_wb wb_idx;
enum msm_display_compression_type comp_type;
spinlock_t *enc_spinlock;
+ struct mutex *vblank_ctl_lock;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 828d771..f06ceb7 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -680,6 +680,7 @@
return -EINVAL;
}
+ mutex_lock(phys_enc->vblank_ctl_lock);
refcount = atomic_read(&phys_enc->vblank_refcount);
/* Slave encoders don't report vblank */
@@ -713,6 +714,7 @@
enable, refcount, SDE_EVTLOG_ERROR);
}
+ mutex_unlock(phys_enc->vblank_ctl_lock);
return ret;
}
@@ -1398,6 +1400,7 @@
phys_enc->split_role = p->split_role;
phys_enc->intf_mode = INTF_MODE_CMD;
phys_enc->enc_spinlock = p->enc_spinlock;
+ phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
cmd_enc->stream_sel = 0;
phys_enc->enable_state = SDE_ENC_DISABLED;
phys_enc->comp_type = p->comp_type;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 862a8b3..d363d62 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -625,6 +625,7 @@
return -EINVAL;
}
+ mutex_lock(phys_enc->vblank_ctl_lock);
refcount = atomic_read(&phys_enc->vblank_refcount);
vid_enc = to_sde_encoder_phys_vid(phys_enc);
@@ -645,11 +646,17 @@
SDE_EVT32(DRMID(phys_enc->parent), enable,
atomic_read(&phys_enc->vblank_refcount));
- if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
+ if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) {
ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC);
- else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
+ if (ret)
+ atomic_dec_return(&phys_enc->vblank_refcount);
+ } else if (!enable &&
+ atomic_dec_return(&phys_enc->vblank_refcount) == 0) {
ret = sde_encoder_helper_unregister_irq(phys_enc,
INTR_IDX_VSYNC);
+ if (ret)
+ atomic_inc_return(&phys_enc->vblank_refcount);
+ }
end:
if (ret) {
@@ -660,6 +667,7 @@
vid_enc->hw_intf->idx - INTF_0,
enable, refcount, SDE_EVTLOG_ERROR);
}
+ mutex_unlock(phys_enc->vblank_ctl_lock);
return ret;
}
@@ -1233,6 +1241,7 @@
phys_enc->split_role = p->split_role;
phys_enc->intf_mode = INTF_MODE_VIDEO;
phys_enc->enc_spinlock = p->enc_spinlock;
+ phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
phys_enc->comp_type = p->comp_type;
for (i = 0; i < INTR_IDX_MAX; i++) {
irq = &phys_enc->irq[i];
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 9a90075..82dd64a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,6 +32,7 @@
#define TO_S15D16(_x_) ((_x_) << 7)
+#define MULTIPLE_CONN_DETECTED(x) (x > 1)
/**
* sde_rgb2yuv_601l - rgb to yuv color space conversion matrix
*
@@ -398,6 +399,31 @@
}
}
+static void _sde_encoder_phys_wb_setup_cwb(struct sde_encoder_phys *phys_enc,
+ bool enable)
+{
+ struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+ struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
+ struct sde_hw_intf_cfg *intf_cfg = &wb_enc->intf_cfg;
+ struct sde_hw_ctl *hw_ctl = phys_enc->hw_ctl;
+ struct sde_crtc *crtc = to_sde_crtc(wb_enc->crtc);
+
+ if (!phys_enc->in_clone_mode) {
+ SDE_DEBUG("not in CWB mode. early return\n");
+ return;
+ }
+
+ memset(intf_cfg, 0, sizeof(struct sde_hw_intf_cfg));
+ intf_cfg->intf = SDE_NONE;
+ intf_cfg->wb = hw_wb->idx;
+
+ hw_ctl = crtc->mixers[0].hw_ctl;
+ if (hw_ctl && hw_ctl->ops.update_wb_cfg) {
+ hw_ctl->ops.update_wb_cfg(hw_ctl, intf_cfg, enable);
+ SDE_DEBUG("in CWB mode adding WB for CTL_%d\n",
+ hw_ctl->idx - CTL_0);
+ }
+}
/**
* sde_encoder_phys_wb_setup_cdp - setup chroma down prefetch block
* @phys_enc: Pointer to physical encoder
@@ -408,8 +434,12 @@
struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
struct sde_hw_intf_cfg *intf_cfg = &wb_enc->intf_cfg;
- memset(intf_cfg, 0, sizeof(struct sde_hw_intf_cfg));
+ if (phys_enc->in_clone_mode) {
+ SDE_DEBUG("in CWB mode. early return\n");
+ return;
+ }
+ memset(intf_cfg, 0, sizeof(struct sde_hw_intf_cfg));
intf_cfg->intf = SDE_NONE;
intf_cfg->wb = hw_wb->idx;
intf_cfg->mode_3d = sde_encoder_helper_get_3d_blend_mode(phys_enc);
@@ -417,6 +447,98 @@
if (phys_enc->hw_ctl && phys_enc->hw_ctl->ops.setup_intf_cfg)
phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl,
intf_cfg);
+
+}
+
+static void _sde_enc_phys_wb_detect_cwb(struct sde_encoder_phys *phys_enc,
+ struct drm_crtc_state *crtc_state)
+{
+ struct drm_connector *conn;
+ struct drm_connector_state *conn_state;
+ struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+ const struct sde_wb_cfg *wb_cfg = wb_enc->hw_wb->caps;
+ int conn_count = 0;
+
+ phys_enc->in_clone_mode = false;
+
+ /* Check if WB has CWB support */
+ if (!(wb_cfg->features & BIT(SDE_WB_HAS_CWB)))
+ return;
+
+ /* Count the number of connectors on the given crtc */
+ drm_for_each_connector(conn, crtc_state->crtc->dev) {
+ conn_state =
+ drm_atomic_get_connector_state(crtc_state->state, conn);
+ if ((conn->state && conn->state->crtc == crtc_state->crtc) ||
+ (conn_state &&
+ conn_state->crtc == crtc_state->crtc))
+ conn_count++;
+ }
+
+
+ /* Enable clone mode If crtc has multiple connectors & one is WB */
+ if (MULTIPLE_CONN_DETECTED(conn_count))
+ phys_enc->in_clone_mode = true;
+
+ SDE_DEBUG("detect CWB - status:%d\n", phys_enc->in_clone_mode);
+}
+
+static int _sde_enc_phys_wb_validate_cwb(struct sde_encoder_phys *phys_enc,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct sde_crtc_state *cstate = to_sde_crtc_state(crtc_state);
+ struct sde_rect wb_roi = {0,};
+ int data_pt;
+ int ds_outw = 0;
+ int ds_outh = 0;
+ int ds_in_use = false;
+ int i = 0;
+ int ret = 0;
+
+ if (!phys_enc->in_clone_mode) {
+ SDE_DEBUG("not in CWB mode. early return\n");
+ goto exit;
+ }
+
+ ret = sde_wb_connector_state_get_output_roi(conn_state, &wb_roi);
+ if (ret) {
+ SDE_ERROR("failed to get roi %d\n", ret);
+ goto exit;
+ }
+
+ data_pt = sde_crtc_get_property(cstate, CRTC_PROP_CAPTURE_OUTPUT);
+
+ /* compute cumulative ds output dimensions if in use */
+ for (i = 0; i < cstate->num_ds; i++)
+ if (cstate->ds_cfg[i].scl3_cfg.enable) {
+ ds_in_use = true;
+ ds_outw += cstate->ds_cfg[i].scl3_cfg.dst_width;
+ ds_outh += cstate->ds_cfg[i].scl3_cfg.dst_height;
+ }
+
+ /* if ds in use check wb roi against ds output dimensions */
+ if ((data_pt == CAPTURE_DSPP_OUT) && ds_in_use &&
+ ((wb_roi.w != ds_outw) || (wb_roi.h != ds_outh))) {
+ SDE_ERROR("invalid wb roi with dest scalar [%dx%d vs %dx%d]\n",
+ wb_roi.w, wb_roi.h, ds_outw, ds_outh);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* validate conn roi against pu rect */
+ if (!sde_kms_rect_is_null(&cstate->crtc_roi)) {
+ if (wb_roi.w != cstate->crtc_roi.w ||
+ wb_roi.h != cstate->crtc_roi.h) {
+ SDE_ERROR("invalid wb roi with pu [%dx%d vs %dx%d]\n",
+ wb_roi.w, wb_roi.h, cstate->crtc_roi.w,
+ cstate->crtc_roi.h);
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+exit:
+ return ret;
}
/**
@@ -453,6 +575,8 @@
return -EINVAL;
}
+ _sde_enc_phys_wb_detect_cwb(phys_enc, crtc_state);
+
memset(&wb_roi, 0, sizeof(struct sde_rect));
rc = sde_wb_connector_state_get_output_roi(conn_state, &wb_roi);
@@ -542,7 +666,62 @@
}
}
- return 0;
+ rc = _sde_enc_phys_wb_validate_cwb(phys_enc, crtc_state, conn_state);
+ if (rc) {
+ SDE_ERROR("failed in cwb validation %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void _sde_encoder_phys_wb_update_cwb_flush(
+ struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_phys_wb *wb_enc;
+ struct sde_hw_wb *hw_wb;
+ struct sde_hw_ctl *hw_ctl;
+ struct sde_hw_cdm *hw_cdm;
+ struct sde_crtc *crtc;
+ struct sde_crtc_state *crtc_state;
+ u32 flush_mask = 0;
+ int capture_point = 0;
+
+ if (!phys_enc->in_clone_mode) {
+ SDE_DEBUG("not in CWB mode. early return\n");
+ return;
+ }
+
+ wb_enc = to_sde_encoder_phys_wb(phys_enc);
+ crtc = to_sde_crtc(wb_enc->crtc);
+ crtc_state = to_sde_crtc_state(wb_enc->crtc->state);
+
+ hw_wb = wb_enc->hw_wb;
+ hw_cdm = phys_enc->hw_cdm;
+
+ /* In CWB mode, program actual source master sde_hw_ctl from crtc */
+ hw_ctl = crtc->mixers[0].hw_ctl;
+ if (!hw_ctl) {
+ SDE_DEBUG("[wb:%d] no ctl assigned for CWB\n",
+ hw_wb->idx - WB_0);
+ return;
+ }
+
+ capture_point = sde_crtc_get_property(crtc_state,
+ CRTC_PROP_CAPTURE_OUTPUT);
+
+ phys_enc->hw_mdptop->ops.set_cwb_ppb_cntl(phys_enc->hw_mdptop,
+ crtc->num_mixers == CRTC_DUAL_MIXERS,
+ capture_point == CAPTURE_DSPP_OUT);
+
+ if (hw_ctl->ops.get_bitmask_wb)
+ hw_ctl->ops.get_bitmask_wb(hw_ctl, &flush_mask, hw_wb->idx);
+
+ if (hw_ctl->ops.get_bitmask_cdm && hw_cdm)
+ hw_ctl->ops.get_bitmask_cdm(hw_ctl, &flush_mask, hw_cdm->idx);
+
+ if (hw_ctl->ops.update_pending_flush)
+ hw_ctl->ops.update_pending_flush(hw_ctl, flush_mask);
}
/**
@@ -551,7 +730,7 @@
*/
static void _sde_encoder_phys_wb_update_flush(struct sde_encoder_phys *phys_enc)
{
- struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+ struct sde_encoder_phys_wb *wb_enc;
struct sde_hw_wb *hw_wb;
struct sde_hw_ctl *hw_ctl;
struct sde_hw_cdm *hw_cdm;
@@ -560,12 +739,18 @@
if (!phys_enc)
return;
+ wb_enc = to_sde_encoder_phys_wb(phys_enc);
hw_wb = wb_enc->hw_wb;
- hw_ctl = phys_enc->hw_ctl;
hw_cdm = phys_enc->hw_cdm;
+ hw_ctl = phys_enc->hw_ctl;
SDE_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
+ if (phys_enc->in_clone_mode) {
+ SDE_DEBUG("in CWB mode. early return\n");
+ return;
+ }
+
if (!hw_ctl) {
SDE_DEBUG("[wb:%d] no ctl assigned\n", hw_wb->idx - WB_0);
return;
@@ -659,54 +844,28 @@
sde_encoder_phys_wb_setup_fb(phys_enc, fb, wb_roi);
sde_encoder_phys_wb_setup_cdp(phys_enc);
+
+ _sde_encoder_phys_wb_setup_cwb(phys_enc, true);
}
-/**
- * sde_encoder_phys_wb_unregister_irq - unregister writeback interrupt handler
- * @phys_enc: Pointer to physical encoder
- */
-static int sde_encoder_phys_wb_unregister_irq(
- struct sde_encoder_phys *phys_enc)
-{
- struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
- struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
-
- if (wb_enc->bypass_irqreg)
- return 0;
-
- sde_core_irq_disable(phys_enc->sde_kms, &wb_enc->irq_idx, 1);
- sde_core_irq_unregister_callback(phys_enc->sde_kms, wb_enc->irq_idx,
- &wb_enc->irq_cb);
-
- SDE_DEBUG("un-register IRQ for wb %d, irq_idx=%d\n",
- hw_wb->idx - WB_0,
- wb_enc->irq_idx);
-
- return 0;
-}
-
-/**
- * sde_encoder_phys_wb_done_irq - writeback interrupt handler
- * @arg: Pointer to writeback encoder
- * @irq_idx: interrupt index
- */
-static void sde_encoder_phys_wb_done_irq(void *arg, int irq_idx)
+static void _sde_encoder_phys_wb_frame_done_helper(void *arg, bool frame_error)
{
struct sde_encoder_phys_wb *wb_enc = arg;
struct sde_encoder_phys *phys_enc = &wb_enc->base;
struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
- u32 event = 0;
+ u32 event = frame_error ? SDE_ENCODER_FRAME_EVENT_ERROR : 0;
- SDE_DEBUG("[wb:%d,%u]\n", hw_wb->idx - WB_0,
- wb_enc->frame_count);
+ event |= SDE_ENCODER_FRAME_EVENT_DONE |
+ SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE;
+
+ SDE_DEBUG("[wb:%d,%u]\n", hw_wb->idx - WB_0, wb_enc->frame_count);
/* don't notify upper layer for internal commit */
if (phys_enc->enable_state == SDE_ENC_DISABLING)
goto complete;
- event = SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE
- | SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE
- | SDE_ENCODER_FRAME_EVENT_DONE;
+ if (!phys_enc->in_clone_mode)
+ event |= SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0);
if (phys_enc->parent_ops.handle_frame_done)
@@ -724,58 +883,60 @@
}
/**
- * sde_encoder_phys_wb_register_irq - register writeback interrupt handler
- * @phys_enc: Pointer to physical encoder
+ * sde_encoder_phys_wb_done_irq - Pingpong overflow interrupt handler for CWB
+ * @arg: Pointer to writeback encoder
+ * @irq_idx: interrupt index
*/
-static int sde_encoder_phys_wb_register_irq(struct sde_encoder_phys *phys_enc)
+static void sde_encoder_phys_cwb_ovflow(void *arg, int irq_idx)
{
- struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
- struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
- struct sde_irq_callback *irq_cb = &wb_enc->irq_cb;
- enum sde_intr_type intr_type;
- int ret = 0;
+ _sde_encoder_phys_wb_frame_done_helper(arg, true);
+}
+
+/**
+ * sde_encoder_phys_wb_done_irq - writeback interrupt handler
+ * @arg: Pointer to writeback encoder
+ * @irq_idx: interrupt index
+ */
+static void sde_encoder_phys_wb_done_irq(void *arg, int irq_idx)
+{
+ _sde_encoder_phys_wb_frame_done_helper(arg, false);
+}
+
+/**
+ * sde_encoder_phys_wb_irq_ctrl - irq control of WB
+ * @phys: Pointer to physical encoder
+ * @enable: indicates enable or disable interrupts
+ */
+static void sde_encoder_phys_wb_irq_ctrl(
+ struct sde_encoder_phys *phys, bool enable)
+{
+
+ struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys);
+ int index = 0;
+
+ if (!wb_enc)
+ return;
if (wb_enc->bypass_irqreg)
- return 0;
+ return;
- intr_type = sde_encoder_phys_wb_get_intr_type(hw_wb);
- wb_enc->irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms,
- intr_type, hw_wb->idx);
- if (wb_enc->irq_idx < 0) {
- SDE_ERROR(
- "failed to lookup IRQ index for WB_DONE with wb=%d\n",
- hw_wb->idx - WB_0);
- return -EINVAL;
+ if (enable) {
+ sde_encoder_helper_register_irq(phys, INTR_IDX_WB_DONE);
+ if (phys->in_clone_mode) {
+ for (index = 0; index < CRTC_DUAL_MIXERS; index++)
+ sde_encoder_helper_register_irq(phys,
+ index ? INTR_IDX_PP3_OVFL
+ : INTR_IDX_PP2_OVFL);
+ }
+ } else {
+ sde_encoder_helper_unregister_irq(phys, INTR_IDX_WB_DONE);
+ if (phys->in_clone_mode) {
+ for (index = 0; index < CRTC_DUAL_MIXERS; index++)
+ sde_encoder_helper_unregister_irq(phys,
+ index ? INTR_IDX_PP3_OVFL
+ : INTR_IDX_PP2_OVFL);
+ }
}
-
- irq_cb->func = sde_encoder_phys_wb_done_irq;
- irq_cb->arg = wb_enc;
- ret = sde_core_irq_register_callback(phys_enc->sde_kms,
- wb_enc->irq_idx, irq_cb);
- if (ret) {
- SDE_ERROR("failed to register IRQ callback WB_DONE\n");
- return ret;
- }
-
- ret = sde_core_irq_enable(phys_enc->sde_kms, &wb_enc->irq_idx, 1);
- if (ret) {
- SDE_ERROR(
- "failed to enable IRQ for WB_DONE, wb %d, irq_idx=%d\n",
- hw_wb->idx - WB_0,
- wb_enc->irq_idx);
- wb_enc->irq_idx = -EINVAL;
-
- /* Unregister callback on IRQ enable failure */
- sde_core_irq_unregister_callback(phys_enc->sde_kms,
- wb_enc->irq_idx, irq_cb);
- return ret;
- }
-
- SDE_DEBUG("registered IRQ for wb %d, irq_idx=%d\n",
- hw_wb->idx - WB_0,
- wb_enc->irq_idx);
-
- return ret;
}
/**
@@ -846,6 +1007,7 @@
u32 irq_status, event = 0;
u64 wb_time = 0;
int rc = 0;
+ int irq_idx = phys_enc->irq[INTR_IDX_WB_DONE].irq_idx;
u32 timeout = max_t(u32, wb_enc->wbdone_timeout, KICKOFF_TIMEOUT_MS);
/* Return EWOULDBLOCK since we know the wait isn't necessary */
@@ -860,7 +1022,7 @@
/* signal completion if commit with no framebuffer */
if (!wb_enc->wb_fb) {
SDE_DEBUG("no output framebuffer\n");
- sde_encoder_phys_wb_done_irq(wb_enc, wb_enc->irq_idx);
+ _sde_encoder_phys_wb_frame_done_helper(wb_enc, false);
}
ret = wait_for_completion_timeout(&wb_enc->wbdone_complete,
@@ -870,11 +1032,11 @@
SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc),
wb_enc->frame_count);
irq_status = sde_core_irq_read(phys_enc->sde_kms,
- wb_enc->irq_idx, true);
+ irq_idx, true);
if (irq_status) {
SDE_DEBUG("wb:%d done but irq not triggered\n",
WBID(wb_enc));
- sde_encoder_phys_wb_done_irq(wb_enc, wb_enc->irq_idx);
+ _sde_encoder_phys_wb_frame_done_helper(wb_enc, false);
} else {
SDE_ERROR("wb:%d kickoff timed out\n",
WBID(wb_enc));
@@ -891,8 +1053,6 @@
}
}
- sde_encoder_phys_wb_unregister_irq(phys_enc);
-
if (!rc)
wb_enc->end_time = ktime_get();
@@ -937,19 +1097,12 @@
struct sde_encoder_kickoff_params *params)
{
struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
- int ret;
SDE_DEBUG("[wb:%d,%u]\n", wb_enc->hw_wb->idx - WB_0,
wb_enc->kickoff_count);
reinit_completion(&wb_enc->wbdone_complete);
- ret = sde_encoder_phys_wb_register_irq(phys_enc);
- if (ret) {
- SDE_ERROR("failed to register irq %d\n", ret);
- return ret;
- }
-
wb_enc->kickoff_count++;
/* set OT limit & enable traffic shaper */
@@ -957,6 +1110,8 @@
_sde_encoder_phys_wb_update_flush(phys_enc);
+ _sde_encoder_phys_wb_update_cwb_flush(phys_enc);
+
/* vote for iommu/clk/bus */
wb_enc->start_time = ktime_get();
@@ -977,6 +1132,15 @@
return;
}
+ /*
+ * Bail out iff in CWB mode. In case of CWB, primary control-path
+ * which is actually driving would trigger the flush
+ */
+ if (phys_enc->in_clone_mode) {
+ SDE_DEBUG("in CWB mode. early return\n");
+ return;
+ }
+
SDE_DEBUG("[wb:%d]\n", wb_enc->hw_wb->idx - WB_0);
/* clear pending flush if commit with no framebuffer */
@@ -1183,6 +1347,13 @@
goto exit;
}
+ /* avoid reset frame for CWB */
+ if (phys_enc->in_clone_mode) {
+ _sde_encoder_phys_wb_setup_cwb(phys_enc, false);
+ phys_enc->in_clone_mode = false;
+ goto exit;
+ }
+
/* reset h/w before final flush */
if (phys_enc->hw_ctl->ops.clear_pending_flush)
phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
@@ -1320,6 +1491,7 @@
ops->trigger_flush = sde_encoder_phys_wb_trigger_flush;
ops->trigger_start = sde_encoder_helper_trigger_start;
ops->hw_reset = sde_encoder_helper_hw_reset;
+ ops->irq_control = sde_encoder_phys_wb_irq_ctrl;
}
/**
@@ -1332,6 +1504,7 @@
struct sde_encoder_phys *phys_enc;
struct sde_encoder_phys_wb *wb_enc;
struct sde_hw_mdp *hw_mdp;
+ struct sde_encoder_irq *irq;
int ret = 0;
SDE_DEBUG("\n");
@@ -1348,7 +1521,6 @@
ret = -ENOMEM;
goto fail_alloc;
}
- wb_enc->irq_idx = -EINVAL;
wb_enc->wbdone_timeout = KICKOFF_TIMEOUT_MS;
init_completion(&wb_enc->wbdone_complete);
@@ -1410,8 +1582,38 @@
phys_enc->intf_mode = INTF_MODE_WB_LINE;
phys_enc->intf_idx = p->intf_idx;
phys_enc->enc_spinlock = p->enc_spinlock;
+ phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
- INIT_LIST_HEAD(&wb_enc->irq_cb.list);
+
+ irq = &phys_enc->irq[INTR_IDX_WB_DONE];
+ INIT_LIST_HEAD(&irq->cb.list);
+ irq->name = "wb_done";
+ irq->hw_idx = wb_enc->hw_wb->idx;
+ irq->irq_idx = -1;
+ irq->intr_type = sde_encoder_phys_wb_get_intr_type(wb_enc->hw_wb);
+ irq->intr_idx = INTR_IDX_WB_DONE;
+ irq->cb.arg = wb_enc;
+ irq->cb.func = sde_encoder_phys_wb_done_irq;
+
+ irq = &phys_enc->irq[INTR_IDX_PP2_OVFL];
+ INIT_LIST_HEAD(&irq->cb.list);
+ irq->name = "pp2_overflow";
+ irq->hw_idx = CWB_2;
+ irq->irq_idx = -1;
+ irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+ irq->intr_idx = INTR_IDX_PP2_OVFL;
+ irq->cb.arg = wb_enc;
+ irq->cb.func = sde_encoder_phys_cwb_ovflow;
+
+ irq = &phys_enc->irq[INTR_IDX_PP3_OVFL];
+ INIT_LIST_HEAD(&irq->cb.list);
+ irq->name = "pp3_overflow";
+ irq->hw_idx = CWB_3;
+ irq->irq_idx = -1;
+ irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+ irq->intr_idx = INTR_IDX_PP3_OVFL;
+ irq->cb.arg = wb_enc;
+ irq->cb.func = sde_encoder_phys_cwb_ovflow;
/* create internal buffer for disable logic */
if (_sde_encoder_phys_wb_init_internal_fb(wb_enc,
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.c b/drivers/gpu/drm/msm/sde/sde_fence.c
index 686c640..8ffbb98 100644
--- a/drivers/gpu/drm/msm/sde/sde_fence.c
+++ b/drivers/gpu/drm/msm/sde/sde_fence.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -278,7 +278,8 @@
}
}
-static void _sde_fence_trigger(struct sde_fence_context *ctx, ktime_t ts)
+static void _sde_fence_trigger(struct sde_fence_context *ctx,
+ ktime_t ts, bool error)
{
unsigned long flags;
struct sde_fence *fc, *next;
@@ -300,6 +301,7 @@
list_for_each_entry_safe(fc, next, &local_list_head, fence_list) {
spin_lock_irqsave(&ctx->lock, flags);
+ fc->base.error = error ? -EBUSY : 0;
fc->base.timestamp = ts;
is_signaled = fence_is_signaled_locked(&fc->base);
spin_unlock_irqrestore(&ctx->lock, flags);
@@ -351,7 +353,7 @@
if (fd >= 0) {
rc = 0;
- _sde_fence_trigger(ctx, ktime_get());
+ _sde_fence_trigger(ctx, ktime_get(), false);
} else {
rc = fd;
}
@@ -360,7 +362,7 @@
}
void sde_fence_signal(struct sde_fence_context *ctx, ktime_t ts,
- bool reset_timeline)
+ enum sde_fence_event fence_event)
{
unsigned long flags;
@@ -370,7 +372,7 @@
}
spin_lock_irqsave(&ctx->lock, flags);
- if (reset_timeline) {
+ if (fence_event == SDE_FENCE_RESET_TIMELINE) {
if ((int)(ctx->done_count - ctx->commit_count) < 0) {
SDE_ERROR(
"timeline reset attempt! done count:%d commit:%d\n",
@@ -378,7 +380,7 @@
ctx->done_count = ctx->commit_count;
SDE_EVT32(ctx->drm_id, ctx->done_count,
ctx->commit_count, ktime_to_us(ts),
- reset_timeline, SDE_EVTLOG_FATAL);
+ fence_event, SDE_EVTLOG_FATAL);
} else {
spin_unlock_irqrestore(&ctx->lock, flags);
return;
@@ -391,7 +393,7 @@
SDE_ERROR("extra signal attempt! done count:%d commit:%d\n",
ctx->done_count, ctx->commit_count);
SDE_EVT32(ctx->drm_id, ctx->done_count, ctx->commit_count,
- ktime_to_us(ts), reset_timeline, SDE_EVTLOG_FATAL);
+ ktime_to_us(ts), fence_event, SDE_EVTLOG_FATAL);
spin_unlock_irqrestore(&ctx->lock, flags);
return;
}
@@ -400,7 +402,7 @@
SDE_EVT32(ctx->drm_id, ctx->done_count, ctx->commit_count,
ktime_to_us(ts));
- _sde_fence_trigger(ctx, ts);
+ _sde_fence_trigger(ctx, ts, (fence_event == SDE_FENCE_SIGNAL_ERROR));
}
void sde_fence_timeline_status(struct sde_fence_context *ctx,
@@ -429,3 +431,55 @@
obj_name, drm_obj->id, drm_obj->type, ctx->done_count,
ctx->commit_count);
}
+
+void sde_fence_list_dump(struct fence *fence, struct seq_file **s)
+{
+ char timeline_str[TIMELINE_VAL_LENGTH];
+
+ if (fence->ops->timeline_value_str)
+ fence->ops->timeline_value_str(fence,
+ timeline_str, TIMELINE_VAL_LENGTH);
+
+ seq_printf(*s, "fence name:%s timeline name:%s seqno:0x%x timeline:%s signaled:0x%x\n",
+ fence->ops->get_driver_name(fence),
+ fence->ops->get_timeline_name(fence),
+ fence->seqno, timeline_str,
+ fence->ops->signaled ?
+ fence->ops->signaled(fence) : 0xffffffff);
+}
+
+void sde_debugfs_timeline_dump(struct sde_fence_context *ctx,
+ struct drm_mode_object *drm_obj, struct seq_file **s)
+{
+ char *obj_name;
+ struct sde_fence *fc, *next;
+ struct fence *fence;
+
+ if (!ctx || !drm_obj) {
+ SDE_ERROR("invalid input params\n");
+ return;
+ }
+
+ switch (drm_obj->type) {
+ case DRM_MODE_OBJECT_CRTC:
+ obj_name = "crtc";
+ break;
+ case DRM_MODE_OBJECT_CONNECTOR:
+ obj_name = "connector";
+ break;
+ default:
+ obj_name = "unknown";
+ break;
+ }
+
+ seq_printf(*s, "drm obj:%s id:%d type:0x%x done_count:%d commit_count:%d\n",
+ obj_name, drm_obj->id, drm_obj->type, ctx->done_count,
+ ctx->commit_count);
+
+ spin_lock(&ctx->list_lock);
+ list_for_each_entry_safe(fc, next, &ctx->fence_list_head, fence_list) {
+ fence = &fc->base;
+ sde_fence_list_dump(fence, s);
+ }
+ spin_unlock(&ctx->list_lock);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.h b/drivers/gpu/drm/msm/sde/sde_fence.h
index 29d2ec7..7d7fd02 100644
--- a/drivers/gpu/drm/msm/sde/sde_fence.h
+++ b/drivers/gpu/drm/msm/sde/sde_fence.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -47,6 +47,18 @@
char name[SDE_FENCE_NAME_SIZE];
};
+/**
+ * enum sde_fence_event - sde fence event as hint fence operation
+ * @SDE_FENCE_SIGNAL: Signal the fence cleanly with current timeline
+ * @SDE_FENCE_RESET_TIMELINE: Reset timeline of the fence context
+ * @SDE_FENCE_SIGNAL: Signal the fence but indicate error throughfence status
+ */
+enum sde_fence_event {
+ SDE_FENCE_SIGNAL,
+ SDE_FENCE_RESET_TIMELINE,
+ SDE_FENCE_SIGNAL_ERROR
+};
+
#if IS_ENABLED(CONFIG_SYNC_FILE)
/**
* sde_sync_get - Query sync fence object from a file handle
@@ -128,10 +140,10 @@
* sde_fence_signal - advance fence timeline to signal outstanding fences
* @fence: Pointer fence container
* @ts: fence timestamp
- * @reset_timeline: reset the fence timeline to done count equal to commit count
+ * @fence_event: fence event to indicate nature of fence signal.
*/
void sde_fence_signal(struct sde_fence_context *fence, ktime_t ts,
- bool reset_timeline);
+ enum sde_fence_event fence_event);
/**
* sde_fence_timeline_status - prints fence timeline status
@@ -141,6 +153,22 @@
void sde_fence_timeline_status(struct sde_fence_context *ctx,
struct drm_mode_object *drm_obj);
+/**
+ * sde_fence_timeline_dump - utility to dump fence list info in debugfs node
+ * @fence: Pointer fence container
+ * @drm_obj: Pointer to drm object associated with fence timeline
+ * @s: used to writing on debugfs node
+ */
+void sde_debugfs_timeline_dump(struct sde_fence_context *ctx,
+ struct drm_mode_object *drm_obj, struct seq_file **s);
+
+/**
+ * sde_fence_timeline_status - dumps fence timeline in debugfs node
+ * @fence: Pointer fence container
+ * @s: used to writing on debugfs node
+ */
+void sde_fence_list_dump(struct fence *fence, struct seq_file **s);
+
#else
static inline void *sde_sync_get(uint64_t fd)
{
@@ -200,6 +228,18 @@
{
/* do nothing */
}
+
+void sde_debugfs_timeline_dump(struct sde_fence_context *ctx,
+ struct drm_mode_object *drm_obj, struct seq_file **s)
+{
+ /* do nothing */
+}
+
+void sde_fence_list_dump(struct fence *fence, struct seq_file **s)
+{
+ /* do nothing */
+}
+
#endif /* IS_ENABLED(CONFIG_SW_SYNC) */
#endif /* _SDE_FENCE_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
index 66445da..e60defd 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
@@ -109,6 +109,9 @@
static int ad4_cfg_ipc_reset(struct sde_hw_dspp *dspp,
struct sde_ad_hw_cfg *cfg);
+static int ad4_vsync_update(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg);
+
static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
[ad4_state_idle][AD_MODE] = ad4_mode_setup_common,
[ad4_state_idle][AD_INIT] = ad4_init_setup_idle,
@@ -121,6 +124,7 @@
[ad4_state_idle][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_idle][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_idle][AD_IPC_RESET] = ad4_no_op_setup,
+ [ad4_state_idle][AD_VSYNC_UPDATE] = ad4_no_op_setup,
[ad4_state_startup][AD_MODE] = ad4_mode_setup_common,
[ad4_state_startup][AD_INIT] = ad4_init_setup,
@@ -133,6 +137,7 @@
[ad4_state_startup][AD_STRENGTH] = ad4_no_op_setup,
[ad4_state_startup][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_startup][AD_IPC_RESET] = ad4_ipc_reset_setup_startup,
+ [ad4_state_startup][AD_VSYNC_UPDATE] = ad4_vsync_update,
[ad4_state_run][AD_MODE] = ad4_mode_setup_common,
[ad4_state_run][AD_INIT] = ad4_init_setup_run,
@@ -145,6 +150,7 @@
[ad4_state_run][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_run,
[ad4_state_run][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_run][AD_IPC_RESET] = ad4_setup_debug,
+ [ad4_state_run][AD_VSYNC_UPDATE] = ad4_vsync_update,
[ad4_state_ipcs][AD_MODE] = ad4_no_op_setup,
[ad4_state_ipcs][AD_INIT] = ad4_no_op_setup,
@@ -157,6 +163,7 @@
[ad4_state_ipcs][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_ipcs][AD_IPC_RESUME] = ad4_ipc_resume_setup_ipcs,
[ad4_state_ipcs][AD_IPC_RESET] = ad4_no_op_setup,
+ [ad4_state_ipcs][AD_VSYNC_UPDATE] = ad4_no_op_setup,
[ad4_state_ipcr][AD_MODE] = ad4_mode_setup_common,
[ad4_state_ipcr][AD_INIT] = ad4_init_setup_ipcr,
@@ -169,6 +176,7 @@
[ad4_state_ipcr][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_ipcr,
[ad4_state_ipcr][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_ipcr][AD_IPC_RESET] = ad4_ipc_reset_setup_ipcr,
+ [ad4_state_ipcr][AD_VSYNC_UPDATE] = ad4_no_op_setup,
[ad4_state_manual][AD_MODE] = ad4_mode_setup_common,
[ad4_state_manual][AD_INIT] = ad4_init_setup,
@@ -181,6 +189,7 @@
[ad4_state_manual][AD_IPC_SUSPEND] = ad4_no_op_setup,
[ad4_state_manual][AD_IPC_RESUME] = ad4_no_op_setup,
[ad4_state_manual][AD_IPC_RESET] = ad4_setup_debug_manual,
+ [ad4_state_manual][AD_VSYNC_UPDATE] = ad4_no_op_setup,
};
struct ad4_info {
@@ -201,6 +210,7 @@
u32 irdx_control_0;
u32 tf_ctrl;
u32 vc_control_0;
+ u32 frame_pushes;
};
static struct ad4_info info[DSPP_MAX] = {
@@ -905,6 +915,8 @@
val = (ad_cfg->cfg_param_046 & (BIT(16) - 1));
SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
+ info[dspp->idx].frame_pushes = ad_cfg->cfg_param_047;
+
return 0;
}
@@ -1546,3 +1558,25 @@
ad4_mode_setup(dspp, info[dspp->idx].mode);
return 0;
}
+
+static int ad4_vsync_update(struct sde_hw_dspp *dspp,
+ struct sde_ad_hw_cfg *cfg)
+{
+ u32 *count;
+ struct sde_hw_mixer *hw_lm;
+
+ if (cfg->hw_cfg->len != sizeof(u32) || !cfg->hw_cfg->payload) {
+ DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
+ sizeof(u32), cfg->hw_cfg->len, cfg->hw_cfg->payload);
+ return -EINVAL;
+ }
+
+ count = (u32 *)(cfg->hw_cfg->payload);
+ hw_lm = cfg->hw_cfg->mixer_info;
+
+ if (hw_lm && !hw_lm->cfg.right_mixer &&
+ (*count < info[dspp->idx].frame_pushes))
+ (*count)++;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index eacafe6..baec526 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -249,6 +249,7 @@
DITHER_OFF,
DITHER_LEN,
DITHER_VER,
+ TE_SOURCE,
PP_PROP_MAX,
};
@@ -589,6 +590,7 @@
{DITHER_OFF, "qcom,sde-dither-off", false, PROP_TYPE_U32_ARRAY},
{DITHER_LEN, "qcom,sde-dither-size", false, PROP_TYPE_U32},
{DITHER_VER, "qcom,sde-dither-version", false, PROP_TYPE_U32},
+ {TE_SOURCE, "qcom,sde-te-source", false, PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type dsc_prop[] = {
@@ -1694,6 +1696,9 @@
set_bit(SDE_WB_XY_ROI_OFFSET, &wb->features);
+ if (sde_cfg->has_cwb_support)
+ set_bit(SDE_WB_HAS_CWB, &wb->features);
+
for (j = 0; j < sde_cfg->mdp_count; j++) {
sde_cfg->mdp[j].clk_ctrls[wb->clk_ctrl].reg_off =
PROP_BITVALUE_ACCESS(prop_value,
@@ -2625,6 +2630,10 @@
snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u",
pp->id - PINGPONG_0);
pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0);
+ pp->te_source = PROP_VALUE_ACCESS(prop_value, TE_SOURCE, i);
+ if (!prop_exists[TE_SOURCE] ||
+ pp->te_source > SDE_VSYNC_SOURCE_WD_TIMER_0)
+ pp->te_source = SDE_VSYNC0_SOURCE_GPIO;
sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i);
sblk->te.id = SDE_PINGPONG_TE;
@@ -3248,6 +3257,7 @@
} else if (IS_SDM845_TARGET(hw_rev)) {
/* update sdm845 target here */
sde_cfg->has_wb_ubwc = true;
+ sde_cfg->has_cwb_support = true;
sde_cfg->perf.min_prefill_lines = 24;
sde_cfg->vbif_qos_nlvl = 8;
sde_cfg->ts_prefill_rev = 2;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 963b48f..52bdc78 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -261,6 +261,7 @@
* @SDE_WB_QOS, Writeback supports QoS control, danger/safe/creq
* @SDE_WB_QOS_8LVL, Writeback supports 8-level QoS control
* @SDE_WB_CDP Writeback supports client driven prefetch
+ * @SDE_WB_HAS_CWB Writeback block supports concurrent writeback
* @SDE_WB_MAX maximum value
*/
enum {
@@ -279,6 +280,7 @@
SDE_WB_QOS,
SDE_WB_QOS_8LVL,
SDE_WB_CDP,
+ SDE_WB_HAS_CWB,
SDE_WB_MAX
};
@@ -651,6 +653,7 @@
*/
struct sde_pingpong_cfg {
SDE_HW_BLK_INFO;
+ u32 te_source;
const struct sde_pingpong_sub_blks *sblk;
};
@@ -922,6 +925,7 @@
* @has_src_split source split feature status
* @has_cdp Client driven prefetch feature status
* @has_wb_ubwc UBWC feature supported on WB
+ * @has_cwb_support indicates if device supports primary capture through CWB
* @ubwc_version UBWC feature version (0x0 for not supported)
* @has_sbuf indicate if stream buffer is available
* @sbuf_headroom stream buffer headroom in lines
@@ -959,6 +963,7 @@
bool has_cdp;
bool has_dim_layer;
bool has_wb_ubwc;
+ bool has_cwb_support;
u32 ubwc_version;
bool has_sbuf;
u32 sbuf_headroom;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index 9e64d78..c7989cd 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -680,7 +680,7 @@
void __iomem *base;
if (!hw_cfg || (hw_cfg->len != sizeof(*pcc) && hw_cfg->payload)) {
- DRM_ERROR("invalid params hw %p payload %p payloadsize %d \"\
+ DRM_ERROR("invalid params hw %pK payload %pK payloadsize %d \"\
exp size %zd\n",
hw_cfg, ((hw_cfg) ? hw_cfg->payload : NULL),
((hw_cfg) ? hw_cfg->len : 0), sizeof(*pcc));
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 303d96e..2146611 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -576,6 +576,23 @@
SDE_REG_WRITE(c, CTL_TOP, intf_cfg);
}
+static void sde_hw_ctl_update_wb_cfg(struct sde_hw_ctl *ctx,
+ struct sde_hw_intf_cfg *cfg, bool enable) {
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 intf_cfg = 0;
+
+ if (!cfg->wb)
+ return;
+
+ intf_cfg = SDE_REG_READ(c, CTL_TOP);
+ if (enable)
+ intf_cfg |= (cfg->wb & 0x3) + 2;
+ else
+ intf_cfg &= ~((cfg->wb & 0x3) + 2);
+
+ SDE_REG_WRITE(c, CTL_TOP, intf_cfg);
+}
+
static inline u32 sde_hw_ctl_read_ctl_top(struct sde_hw_ctl *ctx)
{
struct sde_hw_blk_reg_map *c;
@@ -638,6 +655,7 @@
ops->read_ctl_top = sde_hw_ctl_read_ctl_top;
ops->read_ctl_layers = sde_hw_ctl_read_ctl_layers;
ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
+ ops->update_wb_cfg = sde_hw_ctl_update_wb_cfg;
ops->reset = sde_hw_ctl_reset_control;
ops->get_reset = sde_hw_ctl_get_reset_status;
ops->hard_reset = sde_hw_ctl_hard_reset;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 9eb31f1..75631dd8 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -150,6 +150,15 @@
void (*setup_intf_cfg)(struct sde_hw_ctl *ctx,
struct sde_hw_intf_cfg *cfg);
+ /**
+ * Update the interface selection with input WB config
+ * @ctx : ctl path ctx pointer
+ * @cfg : pointer to input wb config
+ * @enable : set if true, clear otherwise
+ */
+ void (*update_wb_cfg)(struct sde_hw_ctl *ctx,
+ struct sde_hw_intf_cfg *cfg, bool enable);
+
int (*reset)(struct sde_hw_ctl *c);
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 4e677c2..97a3ea4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -179,7 +179,7 @@
u32 reset = BIT(16), val;
reset = ~reset;
- for (i = SDE_STAGE_0; i < sblk->maxblendstages; i++) {
+ for (i = SDE_STAGE_0; i <= sblk->maxblendstages; i++) {
stage_off = _stage_offset(ctx, i);
if (WARN_ON(stage_off < 0))
return;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 8022f23..9b4e03d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -570,7 +570,8 @@
/**
* struct sde_splash_data - Struct contains details of continuous splash
* memory region and initial pipeline configuration.
- * @smmu_handoff_pending:boolean to notify handoff from splash memory to smmu
+ * @resource_handoff_pending: boolean to notify boot up resource handoff
+ * is pending.
* @splash_base: Base address of continuous splash region reserved
* by bootloader
* @splash_size: Size of continuous splash region
@@ -585,7 +586,7 @@
* @single_flush_en: Stores if the single flush is enabled.
*/
struct sde_splash_data {
- bool smmu_handoff_pending;
+ bool resource_handoff_pending;
unsigned long splash_base;
u32 splash_size;
struct ctl_top top[CTL_MAX - CTL_0];
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 8b89ebb..cf646bd 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -376,6 +376,18 @@
SDE_REG_WRITE(c, HDMI_DP_CORE_SELECT, 0x1);
}
+static void sde_hw_program_cwb_ppb_ctrl(struct sde_hw_mdp *mdp,
+ bool dual, bool dspp_out)
+{
+ u32 value = dspp_out ? 0x4 : 0x0;
+
+ SDE_REG_WRITE(&mdp->hw, PPB2_CNTL, value);
+ if (dual) {
+ value |= 0x1;
+ SDE_REG_WRITE(&mdp->hw, PPB3_CNTL, value);
+ }
+}
+
static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
unsigned long cap)
{
@@ -385,6 +397,7 @@
ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl;
ops->get_danger_status = sde_hw_get_danger_status;
ops->setup_vsync_source = sde_hw_setup_vsync_source;
+ ops->set_cwb_ppb_cntl = sde_hw_program_cwb_ppb_ctrl;
ops->get_safe_status = sde_hw_get_safe_status;
ops->get_split_flush_status = sde_hw_get_split_flush;
ops->setup_dce = sde_hw_setup_dce;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.h b/drivers/gpu/drm/msm/sde/sde_hw_top.h
index 950a62c..210ea53 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.h
@@ -194,6 +194,15 @@
* @mdp: mdp top context driver
*/
void (*intf_audio_select)(struct sde_hw_mdp *mdp);
+
+ /**
+ * set_cwb_ppb_cntl - select the data point for CWB
+ * @mdp: mdp top context driver
+ * @dual: indicates if dual pipe line needs to be programmed
+ * @dspp_out : true if dspp output required. LM is default tap point
+ */
+ void (*set_cwb_ppb_cntl)(struct sde_hw_mdp *mdp,
+ bool dual, bool dspp_out);
};
struct sde_hw_mdp {
diff --git a/drivers/gpu/drm/msm/sde/sde_hwio.h b/drivers/gpu/drm/msm/sde/sde_hwio.h
index cc020d9..a592223 100644
--- a/drivers/gpu/drm/msm/sde/sde_hwio.h
+++ b/drivers/gpu/drm/msm/sde/sde_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -40,6 +40,8 @@
#define PPB0_CONFIG 0x334
#define PPB1_CNTL 0x338
#define PPB1_CONFIG 0x33C
+#define PPB2_CNTL 0x370
+#define PPB3_CNTL 0x374
#define HW_EVENTS_CTL 0x37C
#define CLK_CTRL3 0x3A8
#define CLK_STATUS3 0x3AC
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 33bdec9..c2fffef 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -936,9 +936,6 @@
}
}
- if (sde_kms->splash_data.smmu_handoff_pending)
- sde_kms->splash_data.smmu_handoff_pending = false;
-
/*
* NOTE: for secure use cases we want to apply the new HW
* configuration only after completing preparation for secure
@@ -972,6 +969,62 @@
}
}
+static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ bool primary_crtc_active = false;
+ struct msm_drm_private *priv;
+ int i, rc = 0;
+
+ priv = sde_kms->dev->dev_private;
+
+ if (!sde_kms->splash_data.resource_handoff_pending)
+ return;
+
+ SDE_EVT32(SDE_EVTLOG_FUNC_CASE1);
+ for_each_crtc_in_state(old_state, crtc, crtc_state, i) {
+ if (crtc->state->active)
+ primary_crtc_active = true;
+ SDE_EVT32(crtc->base.id, crtc->state->active);
+ }
+
+ if (!primary_crtc_active) {
+ SDE_EVT32(SDE_EVTLOG_FUNC_CASE2);
+ return;
+ }
+
+ sde_kms->splash_data.resource_handoff_pending = false;
+
+ if (sde_kms->splash_data.cont_splash_en) {
+ SDE_DEBUG("disabling cont_splash feature\n");
+ sde_kms->splash_data.cont_splash_en = false;
+
+ for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
+ sde_power_data_bus_set_quota(&priv->phandle,
+ sde_kms->core_client,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, i,
+ SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
+ SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
+
+ sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+ false);
+ }
+
+ if (sde_kms->splash_data.splash_base) {
+ _sde_kms_splash_smmu_unmap(sde_kms);
+
+ rc = _sde_kms_release_splash_buffer(
+ sde_kms->splash_data.splash_base,
+ sde_kms->splash_data.splash_size);
+ if (rc)
+ pr_err("failed to release splash memory\n");
+ sde_kms->splash_data.splash_base = 0;
+ sde_kms->splash_data.splash_size = 0;
+ }
+}
+
static void sde_kms_complete_commit(struct msm_kms *kms,
struct drm_atomic_state *old_state)
{
@@ -1019,39 +1072,9 @@
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ _sde_kms_release_splash_resource(sde_kms, old_state);
+
SDE_EVT32_VERBOSE(SDE_EVTLOG_FUNC_EXIT);
-
- if (sde_kms->splash_data.cont_splash_en) {
- /* Releasing splash resources as we have first frame update */
- rc = _sde_kms_splash_smmu_unmap(sde_kms);
- SDE_DEBUG("Disabling cont_splash feature\n");
- sde_kms->splash_data.cont_splash_en = false;
-
- for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
- sde_power_data_bus_set_quota(&priv->phandle,
- sde_kms->core_client,
- SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, i,
- SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
- SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
-
- sde_power_resource_enable(&priv->phandle,
- sde_kms->core_client, false);
- SDE_DEBUG("removing Vote for MDP Resources\n");
- }
-
- /*
- * Even for continuous splash disabled cases we have to release
- * splash memory reservation back to system after first frame update.
- */
- if (sde_kms->splash_data.splash_base) {
- rc = _sde_kms_release_splash_buffer(
- sde_kms->splash_data.splash_base,
- sde_kms->splash_data.splash_size);
- if (rc)
- pr_err("Failed to release splash memory\n");
- sde_kms->splash_data.splash_base = 0;
- sde_kms->splash_data.splash_size = 0;
- }
}
static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
@@ -2128,8 +2151,8 @@
aspace->mmu->funcs->attach(mmu, (const char **)iommu_ports,
ARRAY_SIZE(iommu_ports));
- msm_gem_aspace_domain_attach_detach_update(aspace, false);
aspace->domain_attached = true;
+ msm_gem_aspace_domain_attach_detach_update(aspace, false);
}
return 0;
@@ -2641,21 +2664,22 @@
mutex_lock(&dev->mode_config.mutex);
connector_list = &dev->mode_config.connector_list;
- list_for_each_entry(conn_iter, connector_list, head) {
- /**
- * SDE_KMS doesn't attach more than one encoder to
- * a DSI connector. So it is safe to check only with the
- * first encoder entry. Revisit this logic if we ever have
- * to support continuous splash for external displays in MST
- * configuration.
- */
- if (conn_iter &&
- (conn_iter->encoder_ids[0] == encoder->base.id)) {
- connector = conn_iter;
- break;
+ if (connector_list) {
+ list_for_each_entry(conn_iter, connector_list, head) {
+ /**
+ * SDE_KMS doesn't attach more than one encoder to
+ * a DSI connector. So it is safe to check only with
+ * the first encoder entry. Revisit this logic if we
+ * ever have to support continuous splash for
+ * external displays in MST configuration.
+ */
+ if (conn_iter &&
+ (conn_iter->encoder_ids[0] == encoder->base.id)) {
+ connector = conn_iter;
+ break;
+ }
}
}
-
if (!connector) {
SDE_ERROR("connector not initialized\n");
mutex_unlock(&dev->mode_config.mutex);
@@ -2704,6 +2728,19 @@
return rc;
}
+static bool sde_kms_check_for_splash(struct msm_kms *kms)
+{
+ struct sde_kms *sde_kms;
+
+ if (!kms) {
+ SDE_ERROR("invalid kms\n");
+ return false;
+ }
+
+ sde_kms = to_sde_kms(kms);
+ return sde_kms->splash_data.cont_splash_en;
+}
+
static int sde_kms_pm_suspend(struct device *dev)
{
struct drm_device *ddev;
@@ -2904,6 +2941,7 @@
.register_events = _sde_kms_register_events,
.get_address_space = _sde_kms_get_address_space,
.postopen = _sde_kms_post_open,
+ .check_for_splash = sde_kms_check_for_splash,
};
/* the caller api needs to turn on clock before calling it */
@@ -2956,8 +2994,7 @@
* address. To facilitate this requirement we need to have a
* one to one mapping on SMMU until we have our first frame.
*/
- if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
- sde_kms->splash_data.smmu_handoff_pending) {
+ if (i == MSM_SMMU_DOMAIN_UNSECURE) {
ret = mmu->funcs->set_attribute(mmu,
DOMAIN_ATTR_EARLY_MAP,
&early_map);
@@ -2986,27 +3023,27 @@
}
aspace->domain_attached = true;
early_map = 0;
+
/* Mapping splash memory block */
if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
- sde_kms->splash_data.smmu_handoff_pending) {
+ sde_kms->splash_data.splash_base) {
ret = _sde_kms_splash_smmu_map(sde_kms->dev, mmu,
&sde_kms->splash_data);
if (ret) {
SDE_ERROR("failed to map ret:%d\n", ret);
goto fail;
}
- /*
- * Turning off early map after generating one to one
- * mapping for splash address space.
- */
- ret = mmu->funcs->set_attribute(mmu,
- DOMAIN_ATTR_EARLY_MAP,
- &early_map);
- if (ret) {
- SDE_ERROR("failed to set map att ret:%d\n",
- ret);
- goto early_map_fail;
- }
+ }
+
+ /*
+ * Turning off early map after generating one to one
+ * mapping for splash address space.
+ */
+ ret = mmu->funcs->set_attribute(mmu, DOMAIN_ATTR_EARLY_MAP,
+ &early_map);
+ if (ret) {
+ SDE_ERROR("failed to set map att ret:%d\n", ret);
+ goto early_map_fail;
}
}
@@ -3159,7 +3196,7 @@
sde_kms->mmio = NULL;
goto error;
}
- DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio);
+ DRM_INFO("mapped mdp address space @%pK\n", sde_kms->mmio);
sde_kms->mmio_len = msm_iomap_size(dev->platformdev, "mdp_phys");
rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio,
@@ -3285,12 +3322,7 @@
&sde_kms->splash_data,
sde_kms->catalog);
- /*
- * SMMU handoff is necessary for continuous splash enabled
- * scenario.
- */
- if (sde_kms->splash_data.cont_splash_en)
- sde_kms->splash_data.smmu_handoff_pending = true;
+ sde_kms->splash_data.resource_handoff_pending = true;
/* Initialize reg dma block which is a singleton */
rc = sde_reg_dma_init(sde_kms->reg_dma, sde_kms->catalog,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 2e46599..dc55ad5 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -3449,6 +3449,25 @@
src_w, src_h);
return -EINVAL;
}
+
+ /*
+ * SSPP fetch , unpack output and QSEED3 input lines need
+ * to match for Y plane
+ */
+ if (i == 0 &&
+ (sde_plane_get_property(pstate, PLANE_PROP_SRC_CONFIG) &
+ BIT(SDE_DRM_DEINTERLACE)) &&
+ ((pstate->scaler3_cfg.src_height[i] != (src_h/2)) ||
+ (pstate->pixel_ext.roi_h[i] != (src_h/2)))) {
+ SDE_ERROR_PLANE(psde,
+ "de-interlace fail roi[%d] %d/%d, src %dx%d, src %dx%d\n",
+ i, pstate->pixel_ext.roi_w[i],
+ pstate->pixel_ext.roi_h[i],
+ pstate->scaler3_cfg.src_width[i],
+ pstate->scaler3_cfg.src_height[i],
+ src_w, src_h);
+ return -EINVAL;
+ }
}
pstate->scaler_check_state = SDE_PLANE_SCLCHECK_SCALER_V2;
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
index 3a67a22..758cc53 100644
--- a/drivers/gpu/drm/msm/sde_dbg.c
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -169,6 +169,11 @@
struct vbif_debug_bus_entry *entries;
};
+struct sde_dbg_dsi_debug_bus {
+ u32 *entries;
+ u32 size;
+};
+
/**
* struct sde_dbg_base - global sde debug base structure
* @evtlog: event log instance
@@ -202,6 +207,7 @@
struct sde_dbg_sde_debug_bus dbgbus_sde;
struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt;
+ struct sde_dbg_dsi_debug_bus dbgbus_dsi;
bool dump_all;
bool dsi_dbg_bus;
u32 debugfs_ctrl;
@@ -2038,6 +2044,42 @@
{0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */
};
+static u32 dsi_dbg_bus_sdm845[] = {
+ 0x0001, 0x1001, 0x0001, 0x0011,
+ 0x1021, 0x0021, 0x0031, 0x0041,
+ 0x0051, 0x0061, 0x3061, 0x0061,
+ 0x2061, 0x2061, 0x1061, 0x1061,
+ 0x1061, 0x0071, 0x0071, 0x0071,
+ 0x0081, 0x0081, 0x00A1, 0x00A1,
+ 0x10A1, 0x20A1, 0x30A1, 0x10A1,
+ 0x10A1, 0x30A1, 0x20A1, 0x00B1,
+ 0x00C1, 0x00C1, 0x10C1, 0x20C1,
+ 0x30C1, 0x00D1, 0x00D1, 0x20D1,
+ 0x30D1, 0x00E1, 0x00E1, 0x00E1,
+ 0x00F1, 0x00F1, 0x0101, 0x0101,
+ 0x1101, 0x2101, 0x3101, 0x0111,
+ 0x0141, 0x1141, 0x0141, 0x1141,
+ 0x1141, 0x0151, 0x0151, 0x1151,
+ 0x2151, 0x3151, 0x0161, 0x0161,
+ 0x1161, 0x0171, 0x0171, 0x0181,
+ 0x0181, 0x0191, 0x0191, 0x01A1,
+ 0x01A1, 0x01B1, 0x01B1, 0x11B1,
+ 0x21B1, 0x01C1, 0x01C1, 0x11C1,
+ 0x21C1, 0x31C1, 0x01D1, 0x01D1,
+ 0x01D1, 0x01D1, 0x11D1, 0x21D1,
+ 0x21D1, 0x01E1, 0x01E1, 0x01F1,
+ 0x01F1, 0x0201, 0x0201, 0x0211,
+ 0x0221, 0x0231, 0x0241, 0x0251,
+ 0x0281, 0x0291, 0x0281, 0x0291,
+ 0x02A1, 0x02B1, 0x02C1, 0x0321,
+ 0x0321, 0x1321, 0x2321, 0x3321,
+ 0x0331, 0x0331, 0x1331, 0x0341,
+ 0x0341, 0x1341, 0x2341, 0x3341,
+ 0x0351, 0x0361, 0x0361, 0x1361,
+ 0x2361, 0x0371, 0x0381, 0x0391,
+ 0x03C1, 0x03D1, 0x03E1, 0x03F1,
+};
+
/**
* _sde_dbg_enable_power - use callback to turn power on for hw register access
* @enable: whether to turn power on or off
@@ -2603,7 +2645,8 @@
_sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt);
if (sde_dbg_base.dsi_dbg_bus || dump_all)
- dsi_ctrl_debug_dump();
+ dsi_ctrl_debug_dump(sde_dbg_base.dbgbus_dsi.entries,
+ sde_dbg_base.dbgbus_dsi.size);
if (do_panic && sde_dbg_base.panic_on_err)
panic(name);
@@ -2785,6 +2828,11 @@
len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
SDE_EVTLOG_BUF_MAX, true);
+ if (len < 0 || len > count) {
+ pr_err("len is more than user buffer size");
+ return 0;
+ }
+
if (copy_to_user(buff, evtlog_buf, len))
return -EFAULT;
*ppos += len;
@@ -3350,6 +3398,8 @@
dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
dbg->dbgbus_vbif_rt.cmn.entries_size =
ARRAY_SIZE(vbif_dbg_bus_msm8998);
+ dbg->dbgbus_dsi.entries = NULL;
+ dbg->dbgbus_dsi.size = 0;
} else if (IS_SDM845_TARGET(hwversion) || IS_SDM670_TARGET(hwversion)) {
dbg->dbgbus_sde.entries = dbg_bus_sde_sdm845;
dbg->dbgbus_sde.cmn.entries_size =
@@ -3360,6 +3410,8 @@
dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
dbg->dbgbus_vbif_rt.cmn.entries_size =
ARRAY_SIZE(vbif_dbg_bus_msm8998);
+ dbg->dbgbus_dsi.entries = dsi_dbg_bus_sdm845;
+ dbg->dbgbus_dsi.size = ARRAY_SIZE(dsi_dbg_bus_sdm845);
} else {
pr_err("unsupported chipset id %X\n", hwversion);
}
diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h
index 9efb893..2921a38 100644
--- a/drivers/gpu/drm/msm/sde_dbg.h
+++ b/drivers/gpu/drm/msm/sde_dbg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -344,8 +344,10 @@
/**
* dsi_ctrl_debug_dump - dump dsi debug dump status
+ * @entries: array of debug bus control values
+ * @size: size of the debug bus control array
*/
-void dsi_ctrl_debug_dump(void);
+void dsi_ctrl_debug_dump(u32 *entries, u32 size);
#else
static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
@@ -437,7 +439,7 @@
{
}
-static inline void dsi_ctrl_debug_dump(void)
+static inline void dsi_ctrl_debug_dump(u32 entries, u32 size)
{
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index c108408..56c288f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -271,9 +271,15 @@
nv_connector->edid = NULL;
}
- ret = pm_runtime_get_sync(connector->dev->dev);
- if (ret < 0 && ret != -EACCES)
- return conn_status;
+ /* Outputs are only polled while runtime active, so acquiring a
+ * runtime PM ref here is unnecessary (and would deadlock upon
+ * runtime suspend because it waits for polling to finish).
+ */
+ if (!drm_kms_helper_is_poll_worker()) {
+ ret = pm_runtime_get_sync(connector->dev->dev);
+ if (ret < 0 && ret != -EACCES)
+ return conn_status;
+ }
nv_encoder = nouveau_connector_ddc_detect(connector);
if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
@@ -348,8 +354,10 @@
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return conn_status;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 2c2b86d..6526a33 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -106,7 +106,7 @@
};
struct nouveau_display *disp = nouveau_display(crtc->dev);
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
- int ret, retry = 1;
+ int ret, retry = 20;
do {
ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
index a4cb824..245c946 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
@@ -136,6 +136,13 @@
return ret;
pci->irq = pdev->irq;
+
+ /* Ensure MSI interrupts are armed, for the case where there are
+ * already interrupts pending (for whatever reason) at load time.
+ */
+ if (pci->msi)
+ pci->func->msi_rearm(pci);
+
return ret;
}
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
index e859b3f..6112c32 100644
--- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
+++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
@@ -456,6 +456,8 @@
}
static const struct of_device_id td028ttec1_of_match[] = {
+ { .compatible = "omapdss,tpo,td028ttec1", },
+ /* keep to not break older DTB */
{ .compatible = "omapdss,toppoly,td028ttec1", },
{},
};
@@ -475,6 +477,7 @@
module_spi_driver(td028ttec1_spi_driver);
+MODULE_ALIAS("spi:tpo,td028ttec1");
MODULE_ALIAS("spi:toppoly,td028ttec1");
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 4b83e9e..7def040 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -298,7 +298,12 @@
msecs_to_jiffies(100))) {
dev_err(dmm->dev, "timed out waiting for done\n");
ret = -ETIMEDOUT;
+ goto cleanup;
}
+
+ /* Check the engine status before continue */
+ ret = wait_status(engine, DMM_PATSTATUS_READY |
+ DMM_PATSTATUS_VALID | DMM_PATSTATUS_DONE);
}
cleanup:
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index 505dee0db..ca91651 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -195,7 +195,7 @@
size_t size = PAGE_SIZE * n;
loff_t off = mmap_offset(obj) +
(entry->obj_pgoff << PAGE_SHIFT);
- const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
+ const int m = DIV_ROUND_UP(omap_obj->width << fmt, PAGE_SIZE);
if (m > 1) {
int i;
@@ -442,7 +442,7 @@
* into account in some of the math, so figure out virtual stride
* in pages
*/
- const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
+ const int m = DIV_ROUND_UP(omap_obj->width << fmt, PAGE_SIZE);
/* We don't use vmf->pgoff since that has the fake offset: */
pgoff = ((unsigned long)vmf->virtual_address -
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index 2cd879a..cdbb6e6 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -387,9 +387,11 @@
int qxl_fbdev_init(struct qxl_device *qdev)
{
+ int ret = 0;
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
struct qxl_fbdev *qfbdev;
int bpp_sel = 32; /* TODO: parameter from somewhere? */
- int ret;
qfbdev = kzalloc(sizeof(struct qxl_fbdev), GFP_KERNEL);
if (!qfbdev)
@@ -423,6 +425,8 @@
drm_fb_helper_fini(&qfbdev->helper);
free:
kfree(qfbdev);
+#endif
+
return ret;
}
@@ -438,6 +442,9 @@
void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
{
+ if (!qdev->mode_info.qfbdev)
+ return;
+
drm_fb_helper_set_suspend(&qdev->mode_info.qfbdev->helper, state);
}
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index edee6a5..b99f3e5 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -3244,35 +3244,8 @@
case CHIP_KAVERI:
rdev->config.cik.max_shader_engines = 1;
rdev->config.cik.max_tile_pipes = 4;
- if ((rdev->pdev->device == 0x1304) ||
- (rdev->pdev->device == 0x1305) ||
- (rdev->pdev->device == 0x130C) ||
- (rdev->pdev->device == 0x130F) ||
- (rdev->pdev->device == 0x1310) ||
- (rdev->pdev->device == 0x1311) ||
- (rdev->pdev->device == 0x131C)) {
- rdev->config.cik.max_cu_per_sh = 8;
- rdev->config.cik.max_backends_per_se = 2;
- } else if ((rdev->pdev->device == 0x1309) ||
- (rdev->pdev->device == 0x130A) ||
- (rdev->pdev->device == 0x130D) ||
- (rdev->pdev->device == 0x1313) ||
- (rdev->pdev->device == 0x131D)) {
- rdev->config.cik.max_cu_per_sh = 6;
- rdev->config.cik.max_backends_per_se = 2;
- } else if ((rdev->pdev->device == 0x1306) ||
- (rdev->pdev->device == 0x1307) ||
- (rdev->pdev->device == 0x130B) ||
- (rdev->pdev->device == 0x130E) ||
- (rdev->pdev->device == 0x1315) ||
- (rdev->pdev->device == 0x1318) ||
- (rdev->pdev->device == 0x131B)) {
- rdev->config.cik.max_cu_per_sh = 4;
- rdev->config.cik.max_backends_per_se = 1;
- } else {
- rdev->config.cik.max_cu_per_sh = 3;
- rdev->config.cik.max_backends_per_se = 1;
- }
+ rdev->config.cik.max_cu_per_sh = 8;
+ rdev->config.cik.max_backends_per_se = 2;
rdev->config.cik.max_sh_per_se = 1;
rdev->config.cik.max_texture_channel_caches = 4;
rdev->config.cik.max_gprs = 256;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 27affbd..f416f5c 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -90,25 +90,18 @@
/* don't do anything if sink is not display port, i.e.,
* passive dp->(dvi|hdmi) adaptor
*/
- if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
- int saved_dpms = connector->dpms;
- /* Only turn off the display if it's physically disconnected */
- if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
- } else if (radeon_dp_needs_link_train(radeon_connector)) {
- /* Don't try to start link training before we
- * have the dpcd */
- if (!radeon_dp_getdpcd(radeon_connector))
- return;
+ if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT &&
+ radeon_hpd_sense(rdev, radeon_connector->hpd.hpd) &&
+ radeon_dp_needs_link_train(radeon_connector)) {
+ /* Don't start link training before we have the DPCD */
+ if (!radeon_dp_getdpcd(radeon_connector))
+ return;
- /* set it to OFF so that drm_helper_connector_dpms()
- * won't return immediately since the current state
- * is ON at this point.
- */
- connector->dpms = DRM_MODE_DPMS_OFF;
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
- }
- connector->dpms = saved_dpms;
+ /* Turn the connector off and back on immediately, which
+ * will trigger link training
+ */
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
}
}
}
@@ -897,9 +890,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (encoder) {
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -922,8 +917,12 @@
/* check acpi lid status ??? */
radeon_connector_update_scratch_regs(connector, ret);
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
+
return ret;
}
@@ -1037,9 +1036,11 @@
enum drm_connector_status ret = connector_status_disconnected;
int r;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
encoder = radeon_best_single_encoder(connector);
if (!encoder)
@@ -1106,8 +1107,10 @@
radeon_connector_update_scratch_regs(connector, ret);
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -1171,9 +1174,11 @@
if (!radeon_connector->dac_load_detect)
return ret;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
encoder = radeon_best_single_encoder(connector);
if (!encoder)
@@ -1185,8 +1190,12 @@
if (ret == connector_status_connected)
ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
radeon_connector_update_scratch_regs(connector, ret);
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
+
return ret;
}
@@ -1249,9 +1258,11 @@
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false, broken_edid = false;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (radeon_connector->detected_hpd_without_ddc) {
force = true;
@@ -1434,8 +1445,10 @@
}
exit:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
@@ -1686,9 +1699,11 @@
if (radeon_dig_connector->is_mst)
return connector_status_disconnected;
- r = pm_runtime_get_sync(connector->dev->dev);
- if (r < 0)
- return connector_status_disconnected;
+ if (!drm_kms_helper_is_poll_worker()) {
+ r = pm_runtime_get_sync(connector->dev->dev);
+ if (r < 0)
+ return connector_status_disconnected;
+ }
if (!force && radeon_check_hpd_status_unchanged(connector)) {
ret = connector->status;
@@ -1775,8 +1790,10 @@
}
out:
- pm_runtime_mark_last_busy(connector->dev->dev);
- pm_runtime_put_autosuspend(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker()) {
+ pm_runtime_mark_last_busy(connector->dev->dev);
+ pm_runtime_put_autosuspend(connector->dev->dev);
+ }
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index cdb8cb56..ca1caf4 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -1352,6 +1352,12 @@
return ERR_PTR(-ENOENT);
}
+ /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
+ if (obj->import_attach) {
+ DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
+ return ERR_PTR(-EINVAL);
+ }
+
radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL);
if (radeon_fb == NULL) {
drm_gem_object_unreference_unlocked(obj);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 41b72ce..83e1345 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -238,9 +238,10 @@
* may be slow
* See https://bugs.freedesktop.org/show_bug.cgi?id=88758
*/
-
+#ifndef CONFIG_COMPILE_TEST
#warning Please enable CONFIG_MTRR and CONFIG_X86_PAT for better performance \
thanks to write-combining
+#endif
if (bo->flags & RADEON_GEM_GTT_WC)
DRM_INFO_ONCE("Please enable CONFIG_MTRR and CONFIG_X86_PAT for "
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 574ab00..b82ef5e 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -5969,9 +5969,9 @@
{
u32 lane_width;
u32 new_lane_width =
- (radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
u32 current_lane_width =
- (radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
if (new_lane_width != current_lane_width) {
radeon_set_pcie_lanes(rdev, new_lane_width);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 3322b15..1c4d95d 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -512,6 +512,13 @@
rcar_du_crtc_stop(rcrtc);
rcar_du_crtc_put(rcrtc);
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
rcrtc->outputs = 0;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c7eba30..32d87c6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -503,7 +503,7 @@
ret = pm_runtime_get_sync(vop->dev);
if (ret < 0) {
dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
- goto err_put_pm_runtime;
+ return ret;
}
ret = clk_enable(vop->hclk);
@@ -1348,10 +1348,16 @@
return PTR_ERR(vop->dclk);
}
+ ret = pm_runtime_get_sync(vop->dev);
+ if (ret < 0) {
+ dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+ return ret;
+ }
+
ret = clk_prepare(vop->dclk);
if (ret < 0) {
dev_err(vop->dev, "failed to prepare dclk\n");
- return ret;
+ goto err_put_pm_runtime;
}
/* Enable both the hclk and aclk to setup the vop */
@@ -1380,6 +1386,9 @@
usleep_range(10, 20);
reset_control_deassert(ahb_rst);
+ VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1);
+ VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0);
+
memcpy(vop->regsbak, vop->regs, vop->len);
for (i = 0; i < vop_data->table_size; i++)
@@ -1411,6 +1420,8 @@
vop->is_enabled = false;
+ pm_runtime_put_sync(vop->dev);
+
return 0;
err_disable_aclk:
@@ -1419,6 +1430,8 @@
clk_disable_unprepare(vop->hclk);
err_unprepare_dclk:
clk_unprepare(vop->dclk);
+err_put_pm_runtime:
+ pm_runtime_put_sync(vop->dev);
return ret;
}
@@ -1519,12 +1532,6 @@
if (!vop->regsbak)
return -ENOMEM;
- ret = vop_initial(vop);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
- return ret;
- }
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "cannot find irq for vop\n");
@@ -1537,24 +1544,31 @@
mutex_init(&vop->vsync_mutex);
+ ret = vop_create_crtc(vop);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = vop_initial(vop);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+ goto err_disable_pm_runtime;
+ }
+
ret = devm_request_irq(dev, vop->irq, vop_isr,
IRQF_SHARED, dev_name(dev), vop);
if (ret)
- return ret;
+ goto err_disable_pm_runtime;
/* IRQ is initially disabled; it gets enabled in power_on */
disable_irq(vop->irq);
- ret = vop_create_crtc(vop);
- if (ret)
- goto err_enable_irq;
-
- pm_runtime_enable(&pdev->dev);
-
return 0;
-err_enable_irq:
- enable_irq(vop->irq); /* To balance out the disable_irq above */
+err_disable_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+ vop_destroy_crtc(vop);
return ret;
}
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 4a19221..caba031 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -19,6 +19,7 @@
#include <linux/clk-provider.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
+#include <linux/of_graph.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
@@ -136,5 +137,9 @@
drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
+ /* Set crtc.port to output port node of the tcon */
+ scrtc->crtc.port = of_graph_get_port_by_id(drv->tcon->dev->of_node,
+ 1);
+
return scrtc;
}
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 1feec34..aad2f4a 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -145,7 +145,7 @@
ret = component_bind_all(drm->dev, drm);
if (ret) {
dev_err(drm->dev, "Couldn't bind all pipelines components\n");
- goto free_drm;
+ goto cleanup_mode_config;
}
/* Create our layers */
@@ -153,7 +153,7 @@
if (IS_ERR(drv->layers)) {
dev_err(drm->dev, "Couldn't create the planes\n");
ret = PTR_ERR(drv->layers);
- goto free_drm;
+ goto cleanup_mode_config;
}
/* Create our CRTC */
@@ -161,7 +161,7 @@
if (!drv->crtc) {
dev_err(drm->dev, "Couldn't create the CRTC\n");
ret = -EINVAL;
- goto free_drm;
+ goto cleanup_mode_config;
}
drm->irq_enabled = true;
@@ -173,7 +173,7 @@
if (IS_ERR(drv->fbdev)) {
dev_err(drm->dev, "Couldn't create our framebuffer\n");
ret = PTR_ERR(drv->fbdev);
- goto free_drm;
+ goto cleanup_mode_config;
}
/* Enable connectors polling */
@@ -181,10 +181,16 @@
ret = drm_dev_register(drm, 0);
if (ret)
- goto free_drm;
+ goto finish_poll;
return 0;
+finish_poll:
+ drm_kms_helper_poll_fini(drm);
+ sun4i_framebuffer_free(drm);
+cleanup_mode_config:
+ drm_mode_config_cleanup(drm);
+ drm_vblank_cleanup(drm);
free_drm:
drm_dev_unref(drm);
return ret;
@@ -206,6 +212,11 @@
.unbind = sun4i_drv_unbind,
};
+static bool sun4i_drv_node_is_connector(struct device_node *node)
+{
+ return of_device_is_compatible(node, "hdmi-connector");
+}
+
static bool sun4i_drv_node_is_frontend(struct device_node *node)
{
return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
@@ -246,6 +257,13 @@
!of_device_is_available(node))
return 0;
+ /*
+ * The connectors will be the last nodes in our pipeline, we
+ * can just bail out.
+ */
+ if (sun4i_drv_node_is_connector(node))
+ return 0;
+
if (!sun4i_drv_node_is_frontend(node)) {
/* Add current component */
DRM_DEBUG_DRIVER("Adding component %s\n",
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index c6afb24..f2975a1 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -336,12 +336,11 @@
}
}
- return sun4i_dclk_create(dev, tcon);
+ return 0;
}
static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
{
- sun4i_dclk_free(tcon);
clk_disable_unprepare(tcon->clk);
}
@@ -506,22 +505,28 @@
return ret;
}
- ret = sun4i_tcon_init_regmap(dev, tcon);
- if (ret) {
- dev_err(dev, "Couldn't init our TCON regmap\n");
- goto err_assert_reset;
- }
-
ret = sun4i_tcon_init_clocks(dev, tcon);
if (ret) {
dev_err(dev, "Couldn't init our TCON clocks\n");
goto err_assert_reset;
}
+ ret = sun4i_tcon_init_regmap(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't init our TCON regmap\n");
+ goto err_free_clocks;
+ }
+
+ ret = sun4i_dclk_create(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't create our TCON dot clock\n");
+ goto err_free_clocks;
+ }
+
ret = sun4i_tcon_init_irq(dev, tcon);
if (ret) {
dev_err(dev, "Couldn't init our TCON interrupts\n");
- goto err_free_clocks;
+ goto err_free_dotclock;
}
ret = sun4i_rgb_init(drm);
@@ -530,6 +535,8 @@
return 0;
+err_free_dotclock:
+ sun4i_dclk_free(tcon);
err_free_clocks:
sun4i_tcon_free_clocks(tcon);
err_assert_reset:
@@ -542,6 +549,7 @@
{
struct sun4i_tcon *tcon = dev_get_drvdata(dev);
+ sun4i_dclk_free(tcon);
sun4i_tcon_free_clocks(tcon);
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h
index f57c0d6..19d8dc3 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h
+++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h
@@ -124,7 +124,7 @@
struct tilcdc_drm_private *priv = dev->dev_private;
volatile void __iomem *addr = priv->mmio + reg;
-#ifdef iowrite64
+#if defined(iowrite64) && !defined(iowrite64_is_nonatomic)
iowrite64(data, addr);
#else
__iowmb();
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 94983e8..5a18408 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1209,18 +1209,20 @@
if (likely(!ret))
ret = ttm_bo_validate(bo, placement, interruptible, false);
- if (!resv) {
+ if (!resv)
ttm_bo_unreserve(bo);
- } else if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
+ if (unlikely(ret)) {
+ ttm_bo_unref(&bo);
+ return ret;
+ }
+
+ if (resv && !(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
spin_lock(&bo->glob->lru_lock);
ttm_bo_add_to_lru(bo);
spin_unlock(&bo->glob->lru_lock);
}
- if (unlikely(ret))
- ttm_bo_unref(&bo);
-
return ret;
}
EXPORT_SYMBOL(ttm_bo_init);
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index ddd6badd..c756b2b 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -818,6 +818,8 @@
pr_info("Initializing pool allocator\n");
_manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
+ if (!_manager)
+ return -ENOMEM;
ttm_page_pool_init_locked(&_manager->wc_pool, GFP_HIGHUSER, "wc");
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 611b6b9..67ea2ce 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -158,10 +158,15 @@
{
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long offset;
unsigned long page, pos;
- if (offset + size > info->fix.smem_len)
+ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+ return -EINVAL;
+
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ if (offset > info->fix.smem_len || size > info->fix.smem_len - offset)
return -EINVAL;
pos = (unsigned long)info->fix.smem_start + offset;
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index ec9023b..d53e805 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -80,6 +80,7 @@
struct vc4_dev *vc4 = to_vc4_dev(obj->dev);
if (bo->validated_shader) {
+ kfree(bo->validated_shader->uniform_addr_offsets);
kfree(bo->validated_shader->texture_samples);
kfree(bo->validated_shader);
bo->validated_shader = NULL;
@@ -328,6 +329,7 @@
}
if (bo->validated_shader) {
+ kfree(bo->validated_shader->uniform_addr_offsets);
kfree(bo->validated_shader->texture_samples);
kfree(bo->validated_shader);
bo->validated_shader = NULL;
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index ab30169..b608cd4 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -110,8 +110,8 @@
&handle);
if (ret) {
- state->bo_count = i - 1;
- goto err;
+ state->bo_count = i;
+ goto err_delete_handle;
}
bo_state[i].handle = handle;
bo_state[i].paddr = vc4_bo->base.paddr;
@@ -123,13 +123,16 @@
state->bo_count * sizeof(*bo_state)))
ret = -EFAULT;
- kfree(bo_state);
+err_delete_handle:
+ if (ret) {
+ for (i = 0; i < state->bo_count; i++)
+ drm_gem_handle_delete(file_priv, bo_state[i].handle);
+ }
err_free:
-
vc4_free_hang_state(dev, kernel_state);
+ kfree(bo_state);
-err:
return ret;
}
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index 881bf48..7505655 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -533,7 +533,7 @@
* the scl fields here.
*/
if (num_planes == 1) {
- scl0 = vc4_get_scl_field(state, 1);
+ scl0 = vc4_get_scl_field(state, 0);
scl1 = scl0;
} else {
scl0 = vc4_get_scl_field(state, 1);
diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
index 917321c..19a5bde8 100644
--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
@@ -874,6 +874,7 @@
fail:
kfree(validation_state.branch_targets);
if (validated_shader) {
+ kfree(validated_shader->uniform_addr_offsets);
kfree(validated_shader->texture_samples);
kfree(validated_shader);
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 5a0f8a7..52436b3 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -324,7 +324,7 @@
ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
if (ret == -ENOSPC) {
spin_unlock(&vgdev->ctrlq.qlock);
- wait_event(vgdev->ctrlq.ack_queue, vq->num_free);
+ wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= outcnt + incnt);
spin_lock(&vgdev->ctrlq.qlock);
goto retry;
} else {
@@ -399,7 +399,7 @@
ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC);
if (ret == -ENOSPC) {
spin_unlock(&vgdev->cursorq.qlock);
- wait_event(vgdev->cursorq.ack_queue, vq->num_free);
+ wait_event(vgdev->cursorq.ack_queue, vq->num_free >= outcnt);
spin_lock(&vgdev->cursorq.qlock);
goto retry;
} else {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index d2d9395..aec6e9e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -433,7 +433,7 @@
set.y = 0;
set.mode = NULL;
set.fb = NULL;
- set.num_connectors = 1;
+ set.num_connectors = 0;
set.connectors = &par->con;
ret = drm_mode_set_config_internal(&set);
if (ret) {
@@ -821,7 +821,9 @@
flush_delayed_work(&par->local_work);
mutex_lock(&par->bo_mutex);
+ drm_modeset_lock_all(vmw_priv->dev);
(void) vmw_fb_kms_detach(par, true, false);
+ drm_modeset_unlock_all(vmw_priv->dev);
mutex_unlock(&par->bo_mutex);
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index bf28ccc..33ca24a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -27,7 +27,6 @@
#include "vmwgfx_kms.h"
-
/* Might need a hrtimer here? */
#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
@@ -1933,9 +1932,12 @@
* Helper to be used if an error forces the caller to undo the actions of
* vmw_kms_helper_resource_prepare.
*/
-void vmw_kms_helper_resource_revert(struct vmw_resource *res)
+void vmw_kms_helper_resource_revert(struct vmw_validation_ctx *ctx)
{
- vmw_kms_helper_buffer_revert(res->backup);
+ struct vmw_resource *res = ctx->res;
+
+ vmw_kms_helper_buffer_revert(ctx->buf);
+ vmw_dmabuf_unreference(&ctx->buf);
vmw_resource_unreserve(res, false, NULL, 0);
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
}
@@ -1952,10 +1954,14 @@
* interrupted by a signal.
*/
int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
- bool interruptible)
+ bool interruptible,
+ struct vmw_validation_ctx *ctx)
{
int ret = 0;
+ ctx->buf = NULL;
+ ctx->res = res;
+
if (interruptible)
ret = mutex_lock_interruptible(&res->dev_priv->cmdbuf_mutex);
else
@@ -1974,6 +1980,8 @@
res->dev_priv->has_mob);
if (ret)
goto out_unreserve;
+
+ ctx->buf = vmw_dmabuf_reference(res->backup);
}
ret = vmw_resource_validate(res);
if (ret)
@@ -1981,7 +1989,7 @@
return 0;
out_revert:
- vmw_kms_helper_buffer_revert(res->backup);
+ vmw_kms_helper_buffer_revert(ctx->buf);
out_unreserve:
vmw_resource_unreserve(res, false, NULL, 0);
out_unlock:
@@ -1997,13 +2005,16 @@
* @out_fence: Optional pointer to a fence pointer. If non-NULL, a
* ref-counted fence pointer is returned here.
*/
-void vmw_kms_helper_resource_finish(struct vmw_resource *res,
- struct vmw_fence_obj **out_fence)
+void vmw_kms_helper_resource_finish(struct vmw_validation_ctx *ctx,
+ struct vmw_fence_obj **out_fence)
{
- if (res->backup || out_fence)
- vmw_kms_helper_buffer_finish(res->dev_priv, NULL, res->backup,
+ struct vmw_resource *res = ctx->res;
+
+ if (ctx->buf || out_fence)
+ vmw_kms_helper_buffer_finish(res->dev_priv, NULL, ctx->buf,
out_fence, NULL);
+ vmw_dmabuf_unreference(&ctx->buf);
vmw_resource_unreserve(res, false, NULL, 0);
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index ff4803c..2dd0539 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -183,6 +183,11 @@
int set_gui_y;
};
+struct vmw_validation_ctx {
+ struct vmw_resource *res;
+ struct vmw_dma_buffer *buf;
+};
+
#define vmw_crtc_to_du(x) \
container_of(x, struct vmw_display_unit, crtc)
#define vmw_connector_to_du(x) \
@@ -233,9 +238,10 @@
struct drm_vmw_fence_rep __user *
user_fence_rep);
int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
- bool interruptible);
-void vmw_kms_helper_resource_revert(struct vmw_resource *res);
-void vmw_kms_helper_resource_finish(struct vmw_resource *res,
+ bool interruptible,
+ struct vmw_validation_ctx *ctx);
+void vmw_kms_helper_resource_revert(struct vmw_validation_ctx *ctx);
+void vmw_kms_helper_resource_finish(struct vmw_validation_ctx *ctx,
struct vmw_fence_obj **out_fence);
int vmw_kms_readback(struct vmw_private *dev_priv,
struct drm_file *file_priv,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index f423590..a6ca218 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -753,12 +753,13 @@
struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base);
struct vmw_kms_sou_surface_dirty sdirty;
+ struct vmw_validation_ctx ctx;
int ret;
if (!srf)
srf = &vfbs->surface->res;
- ret = vmw_kms_helper_resource_prepare(srf, true);
+ ret = vmw_kms_helper_resource_prepare(srf, true, &ctx);
if (ret)
return ret;
@@ -777,7 +778,7 @@
ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,
dest_x, dest_y, num_clips, inc,
&sdirty.base);
- vmw_kms_helper_resource_finish(srf, out_fence);
+ vmw_kms_helper_resource_finish(&ctx, out_fence);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index 94ad8d2..8b91450 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -977,12 +977,13 @@
struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base);
struct vmw_stdu_dirty sdirty;
+ struct vmw_validation_ctx ctx;
int ret;
if (!srf)
srf = &vfbs->surface->res;
- ret = vmw_kms_helper_resource_prepare(srf, true);
+ ret = vmw_kms_helper_resource_prepare(srf, true, &ctx);
if (ret)
return ret;
@@ -1005,7 +1006,7 @@
dest_x, dest_y, num_clips, inc,
&sdirty.base);
out_finish:
- vmw_kms_helper_resource_finish(srf, out_fence);
+ vmw_kms_helper_resource_finish(&ctx, out_fence);
return ret;
}
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index f7e9eb3..614b1c7 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -207,9 +207,11 @@
.major = 0,
.minor = 4,
.patchid = ANY_ID,
- .features = ADRENO_PREEMPTION | ADRENO_64BIT,
+ .features = ADRENO_PREEMPTION | ADRENO_64BIT |
+ ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION,
.pm4fw_name = "a530_pm4.fw",
.pfpfw_name = "a530_pfp.fw",
+ .zap_name = "a506_zap",
.gpudev = &adreno_a5xx_gpudev,
.gmem_size = (SZ_128K + SZ_8K),
.num_protected_regs = 0x20,
@@ -221,9 +223,11 @@
.major = 0,
.minor = 5,
.patchid = ANY_ID,
- .features = ADRENO_PREEMPTION | ADRENO_64BIT,
+ .features = ADRENO_PREEMPTION | ADRENO_64BIT |
+ ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION,
.pm4fw_name = "a530_pm4.fw",
.pfpfw_name = "a530_pfp.fw",
+ .zap_name = "a506_zap",
.gpudev = &adreno_a5xx_gpudev,
.gmem_size = (SZ_128K + SZ_8K),
.num_protected_regs = 0x20,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index cb916ae..757f9d73 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -279,6 +279,59 @@
return 0;
}
+void adreno_efuse_speed_bin_array(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ int ret, count, i = 0;
+ unsigned int val, vector_size = 3;
+ unsigned int *bin_vector;
+
+ /*
+ * Here count is no of 32 bit elements in the
+ * speed-bin-vector array. If there are two fuses
+ * i.e If no of fuses are 2 then no of elements will be
+ * 2 * 3 = 6(elements of 32 bit each).
+ */
+ count = of_property_count_u32_elems(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin-vectors");
+ if (count <= 0)
+ return;
+
+ bin_vector = kmalloc(sizeof(count * sizeof(unsigned int)),
+ GFP_KERNEL);
+ if (bin_vector == NULL) {
+ KGSL_DRV_ERR(device,
+ "Unable to allocate memory for speed-bin vector\n");
+ return;
+ }
+
+ if (of_property_read_u32_array(device->pdev->
+ dev.of_node, "qcom,gpu-speed-bin-vectors",
+ bin_vector, count)) {
+ KGSL_DRV_ERR(device,
+ "Speed-bin-vectors is invalid\n");
+ kfree(bin_vector);
+ return;
+ }
+
+ /*
+ * Final value of adreno_dev->speed_bin is the value formed by
+ * OR'ing the values read from all the fuses.
+ */
+ while (i < count) {
+ ret = adreno_efuse_read_u32(adreno_dev, bin_vector[i], &val);
+
+ if (ret < 0)
+ break;
+
+ adreno_dev->speed_bin |= (val & bin_vector[i+1])
+ >> bin_vector[i+2];
+ i += vector_size;
+ }
+
+ kfree(bin_vector);
+}
+
static int _get_counter(struct adreno_device *adreno_dev,
int group, int countable, unsigned int *lo,
unsigned int *hi)
@@ -613,6 +666,7 @@
struct adreno_irq *irq_params = gpudev->irq;
irqreturn_t ret = IRQ_NONE;
unsigned int status = 0, fence = 0, fence_retries = 0, tmp, int_bit;
+ unsigned int shadow_status = 0;
int i;
atomic_inc(&adreno_dev->pending_irq_refcnt);
@@ -635,18 +689,29 @@
* and change the fence back to ALLOW. Poll so that this can happen.
*/
if (kgsl_gmu_isenabled(device)) {
- do {
+ adreno_readreg(adreno_dev,
+ ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
+ &fence);
+
+ while (fence != 0) {
+ /* Wait for small time before trying again */
+ udelay(1);
adreno_readreg(adreno_dev,
ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
&fence);
- if (fence_retries == FENCE_RETRY_MAX) {
+ if (fence_retries == FENCE_RETRY_MAX && fence != 0) {
+ adreno_readreg(adreno_dev,
+ ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS,
+ &shadow_status);
+
KGSL_DRV_CRIT_RATELIMIT(device,
- "AHB fence stuck in ISR\n");
+ "AHB fence stuck in ISR: Shadow INT status=%8.8X\n",
+ shadow_status & irq_params->mask);
goto done;
}
fence_retries++;
- } while (fence != 0);
+ }
}
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 686ed34..8e21958 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -174,6 +174,9 @@
/* Number of times to try hard reset */
#define NUM_TIMES_RESET_RETRY 5
+/* Number of times to try loading of zap shader */
+#define ZAP_RETRY_MAX 5
+
/* Number of times to poll the AHB fence in ISR */
#define FENCE_RETRY_MAX 100
@@ -722,6 +725,7 @@
ADRENO_REG_GMU_HOST2GMU_INTR_RAW_INFO,
ADRENO_REG_GMU_NMI_CONTROL_STATUS,
ADRENO_REG_GMU_CM3_CFG,
+ ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS,
ADRENO_REG_GPMU_POWER_COUNTER_ENABLE,
ADRENO_REG_REGISTER_MAX,
};
@@ -1130,6 +1134,7 @@
int adreno_efuse_read_u32(struct adreno_device *adreno_dev, unsigned int offset,
unsigned int *val);
void adreno_efuse_unmap(struct adreno_device *adreno_dev);
+void adreno_efuse_speed_bin_array(struct adreno_device *adreno_dev);
bool adreno_is_cx_dbgc_register(struct kgsl_device *device,
unsigned int offset);
@@ -1803,7 +1808,7 @@
return ret;
}
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, &val);
- if (val != *perfctr_pwr_hi) {
+ if (val > *perfctr_pwr_hi) {
*perfctr_pwr_hi = val;
ret = true;
}
@@ -1818,14 +1823,17 @@
unsigned int ret = 0;
bool overflow = true;
static unsigned int perfctr_pwr_hi;
+ unsigned int prev_perfctr_pwr_hi = 0;
/* Read the value */
kgsl_regread(device, reg, &val);
if (adreno_is_a5xx(adreno_dev) && reg == adreno_getreg
- (adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO))
+ (adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO)) {
+ prev_perfctr_pwr_hi = perfctr_pwr_hi;
overflow = is_power_counter_overflow(adreno_dev, reg,
*counter, &perfctr_pwr_hi);
+ }
/* Return 0 for the first read */
if (*counter != 0) {
@@ -1838,9 +1846,12 @@
* Since KGSL got abnormal value from the counter,
* We will drop the value from being accumulated.
*/
- pr_warn_once("KGSL: Abnormal value :0x%x (0x%x) from perf counter : 0x%x\n",
- val, *counter, reg);
- return 0;
+ KGSL_DRV_ERR_RATELIMIT(device,
+ "Abnormal value:0x%llx (0x%llx) from perf counter : 0x%x\n",
+ val | ((uint64_t)perfctr_pwr_hi << 32),
+ *counter |
+ ((uint64_t)prev_perfctr_pwr_hi << 32),
+ reg);
}
}
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index e5c8222..4f98912 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1332,7 +1332,7 @@
iommu_regs = kgsl_mmu_get_prot_regs(&device->mmu);
if (iommu_regs)
adreno_set_protected_registers(adreno_dev, &index,
- iommu_regs->base, iommu_regs->range);
+ iommu_regs->base, ilog2(iommu_regs->range));
}
static void a3xx_start(struct adreno_device *adreno_dev)
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index 771d035..432e98d 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -524,7 +524,7 @@
iommu_regs = kgsl_mmu_get_prot_regs(&device->mmu);
if (iommu_regs)
adreno_set_protected_registers(adreno_dev, &index,
- iommu_regs->base, iommu_regs->range);
+ iommu_regs->base, ilog2(iommu_regs->range));
}
static struct adreno_snapshot_sizes a4xx_snap_sizes = {
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index d37753e..2a3ae3e 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -122,14 +122,34 @@
adreno_dev->speed_bin = (val & speed_bin[1]) >> speed_bin[2];
}
+static void a5xx_efuse_speed_bin(struct adreno_device *adreno_dev)
+{
+ unsigned int val;
+ unsigned int speed_bin[3];
+ struct kgsl_device *device = &adreno_dev->dev;
+
+ if (of_get_property(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin-vectors", NULL)) {
+ adreno_efuse_speed_bin_array(adreno_dev);
+ return;
+ }
+
+ if (!of_property_read_u32_array(device->pdev->dev.of_node,
+ "qcom,gpu-speed-bin", speed_bin, 3)) {
+ adreno_efuse_read_u32(adreno_dev, speed_bin[0], &val);
+ adreno_dev->speed_bin = (val & speed_bin[1]) >> speed_bin[2];
+ return;
+ }
+}
+
static const struct {
int (*check)(struct adreno_device *adreno_dev);
void (*func)(struct adreno_device *adreno_dev);
} a5xx_efuse_funcs[] = {
{ adreno_is_a530, a530_efuse_leakage },
{ adreno_is_a530, a530_efuse_speed_bin },
- { adreno_is_a504, a530_efuse_speed_bin },
- { adreno_is_a505, a530_efuse_speed_bin },
+ { adreno_is_a504, a5xx_efuse_speed_bin },
+ { adreno_is_a505, a5xx_efuse_speed_bin },
{ adreno_is_a512, a530_efuse_speed_bin },
{ adreno_is_a508, a530_efuse_speed_bin },
};
@@ -366,7 +386,7 @@
iommu_regs = kgsl_mmu_get_prot_regs(&device->mmu);
if (iommu_regs)
adreno_set_protected_registers(adreno_dev, &index,
- iommu_regs->base, iommu_regs->range);
+ iommu_regs->base, ilog2(iommu_regs->range));
}
/*
@@ -2191,6 +2211,7 @@
struct adreno_firmware *pm4_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PM4);
struct adreno_firmware *pfp_fw = ADRENO_FW(adreno_dev, ADRENO_FW_PFP);
uint64_t gpuaddr;
+ int ret = 0, zap_retry = 0;
gpuaddr = pm4_fw->memdesc.gpuaddr;
kgsl_regwrite(device, A5XX_CP_PM4_INSTR_BASE_LO,
@@ -2205,13 +2226,19 @@
upper_32_bits(gpuaddr));
/*
+ * Do not invoke to load zap shader if MMU does
+ * not support secure mode.
+ */
+ if (!device->mmu.secured)
+ return 0;
+
+ /*
* Resume call to write the zap shader base address into the
* appropriate register,
* skip if retention is supported for the CPZ register
*/
if (adreno_dev->zap_loaded && !(ADRENO_FEATURE(adreno_dev,
ADRENO_CPZ_RETENTION))) {
- int ret;
struct scm_desc desc = {0};
desc.args[0] = 0;
@@ -2228,15 +2255,25 @@
/* Load the zap shader firmware through PIL if its available */
if (adreno_dev->gpucore->zap_name && !adreno_dev->zap_loaded) {
- ptr = subsystem_get(adreno_dev->gpucore->zap_name);
+ /*
+ * subsystem_get() may return -EAGAIN in case system is busy
+ * and unable to load the firmware. So keep trying since this
+ * is not a fatal error.
+ */
+ do {
+ ret = 0;
+ ptr = subsystem_get(adreno_dev->gpucore->zap_name);
- /* Return error if the zap shader cannot be loaded */
- if (IS_ERR_OR_NULL(ptr))
- return (ptr == NULL) ? -ENODEV : PTR_ERR(ptr);
- adreno_dev->zap_loaded = 1;
+ /* Return error if the zap shader cannot be loaded */
+ if (IS_ERR_OR_NULL(ptr)) {
+ ret = (ptr == NULL) ? -ENODEV : PTR_ERR(ptr);
+ ptr = NULL;
+ } else
+ adreno_dev->zap_loaded = 1;
+ } while ((ret == -EAGAIN) && (zap_retry++ < ZAP_RETRY_MAX));
}
- return 0;
+ return ret;
}
static int _me_init_ucode_workarounds(struct adreno_device *adreno_dev)
@@ -3291,11 +3328,11 @@
}
/*
- * a5x_gpc_err_int_callback() - Isr for GPC error interrupts
+ * a5xx_gpc_err_int_callback() - Isr for GPC error interrupts
* @adreno_dev: Pointer to device
* @bit: Interrupt bit
*/
-void a5x_gpc_err_int_callback(struct adreno_device *adreno_dev, int bit)
+static void a5xx_gpc_err_int_callback(struct adreno_device *adreno_dev, int bit)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -3305,7 +3342,7 @@
* with help of register dump.
*/
- KGSL_DRV_CRIT(device, "RBBM: GPC error\n");
+ KGSL_DRV_CRIT_RATELIMIT(device, "RBBM: GPC error\n");
adreno_irqctrl(adreno_dev, 0);
/* Trigger a fault in the dispatcher - this will effect a restart */
@@ -3343,7 +3380,7 @@
ADRENO_IRQ_CALLBACK(a5xx_err_callback),
/* 6 - RBBM_ATB_ASYNC_OVERFLOW */
ADRENO_IRQ_CALLBACK(a5xx_err_callback),
- ADRENO_IRQ_CALLBACK(a5x_gpc_err_int_callback), /* 7 - GPC_ERR */
+ ADRENO_IRQ_CALLBACK(a5xx_gpc_err_int_callback), /* 7 - GPC_ERR */
ADRENO_IRQ_CALLBACK(a5xx_preempt_callback),/* 8 - CP_SW */
ADRENO_IRQ_CALLBACK(a5xx_cp_hw_err_callback), /* 9 - CP_HW_ERROR */
/* 10 - CP_CCU_FLUSH_DEPTH_TS */
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index d1a6005..4bde8c6 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -412,6 +412,15 @@
0xEC00, 0xEC05, 0xEC08, 0xECE9, 0xECF0, 0xECF0,
/* VPC CTX 1 */
0xEA80, 0xEA80, 0xEA82, 0xEAA3, 0xEAA5, 0xEAC2,
+};
+
+/*
+ * GPMU registers to dump for A5XX on snapshot.
+ * Registers in pairs - first value is the start offset, second
+ * is the stop offset (inclusive)
+ */
+
+static const unsigned int a5xx_gpmu_registers[] = {
/* GPMU */
0xA800, 0xA8FF, 0xAC60, 0xAC60,
};
@@ -664,24 +673,23 @@
return kgsl_snapshot_dump_registers(device, buf, remain, &pre_cdregs);
}
+struct registers {
+ const unsigned int *regs;
+ size_t size;
+};
+
static size_t a5xx_legacy_snapshot_registers(struct kgsl_device *device,
- u8 *buf, size_t remain)
+ u8 *buf, size_t remain, const unsigned int *regs, size_t size)
{
- struct kgsl_snapshot_registers regs = {
- .regs = a5xx_registers,
- .count = ARRAY_SIZE(a5xx_registers) / 2,
+ struct kgsl_snapshot_registers snapshot_regs = {
+ .regs = regs,
+ .count = size / 2,
};
- return kgsl_snapshot_dump_registers(device, buf, remain, ®s);
+ return kgsl_snapshot_dump_registers(device, buf, remain,
+ &snapshot_regs);
}
-static struct cdregs {
- const unsigned int *regs;
- unsigned int size;
-} _a5xx_cd_registers[] = {
- { a5xx_registers, ARRAY_SIZE(a5xx_registers) },
-};
-
#define REG_PAIR_COUNT(_a, _i) \
(((_a)[(2 * (_i)) + 1] - (_a)[2 * (_i)]) + 1)
@@ -691,11 +699,13 @@
struct kgsl_snapshot_regs *header = (struct kgsl_snapshot_regs *)buf;
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
unsigned int *src = (unsigned int *) registers.hostptr;
- unsigned int i, j, k;
+ struct registers *regs = (struct registers *)priv;
+ unsigned int j, k;
unsigned int count = 0;
if (crash_dump_valid == false)
- return a5xx_legacy_snapshot_registers(device, buf, remain);
+ return a5xx_legacy_snapshot_registers(device, buf, remain,
+ regs->regs, regs->size);
if (remain < sizeof(*header)) {
SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
@@ -704,24 +714,20 @@
remain -= sizeof(*header);
- for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) {
- struct cdregs *regs = &_a5xx_cd_registers[i];
+ for (j = 0; j < regs->size / 2; j++) {
+ unsigned int start = regs->regs[2 * j];
+ unsigned int end = regs->regs[(2 * j) + 1];
- for (j = 0; j < regs->size / 2; j++) {
- unsigned int start = regs->regs[2 * j];
- unsigned int end = regs->regs[(2 * j) + 1];
+ if (remain < ((end - start) + 1) * 8) {
+ SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
+ goto out;
+ }
- if (remain < ((end - start) + 1) * 8) {
- SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
- goto out;
- }
+ remain -= ((end - start) + 1) * 8;
- remain -= ((end - start) + 1) * 8;
-
- for (k = start; k <= end; k++, count++) {
- *data++ = k;
- *data++ = *src++;
- }
+ for (k = start; k <= end; k++, count++) {
+ *data++ = k;
+ *data++ = *src++;
}
}
@@ -861,6 +867,7 @@
struct adreno_snapshot_data *snap_data = gpudev->snapshot_data;
unsigned int reg, i;
struct adreno_ringbuffer *rb;
+ struct registers regs;
/* Disable Clock gating temporarily for the debug bus to work */
a5xx_hwcg_set(adreno_dev, false);
@@ -877,8 +884,20 @@
/* Try to run the crash dumper */
_a5xx_do_crashdump(device);
- kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS,
- snapshot, a5xx_snapshot_registers, NULL);
+ regs.regs = a5xx_registers;
+ regs.size = ARRAY_SIZE(a5xx_registers);
+
+ kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot,
+ a5xx_snapshot_registers, ®s);
+
+ if (ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) {
+ regs.regs = a5xx_gpmu_registers;
+ regs.size = ARRAY_SIZE(a5xx_gpmu_registers);
+
+ kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS,
+ snapshot, a5xx_snapshot_registers, ®s);
+ }
+
/* Dump SP TP HLSQ registers */
kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot,
@@ -1035,17 +1054,23 @@
* To save the registers, we need 16 bytes per register pair for the
* script and a dword for each register int the data
*/
- for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) {
- struct cdregs *regs = &_a5xx_cd_registers[i];
+ /* Each pair needs 16 bytes (2 qwords) */
+ script_size += (ARRAY_SIZE(a5xx_registers) / 2) * 16;
+
+ /* Each register needs a dword in the data */
+ for (j = 0; j < ARRAY_SIZE(a5xx_registers) / 2; j++)
+ data_size += REG_PAIR_COUNT(a5xx_registers, j) *
+ sizeof(unsigned int);
+
+ if (ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) {
/* Each pair needs 16 bytes (2 qwords) */
- script_size += (regs->size / 2) * 16;
+ script_size += (ARRAY_SIZE(a5xx_gpmu_registers) / 2) * 16;
/* Each register needs a dword in the data */
- for (j = 0; j < regs->size / 2; j++)
- data_size += REG_PAIR_COUNT(regs->regs, j) *
+ for (j = 0; j < ARRAY_SIZE(a5xx_gpmu_registers) / 2; j++)
+ data_size += REG_PAIR_COUNT(a5xx_gpmu_registers, j) *
sizeof(unsigned int);
-
}
/*
@@ -1083,13 +1108,21 @@
ptr = (uint64_t *) capturescript.hostptr;
/* For the registers, program a read command for each pair */
- for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) {
- struct cdregs *regs = &_a5xx_cd_registers[i];
- for (j = 0; j < regs->size / 2; j++) {
- unsigned int r = REG_PAIR_COUNT(regs->regs, j);
+ for (j = 0; j < ARRAY_SIZE(a5xx_registers) / 2; j++) {
+ unsigned int r = REG_PAIR_COUNT(a5xx_registers, j);
+ *ptr++ = registers.gpuaddr + offset;
+ *ptr++ = (((uint64_t) a5xx_registers[2 * j]) << 44)
+ | r;
+ offset += r * sizeof(unsigned int);
+ }
+
+ if (ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) {
+ for (j = 0; j < ARRAY_SIZE(a5xx_gpmu_registers) / 2; j++) {
+ unsigned int r = REG_PAIR_COUNT(a5xx_gpmu_registers, j);
*ptr++ = registers.gpuaddr + offset;
- *ptr++ = (((uint64_t) regs->regs[2 * j]) << 44) | r;
+ *ptr++ = (((uint64_t) a5xx_gpmu_registers[2 * j]) << 44)
+ | r;
offset += r * sizeof(unsigned int);
}
}
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 7096d7c..517b813 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -446,7 +446,7 @@
if (mmu_prot) {
mmu_base = mmu_prot->base;
- mmu_range = 1 << mmu_prot->range;
+ mmu_range = mmu_prot->range;
req_sets += DIV_ROUND_UP(mmu_range, 0x2000);
}
@@ -845,7 +845,7 @@
struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE);
uint64_t gpuaddr;
void *zap;
- int ret = 0;
+ int ret = 0, zap_retry = 0;
gpuaddr = fw->memdesc.gpuaddr;
kgsl_regwrite(device, A6XX_CP_SQE_INSTR_BASE_LO,
@@ -853,16 +853,31 @@
kgsl_regwrite(device, A6XX_CP_SQE_INSTR_BASE_HI,
upper_32_bits(gpuaddr));
+ /*
+ * Do not invoke to load zap shader if MMU does
+ * not support secure mode.
+ */
+ if (!device->mmu.secured)
+ return 0;
+
/* Load the zap shader firmware through PIL if its available */
if (adreno_dev->gpucore->zap_name && !adreno_dev->zap_loaded) {
- zap = subsystem_get(adreno_dev->gpucore->zap_name);
+ /*
+ * subsystem_get() may return -EAGAIN in case system is busy
+ * and unable to load the firmware. So keep trying since this
+ * is not a fatal error.
+ */
+ do {
+ ret = 0;
+ zap = subsystem_get(adreno_dev->gpucore->zap_name);
- /* Return error if the zap shader cannot be loaded */
- if (IS_ERR_OR_NULL(zap)) {
- ret = (zap == NULL) ? -ENODEV : PTR_ERR(zap);
- zap = NULL;
- } else
- adreno_dev->zap_loaded = 1;
+ /* Return error if the zap shader cannot be loaded */
+ if (IS_ERR_OR_NULL(zap)) {
+ ret = (zap == NULL) ? -ENODEV : PTR_ERR(zap);
+ zap = NULL;
+ } else
+ adreno_dev->zap_loaded = 1;
+ } while ((ret == -EAGAIN) && (zap_retry++ < ZAP_RETRY_MAX));
}
return ret;
@@ -1708,6 +1723,10 @@
struct device *dev = &gmu->pdev->dev;
int val;
+ /* Only trigger wakeup sequence if sleep sequence was done earlier */
+ if (!test_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags))
+ return 0;
+
kgsl_gmu_regread(device, A6XX_GPU_CC_GX_DOMAIN_MISC, &val);
if (!(val & 0x1))
dev_err_ratelimited(&gmu->pdev->dev,
@@ -1737,6 +1756,9 @@
kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0);
+ /* Clear sleep sequence flag as wakeup sequence is successful */
+ clear_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags);
+
/* Enable the power counter because it was disabled before slumber */
kgsl_gmu_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 1);
@@ -1752,6 +1774,9 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int ret;
+ if (test_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags))
+ return 0;
+
/* RSC sleep sequence is different on v1 */
if (adreno_is_a630v1(adreno_dev))
kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
@@ -1793,6 +1818,7 @@
test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag))
kgsl_gmu_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 0);
+ set_bit(GMU_RSCC_SLEEP_SEQ_DONE, &gmu->flags);
return 0;
}
@@ -1811,15 +1837,13 @@
unsigned int chipid = 0;
switch (boot_state) {
- case GMU_RESET:
- /* fall through */
case GMU_COLD_BOOT:
/* Turn on TCM retention */
kgsl_gmu_regwrite(device, A6XX_GMU_GENERAL_7, 1);
if (!test_and_set_bit(GMU_BOOT_INIT_DONE, &gmu->flags))
_load_gmu_rpmh_ucode(device);
- else if (boot_state != GMU_RESET) {
+ else {
ret = a6xx_rpmh_power_on_gpu(device);
if (ret)
return ret;
@@ -2630,6 +2654,29 @@
adreno_dispatcher_schedule(device);
}
+/*
+ * a6xx_gpc_err_int_callback() - Isr for GPC error interrupts
+ * @adreno_dev: Pointer to device
+ * @bit: Interrupt bit
+ */
+static void a6xx_gpc_err_int_callback(struct adreno_device *adreno_dev, int bit)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+
+ /*
+ * GPC error is typically the result of mistake SW programming.
+ * Force GPU fault for this interrupt so that we can debug it
+ * with help of register dump.
+ */
+
+ KGSL_DRV_CRIT_RATELIMIT(device, "RBBM: GPC error\n");
+ adreno_irqctrl(adreno_dev, 0);
+
+ /* Trigger a fault in the dispatcher - this will effect a restart */
+ adreno_set_gpu_fault(adreno_dev, ADRENO_SOFT_FAULT);
+ adreno_dispatcher_schedule(device);
+}
+
#define A6XX_INT_MASK \
((1 << A6XX_INT_CP_AHB_ERROR) | \
(1 << A6XX_INT_ATB_ASYNCFIFO_OVERFLOW) | \
@@ -2654,7 +2701,7 @@
ADRENO_IRQ_CALLBACK(NULL), /* 5 - UNUSED */
/* 6 - RBBM_ATB_ASYNC_OVERFLOW */
ADRENO_IRQ_CALLBACK(a6xx_err_callback),
- ADRENO_IRQ_CALLBACK(NULL), /* 7 - GPC_ERR */
+ ADRENO_IRQ_CALLBACK(a6xx_gpc_err_int_callback), /* 7 - GPC_ERR */
ADRENO_IRQ_CALLBACK(a6xx_preemption_callback),/* 8 - CP_SW */
ADRENO_IRQ_CALLBACK(a6xx_cp_hw_err_callback), /* 9 - CP_HW_ERROR */
ADRENO_IRQ_CALLBACK(NULL), /* 10 - CP_CCU_FLUSH_DEPTH_TS */
@@ -3776,6 +3823,8 @@
A6XX_GMU_NMI_CONTROL_STATUS),
ADRENO_REG_DEFINE(ADRENO_REG_GMU_CM3_CFG,
A6XX_GMU_CM3_CFG),
+ ADRENO_REG_DEFINE(ADRENO_REG_GMU_RBBM_INT_UNMASKED_STATUS,
+ A6XX_GMU_RBBM_INT_UNMASKED_STATUS),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TRUST_CONTROL,
A6XX_RBBM_SECVID_TRUST_CNTL),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE,
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index b8c282d..a634d98 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -208,8 +208,8 @@
if (!kgsl_state_is_awake(KGSL_DEVICE(adreno_dev)))
goto ret;
- if (adreno_rb_empty(adreno_dev->cur_rb))
- goto ret;
+ if (!adreno_rb_empty(adreno_dev->cur_rb))
+ return false;
/* only check rbbm status to determine if GPU is idle */
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS, ®_rbbm_status);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index bb9f9ff..3b55fc6 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -458,6 +458,10 @@
type = kgsl_memdesc_usermem_type(&entry->memdesc);
entry->priv->stats[type].cur -= entry->memdesc.size;
+
+ if (type != KGSL_MEM_ENTRY_ION)
+ entry->priv->gpumem_mapped -= entry->memdesc.mapsize;
+
spin_unlock(&entry->priv->mem_lock);
kgsl_mmu_put_gpuaddr(&entry->memdesc);
@@ -2384,7 +2388,6 @@
struct kgsl_gpuobj_import *param = data;
struct kgsl_mem_entry *entry;
int ret, fd = -1;
- struct kgsl_mmu *mmu = &dev_priv->device->mmu;
entry = kgsl_mem_entry_create();
if (entry == NULL)
@@ -2398,18 +2401,10 @@
| KGSL_MEMFLAGS_FORCE_32BIT
| KGSL_MEMFLAGS_IOCOHERENT;
- /* Disable IO coherence if it is not supported on the chip */
- if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
- param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
-
if (kgsl_is_compat_task())
param->flags |= KGSL_MEMFLAGS_FORCE_32BIT;
- entry->memdesc.flags = param->flags;
-
- if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
- entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE;
-
+ kgsl_memdesc_init(dev_priv->device, &entry->memdesc, param->flags);
if (param->type == KGSL_USER_MEM_TYPE_ADDR)
ret = _gpuobj_map_useraddr(dev_priv->device, private->pagetable,
entry, param);
@@ -2648,6 +2643,7 @@
struct kgsl_process_private *private = dev_priv->process_priv;
struct kgsl_mmu *mmu = &dev_priv->device->mmu;
unsigned int memtype;
+ uint64_t flags;
/*
* If content protection is not enabled and secure buffer
@@ -2684,30 +2680,17 @@
* Note: CACHEMODE is ignored for this call. Caching should be
* determined by type of allocation being mapped.
*/
- param->flags &= KGSL_MEMFLAGS_GPUREADONLY
- | KGSL_MEMTYPE_MASK
- | KGSL_MEMALIGN_MASK
- | KGSL_MEMFLAGS_USE_CPU_MAP
- | KGSL_MEMFLAGS_SECURE
- | KGSL_MEMFLAGS_IOCOHERENT;
-
- /* Disable IO coherence if it is not supported on the chip */
- if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
- param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
-
- entry->memdesc.flags = (uint64_t) param->flags;
+ flags = param->flags & (KGSL_MEMFLAGS_GPUREADONLY
+ | KGSL_MEMTYPE_MASK
+ | KGSL_MEMALIGN_MASK
+ | KGSL_MEMFLAGS_USE_CPU_MAP
+ | KGSL_MEMFLAGS_SECURE
+ | KGSL_MEMFLAGS_IOCOHERENT);
if (kgsl_is_compat_task())
- entry->memdesc.flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+ flags |= KGSL_MEMFLAGS_FORCE_32BIT;
- if (!kgsl_mmu_use_cpu_map(mmu))
- entry->memdesc.flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
-
- if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
- entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE;
-
- if (param->flags & KGSL_MEMFLAGS_SECURE)
- entry->memdesc.priv |= KGSL_MEMDESC_SECURE;
+ kgsl_memdesc_init(dev_priv->device, &entry->memdesc, flags);
switch (memtype) {
case KGSL_MEM_ENTRY_USER:
@@ -3103,10 +3086,6 @@
| KGSL_MEMFLAGS_FORCE_32BIT
| KGSL_MEMFLAGS_IOCOHERENT;
- /* Turn off SVM if the system doesn't support it */
- if (!kgsl_mmu_use_cpu_map(mmu))
- flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
-
/* Return not supported error if secure memory isn't enabled */
if (!kgsl_mmu_is_secured(mmu) &&
(flags & KGSL_MEMFLAGS_SECURE)) {
@@ -3115,10 +3094,6 @@
return ERR_PTR(-EOPNOTSUPP);
}
- /* Secure memory disables advanced addressing modes */
- if (flags & KGSL_MEMFLAGS_SECURE)
- flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
-
/* Cap the alignment bits to the highest number we can handle */
align = MEMFLAGS(flags, KGSL_MEMALIGN_MASK, KGSL_MEMALIGN_SHIFT);
if (align >= ilog2(KGSL_MAX_ALIGN)) {
@@ -3137,20 +3112,10 @@
flags = kgsl_filter_cachemode(flags);
- /* Disable IO coherence if it is not supported on the chip */
- if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
- flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
-
entry = kgsl_mem_entry_create();
if (entry == NULL)
return ERR_PTR(-ENOMEM);
- if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
- entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE;
-
- if (flags & KGSL_MEMFLAGS_SECURE)
- entry->memdesc.priv |= KGSL_MEMDESC_SECURE;
-
ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc,
size, flags);
if (ret != 0)
@@ -3334,6 +3299,7 @@
struct kgsl_process_private *process = dev_priv->process_priv;
struct kgsl_sparse_phys_alloc *param = data;
struct kgsl_mem_entry *entry;
+ uint64_t flags;
int ret;
int id;
@@ -3366,11 +3332,12 @@
entry->id = id;
entry->priv = process;
- entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_PHYS;
- kgsl_memdesc_set_align(&entry->memdesc, ilog2(param->pagesize));
+ flags = KGSL_MEMFLAGS_SPARSE_PHYS |
+ ((ilog2(param->pagesize) << KGSL_MEMALIGN_SHIFT) &
+ KGSL_MEMALIGN_MASK);
ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc,
- param->size, entry->memdesc.flags);
+ param->size, flags);
if (ret)
goto err_remove_idr;
@@ -3459,7 +3426,8 @@
if (entry == NULL)
return -ENOMEM;
- entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_VIRT;
+ kgsl_memdesc_init(dev_priv->device, &entry->memdesc,
+ KGSL_MEMFLAGS_SPARSE_VIRT);
entry->memdesc.size = param->size;
entry->memdesc.cur_bindings = 0;
kgsl_memdesc_set_align(&entry->memdesc, ilog2(param->pagesize));
@@ -4143,13 +4111,18 @@
kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct kgsl_mem_entry *entry = vma->vm_private_data;
+ int ret;
if (!entry)
return VM_FAULT_SIGBUS;
if (!entry->memdesc.ops || !entry->memdesc.ops->vmfault)
return VM_FAULT_SIGBUS;
- return entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf);
+ ret = entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf);
+ if ((ret == 0) || (ret == VM_FAULT_NOPAGE))
+ entry->priv->gpumem_mapped += PAGE_SIZE;
+
+ return ret;
}
static void
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index c737801..c8c6456 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -446,6 +446,7 @@
* @kobj: Pointer to a kobj for the sysfs directory for this process
* @debug_root: Pointer to the debugfs root for this process
* @stats: Memory allocation statistics for this process
+ * @gpumem_mapped: KGSL memory mapped in the process address space
* @syncsource_idr: sync sources created by this process
* @syncsource_lock: Spinlock to protect the syncsource idr
* @fd_count: Counter for the number of FDs for this process
@@ -467,6 +468,7 @@
uint64_t cur;
uint64_t max;
} stats[KGSL_MEM_ENTRY_MAX];
+ uint64_t gpumem_mapped;
struct idr syncsource_idr;
spinlock_t syncsource_lock;
int fd_count;
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index dbf517a..dbc3d53 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -131,6 +131,10 @@
fault_type = "translation";
else if (flags & IOMMU_FAULT_PERMISSION)
fault_type = "permission";
+ else if (flags & IOMMU_FAULT_EXTERNAL)
+ fault_type = "external";
+ else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
+ fault_type = "transaction stalled";
dev_err(dev, "GMU fault addr = %lX, context=%s (%s %s fault)\n",
addr, name,
@@ -1318,7 +1322,7 @@
return -ETIMEDOUT;
}
-static int gmu_suspend(struct kgsl_device *device)
+int gmu_suspend(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
@@ -1404,6 +1408,7 @@
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct gmu_device *gmu = &device->gmu;
+ unsigned int boot_state = GMU_WARM_BOOT;
switch (device->state) {
case KGSL_STATE_INIT:
@@ -1440,12 +1445,21 @@
gmu_enable_clks(gmu);
gmu_irq_enable(device);
+ /*
+ * If unrecovered is set that means last
+ * wakeup from SLUMBER state failed. Use GMU
+ * and HFI boot state as COLD as this is a
+ * boot after RESET.
+ */
+ if (gmu->unrecovered)
+ boot_state = GMU_COLD_BOOT;
+
ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
- GMU_WARM_BOOT, 0);
+ boot_state, 0);
if (ret)
goto error_gmu;
- ret = hfi_start(gmu, GMU_WARM_BOOT);
+ ret = hfi_start(gmu, boot_state);
if (ret)
goto error_gmu;
@@ -1461,7 +1475,7 @@
gmu_irq_enable(device);
ret = gpudev->rpmh_gpu_pwrctrl(
- adreno_dev, GMU_FW_START, GMU_RESET, 0);
+ adreno_dev, GMU_FW_START, GMU_COLD_BOOT, 0);
if (ret)
goto error_gmu;
@@ -1478,7 +1492,7 @@
hfi_stop(gmu);
ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
- GMU_RESET, 0);
+ GMU_COLD_BOOT, 0);
if (ret)
goto error_gmu;
@@ -1491,6 +1505,8 @@
break;
}
+ /* Clear unrecovered as GMU start is successful */
+ gmu->unrecovered = false;
return ret;
error_gmu:
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index 19fa972..130d400 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -102,6 +102,7 @@
GMU_HFI_ON = 2,
GMU_FAULT = 3,
GMU_DCVS_REPLAY = 4,
+ GMU_RSCC_SLEEP_SEQ_DONE = 5,
};
/**
@@ -139,13 +140,11 @@
/*
* These are the different ways the GMU can boot. GMU_WARM_BOOT is waking up
- * from slumber. GMU_COLD_BOOT is booting for the first time. GMU_RESET
- * is a soft reset of the GMU.
+ * from slumber. GMU_COLD_BOOT is booting for the first time.
*/
enum gmu_boot {
GMU_WARM_BOOT = 0,
GMU_COLD_BOOT = 1,
- GMU_RESET = 2
};
enum gmu_load_mode {
@@ -213,6 +212,7 @@
* @ccl: CNOC BW scaling client
* @idle_level: Minimal GPU idle power level
* @fault_count: GMU fault count
+ * @unrecovered: Indicates whether GMU recovery failed or not
*/
struct gmu_device {
unsigned int ver;
@@ -247,6 +247,7 @@
unsigned int ccl;
unsigned int idle_level;
unsigned int fault_count;
+ bool unrecovered;
};
void gmu_snapshot(struct kgsl_device *device);
@@ -256,6 +257,7 @@
int allocate_gmu_image(struct gmu_device *gmu, unsigned int size);
int gmu_start(struct kgsl_device *device);
void gmu_stop(struct kgsl_device *device);
+int gmu_suspend(struct kgsl_device *device);
int gmu_dcvs_set(struct gmu_device *gmu, unsigned int gpu_pwrlevel,
unsigned int bus_level);
#endif /* __KGSL_GMU_H */
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0ce72f6..3539cda 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -262,13 +262,12 @@
return;
}
- gpu_qdss_desc.flags = 0;
+ kgsl_memdesc_init(device, &gpu_qdss_desc, 0);
gpu_qdss_desc.priv = 0;
gpu_qdss_desc.physaddr = gpu_qdss_entry[0];
gpu_qdss_desc.size = gpu_qdss_entry[1];
gpu_qdss_desc.pagetable = NULL;
gpu_qdss_desc.ops = NULL;
- gpu_qdss_desc.dev = device->dev->parent;
gpu_qdss_desc.hostptr = NULL;
result = memdesc_sg_dma(&gpu_qdss_desc, gpu_qdss_desc.physaddr,
@@ -307,13 +306,12 @@
return;
}
- gpu_qtimer_desc.flags = 0;
+ kgsl_memdesc_init(device, &gpu_qtimer_desc, 0);
gpu_qtimer_desc.priv = 0;
gpu_qtimer_desc.physaddr = gpu_qtimer_entry[0];
gpu_qtimer_desc.size = gpu_qtimer_entry[1];
gpu_qtimer_desc.pagetable = NULL;
gpu_qtimer_desc.ops = NULL;
- gpu_qtimer_desc.dev = device->dev->parent;
gpu_qtimer_desc.hostptr = NULL;
result = memdesc_sg_dma(&gpu_qtimer_desc, gpu_qtimer_desc.physaddr,
@@ -789,6 +787,10 @@
fault_type = "translation";
else if (flags & IOMMU_FAULT_PERMISSION)
fault_type = "permission";
+ else if (flags & IOMMU_FAULT_EXTERNAL)
+ fault_type = "external";
+ else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
+ fault_type = "transaction stalled";
if (kgsl_iommu_suppress_pagefault(addr, write, context)) {
iommu->pagefault_suppression_count++;
@@ -1482,6 +1484,7 @@
{
int ret;
+ kgsl_memdesc_init(device, &iommu->setstate, 0);
ret = kgsl_sharedmem_alloc_contig(device, &iommu->setstate, PAGE_SIZE);
if (!ret) {
@@ -2626,7 +2629,7 @@
return -EINVAL;
}
iommu->protect.base = reg_val[0] / sizeof(u32);
- iommu->protect.range = ilog2(reg_val[1] / sizeof(u32));
+ iommu->protect.range = reg_val[1] / sizeof(u32);
of_property_for_each_string(node, "clock-names", prop, cname) {
struct clk *c = devm_clk_get(&pdev->dev, cname);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index ee9e5df..2a149ac 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2798,6 +2798,25 @@
WARN_ONCE(1, "Failed to recover GMU\n");
if (device->snapshot)
device->snapshot->recovered = false;
+ /*
+ * On recovery failure, we are clearing
+ * GMU_FAULT bit and also not keeping
+ * the state as RESET to make sure any
+ * attempt to wake GMU/GPU after this
+ * is treated as a fresh start. But on
+ * recovery failure, GMU HS, clocks and
+ * IRQs are still ON/enabled because of
+ * which next GMU/GPU wakeup results in
+ * multiple warnings from GMU start as HS,
+ * clocks and IRQ were ON while doing a
+ * fresh start i.e. wake from SLUMBER.
+ *
+ * Suspend the GMU on recovery failure
+ * to make sure next attempt to wake up
+ * GMU/GPU is indeed a fresh start.
+ */
+ gmu_suspend(device);
+ gmu->unrecovered = true;
kgsl_pwrctrl_set_state(device, state);
} else {
if (device->snapshot)
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 3702893..df88b9a 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -97,6 +97,73 @@
static void kgsl_cma_unlock_secure(struct kgsl_memdesc *memdesc);
+static ssize_t
+imported_mem_show(struct kgsl_process_private *priv,
+ int type, char *buf)
+{
+ struct kgsl_mem_entry *entry;
+ uint64_t imported_mem = 0;
+ int id = 0;
+
+ spin_lock(&priv->mem_lock);
+ for (entry = idr_get_next(&priv->mem_idr, &id); entry;
+ id++, entry = idr_get_next(&priv->mem_idr, &id)) {
+
+ int egl_surface_count = 0, egl_image_count = 0;
+ struct kgsl_memdesc *m = &entry->memdesc;
+
+ if ((kgsl_memdesc_usermem_type(m) != KGSL_MEM_ENTRY_ION) ||
+ entry->pending_free || (kgsl_mem_entry_get(entry) == 0))
+ continue;
+ spin_unlock(&priv->mem_lock);
+
+ kgsl_get_egl_counts(entry, &egl_surface_count,
+ &egl_image_count);
+
+ if (kgsl_memdesc_get_memtype(m) ==
+ KGSL_MEMTYPE_EGL_SURFACE)
+ imported_mem += m->size;
+ else if (egl_surface_count == 0) {
+ uint64_t size = m->size;
+
+ do_div(size, (egl_image_count ?
+ egl_image_count : 1));
+ imported_mem += size;
+ }
+
+ kgsl_mem_entry_put(entry);
+ spin_lock(&priv->mem_lock);
+ }
+ spin_unlock(&priv->mem_lock);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", imported_mem);
+}
+
+static ssize_t
+gpumem_mapped_show(struct kgsl_process_private *priv,
+ int type, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ priv->gpumem_mapped);
+}
+
+static ssize_t
+gpumem_unmapped_show(struct kgsl_process_private *priv, int type, char *buf)
+{
+ if (priv->gpumem_mapped > priv->stats[type].cur)
+ return -EIO;
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ priv->stats[type].cur - priv->gpumem_mapped);
+}
+
+static struct kgsl_mem_entry_attribute debug_memstats[] = {
+ __MEM_ENTRY_ATTR(0, imported_mem, imported_mem_show),
+ __MEM_ENTRY_ATTR(0, gpumem_mapped, gpumem_mapped_show),
+ __MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_KERNEL, gpumem_unmapped,
+ gpumem_unmapped_show),
+};
+
/**
* Show the current amount of memory allocated for the given memtype
*/
@@ -219,7 +286,13 @@
&mem_stats[i].max_attr.attr))
WARN(1, "Couldn't create sysfs file '%s'\n",
mem_stats[i].max_attr.attr.name);
+ }
+ for (i = 0; i < ARRAY_SIZE(debug_memstats); i++) {
+ if (sysfs_create_file(&private->kobj,
+ &debug_memstats[i].attr))
+ WARN(1, "Couldn't create sysfs file '%s'\n",
+ debug_memstats[i].attr.name);
}
}
@@ -340,7 +413,7 @@
{
int ret;
- memdesc->flags = flags;
+ kgsl_memdesc_init(device, memdesc, flags);
if (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE)
ret = kgsl_sharedmem_alloc_contig(device, memdesc, size);
@@ -696,6 +769,40 @@
}
EXPORT_SYMBOL(kgsl_cache_range_op);
+void kgsl_memdesc_init(struct kgsl_device *device,
+ struct kgsl_memdesc *memdesc, uint64_t flags)
+{
+ struct kgsl_mmu *mmu = &device->mmu;
+ unsigned int align;
+
+ memset(memdesc, 0, sizeof(*memdesc));
+ /* Turn off SVM if the system doesn't support it */
+ if (!kgsl_mmu_use_cpu_map(mmu))
+ flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
+
+ /* Secure memory disables advanced addressing modes */
+ if (flags & KGSL_MEMFLAGS_SECURE)
+ flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
+
+ /* Disable IO coherence if it is not supported on the chip */
+ if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
+ flags &= ~((uint64_t) KGSL_MEMFLAGS_IOCOHERENT);
+
+ if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
+ memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
+
+ if (flags & KGSL_MEMFLAGS_SECURE)
+ memdesc->priv |= KGSL_MEMDESC_SECURE;
+
+ memdesc->flags = flags;
+ memdesc->dev = device->dev->parent;
+
+ align = max_t(unsigned int,
+ (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT,
+ ilog2(PAGE_SIZE));
+ kgsl_memdesc_set_align(memdesc, align);
+}
+
int
kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
uint64_t size)
@@ -896,8 +1003,6 @@
if (memdesc->pages)
kgsl_free(memdesc->pages);
-
- memset(memdesc, 0, sizeof(*memdesc));
}
EXPORT_SYMBOL(kgsl_sharedmem_free);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 55bb34f..976752d 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -57,6 +57,9 @@
uint64_t offset, uint64_t size,
unsigned int op);
+void kgsl_memdesc_init(struct kgsl_device *device,
+ struct kgsl_memdesc *memdesc, uint64_t flags);
+
void kgsl_process_init_sysfs(struct kgsl_device *device,
struct kgsl_process_private *private);
void kgsl_process_uninit_sysfs(struct kgsl_process_private *private);
@@ -282,8 +285,8 @@
{
int ret;
- memdesc->flags = flags;
- memdesc->priv = priv;
+ kgsl_memdesc_init(device, memdesc, flags);
+ memdesc->priv |= priv;
if (((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0) ||
(kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE))
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index b5888db..aea6267 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1370,7 +1370,7 @@
* of implement() working on 8 byte chunks
*/
- int len = hid_report_len(report) + 7;
+ u32 len = hid_report_len(report) + 7;
return kmalloc(len, flags);
}
@@ -1435,7 +1435,7 @@
{
char *buf;
int ret;
- int len;
+ u32 len;
buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!buf)
@@ -1461,14 +1461,14 @@
}
EXPORT_SYMBOL_GPL(__hid_request);
-int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
int interrupt)
{
struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_report *report;
struct hid_driver *hdrv;
unsigned int a;
- int rsize, csize = size;
+ u32 rsize, csize = size;
u8 *cdata = data;
int ret = 0;
@@ -1526,7 +1526,7 @@
*
* This is data entry for lower layers.
*/
-int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt)
{
struct hid_report_enum *report_enum;
struct hid_driver *hdrv;
@@ -2444,6 +2444,9 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERANALYSERCASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETESTCASSY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c
index 0cd4f72..5eea6fe 100644
--- a/drivers/hid/hid-elo.c
+++ b/drivers/hid/hid-elo.c
@@ -42,6 +42,12 @@
{
struct input_dev *input = hidinput->input;
+ /*
+ * ELO devices have one Button usage in GenDesk field, which makes
+ * hid-input map it to BTN_LEFT; that confuses userspace, which then
+ * considers the device to be a mouse/touchpad instead of touchscreen.
+ */
+ clear_bit(BTN_LEFT, input->keybit);
set_bit(BTN_TOUCH, input->keybit);
set_bit(ABS_PRESSURE, input->absbit);
input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 244b97c..9347b37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -608,6 +608,9 @@
#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033
#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035
#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038
+#define USB_DEVICE_ID_LD_POWERANALYSERCASSY 0x1040
+#define USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY 0x1042
+#define USB_DEVICE_ID_LD_MACHINETESTCASSY 0x1043
#define USB_DEVICE_ID_LD_JWM 0x1080
#define USB_DEVICE_ID_LD_DMMP 0x1081
#define USB_DEVICE_ID_LD_UMIP 0x1090
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index fb9ace1..5ff6dd8 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1149,18 +1149,26 @@
/*
* Ignore out-of-range values as per HID specification,
- * section 5.10 and 6.2.25.
+ * section 5.10 and 6.2.25, when NULL state bit is present.
+ * When it's not, clamp the value to match Microsoft's input
+ * driver as mentioned in "Required HID usages for digitizers":
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp
*
* The logical_minimum < logical_maximum check is done so that we
* don't unintentionally discard values sent by devices which
* don't specify logical min and max.
*/
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
- (field->logical_minimum < field->logical_maximum) &&
- (value < field->logical_minimum ||
- value > field->logical_maximum)) {
- dbg_hid("Ignoring out-of-range value %x\n", value);
- return;
+ (field->logical_minimum < field->logical_maximum)) {
+ if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
+ (value < field->logical_minimum ||
+ value > field->logical_maximum)) {
+ dbg_hid("Ignoring out-of-range value %x\n", value);
+ return;
+ }
+ value = clamp(value,
+ field->logical_minimum,
+ field->logical_maximum);
}
/*
@@ -1271,7 +1279,8 @@
led_work);
struct hid_field *field;
struct hid_report *report;
- int len, ret;
+ int ret;
+ u32 len;
__u8 *buf;
field = hidinput_get_led_field(hid);
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 20b40ad..42ed887 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -34,7 +34,8 @@
MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
static unsigned int scroll_speed = 32;
-static int param_set_scroll_speed(const char *val, struct kernel_param *kp) {
+static int param_set_scroll_speed(const char *val,
+ const struct kernel_param *kp) {
unsigned long speed;
if (!val || kstrtoul(val, 0, &speed) || speed > 63)
return -EINVAL;
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 89e9032..fba655d 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -315,7 +315,8 @@
static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
{
struct mt_device *td = hid_get_drvdata(hdev);
- int ret, size = hid_report_len(report);
+ int ret;
+ u32 size = hid_report_len(report);
u8 *buf;
/*
@@ -919,7 +920,7 @@
struct hid_report_enum *re;
struct mt_class *cls = &td->mtclass;
char *buf;
- int report_len;
+ u32 report_len;
if (td->inputmode < 0)
return;
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index be89bcb..276d12d 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -110,8 +110,8 @@
u8 *writeReport;
u8 *readReport;
- int input_report_size;
- int output_report_size;
+ u32 input_report_size;
+ u32 output_report_size;
unsigned long flags;
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index b0bb99a..1b1dccd 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1056,7 +1056,6 @@
u8 battery_charging;
u8 battery_capacity;
u8 led_state[MAX_LEDS];
- u8 resume_led_state[MAX_LEDS];
u8 led_delay_on[MAX_LEDS];
u8 led_delay_off[MAX_LEDS];
u8 led_count;
@@ -1793,6 +1792,7 @@
led->name = name;
led->brightness = sc->led_state[n];
led->max_brightness = max_brightness[n];
+ led->flags = LED_CORE_SUSPENDRESUME;
led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_brightness;
@@ -2509,47 +2509,32 @@
static int sony_suspend(struct hid_device *hdev, pm_message_t message)
{
- /*
- * On suspend save the current LED state,
- * stop running force-feedback and blank the LEDS.
- */
- if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
+#ifdef CONFIG_SONY_FF
+
+ /* On suspend stop any running force-feedback events */
+ if (SONY_FF_SUPPORT) {
struct sony_sc *sc = hid_get_drvdata(hdev);
-#ifdef CONFIG_SONY_FF
sc->left = sc->right = 0;
-#endif
-
- memcpy(sc->resume_led_state, sc->led_state,
- sizeof(sc->resume_led_state));
- memset(sc->led_state, 0, sizeof(sc->led_state));
-
sony_send_output_report(sc);
}
+#endif
return 0;
}
static int sony_resume(struct hid_device *hdev)
{
- /* Restore the state of controller LEDs on resume */
- if (SONY_LED_SUPPORT) {
- struct sony_sc *sc = hid_get_drvdata(hdev);
+ struct sony_sc *sc = hid_get_drvdata(hdev);
- memcpy(sc->led_state, sc->resume_led_state,
- sizeof(sc->led_state));
-
- /*
- * The Sixaxis and navigation controllers on USB need to be
- * reinitialized on resume or they won't behave properly.
- */
- if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
- (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
- sixaxis_set_operational_usb(sc->hdev);
- sc->defer_initialization = 1;
- }
-
- sony_set_leds(sc);
+ /*
+ * The Sixaxis and navigation controllers on USB need to be
+ * reinitialized on resume or they won't behave properly.
+ */
+ if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
+ sixaxis_set_operational_usb(sc->hdev);
+ sc->defer_initialization = 1;
}
return 0;
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index f0e2757..216f033 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -192,6 +192,11 @@
int ret = 0, len;
unsigned char report_number;
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
+ ret = -ENODEV;
+ goto out;
+ }
+
dev = hidraw_table[minor]->hid;
if (!dev->ll_driver->raw_request) {
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 865e7c2..2548c5d 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -142,10 +142,10 @@
* register of the HID
* descriptor. */
unsigned int bufsize; /* i2c buffer size */
- char *inbuf; /* Input buffer */
- char *rawbuf; /* Raw Input buffer */
- char *cmdbuf; /* Command buffer */
- char *argsbuf; /* Command arguments buffer */
+ u8 *inbuf; /* Input buffer */
+ u8 *rawbuf; /* Raw Input buffer */
+ u8 *cmdbuf; /* Command buffer */
+ u8 *argsbuf; /* Command arguments buffer */
unsigned long flags; /* device flags */
unsigned long quirks; /* Various quirks */
@@ -451,7 +451,8 @@
static void i2c_hid_get_input(struct i2c_hid *ihid)
{
- int ret, ret_size;
+ int ret;
+ u32 ret_size;
int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
if (size > ihid->bufsize)
@@ -476,7 +477,7 @@
return;
}
- if (ret_size > size) {
+ if ((ret_size > size) || (ret_size <= 2)) {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
__func__, size, ret_size);
return;
@@ -968,6 +969,15 @@
return ret < 0 && ret != -ENXIO ? ret : 0;
}
+static void i2c_hid_acpi_fix_up_power(struct device *dev)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ struct acpi_device *adev;
+
+ if (handle && acpi_bus_get_device(handle, &adev) == 0)
+ acpi_device_fix_up_power(adev);
+}
+
static const struct acpi_device_id i2c_hid_acpi_match[] = {
{"ACPI0C50", 0 },
{"PNP0C50", 0 },
@@ -980,6 +990,8 @@
{
return -ENODEV;
}
+
+static inline void i2c_hid_acpi_fix_up_power(struct device *dev) {}
#endif
#ifdef CONFIG_OF
@@ -1082,6 +1094,8 @@
if (ret < 0)
goto err;
+ i2c_hid_acpi_fix_up_power(&client->dev);
+
pm_runtime_get_noresume(&client->dev);
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 7a4d39c..e8b90b5 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -351,7 +351,7 @@
u8 *rep_data;
struct hid_report *r;
struct hid_report_enum *re;
- int length;
+ u32 length;
int error = -ENOMEM, limit = 0;
if (wacom_wac->mode_report < 0)
diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c
index 6031cd1..802afc9 100644
--- a/drivers/hsi/clients/ssi_protocol.c
+++ b/drivers/hsi/clients/ssi_protocol.c
@@ -989,7 +989,7 @@
goto drop;
/* Pad to 32-bits - FIXME: Revisit*/
if ((skb->len & 3) && skb_pad(skb, 4 - (skb->len & 3)))
- goto drop;
+ goto inc_dropped;
/*
* Modem sends Phonet messages over SSI with its own endianess...
@@ -1041,8 +1041,9 @@
drop2:
hsi_free_msg(msg);
drop:
- dev->stats.tx_dropped++;
dev_kfree_skb(skb);
+inc_dropped:
+ dev->stats.tx_dropped++;
return 0;
}
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index d8bc4b9..9360cdc 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -70,7 +70,7 @@
/* PCIE */
{ .dev_type = HV_PCIE,
HV_PCIE_GUID,
- .perf_device = true,
+ .perf_device = false,
},
/* Synthetic Frame Buffer */
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index b24f1d3..ac63e56 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -94,18 +94,20 @@
struct ina2xx_config {
u16 config_default;
- int calibration_factor;
+ int calibration_value;
int registers;
int shunt_div;
int bus_voltage_shift;
int bus_voltage_lsb; /* uV */
- int power_lsb; /* uW */
+ int power_lsb_factor;
};
struct ina2xx_data {
const struct ina2xx_config *config;
long rshunt;
+ long current_lsb_uA;
+ long power_lsb_uW;
struct mutex config_lock;
struct regmap *regmap;
@@ -115,21 +117,21 @@
static const struct ina2xx_config ina2xx_config[] = {
[ina219] = {
.config_default = INA219_CONFIG_DEFAULT,
- .calibration_factor = 40960000,
+ .calibration_value = 4096,
.registers = INA219_REGISTERS,
.shunt_div = 100,
.bus_voltage_shift = 3,
.bus_voltage_lsb = 4000,
- .power_lsb = 20000,
+ .power_lsb_factor = 20,
},
[ina226] = {
.config_default = INA226_CONFIG_DEFAULT,
- .calibration_factor = 5120000,
+ .calibration_value = 2048,
.registers = INA226_REGISTERS,
.shunt_div = 400,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
- .power_lsb = 25000,
+ .power_lsb_factor = 25,
},
};
@@ -168,12 +170,16 @@
return INA226_SHIFT_AVG(avg_bits);
}
+/*
+ * Calibration register is set to the best value, which eliminates
+ * truncation errors on calculating current register in hardware.
+ * According to datasheet (eq. 3) the best values are 2048 for
+ * ina226 and 4096 for ina219. They are hardcoded as calibration_value.
+ */
static int ina2xx_calibrate(struct ina2xx_data *data)
{
- u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
- data->rshunt);
-
- return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
+ return regmap_write(data->regmap, INA2XX_CALIBRATION,
+ data->config->calibration_value);
}
/*
@@ -186,10 +192,6 @@
if (ret < 0)
return ret;
- /*
- * Set current LSB to 1mA, shunt is in uOhms
- * (equation 13 in datasheet).
- */
return ina2xx_calibrate(data);
}
@@ -267,15 +269,15 @@
val = DIV_ROUND_CLOSEST(val, 1000);
break;
case INA2XX_POWER:
- val = regval * data->config->power_lsb;
+ val = regval * data->power_lsb_uW;
break;
case INA2XX_CURRENT:
- /* signed register, LSB=1mA (selected), in mA */
- val = (s16)regval;
+ /* signed register, result in mA */
+ val = regval * data->current_lsb_uA;
+ val = DIV_ROUND_CLOSEST(val, 1000);
break;
case INA2XX_CALIBRATION:
- val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
- regval);
+ val = regval;
break;
default:
/* programmer goofed */
@@ -303,9 +305,32 @@
ina2xx_get_value(data, attr->index, regval));
}
-static ssize_t ina2xx_set_shunt(struct device *dev,
- struct device_attribute *da,
- const char *buf, size_t count)
+/*
+ * In order to keep calibration register value fixed, the product
+ * of current_lsb and shunt_resistor should also be fixed and equal
+ * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order
+ * to keep the scale.
+ */
+static int ina2xx_set_shunt(struct ina2xx_data *data, long val)
+{
+ unsigned int dividend = DIV_ROUND_CLOSEST(1000000000,
+ data->config->shunt_div);
+ if (val <= 0 || val > dividend)
+ return -EINVAL;
+
+ mutex_lock(&data->config_lock);
+ data->rshunt = val;
+ data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val);
+ data->power_lsb_uW = data->config->power_lsb_factor *
+ data->current_lsb_uA;
+ mutex_unlock(&data->config_lock);
+
+ return 0;
+}
+
+static ssize_t ina2xx_store_shunt(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
{
unsigned long val;
int status;
@@ -315,18 +340,9 @@
if (status < 0)
return status;
- if (val == 0 ||
- /* Values greater than the calibration factor make no sense. */
- val > data->config->calibration_factor)
- return -EINVAL;
-
- mutex_lock(&data->config_lock);
- data->rshunt = val;
- status = ina2xx_calibrate(data);
- mutex_unlock(&data->config_lock);
+ status = ina2xx_set_shunt(data, val);
if (status < 0)
return status;
-
return count;
}
@@ -386,7 +402,7 @@
/* shunt resistance */
static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR,
- ina2xx_show_value, ina2xx_set_shunt,
+ ina2xx_show_value, ina2xx_store_shunt,
INA2XX_CALIBRATION);
/* update interval (ina226 only) */
@@ -431,6 +447,7 @@
/* set the device type */
data->config = &ina2xx_config[id->driver_data];
+ mutex_init(&data->config_lock);
if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
@@ -441,10 +458,7 @@
val = INA2XX_RSHUNT_DEFAULT;
}
- if (val <= 0 || val > data->config->calibration_factor)
- return -ENODEV;
-
- data->rshunt = val;
+ ina2xx_set_shunt(data, val);
ina2xx_regmap_config.max_register = data->config->registers;
@@ -460,8 +474,6 @@
return -ENODEV;
}
- mutex_init(&data->config_lock);
-
data->groups[group++] = &ina2xx_group;
if (id->driver_data == ina226)
data->groups[group++] = &ina226_group;
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 3baa4f4a..d659a02 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -101,8 +101,8 @@
[0] = { 27169, 0, -1 }, /* voltage */
[1] = { 806, 20475, -1 }, /* current, irange25 */
[2] = { 404, 20475, -1 }, /* current, irange50 */
- [3] = { 0, -1, 8549 }, /* power, irange25 */
- [4] = { 0, -1, 4279 }, /* power, irange50 */
+ [3] = { 8549, 0, -1 }, /* power, irange25 */
+ [4] = { 4279, 0, -1 }, /* power, irange50 */
};
static const struct coefficients adm1275_coefficients[] = {
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 62ad4f4..b900f76 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -751,6 +751,154 @@
{107, 980}
};
+/* Voltage to temperature */
+static const struct qpnp_vadc_map_pt adcmap_batt_therm_pu30[] = {
+ {1842, -400},
+ {1838, -380},
+ {1833, -360},
+ {1828, -340},
+ {1822, -320},
+ {1816, -300},
+ {1809, -280},
+ {1801, -260},
+ {1793, -240},
+ {1784, -220},
+ {1774, -200},
+ {1763, -180},
+ {1752, -160},
+ {1739, -140},
+ {1726, -120},
+ {1712, -100},
+ {1697, -80},
+ {1680, -60},
+ {1663, -40},
+ {1645, -20},
+ {1625, 0},
+ {1605, 20},
+ {1583, 40},
+ {1561, 60},
+ {1537, 80},
+ {1513, 100},
+ {1487, 120},
+ {1461, 140},
+ {1433, 160},
+ {1405, 180},
+ {1376, 200},
+ {1347, 220},
+ {1316, 240},
+ {1286, 260},
+ {1254, 280},
+ {1223, 300},
+ {1191, 320},
+ {1159, 340},
+ {1126, 360},
+ {1094, 380},
+ {1062, 400},
+ {1029, 420},
+ {997, 440},
+ {966, 460},
+ {934, 480},
+ {903, 500},
+ {873, 520},
+ {843, 540},
+ {813, 560},
+ {784, 580},
+ {756, 600},
+ {728, 620},
+ {702, 640},
+ {675, 660},
+ {650, 680},
+ {625, 700},
+ {601, 720},
+ {578, 740},
+ {556, 760},
+ {534, 780},
+ {513, 800},
+ {493, 820},
+ {474, 840},
+ {455, 860},
+ {437, 880},
+ {420, 900},
+ {403, 920},
+ {387, 940},
+ {372, 960},
+ {357, 980}
+};
+
+/* Voltage to temp0erature */
+static const struct qpnp_vadc_map_pt adcmap_batt_therm_pu400[] = {
+ {1516, -400},
+ {1478, -380},
+ {1438, -360},
+ {1396, -340},
+ {1353, -320},
+ {1307, -300},
+ {1261, -280},
+ {1213, -260},
+ {1164, -240},
+ {1115, -220},
+ {1066, -200},
+ {1017, -180},
+ {968, -160},
+ {920, -140},
+ {872, -120},
+ {826, -100},
+ {781, -80},
+ {737, -60},
+ {694, -40},
+ {654, -20},
+ {615, 0},
+ {578, 20},
+ {542, 40},
+ {509, 60},
+ {477, 80},
+ {447, 100},
+ {419, 120},
+ {392, 140},
+ {367, 160},
+ {343, 180},
+ {321, 200},
+ {301, 220},
+ {282, 240},
+ {264, 260},
+ {247, 280},
+ {231, 300},
+ {216, 320},
+ {203, 340},
+ {190, 360},
+ {178, 380},
+ {167, 400},
+ {157, 420},
+ {147, 440},
+ {138, 460},
+ {130, 480},
+ {122, 500},
+ {115, 520},
+ {108, 540},
+ {102, 560},
+ {96, 580},
+ {90, 600},
+ {85, 620},
+ {80, 640},
+ {76, 660},
+ {72, 680},
+ {68, 700},
+ {64, 720},
+ {61, 740},
+ {57, 760},
+ {54, 780},
+ {52, 800},
+ {49, 820},
+ {46, 840},
+ {44, 860},
+ {42, 880},
+ {40, 900},
+ {38, 920},
+ {36, 940},
+ {34, 960},
+ {32, 980}
+};
+
/*
* Voltage to temperature table for 100k pull up for NTCG104EF104 with
* 1.875V reference.
@@ -1120,6 +1268,62 @@
}
EXPORT_SYMBOL(qpnp_adc_batt_therm_qrd);
+int32_t qpnp_adc_batt_therm_pu30(struct qpnp_vadc_chip *chip,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t batt_thm_voltage = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ /* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
+ batt_thm_voltage = (int64_t) adc_code;
+ batt_thm_voltage *= (adc_properties->adc_vdd_reference
+ * 1000);
+ batt_thm_voltage = div64_s64(batt_thm_voltage,
+ adc_properties->full_scale_code * 1000);
+ qpnp_adc_map_voltage_temp(adcmap_batt_therm_pu30,
+ ARRAY_SIZE(adcmap_batt_therm_pu30),
+ batt_thm_voltage, &adc_chan_result->physical);
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_batt_therm_pu30);
+
+int32_t qpnp_adc_batt_therm_pu400(struct qpnp_vadc_chip *chip,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t batt_thm_voltage = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+
+ /* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
+ batt_thm_voltage = (int64_t) adc_code;
+ batt_thm_voltage *= (adc_properties->adc_vdd_reference
+ * 1000);
+ batt_thm_voltage = div64_s64(batt_thm_voltage,
+ adc_properties->full_scale_code * 1000);
+ qpnp_adc_map_voltage_temp(adcmap_batt_therm_pu400,
+ ARRAY_SIZE(adcmap_batt_therm_pu400),
+ batt_thm_voltage, &adc_chan_result->physical);
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_batt_therm_pu400);
+
int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *chip,
int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
@@ -2291,6 +2495,12 @@
}
}
+ if (of_device_is_compatible(node, "qcom,qpnp-adc-hc-pm5") ||
+ of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc-pm5"))
+ adc_prop->is_pmic_5 = true;
+ else
+ adc_prop->is_pmic_5 = false;
+
for_each_child_of_node(node, child) {
int channel_num, scaling = 0, post_scaling = 0;
int fast_avg_setup, calib_type = 0, rc, hw_settle_time = 0;
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 1f38574..7e6af65 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -35,8 +35,6 @@
#include <linux/power_supply.h>
#include <linux/thermal.h>
-#define QPNP_VADC_HC_VREF_CODE 0x4000
-
/* QPNP VADC register definition */
#define QPNP_VADC_REVISION1 0x0
#define QPNP_VADC_REVISION2 0x1
@@ -228,6 +226,8 @@
[SCALE_USBIN_I] = {qpnp_adc_scale_usbin_curr},
[SCALE_BATT_THERM_TEMP_QRD] = {qpnp_adc_batt_therm_qrd},
[SCALE_SMB1390_DIE_TEMP] = {qpnp_adc_scale_die_temp_1390},
+ [SCALE_BATT_THERM_TEMP_PU30] = {qpnp_adc_batt_therm_pu30},
+ [SCALE_BATT_THERM_TEMP_PU400] = {qpnp_adc_batt_therm_pu400},
};
static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = {
@@ -506,7 +506,7 @@
goto fail_unlock;
}
- if (vadc->adc->adc_prop->full_scale_code == QPNP_VADC_HC_VREF_CODE) {
+ if (!vadc->adc->adc_prop->is_pmic_5) {
if (!vadc->vadc_init_calib) {
rc = qpnp_vadc_calib_device(vadc);
if (rc) {
@@ -2593,6 +2593,8 @@
},
{ .compatible = "qcom,qpnp-vadc-hc",
},
+ { .compatible = "qcom,qpnp-adc-hc-pm5",
+ },
{}
};
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 95c887c..2d4da74 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -63,7 +63,6 @@
config CORESIGHT_SOURCE_ETM4X
bool "CoreSight Embedded Trace Macrocell 4.x driver"
- depends on ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for the ETM4.x tracer module, tracing the
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index d7325c65..7e041d9 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012,2018 The Linux Foundation. All rights reserved.
*
* Description: CoreSight Embedded Trace Buffer driver
*
@@ -669,7 +669,6 @@
spin_lock_init(&drvdata->spinlock);
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
- pm_runtime_put(&adev->dev);
if (drvdata->buffer_depth & 0x80000000)
return -EINVAL;
@@ -698,6 +697,7 @@
ret = misc_register(&drvdata->miscdev);
if (ret)
goto err_misc_register;
+ pm_runtime_put(&adev->dev);
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index f57ad2e..04870ce 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012, 2016, 2018, The Linux Foundation. All rights reserved.
*
* Description: CoreSight Program Flow Trace driver
*
@@ -39,6 +39,7 @@
#include "coresight-etm.h"
#include "coresight-etm-perf.h"
+#include "coresight-priv.h"
/*
* Not really modular but using module_param is the easiest way to
@@ -720,6 +721,11 @@
* certain registers might be ignored.
*/
etm_clr_pwrdwn(drvdata);
+
+ /* check the state of the fuse */
+ if (!coresight_authstatus_enabled(drvdata->base))
+ goto out;
+
/*
* Set prog bit. It will be set from reset but this is included to
* ensure it is set
@@ -742,6 +748,7 @@
etm_set_pwrdwn(drvdata);
etm_clr_pwrup(drvdata);
+out:
CS_LOCK(drvdata->base);
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index a4bf109..95d13a9 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,6 +27,7 @@
#include <linux/cpu.h>
#include <linux/coresight.h>
#include <linux/coresight-pmu.h>
+#include <linux/of.h>
#include <linux/pm_wakeup.h>
#include <linux/amba/bus.h>
#include <linux/seq_file.h>
@@ -38,6 +39,7 @@
#include "coresight-etm4x.h"
#include "coresight-etm-perf.h"
+#include "coresight-priv.h"
static int boot_enable;
module_param_named(boot_enable, boot_enable, int, 0444);
@@ -166,12 +168,14 @@
writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0);
writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1);
- /*
- * Request to keep the trace unit powered and also
- * emulation of powerdown
- */
- writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | TRCPDCR_PU,
- drvdata->base + TRCPDCR);
+ if (!drvdata->tupwr_disable) {
+ /*
+ * Request to keep the trace unit powered and also
+ * emulation of powerdown
+ */
+ writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR)
+ | TRCPDCR_PU, drvdata->base + TRCPDCR);
+ }
/* Enable the trace unit */
writel_relaxed(1, drvdata->base + TRCPRGCTLR);
@@ -312,10 +316,12 @@
CS_UNLOCK(drvdata->base);
- /* power can be removed from the trace unit now */
- control = readl_relaxed(drvdata->base + TRCPDCR);
- control &= ~TRCPDCR_PU;
- writel_relaxed(control, drvdata->base + TRCPDCR);
+ if (!drvdata->tupwr_disable) {
+ /* power can be removed from the trace unit now */
+ control = readl_relaxed(drvdata->base + TRCPDCR);
+ control &= ~TRCPDCR_PU;
+ writel_relaxed(control, drvdata->base + TRCPDCR);
+ }
control = readl_relaxed(drvdata->base + TRCPRGCTLR);
@@ -436,6 +442,9 @@
CS_UNLOCK(drvdata->base);
+ if (!coresight_authstatus_enabled(drvdata->base))
+ goto out;
+
/* find all capabilities of the tracing unit */
etmidr0 = readl_relaxed(drvdata->base + TRCIDR0);
@@ -582,6 +591,8 @@
drvdata->nrseqstate = BMVAL(etmidr5, 25, 27);
/* NUMCNTR, bits[30:28] number of counters available for tracing */
drvdata->nr_cntr = BMVAL(etmidr5, 28, 30);
+
+out:
CS_LOCK(drvdata->base);
}
@@ -1035,6 +1046,9 @@
etmdrvdata[drvdata->cpu] = drvdata;
+ drvdata->tupwr_disable = of_property_read_bool(drvdata->dev->of_node,
+ "qcom,tupwr-disable");
+
dev_info(dev, "CPU%d: %s initialized\n",
drvdata->cpu, (char *)id->data);
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index 4e51ecdc..5f34bdc 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -345,6 +345,7 @@
* @nooverflow: Indicate if overflow prevention is supported.
* @atbtrig: If the implementation can support ATB triggers
* @lpoverride: If the implementation can support low-power state over.
+ * @tupwr_disable: If disable the support of keeping trace unit powered.
* @config: structure holding configuration parameters.
*/
struct etmv4_drvdata {
@@ -391,6 +392,7 @@
bool nooverflow;
bool atbtrig;
bool lpoverride;
+ bool tupwr_disable;
struct etmv4_config config;
};
diff --git a/drivers/hwtracing/coresight/coresight-event.c b/drivers/hwtracing/coresight/coresight-event.c
index d5f304f..86e2b29 100644
--- a/drivers/hwtracing/coresight/coresight-event.c
+++ b/drivers/hwtracing/coresight/coresight-event.c
@@ -20,12 +20,13 @@
#include <trace/events/exception.h>
static int event_abort_enable;
-static int event_abort_set(const char *val, struct kernel_param *kp);
+static int event_abort_set(const char *val, const struct kernel_param *kp);
module_param_call(event_abort_enable, event_abort_set, param_get_int,
&event_abort_enable, 0644);
static int event_abort_early_panic = 1;
-static int event_abort_on_panic_set(const char *val, struct kernel_param *kp);
+static int event_abort_on_panic_set(const char *val,
+ const struct kernel_param *kp);
module_param_call(event_abort_early_panic, event_abort_on_panic_set,
param_get_int, &event_abort_early_panic, 0644);
@@ -96,7 +97,7 @@
unregister_trace_unhandled_abort(event_abort_unhandled_abort, NULL);
}
-static int event_abort_set(const char *val, struct kernel_param *kp)
+static int event_abort_set(const char *val, const struct kernel_param *kp)
{
int ret;
@@ -114,7 +115,8 @@
return ret;
}
-static int event_abort_on_panic_set(const char *val, struct kernel_param *kp)
+static int event_abort_on_panic_set(const char *val,
+ const struct kernel_param *kp)
{
int ret;
diff --git a/drivers/hwtracing/coresight/coresight-ost.c b/drivers/hwtracing/coresight/coresight-ost.c
index a5075ba..340c589 100644
--- a/drivers/hwtracing/coresight/coresight-ost.c
+++ b/drivers/hwtracing/coresight/coresight-ost.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -280,14 +280,13 @@
int stm_set_ost_params(struct stm_drvdata *drvdata, size_t bitmap_size)
{
- stmdrvdata = drvdata;
-
drvdata->chs.bitmap = devm_kzalloc(drvdata->dev, bitmap_size,
GFP_KERNEL);
if (!drvdata->chs.bitmap)
return -ENOMEM;
bitmap_fill(drvdata->entities, OST_ENTITY_MAX);
+ stmdrvdata = drvdata;
return 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
index 98547a9..0990164 100644
--- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c
+++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2015,2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -132,7 +132,6 @@
drvdata->base = base;
dev_set_drvdata(dev, drvdata);
- pm_runtime_put(&adev->dev);
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
@@ -143,6 +142,7 @@
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
+ pm_runtime_put(&adev->dev);
dev_info(dev, "%s initialized\n", (char *)id->data);
return 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 159512c..caeda7b 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* Description: CoreSight System Trace Macrocell driver
*
@@ -839,11 +839,6 @@
}
bitmap_size = BITS_TO_LONGS(drvdata->numsp) * sizeof(long);
- /* Store the driver data pointer for use in exported functions */
- ret = stm_set_ost_params(drvdata, bitmap_size);
- if (ret)
- return ret;
-
guaranteed = devm_kzalloc(dev, bitmap_size, GFP_KERNEL);
if (!guaranteed)
return -ENOMEM;
@@ -872,6 +867,11 @@
goto stm_unregister;
}
+ /* Store the driver data pointer for use in exported functions */
+ ret = stm_set_ost_params(drvdata, bitmap_size);
+ if (ret)
+ goto stm_unregister;
+
pm_runtime_put(&adev->dev);
dev_info(dev, "%s initialized\n", (char *)id->data);
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 9d2ab01..e369ea1 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -845,7 +845,6 @@
}
drvdata->enable = true;
- drvdata->sticky_enable = true;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 3948a50..8aed31a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -139,7 +139,6 @@
void tmc_enable_hw(struct tmc_drvdata *drvdata)
{
drvdata->enable = true;
- drvdata->sticky_enable = true;
writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
if (drvdata->force_reg_dump)
__tmc_reg_dump(drvdata);
@@ -155,7 +154,7 @@
{
int ret = 0;
- if (!drvdata->sticky_enable)
+ if (!drvdata->enable)
return -EPERM;
switch (drvdata->config_type) {
@@ -689,8 +688,6 @@
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
}
- pm_runtime_put(&adev->dev);
-
ctidata = of_get_coresight_cti_data(dev, adev->dev.of_node);
if (IS_ERR(ctidata)) {
dev_err(dev, "invalid cti data\n");
@@ -735,6 +732,13 @@
ret = tmc_etr_bam_init(adev, drvdata);
if (ret)
goto out;
+ /*
+ * ETR configuration uses a 40-bit AXI master in place of
+ * the embedded SRAM of ETB/ETF.
+ */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ if (ret)
+ goto out;
} else {
desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
desc.ops = &tmc_etf_cs_ops;
@@ -754,6 +758,10 @@
ret = misc_register(&drvdata->miscdev);
if (ret)
coresight_unregister(drvdata->csdev);
+
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
out:
return ret;
}
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c
index 5d2d087..261b8c8 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.c
+++ b/drivers/hwtracing/coresight/coresight-tpda.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -678,8 +678,6 @@
if (!coresight_authstatus_enabled(drvdata->base))
goto err;
- pm_runtime_put(&adev->dev);
-
tpda_init_default_data(drvdata);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
@@ -695,6 +693,8 @@
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
+ pm_runtime_put(&adev->dev);
+
dev_dbg(drvdata->dev, "TPDA initialized\n");
return 0;
err:
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 7cdb459..6463a47 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -4029,8 +4029,6 @@
drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;
- pm_runtime_put(&adev->dev);
-
drvdata->traceid = traceid++;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
@@ -4051,6 +4049,8 @@
if (boot_enable)
coresight_enable(drvdata->csdev);
+ pm_runtime_put(&adev->dev);
+
return 0;
}
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 1cf60f3..21c74ec 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -181,8 +181,10 @@
if (atomic_inc_return(&csdev->refcnt[refport]) == 1) {
if (link_ops(csdev)->enable) {
ret = link_ops(csdev)->enable(csdev, inport, outport);
- if (ret)
+ if (ret) {
+ atomic_dec(&csdev->refcnt[refport]);
return ret;
+ }
}
}
@@ -263,42 +265,66 @@
}
}
-void coresight_disable_path(struct list_head *path)
+static void coresigh_disable_list_node(struct list_head *path,
+ struct coresight_node *nd)
{
u32 type;
- struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
+ csdev = nd->csdev;
+ type = csdev->type;
+
+ /*
+ * ETF devices are tricky... They can be a link or a sink,
+ * depending on how they are configured. If an ETF has been
+ * "activated" it will be configured as a sink, otherwise
+ * go ahead with the link configuration.
+ */
+ if (type == CORESIGHT_DEV_TYPE_LINKSINK)
+ type = (csdev == coresight_get_sink(path)) ?
+ CORESIGHT_DEV_TYPE_SINK :
+ CORESIGHT_DEV_TYPE_LINK;
+
+ switch (type) {
+ case CORESIGHT_DEV_TYPE_SINK:
+ coresight_disable_sink(csdev);
+ break;
+ case CORESIGHT_DEV_TYPE_SOURCE:
+ /* sources are disabled from either sysFS or Perf */
+ break;
+ case CORESIGHT_DEV_TYPE_LINK:
+ parent = list_prev_entry(nd, link)->csdev;
+ child = list_next_entry(nd, link)->csdev;
+ coresight_disable_link(csdev, parent, child);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * During enabling path, if it is failed, then only those enabled
+ * devices need to be disabled. This function is to disable devices
+ * which is enabled before the failed device.
+ *
+ * @path the head of the list
+ * @nd the failed device node
+ */
+static void coresight_disable_previous_devs(struct list_head *path,
+ struct coresight_node *nd)
+{
+
+ list_for_each_entry_continue(nd, path, link) {
+ coresigh_disable_list_node(path, nd);
+ }
+}
+
+void coresight_disable_path(struct list_head *path)
+{
+ struct coresight_node *nd;
+
list_for_each_entry(nd, path, link) {
- csdev = nd->csdev;
- type = csdev->type;
-
- /*
- * ETF devices are tricky... They can be a link or a sink,
- * depending on how they are configured. If an ETF has been
- * "activated" it will be configured as a sink, otherwise
- * go ahead with the link configuration.
- */
- if (type == CORESIGHT_DEV_TYPE_LINKSINK)
- type = (csdev == coresight_get_sink(path)) ?
- CORESIGHT_DEV_TYPE_SINK :
- CORESIGHT_DEV_TYPE_LINK;
-
- switch (type) {
- case CORESIGHT_DEV_TYPE_SINK:
- coresight_disable_sink(csdev);
- break;
- case CORESIGHT_DEV_TYPE_SOURCE:
- /* sources are disabled from either sysFS or Perf */
- break;
- case CORESIGHT_DEV_TYPE_LINK:
- parent = list_prev_entry(nd, link)->csdev;
- child = list_next_entry(nd, link)->csdev;
- coresight_disable_link(csdev, parent, child);
- break;
- default:
- break;
- }
+ coresigh_disable_list_node(path, nd);
}
}
@@ -349,7 +375,7 @@
out:
return ret;
err:
- coresight_disable_path(path);
+ coresight_disable_previous_devs(path, nd);
goto out;
}
@@ -589,6 +615,9 @@
int ret = 0;
struct coresight_device *sink;
struct list_head *path;
+ enum coresight_dev_subtype_source subtype;
+
+ subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
@@ -596,8 +625,16 @@
if (ret)
goto out;
- if (csdev->enable)
+ if (csdev->enable) {
+ /*
+ * There could be multiple applications driving the software
+ * source. So keep the refcount for each such user when the
+ * source is already enabled.
+ */
+ if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
+ atomic_inc(csdev->refcnt);
goto out;
+ }
/*
* Search for a valid sink for this session but don't reset the
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index be810fe..5e2f89a 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -152,7 +152,7 @@
continue;
/* The local out port number */
- pdata->outports[i] = endpoint.id;
+ pdata->outports[i] = endpoint.port;
/*
* Get a handle on the remote port and parent
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index 2dd60ee..b8e2992 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -174,8 +174,9 @@
{
struct stp_master *master;
size_t size;
+ unsigned long align = sizeof(unsigned long);
- size = ALIGN(stm->data->sw_nchannels, 8) / 8;
+ size = ALIGN(stm->data->sw_nchannels, align) / align;
size += sizeof(struct stp_master);
master = kzalloc(size, GFP_ATOMIC);
if (!master)
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 809f4d4..884c1ec 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -509,6 +509,9 @@
/* Enable the adapter */
__i2c_dw_enable(dev, true);
+ /* Dummy read to avoid the register getting stuck on Bay Trail */
+ dw_readl(dev, DW_IC_ENABLE_STATUS);
+
/* Clear and enable interrupts */
dw_readl(dev, DW_IC_CLR_INTR);
dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index e6fe21a..b32bf7e 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -243,6 +243,7 @@
struct i2c_adapter adapter;
unsigned long smba;
unsigned char original_hstcfg;
+ unsigned char original_slvcmd;
struct pci_dev *pci_dev;
unsigned int features;
@@ -962,13 +963,24 @@
if (!priv->host_notify)
return -ENOMEM;
- outb_p(SMBSLVCMD_HST_NTFY_INTREN, SMBSLVCMD(priv));
+ if (!(SMBSLVCMD_HST_NTFY_INTREN & priv->original_slvcmd))
+ outb_p(SMBSLVCMD_HST_NTFY_INTREN | priv->original_slvcmd,
+ SMBSLVCMD(priv));
+
/* clear Host Notify bit to allow a new notification */
outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
return 0;
}
+static void i801_disable_host_notify(struct i801_priv *priv)
+{
+ if (!(priv->features & FEATURE_HOST_NOTIFY))
+ return;
+
+ outb_p(priv->original_slvcmd, SMBSLVCMD(priv));
+}
+
static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = i801_access,
.functionality = i801_func,
@@ -1589,6 +1601,10 @@
outb_p(inb_p(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
+ /* Remember original Host Notify setting */
+ if (priv->features & FEATURE_HOST_NOTIFY)
+ priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
+
/* Default timeout in interrupt mode: 200 ms */
priv->adapter.timeout = HZ / 5;
@@ -1666,6 +1682,7 @@
pm_runtime_forbid(&dev->dev);
pm_runtime_get_noresume(&dev->dev);
+ i801_disable_host_notify(priv);
i801_del_mux(priv);
i2c_del_adapter(&priv->adapter);
i801_acpi_remove(priv);
@@ -1679,6 +1696,15 @@
*/
}
+static void i801_shutdown(struct pci_dev *dev)
+{
+ struct i801_priv *priv = pci_get_drvdata(dev);
+
+ /* Restore config registers to avoid hard hang on some systems */
+ i801_disable_host_notify(priv);
+ pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
+}
+
#ifdef CONFIG_PM
static int i801_suspend(struct device *dev)
{
@@ -1711,6 +1737,7 @@
.id_table = i801_ids,
.probe = i801_probe,
.remove = i801_remove,
+ .shutdown = i801_shutdown,
.driver = {
.pm = &i801_pm_ops,
},
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
index dfc98df..7aa7b9c 100644
--- a/drivers/i2c/busses/i2c-scmi.c
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -18,6 +18,9 @@
#define ACPI_SMBUS_HC_CLASS "smbus"
#define ACPI_SMBUS_HC_DEVICE_NAME "cmi"
+/* SMBUS HID definition as supported by Microsoft Windows */
+#define ACPI_SMBUS_MS_HID "SMB0001"
+
ACPI_MODULE_NAME("smbus_cmi");
struct smbus_methods_t {
@@ -51,6 +54,7 @@
static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
{"SMBUS01", (kernel_ulong_t)&smbus_methods},
{ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},
+ {ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods},
{"", 0}
};
MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);
diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c
index c6a90b4..91b10af 100644
--- a/drivers/i2c/muxes/i2c-mux-reg.c
+++ b/drivers/i2c/muxes/i2c-mux-reg.c
@@ -196,20 +196,25 @@
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mux->data.reg_size = resource_size(res);
mux->data.reg = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(mux->data.reg))
- return PTR_ERR(mux->data.reg);
+ if (IS_ERR(mux->data.reg)) {
+ ret = PTR_ERR(mux->data.reg);
+ goto err_put_parent;
+ }
}
if (mux->data.reg_size != 4 && mux->data.reg_size != 2 &&
mux->data.reg_size != 1) {
dev_err(&pdev->dev, "Invalid register size\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_put_parent;
}
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0,
i2c_mux_reg_select, NULL);
- if (!muxc)
- return -ENOMEM;
+ if (!muxc) {
+ ret = -ENOMEM;
+ goto err_put_parent;
+ }
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
@@ -235,6 +240,8 @@
add_adapter_failed:
i2c_mux_del_adapters(muxc);
+err_put_parent:
+ i2c_put_adapter(parent);
return ret;
}
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index d127ace..6ee866f 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -244,7 +244,7 @@
static unsigned int ide_disks;
static struct chs_geom ide_disks_chs[MAX_HWIFS * MAX_DRIVES];
-static int ide_set_disk_chs(const char *str, struct kernel_param *kp)
+static int ide_set_disk_chs(const char *str, const struct kernel_param *kp)
{
unsigned int a, b, c = 0, h = 0, s = 0, i, j = 1;
@@ -328,7 +328,7 @@
static unsigned int ide_ignore_cable;
-static int ide_set_ignore_cable(const char *s, struct kernel_param *kp)
+static int ide_set_ignore_cable(const char *s, const struct kernel_param *kp)
{
int i, j = 1;
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 3a557e3..32cd64c 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -827,6 +827,8 @@
int st_accel_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
+ struct st_sensors_platform_data *pdata =
+ (struct st_sensors_platform_data *)adata->dev->platform_data;
int irq = adata->get_irq_data_ready(indio_dev);
int err;
@@ -853,11 +855,10 @@
&adata->sensor_settings->fs.fs_avl[0];
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
- if (!adata->dev->platform_data)
- adata->dev->platform_data =
- (struct st_sensors_platform_data *)&default_accel_pdata;
+ if (!pdata)
+ pdata = (struct st_sensors_platform_data *)&default_accel_pdata;
- err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data);
+ err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0)
goto st_accel_power_off;
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index 678e8c7..fec696e 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -121,10 +121,21 @@
enum iio_event_direction dir, int state)
{
struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u32 tmp;
- priv->event_scan_mask &= ~BIT(chan->channel);
- if (state)
+ if (state) {
+ ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);
+ if (ret < 0)
+ return ret;
+ if (tmp & BIT(chan->channel))
+ priv->event_prev_val |= BIT(chan->channel);
+ else
+ priv->event_prev_val &= ~BIT(chan->channel);
+
priv->event_scan_mask |= BIT(chan->channel);
+ } else
+ priv->event_scan_mask &= ~BIT(chan->channel);
return 0;
}
@@ -442,13 +453,15 @@
priv->spi = spi;
reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);
- if (IS_ERR(reset_gpio)) {
- /* chip s/w reset if h/w reset failed */
+ if (!IS_ERR(reset_gpio)) {
+ /* need >=100ns low pulse to reset chip */
+ gpiod_set_raw_value_cansleep(reset_gpio, 0);
+ udelay(1);
+ gpiod_set_raw_value_cansleep(reset_gpio, 1);
+ } else {
+ /* s/w reset chip if h/w reset is not available */
hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
hi8435_writeb(priv, HI8435_CTRL_REG, 0);
- } else {
- udelay(5);
- gpiod_set_value(reset_gpio, 1);
}
spi_set_drvdata(spi, idev);
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index ab646a9..af85db5 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -215,7 +215,7 @@
ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id,
st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0)
- ret = -EINVAL;
+ return -EINVAL;
ret = sensor_hub_get_feature(st->hsdev,
st->poll.report_id,
@@ -265,7 +265,7 @@
st->sensitivity.index, sizeof(value),
&value);
if (ret < 0 || value < 0)
- ret = -EINVAL;
+ return -EINVAL;
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity.report_id,
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 1f1ad41..0a8b763 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -39,6 +39,7 @@
be called kmx61.
source "drivers/iio/imu/inv_mpu6050/Kconfig"
+source "drivers/iio/imu/inv_icm20602/Kconfig"
endmenu
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index c71bcd3..fab6a5e 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -15,5 +15,6 @@
obj-y += bmi160/
obj-y += inv_mpu6050/
+obj-y += inv_icm20602/
obj-$(CONFIG_KMX61) += kmx61.o
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
index f53e9a8..93b99bd 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu/adis_trigger.c
@@ -47,6 +47,10 @@
if (adis->trig == NULL)
return -ENOMEM;
+ adis->trig->dev.parent = &adis->spi->dev;
+ adis->trig->ops = &adis_trigger_ops;
+ iio_trigger_set_drvdata(adis->trig, adis);
+
ret = request_irq(adis->spi->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
@@ -55,9 +59,6 @@
if (ret)
goto error_free_trig;
- adis->trig->dev.parent = &adis->spi->dev;
- adis->trig->ops = &adis_trigger_ops;
- iio_trigger_set_drvdata(adis->trig, adis);
ret = iio_trigger_register(adis->trig);
indio_dev->trig = iio_trigger_get(adis->trig);
diff --git a/drivers/iio/imu/inv_icm20602/Kconfig b/drivers/iio/imu/inv_icm20602/Kconfig
new file mode 100644
index 0000000..fa88689
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/Kconfig
@@ -0,0 +1,14 @@
+#
+# inv-icm20602 drivers for Invensense MPU devices and combos
+#
+config INV_ICM20602_IIO
+ tristate "Invensense ICM20602 devices"
+ depends on I2C && SYSFS
+ select IIO_BUFFER
+ select IIO_BUFFER_CB
+ select IIO_TRIGGERED_BUFFER
+ help
+ This driver supports the Invensense ICM20602 devices.
+ It is a gyroscope/accelerometer combo device.
+ This driver can be built as a module. The module will be called
+ inv-icm20602.
diff --git a/drivers/iio/imu/inv_icm20602/Makefile b/drivers/iio/imu/inv_icm20602/Makefile
new file mode 100644
index 0000000..d60c10a
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense icm20602 device.
+#
+
+obj-$(CONFIG_INV_ICM20602_IIO) += inv-icm20602.o
+inv-icm20602-objs := inv_icm20602_core.o inv_icm20602_ring.o inv_icm20602_trigger.o inv_icm20602_bsp.o
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
new file mode 100644
index 0000000..03e9670
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include "inv_icm20602_bsp.h"
+#include "inv_icm20602_iio.h"
+
+#define icm20602_init_reg_addr(head, tail, reg_map) { \
+ enum inv_icm20602_reg_addr i;\
+ for (i = head; i <= tail; i++) {\
+ reg_map->address = i;\
+ reg_map->reg_u.reg = 0x0;\
+ reg_map++;\
+ } \
+}
+
+#define icm20602_write_reg_simple(st, register) \
+ icm20602_write_reg(st, \
+ register.address, \
+ register.reg_u.reg)
+
+static struct inv_icm20602_reg_map reg_set_20602;
+
+int icm20602_init_reg_map(void)
+{
+ struct struct_XG_OFFS_TC_H *reg_map = &(reg_set_20602.XG_OFFS_TC_H);
+
+ icm20602_init_reg_addr(ADDR_XG_OFFS_TC_H,
+ ADDR_XG_OFFS_TC_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_YG_OFFS_TC_H,
+ ADDR_YG_OFFS_TC_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_ZG_OFFS_TC_H,
+ ADDR_ZG_OFFS_TC_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_SELF_TEST_X_ACCEL,
+ ADDR_SELF_TEST_Z_ACCEL, reg_map);
+
+ icm20602_init_reg_addr(ADDR_XG_OFFS_USRH,
+ ADDR_LP_MODE_CFG, reg_map);
+
+ icm20602_init_reg_addr(ADDR_ACCEL_WOM_X_THR,
+ ADDR_FIFO_EN, reg_map);
+
+ icm20602_init_reg_addr(ADDR_FSYNC_INT,
+ ADDR_GYRO_ZOUT_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_SELF_TEST_X_GYRO,
+ ADDR_SELF_TEST_Z_GYRO, reg_map);
+
+ icm20602_init_reg_addr(ADDR_FIFO_WM_TH1,
+ ADDR_FIFO_WM_TH2, reg_map);
+
+ icm20602_init_reg_addr(ADDR_SIGNAL_PATH_RESET,
+ ADDR_PWR_MGMT_2, reg_map);
+
+ icm20602_init_reg_addr(ADDR_I2C_IF,
+ ADDR_I2C_IF, reg_map);
+
+ icm20602_init_reg_addr(ADDR_FIFO_COUNTH,
+ ADDR_XA_OFFSET_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_YA_OFFSET_H,
+ ADDR_YA_OFFSET_L, reg_map);
+
+ icm20602_init_reg_addr(ADDR_ZA_OFFSET_H,
+ ADDR_ZA_OFFSET_L, reg_map);
+
+ return MPU_SUCCESS;
+}
+
+#define W_FLG 0
+#define R_FLG 1
+int icm20602_bulk_read(struct inv_icm20602_state *st,
+ int reg, u8 *buf, int size)
+{
+ int result = MPU_SUCCESS;
+ char tx_buf[2] = {0x0, 0x0};
+ u8 *tmp_buf = buf;
+ struct i2c_msg msg[2];
+
+ if (!st || !buf)
+ return -MPU_FAIL;
+
+ if (st->interface == ICM20602_SPI) {
+ tx_buf[0] = ICM20602_READ_REG(reg);
+ result = spi_write_then_read(st->spi, &tx_buf[0],
+ 1, tmp_buf, size);
+ if (result) {
+ pr_err("mpu read reg %u failed, rc %d\n",
+ reg, result);
+ result = -MPU_READ_FAIL;
+ }
+ } else {
+ result = size;
+#ifdef ICM20602_I2C_SMBUS
+ result += i2c_smbus_read_i2c_block_data(st->client,
+ reg, size, tmp_buf);
+#else
+ tx_buf[0] = reg;
+ msg[0].addr = st->client->addr;
+ msg[0].flags = W_FLG;
+ msg[0].len = 1;
+ msg[0].buf = tx_buf;
+
+ msg[1].addr = st->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = size;
+ msg[1].buf = tmp_buf;
+ i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+ }
+ return result;
+}
+
+static int icm20602_write_reg(struct inv_icm20602_state *st,
+ uint8_t reg, uint8_t val)
+{
+ int result = MPU_SUCCESS;
+ char txbuf[2] = {0x0, 0x0};
+ struct i2c_msg msg[1];
+
+ if (st->interface == ICM20602_SPI) {
+ txbuf[0] = ICM20602_WRITE_REG(reg);
+ txbuf[1] = val;
+ result = spi_write_then_read(st->spi, &txbuf[0], 2, NULL, 0);
+ if (result) {
+ pr_err("mpu write reg %u failed, rc %d\n",
+ reg, val);
+ result = -MPU_READ_FAIL;
+ }
+ } else if (st->interface == ICM20602_I2C) {
+#ifdef ICM20602_I2C_SMBUS
+ result = i2c_smbus_write_i2c_block_data(st->client,
+ reg, 1, &val);
+#else
+ txbuf[0] = reg;
+ txbuf[1] = val;
+ msg[0].addr = st->client->addr;
+ msg[0].flags = I2C_M_IGNORE_NAK;
+ msg[0].len = 2;
+ msg[0].buf = txbuf;
+
+ i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+ }
+
+ return result;
+}
+
+static int icm20602_read_reg(struct inv_icm20602_state *st,
+ uint8_t reg, uint8_t *val)
+{
+ int result = MPU_SUCCESS;
+ char txbuf[1] = {0x0};
+ char rxbuf[1] = {0x0};
+ struct i2c_msg msg[2];
+
+ if (st->interface == ICM20602_SPI) {
+ txbuf[0] = ICM20602_READ_REG(reg);
+ result = spi_write_then_read(st->spi,
+ &txbuf[0], 1, rxbuf, 1);
+ if (result) {
+ pr_err("mpu read reg %u failed, rc %d\n",
+ reg, result);
+ result = -MPU_READ_FAIL;
+ }
+ } else if (st->interface == ICM20602_I2C) {
+#ifdef ICM20602_I2C_SMBUS
+ result = i2c_smbus_read_i2c_block_data(st->client,
+ reg, 1, rxbuf);
+ if (result != 1) {
+ pr_err("mpu read reg %u failed, rc %d\n",
+ reg, result);
+ result = -MPU_READ_FAIL;
+ }
+#else
+ txbuf[0] = reg;
+ msg[0].addr = st->client->addr;
+ msg[0].flags = W_FLG;
+ msg[0].len = 1;
+ msg[0].buf = txbuf;
+
+ msg[1].addr = st->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = rxbuf;
+
+ i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+ }
+ *val = rxbuf[0];
+
+ return result;
+}
+
+#define combine_8_to_16(upper, lower) ((upper << 8) | lower)
+
+int icm20602_read_raw(struct inv_icm20602_state *st,
+ struct struct_icm20602_real_data *real_data, uint32_t type)
+{
+ struct struct_icm20602_raw_data raw_data;
+
+ if ((type & ACCEL) != 0) {
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_XOUT_H.address,
+ &raw_data.ACCEL_XOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_XOUT_L.address,
+ &raw_data.ACCEL_XOUT_L);
+ real_data->ACCEL_XOUT =
+ combine_8_to_16(raw_data.ACCEL_XOUT_H,
+ raw_data.ACCEL_XOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_YOUT_H.address,
+ &raw_data.ACCEL_YOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_YOUT_L.address,
+ &raw_data.ACCEL_YOUT_L);
+ real_data->ACCEL_YOUT =
+ combine_8_to_16(raw_data.ACCEL_YOUT_H,
+ raw_data.ACCEL_YOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_ZOUT_H.address,
+ &raw_data.ACCEL_ZOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.ACCEL_ZOUT_L.address,
+ &raw_data.ACCEL_ZOUT_L);
+ real_data->ACCEL_ZOUT =
+ combine_8_to_16(raw_data.ACCEL_ZOUT_H,
+ raw_data.ACCEL_ZOUT_L);
+ }
+
+ if ((type & GYRO) != 0) {
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_XOUT_H.address,
+ &raw_data.GYRO_XOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_XOUT_L.address,
+ &raw_data.GYRO_XOUT_L);
+ real_data->GYRO_XOUT =
+ combine_8_to_16(raw_data.GYRO_XOUT_H,
+ raw_data.GYRO_XOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_YOUT_H.address,
+ &raw_data.GYRO_YOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_YOUT_L.address,
+ &raw_data.GYRO_YOUT_L);
+ real_data->GYRO_YOUT =
+ combine_8_to_16(raw_data.GYRO_YOUT_H,
+ raw_data.GYRO_YOUT_L);
+
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_ZOUT_H.address,
+ &raw_data.GYRO_ZOUT_H);
+ icm20602_read_reg(st,
+ reg_set_20602.GYRO_ZOUT_L.address,
+ &raw_data.GYRO_ZOUT_L);
+ real_data->GYRO_ZOUT =
+ combine_8_to_16(raw_data.GYRO_ZOUT_H,
+ raw_data.GYRO_ZOUT_L);
+ }
+
+ return MPU_SUCCESS;
+}
+
+int icm20602_read_fifo(struct inv_icm20602_state *st,
+ void *buf, const int size)
+{
+ return icm20602_bulk_read(st,
+ reg_set_20602.FIFO_R_W.address, buf, size);
+}
+
+int icm20602_start_fifo(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+
+ config = st->config;
+
+ /* enable fifo */
+ if (config->fifo_enabled) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_EN = 0x1;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.USER_CTRL)) {
+ pr_err("icm20602 start fifo failed\n");
+ return -MPU_FAIL;
+ }
+
+ /* enable interrupt, need to test */
+ reg_set_20602.INT_ENABLE.reg_u.REG.FIFO_OFLOW_EN = 0x1;
+ reg_set_20602.INT_ENABLE.reg_u.REG.DATA_RDY_INT_EN = 0x0;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.INT_ENABLE)) {
+ pr_err("icm20602 set FIFO_OFLOW_EN failed\n");
+ return -MPU_FAIL;
+ }
+ }
+
+ return MPU_SUCCESS;
+}
+
+int icm20602_stop_fifo(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+
+ config = st->config;
+ /* disable fifo */
+ if (config->fifo_enabled) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_EN = 0x0;
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.USER_CTRL)) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ pr_err("icm20602 stop fifo failed\n");
+ return -MPU_FAIL;
+ }
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ }
+
+ return MPU_SUCCESS;
+}
+
+int icm20602_int_status(struct inv_icm20602_state *st,
+ u8 *int_status)
+{
+ return icm20602_read_reg(st,
+ reg_set_20602.INT_STATUS.address, int_status);
+}
+
+int icm20602_int_wm_status(struct inv_icm20602_state *st,
+ u8 *int_status)
+{
+ return icm20602_read_reg(st,
+ reg_set_20602.FIFO_WM_INT_STATUS.address, int_status);
+}
+
+int icm20602_fifo_count(struct inv_icm20602_state *st,
+ u16 *fifo_count)
+{
+ u8 count_h, count_l;
+
+ *fifo_count = 0;
+ icm20602_read_reg(st, reg_set_20602.FIFO_COUNTH.address, &count_h);
+ icm20602_read_reg(st, reg_set_20602.FIFO_COUNTL.address, &count_l);
+ *fifo_count |= (count_h << 8);
+ *fifo_count |= count_l;
+ return MPU_SUCCESS;
+}
+
+static int icm20602_config_waterlevel(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+ uint8_t val = 0;
+
+ config = st->config;
+ if (config->fifo_enabled != true)
+ return MPU_SUCCESS;
+ /* config waterlevel as the fps need */
+ config->fifo_waterlevel = (config->user_fps_in_ms /
+ (1000 / config->gyro_accel_sample_rate))
+ *ICM20602_PACKAGE_SIZE;
+
+ if (config->fifo_waterlevel > 1023 ||
+ config->fifo_waterlevel/50 >
+ (1023-config->fifo_waterlevel)/ICM20602_PACKAGE_SIZE) {
+ pr_err("set fifo_waterlevel failed %d\n",
+ config->fifo_waterlevel);
+ return MPU_FAIL;
+ }
+ reg_set_20602.FIFO_WM_TH1.reg_u.reg =
+ (config->fifo_waterlevel & 0xff00) >> 8;
+ reg_set_20602.FIFO_WM_TH2.reg_u.reg =
+ (config->fifo_waterlevel & 0x00ff);
+
+ icm20602_write_reg_simple(st, reg_set_20602.FIFO_WM_TH1);
+ icm20602_write_reg_simple(st, reg_set_20602.FIFO_WM_TH2);
+ icm20602_read_reg(st, reg_set_20602.FIFO_WM_TH1.address, &val);
+ icm20602_read_reg(st, reg_set_20602.FIFO_WM_TH2.address, &val);
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_read_ST_code(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+ int result = 0;
+
+ config = st->config;
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_X_ACCEL.address,
+ &(config->acc_self_test.X));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Y_ACCEL.address,
+ &(config->acc_self_test.Y));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Z_ACCEL.address,
+ &(config->acc_self_test.Z));
+
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_X_GYRO.address,
+ &(config->gyro_self_test.X));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Y_GYRO.address,
+ &(config->gyro_self_test.Y));
+ result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Z_GYRO.address,
+ &(config->gyro_self_test.Z));
+
+ return result;
+}
+
+static int icm20602_set_self_test(struct inv_icm20602_state *st)
+{
+ int result = 0;
+
+ reg_set_20602.SMPLRT_DIV.reg_u.REG.SMPLRT_DIV = 0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.SMPLRT_DIV);
+
+ reg_set_20602.CONFIG.reg_u.REG.DLFP_CFG = INV_ICM20602_GYRO_LFP_92HZ;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FCHOICE_B = 0x0;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FS_SEL = ICM20602_GYRO_FSR_250DPS;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.A_DLPF_CFG = ICM20602_ACCLFP_99;
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.ACCEL_FCHOICE_B = 0X0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG2);
+
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.ACCEL_FS_SEL = ICM20602_ACC_FSR_2G;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+ icm20602_read_ST_code(st);
+
+ return 0;
+}
+
+static int icm20602_do_test_acc(struct inv_icm20602_state *st,
+ struct X_Y_Z *acc, struct X_Y_Z *acc_st)
+{
+ struct struct_icm20602_real_data *real_data =
+ kmalloc(sizeof(struct inv_icm20602_state), GFP_ATOMIC);
+ int i;
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, ACCEL);
+ acc->X += real_data->ACCEL_XOUT;
+ acc->Y += real_data->ACCEL_YOUT;
+ acc->Z += real_data->ACCEL_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ acc->X /= SELFTEST_COUNT;
+ acc->X *= ST_PRECISION;
+
+ acc->Y /= SELFTEST_COUNT;
+ acc->Y *= ST_PRECISION;
+
+ acc->Z /= SELFTEST_COUNT;
+ acc->Z *= ST_PRECISION;
+
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.XG_ST = 0x1;
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.YG_ST = 0x1;
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.ZG_ST = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, ACCEL);
+ acc_st->X += real_data->ACCEL_XOUT;
+ acc_st->Y += real_data->ACCEL_YOUT;
+ acc_st->Z += real_data->ACCEL_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ acc_st->X /= SELFTEST_COUNT;
+ acc_st->X *= ST_PRECISION;
+
+ acc_st->Y /= SELFTEST_COUNT;
+ acc_st->Y *= ST_PRECISION;
+
+ acc_st->Z /= SELFTEST_COUNT;
+ acc_st->Z *= ST_PRECISION;
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_do_test_gyro(struct inv_icm20602_state *st,
+ struct X_Y_Z *gyro, struct X_Y_Z *gyro_st)
+{
+ struct struct_icm20602_real_data *real_data =
+ kmalloc(sizeof(struct inv_icm20602_state), GFP_ATOMIC);
+ int i;
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, GYRO);
+ gyro->X += real_data->GYRO_XOUT;
+ gyro->Y += real_data->GYRO_YOUT;
+ gyro->Z += real_data->GYRO_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ gyro->X /= SELFTEST_COUNT;
+ gyro->X *= ST_PRECISION;
+
+ gyro->Y /= SELFTEST_COUNT;
+ gyro->Y *= ST_PRECISION;
+
+ gyro->Z /= SELFTEST_COUNT;
+ gyro->Z *= ST_PRECISION;
+
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.XG_ST = 0x1;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.YG_ST = 0x1;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.ZG_ST = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+ for (i = 0; i < SELFTEST_COUNT; i++) {
+ icm20602_read_raw(st, real_data, ACCEL);
+ gyro_st->X += real_data->GYRO_XOUT;
+ gyro_st->Y += real_data->GYRO_YOUT;
+ gyro_st->Z += real_data->GYRO_ZOUT;
+ usleep_range(1000, 1001);
+ }
+ gyro_st->X /= SELFTEST_COUNT;
+ gyro_st->X *= ST_PRECISION;
+
+ gyro_st->Y /= SELFTEST_COUNT;
+ gyro_st->Y *= ST_PRECISION;
+
+ gyro_st->Z /= SELFTEST_COUNT;
+ gyro_st->Z *= ST_PRECISION;
+
+ return MPU_SUCCESS;
+}
+
+static bool icm20602_check_acc_selftest(struct inv_icm20602_state *st,
+ struct X_Y_Z *acc, struct X_Y_Z *acc_st)
+{
+ struct X_Y_Z acc_ST_code, st_otp, st_shift_cust;
+ bool otp_value_zero = false, test_result = true;
+
+ acc_ST_code.X = st->config->acc_self_test.X;
+ acc_ST_code.Y = st->config->acc_self_test.Y;
+ acc_ST_code.Z = st->config->acc_self_test.Z;
+
+ st_otp.X = (st_otp.X != 0) ? mpu_st_tb[acc_ST_code.X - 1] : 0;
+ st_otp.Y = (st_otp.Y != 0) ? mpu_st_tb[acc_ST_code.Y - 1] : 0;
+ st_otp.Z = (st_otp.Z != 0) ? mpu_st_tb[acc_ST_code.Z - 1] : 0;
+
+ if ((st_otp.X & st_otp.Y & st_otp.Z) == 0)
+ otp_value_zero = true;
+
+ st_shift_cust.X = acc_st->X - acc->X;
+ st_shift_cust.Y = acc_st->X - acc->Y;
+ st_shift_cust.Z = acc_st->X - acc->Z;
+ if (!otp_value_zero) {
+ if (
+ st_shift_cust.X <
+ (st_otp.X * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+ st_shift_cust.Y <
+ (st_otp.Y * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+ st_shift_cust.Z <
+ (st_otp.Z * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+
+ st_shift_cust.X >
+ (st_otp.X * ST_PRECISION * ACC_ST_SHIFT_MAX / 100) ||
+ st_shift_cust.Y >
+ (st_otp.Y * ST_PRECISION * ACC_ST_SHIFT_MAX / 100) ||
+ st_shift_cust.Z >
+ (st_otp.Z * ST_PRECISION * ACC_ST_SHIFT_MAX / 100)
+ ) {
+ test_result = false;
+ }
+ } else {
+ if (
+ abs(st_shift_cust.X) <
+ (ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Y) <
+ (ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Z) <
+ (ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+
+ abs(st_shift_cust.X) >
+ (ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Y) >
+ (ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION) ||
+ abs(st_shift_cust.Z) >
+ (ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION)
+ ) {
+ test_result = false;
+ }
+ }
+
+ return test_result;
+}
+
+static int icm20602_check_gyro_selftest(struct inv_icm20602_state *st,
+ struct X_Y_Z *gyro, struct X_Y_Z *gyro_st)
+{
+ struct X_Y_Z gyro_ST_code, st_otp, st_shift_cust;
+ bool otp_value_zero = false, test_result = true;
+
+ gyro_ST_code.X = st->config->gyro_self_test.X;
+ gyro_ST_code.Y = st->config->gyro_self_test.Y;
+ gyro_ST_code.Z = st->config->gyro_self_test.Z;
+
+ st_otp.X = (gyro_ST_code.X != 0) ? mpu_st_tb[gyro_ST_code.X - 1] : 0;
+ st_otp.Y = (gyro_ST_code.Y != 0) ? mpu_st_tb[gyro_ST_code.Y - 1] : 0;
+ st_otp.Z = (gyro_ST_code.Z != 0) ? mpu_st_tb[gyro_ST_code.Z - 1] : 0;
+
+ if ((st_otp.X & st_otp.Y & st_otp.Z) == 0)
+ otp_value_zero = true;
+
+ st_shift_cust.X = gyro_st->X - gyro->X;
+ st_shift_cust.Y = gyro_st->X - gyro->Y;
+ st_shift_cust.Z = gyro_st->X - gyro->Z;
+ if (!otp_value_zero) {
+ if (
+ st_shift_cust.X <
+ (st_otp.X * ST_PRECISION * GYRO_ST_SHIFT / 100) ||
+ st_shift_cust.Y <
+ (st_otp.Y * ST_PRECISION * GYRO_ST_SHIFT / 100) ||
+ st_shift_cust.Z <
+ (st_otp.Z * ST_PRECISION * GYRO_ST_SHIFT / 100)
+ ) {
+ test_result = false;
+ }
+ } else {
+ if (
+ abs(st_shift_cust.X) <
+ (GYRO_ST_AL * 32768 / 250 * ST_PRECISION) ||
+ abs(st_shift_cust.Y) <
+ (GYRO_ST_AL * 32768 / 250 * ST_PRECISION) ||
+ abs(st_shift_cust.Z) <
+ (GYRO_ST_AL * 32768 / 250 * ST_PRECISION)
+ ) {
+ test_result = false;
+ }
+ }
+
+ if (test_result == true) {
+ /* Self Test Pass/Fail Criteria C */
+ if (
+ abs(st_shift_cust.X) >
+ GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION ||
+ abs(st_shift_cust.Y) >
+ GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION ||
+ abs(st_shift_cust.Z) >
+ GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION
+ ) {
+ test_result = false;
+ }
+ }
+
+ return test_result;
+}
+
+bool icm20602_self_test(struct inv_icm20602_state *st)
+{
+ struct X_Y_Z acc, acc_st;
+ struct X_Y_Z gyro, gyro_st;
+ bool test_result = true;
+
+ icm20602_set_self_test(st);
+ icm20602_do_test_acc(st, &acc, &acc_st);
+ icm20602_do_test_gyro(st, &gyro, &gyro_st);
+ test_result = icm20602_check_acc_selftest(st, &acc, &acc_st);
+ test_result = icm20602_check_gyro_selftest(st, &gyro, &gyro_st);
+
+ return test_result;
+}
+
+static int icm20602_config_fifo(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+
+ config = st->config;
+ if (config->fifo_enabled != true)
+ return MPU_SUCCESS;
+
+ /*
+ * Set CONFIG.USER_SET_BIT = 0, No reason as datasheet said
+ */
+ reg_set_20602.CONFIG.reg_u.REG.USER_SET_BIT = 0x0;
+ /*
+ * Set CONFIG.FIFO_MODE = 1,
+ * i.e. when FIFO is full, additional writes will
+ * not be written to FIFO
+ */
+ reg_set_20602.CONFIG.reg_u.REG.FIFO_MODE = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.CONFIG))
+ return -MPU_FAIL;
+
+ /* reset fifo */
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL)) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ return -MPU_FAIL;
+ }
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+
+ /* Enable FIFO on specified sensors */
+ reg_set_20602.FIFO_EN.reg_u.REG.GYRO_FIFO_EN = 0x1;
+ reg_set_20602.FIFO_EN.reg_u.REG.ACCEL_FIFO_EN = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.FIFO_EN))
+ return -MPU_FAIL;
+
+ if (icm20602_config_waterlevel(st))
+ return -MPU_FAIL;
+
+ if (icm20602_start_fifo(st))
+ return -MPU_FAIL;
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_initialize_gyro(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = NULL;
+ int result = MPU_SUCCESS;
+
+ if (st == NULL)
+ return -MPU_FAIL;
+
+ /*
+ * ICM20602 supports gyro sampling rate up to 32KHz
+ * when fchoice_b != 0x00
+ * In our driver, we supports up to 8KHz
+ * thus always set fchoice_b to 0x00;
+ */
+ config = st->config;
+ /*
+ * SAPLRT_DIV in ICM20602_REG_SMPLRT_DIV is only used for 1kHz internal
+ * sampling, i.e. fchoice_b in ICM20602_REG_GYRO_CONFIG is 00
+ * and 0 < dlpf_cfg in ICM20602_REG_CONFIG < 7
+ * SAMPLE_RATE=Internal_Sample_Rate / (1 + SMPLRT_DIV)
+ */
+ if (config->gyro_accel_sample_rate <= ICM20602_SAMPLE_RATE_1000HZ)
+ reg_set_20602.SMPLRT_DIV.reg_u.reg =
+ ICM20602_INTERNAL_SAMPLE_RATE_HZ /
+ config->gyro_accel_sample_rate - 1;
+
+ result = icm20602_write_reg_simple(st, reg_set_20602.SMPLRT_DIV);
+
+ /* Set gyro&temperature(combine) LPF */
+ reg_set_20602.CONFIG.reg_u.REG.DLFP_CFG = config->gyro_lpf;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+ /* Set gyro full scale range */
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FCHOICE_B = 0x0;
+ reg_set_20602.GYRO_CONFIG.reg_u.REG.FS_SEL = config->gyro_fsr;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+ /* Set Accel full scale range */
+ reg_set_20602.ACCEL_CONFIG.reg_u.REG.ACCEL_FS_SEL = config->acc_fsr;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+ /*
+ * Set accel LPF
+ * Support accel sample rate up to 1KHz
+ * thus set accel_fchoice_b to 0x00
+ * The actual accel sample rate is 1KHz/(1+SMPLRT_DIV)
+ */
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.ACCEL_FCHOICE_B = 0x0;
+ reg_set_20602.ACCEL_CONFIG2.reg_u.REG.A_DLPF_CFG = config->acc_lpf;
+ result |= icm20602_write_reg_simple(st,
+ reg_set_20602.ACCEL_CONFIG2);
+
+ if (result) {
+ pr_err("icm20602 init gyro and accel failed\n");
+ return -MPU_FAIL;
+ }
+
+ return result;
+}
+
+int icm20602_set_power_itg(struct inv_icm20602_state *st, bool power_on)
+{
+ int result = MPU_SUCCESS;
+
+ if (power_on) {
+ reg_set_20602.PWR_MGMT_1.reg_u.reg = 0;
+ result = icm20602_write_reg_simple(st,
+ reg_set_20602.PWR_MGMT_1);
+ } else {
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.SLEEP = 0x1;
+ result = icm20602_write_reg_simple(st,
+ reg_set_20602.PWR_MGMT_1);
+ }
+ if (result) {
+ pr_err("set power failed power %d err %d\n",
+ power_on, result);
+ return result;
+ }
+
+ if (power_on)
+ msleep(30);
+
+ return result;
+}
+
+int icm20602_init_device(struct inv_icm20602_state *st)
+{
+ int result = MPU_SUCCESS;
+ struct icm20602_user_config *config = NULL;
+ int package_count;
+ int i;
+
+ config = st->config;
+ if (st == NULL || st->config == NULL) {
+ pr_err("icm20602 validate config failed\n");
+ return -MPU_FAIL;
+ }
+
+ /* turn on gyro and accel */
+ reg_set_20602.PWR_MGMT_2.reg_u.reg = 0x0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_2);
+ msleep(30);
+
+ /* disable INT */
+ reg_set_20602.INT_ENABLE.reg_u.reg = 0x0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.INT_ENABLE);
+
+ /* disbale FIFO */
+ reg_set_20602.FIFO_EN.reg_u.reg = 0x0;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.FIFO_EN);
+
+ /* reset FIFO */
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ result |= icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL);
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ msleep(30);
+
+ /* init gyro and accel */
+ if (icm20602_initialize_gyro(st)) {
+ pr_err("icm20602 init device failed\n");
+ return -MPU_FAIL;
+ }
+
+ /* if FIFO enable, config FIFO */
+ if (config->fifo_enabled) {
+ if (icm20602_config_fifo(st)) {
+ pr_err("icm20602 init config fifo failed\n");
+ return -MPU_FAIL;
+ }
+ } else {
+ /* enable interrupt */
+ reg_set_20602.INT_ENABLE.reg_u.REG.DATA_RDY_INT_EN = 0x0;
+ if (icm20602_write_reg_simple(st,
+ reg_set_20602.INT_ENABLE)) {
+ pr_err("icm20602 set raw rdy failed\n");
+ return -MPU_FAIL;
+ }
+ }
+
+ /* buffer malloc */
+ package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
+
+ st->buf = kzalloc(config->fifo_waterlevel * 2, GFP_ATOMIC);
+ if (!st->buf)
+ return -ENOMEM;
+
+ st->data_push = kcalloc(package_count,
+ sizeof(struct struct_icm20602_data), GFP_ATOMIC);
+ if (!st->data_push)
+ return -ENOMEM;
+
+ for (i = 0; i < package_count; i++) {
+ st->data_push[i].raw_data =
+ kzalloc(ICM20602_PACKAGE_SIZE, GFP_ATOMIC);
+ }
+
+ return result;
+}
+
+int icm20602_reset_fifo(struct inv_icm20602_state *st)
+{
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+ if (icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL)) {
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ return -MPU_FAIL;
+ }
+ reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+ return MPU_SUCCESS;
+}
+
+
+void icm20602_rw_test(struct inv_icm20602_state *st)
+{
+ uint8_t val = 0;
+
+ reg_set_20602.PWR_MGMT_2.reg_u.REG.STBY_ZG = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_2);
+ reg_set_20602.CONFIG.reg_u.REG.FIFO_MODE = 0x1;
+ icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+ icm20602_read_reg(st, reg_set_20602.PWR_MGMT_2.address, &val);
+ icm20602_read_reg(st, reg_set_20602.CONFIG.address, &val);
+
+}
+
+int icm20602_detect(struct inv_icm20602_state *st)
+{
+ int result = MPU_SUCCESS;
+ uint8_t retry = 0, val = 0;
+
+ pr_debug("icm20602_detect\n");
+ /* reset to make sure previous state are not there */
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET = 0x1;
+ result = icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_1);
+ if (result) {
+ pr_err("mpu write reg 0x%x value 0x%x failed\n",
+ reg_set_20602.PWR_MGMT_1.reg_u.reg,
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET);
+ return result;
+ }
+ reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET = 0x0;
+
+ /* the power up delay */
+ msleep(30);
+
+ /* out of sleep */
+ result = icm20602_set_power_itg(st, true);
+ if (result)
+ return result;
+ /* get who am i register */
+ while (retry < 10) {
+ /* get version (expecting 0x12 for the icm20602) */
+ icm20602_read_reg(st, reg_set_20602.WHO_AM_I.address, &val);
+ if (val == ICM20602_WHO_AM_I)
+ break;
+ retry++;
+ }
+
+ if (val != ICM20602_WHO_AM_I) {
+ pr_err("detect mpu failed,whoami reg 0x%x\n", val);
+ result = -MPU_FAIL;
+ } else {
+ pr_debug("detect mpu ok,whoami reg 0x%x\n", val);
+ }
+ icm20602_rw_test(st);
+
+ return result;
+}
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h
new file mode 100644
index 0000000..9c4dbd0
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h
@@ -0,0 +1,978 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* register high bit=1 for read */
+#define ICM20602_READ_REG(reg) (reg | 0x80)
+
+/* register high bit=0 for write */
+#define ICM20602_WRITE_REG(reg) (reg & (~0x80))
+#define ICM20602_WHO_AM_I 0x12
+#define ICM20602_INTERNAL_SAMPLE_RATE_HZ 1000
+
+#define SELFTEST_COUNT 200
+#define ST_PRECISION 1000
+#define ACC_ST_SHIFT_MAX 150
+#define ACC_ST_SHIFT_MIN 50
+#define ACC_ST_AL_MIN 225
+#define ACC_ST_AL_MAX 675
+#define GYRO_ST_SHIFT 50
+#define GYRO_ST_AL 60
+#define GYRO_OFFSET_MAX 20
+
+static const uint16_t mpu_st_tb[256] = {
+ 2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+ 2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+ 3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+ 3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+ 3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+ 3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+ 4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+ 4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+ 4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+ 5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+ 5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+ 6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+ 6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+ 7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+ 7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+ 8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+ 9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+ 10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+ 10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+ 11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+ 12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+ 13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+ 15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+ 16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+ 17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+ 19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+ 20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+ 22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+ 24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+ 26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+ 28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+ 30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+enum inv_icm20602_reg_addr {
+ ADDR_XG_OFFS_TC_H = 0x04,
+ ADDR_XG_OFFS_TC_L,
+ ADDR_YG_OFFS_TC_H = 0x07,
+ ADDR_YG_OFFS_TC_L,
+ ADDR_ZG_OFFS_TC_H = 0x0A,
+ ADDR_ZG_OFFS_TC_L,
+
+ ADDR_SELF_TEST_X_ACCEL = 0x0D,
+ ADDR_SELF_TEST_Y_ACCEL,
+ ADDR_SELF_TEST_Z_ACCEL,
+
+ ADDR_XG_OFFS_USRH = 0x13,
+ ADDR_XG_OFFS_USRL,
+ ADDR_YG_OFFS_USRH,
+ ADDR_YG_OFFS_USRL,
+ ADDR_ZG_OFFS_USRH,
+ ADDR_ZG_OFFS_USRL,
+
+ ADDR_SMPLRT_DIV,
+ ADDR_CONFIG,
+
+ ADDR_GYRO_CONFIG,
+
+ ADDR_ACCEL_CONFIG,
+ ADDR_ACCEL_CONFIG2,
+
+ ADDR_LP_MODE_CFG,
+
+ ADDR_ACCEL_WOM_X_THR = 0x20,
+ ADDR_ACCEL_WOM_Y_THR,
+ ADDR_ACCEL_WOM_Z_THR,
+ ADDR_FIFO_EN,
+
+ ADDR_FSYNC_INT = 0x36,
+ ADDR_INT_PIN_CFG,
+ ADDR_INT_ENABLE,
+ ADDR_FIFO_WM_INT_STATUS,
+ ADDR_INT_STATUS,
+
+ ADDR_ACCEL_XOUT_H,
+ ADDR_ACCEL_XOUT_L,
+ ADDR_ACCEL_YOUT_H,
+ ADDR_ACCEL_YOUT_L,
+ ADDR_ACCEL_ZOUT_H,
+ ADDR_ACCEL_ZOUT_L,
+
+ ADDR_TEMP_OUT_H,
+ ADDR_TEMP_OUT_L,
+
+ ADDR_GYRO_XOUT_H,
+ ADDR_GYRO_XOUT_L,
+ ADDR_GYRO_YOUT_H,
+ ADDR_GYRO_YOUT_L,
+ ADDR_GYRO_ZOUT_H,
+ ADDR_GYRO_ZOUT_L,
+
+ ADDR_SELF_TEST_X_GYRO = 0x50,
+ ADDR_SELF_TEST_Y_GYRO,
+ ADDR_SELF_TEST_Z_GYRO,
+
+ ADDR_FIFO_WM_TH1 = 0x60,
+ ADDR_FIFO_WM_TH2,
+
+ ADDR_SIGNAL_PATH_RESET = 0x68,
+ ADDR_ACCEL_INTEL_CTRL,
+ ADDR_USER_CTRL,
+
+ ADDR_PWR_MGMT_1,
+ ADDR_PWR_MGMT_2,
+
+ ADDR_I2C_IF = 0x70,
+
+ ADDR_FIFO_COUNTH = 0x72,
+ ADDR_FIFO_COUNTL,
+ ADDR_FIFO_R_W,
+
+ ADDR_WHO_AM_I,
+
+ ADDR_XA_OFFSET_H,
+ ADDR_XA_OFFSET_L,
+ ADDR_YA_OFFSET_H = 0x7A,
+ ADDR_YA_OFFSET_L,
+ ADDR_ZA_OFFSET_H = 0x7D,
+ ADDR_ZA_OFFSET_L
+};
+
+struct struct_XG_OFFS_TC_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_XG_OFFS_TC_H :2;
+ u8 REG_XG_OFFS_LP :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XG_OFFS_TC_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_XG_OFFS_TC_L :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_TC_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_YG_OFFS_TC_H :2;
+ u8 REG_YG_OFFS_LP :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_TC_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_YG_OFFS_TC_L :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_TC_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_ZG_OFFS_TC_H :2;
+ u8 REG_ZG_OFFS_LP :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_TC_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 REG_ZG_OFFS_TC_L :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_X_ACCEL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XA_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Y_ACCEL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YA_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Z_ACCEL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZA_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XG_OFFS_USRH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 X_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XG_OFFS_USRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 X_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_USRH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Y_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YG_OFFS_USRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Y_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_USRH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Z_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZG_OFFS_USRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 Z_OFFS_USR :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SMPLRT_DIV {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 SMPLRT_DIV :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_CONFIG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 DLFP_CFG :3;
+ u8 EXT_SYNC_SET :3;
+ u8 FIFO_MODE :1;
+ u8 USER_SET_BIT :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_CONFIG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FCHOICE_B :2;
+ u8 RESERVE0 :1;
+ u8 FS_SEL :2;
+ u8 ZG_ST :1;
+ u8 YG_ST :1;
+ u8 XG_ST :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_CONFIG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :3;
+ u8 ACCEL_FS_SEL :2;
+ u8 ZG_ST :1;
+ u8 YG_ST :1;
+ u8 XG_ST :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_CONFIG2 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 A_DLPF_CFG :3;
+ u8 ACCEL_FCHOICE_B :1;
+ u8 DEC2_CFG :2;
+ u8 RESERVE0 :2;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_LP_MODE_CFG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :4;
+ u8 G_AVGCFG :3;
+ u8 GYRO_CYCLE :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_WOM_X_THR {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_X_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+
+struct struct_ACCEL_WOM_Y_THR {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_Y_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_WOM_Z_THR {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_Z_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_EN {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE1 :3;
+ u8 ACCEL_FIFO_EN :1;
+ u8 GYRO_FIFO_EN :1;
+ u8 RESERVE0 :3;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FSYNC_INT {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :7;
+ u8 FSYNC_INT :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_INT_PIN_CFG {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :2;
+ u8 FSYNC_INT_MODE_EN:1;
+ u8 FSYNC_INT_LEVEL :1;
+ u8 INT_RD_CLEAR :1;
+ u8 LATCH_INT_EN :1;
+ u8 INT_OPEN :1;
+ u8 INT_LEVEL :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_INT_ENABLE {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 DATA_RDY_INT_EN :1;
+ u8 RESERVE0 :1;
+ u8 GDRIVE_INT_EN :1;
+ u8 FSYNC_INT_EN :1;
+ u8 FIFO_OFLOW_EN :1;
+ u8 WOM_Z_INT_EN :1;
+ u8 WOM_Y_INT_EN :1;
+ u8 WOM_X_INT_EN :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_WM_INT_STATUS {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE1 :6;
+ u8 FIFO_WM_INT :1;
+ u8 RESERVE0 :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_INT_STATUS {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 DATA_RDY_INT :1;
+ u8 RESERVE1 :1;
+ u8 GDRIVE_INT :1;
+ u8 RESERVE0 :1;
+ u8 FIFO_OFLOW_INT :1;
+ u8 WOM_Z_INT :1;
+ u8 WOM_Y_INT :1;
+ u8 WOM_X_INT :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_XOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_XOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_YOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_YOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_ZOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_ZOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ACCEL_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_TEMP_OUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 TEMP_OUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_TEMP_OUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 TEMP_OUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_XOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_XOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_XOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_YOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_YOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_YOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_ZOUT_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_GYRO_ZOUT_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 GYRO_ZOUT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_X_GYRO {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XG_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Y_GYRO {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YG_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SELF_TEST_Z_GYRO {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZG_ST_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_WM_TH1 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE0 :6;
+ u8 FIFO_WM_TH :2;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_WM_TH2 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_WM_TH :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_SIGNAL_PATH_RESET {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 TEMP_RST :1;
+ u8 ACCEL_RST :1;
+ u8 FIFO_WM_TH :6;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ACCEL_INTEL_CTRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WOM_TH_MODE :1;
+ u8 OUTPUT_LIMIT :1;
+ u8 RESERVE0 :4;
+ u8 ACCEL_INTEL_MODE :1;
+ u8 ACCEL_INTEL_EN :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_USER_CTRL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 SIG_COND_RST :1;
+ u8 RESERVE2 :1;
+ u8 FIFO_RST :1;
+ u8 RESERVE1 :3;
+ u8 FIFO_EN :1;
+ u8 RESERVE0 :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_PWR_MGMT_1 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 CLKSEL :3;
+ u8 TEMP_DIS :1;
+ u8 GYRO_STANDBY :1;
+ u8 CYCLE :1;
+ u8 SLEEP :1;
+ u8 DEVICE_RESET :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_PWR_MGMT_2 {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 STBY_ZG :1;
+ u8 STBY_YG :1;
+ u8 STBY_XG :1;
+ u8 STBY_ZA :1;
+ u8 STBY_YA :1;
+ u8 STBY_XA :1;
+ u8 RESERVE0 :2;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_I2C_IF {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 RESERVE1 :6;
+ u8 I2C_IF_DIS :1;
+ u8 RESERVE0 :1;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_COUNTH {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_COUNT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_COUNTL {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_COUNT :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_FIFO_R_W {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 FIFO_DATA :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_WHO_AM_I {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 WHOAMI :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XA_OFFSET_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_XA_OFFSET_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 XA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YA_OFFSET_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_YA_OFFSET_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 YA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZA_OFFSET_H {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+struct struct_ZA_OFFSET_L {
+ enum inv_icm20602_reg_addr address;
+ union {
+ struct {
+ u8 ZA_OFFS :8;
+ } REG;
+ u8 reg;
+ } reg_u;
+};
+
+/*
+ * struct inv_icm20602_reg_map - Notable registers.
+ * @sample_rate_div: Divider applied to gyro output rate.
+ * @lpf: Configures internal low pass filter.
+ * @user_ctrl: Enables/resets the FIFO.
+ * @fifo_en: Determines which data will appear in FIFO.
+ * @gyro_config: gyro config register.
+ * @accl_config: accel config register
+ * @fifo_count_h: Upper byte of FIFO count.
+ * @fifo_r_w: FIFO register.
+ * @raw_gyro: Address of first gyro register.
+ * @raw_accl: Address of first accel register.
+ * @temperature: temperature register
+ * @int_enable: Interrupt enable register.
+ * @pwr_mgmt_1: Controls chip's power state and clock source.
+ * @pwr_mgmt_2: Controls power state of individual sensors.
+ */
+struct inv_icm20602_reg_map {
+ struct struct_XG_OFFS_TC_H XG_OFFS_TC_H;
+ struct struct_XG_OFFS_TC_L XG_OFFS_TC_L;
+ struct struct_YG_OFFS_TC_H YG_OFFS_TC_H;
+ struct struct_YG_OFFS_TC_L YG_OFFS_TC_L;
+ struct struct_ZG_OFFS_TC_H ZG_OFFS_TC_H;
+ struct struct_ZG_OFFS_TC_L ZG_OFFS_TC_L;
+
+ struct struct_SELF_TEST_X_ACCEL SELF_TEST_X_ACCEL;
+ struct struct_SELF_TEST_Y_ACCEL SELF_TEST_Y_ACCEL;
+ struct struct_SELF_TEST_Z_ACCEL SELF_TEST_Z_ACCEL;
+
+ struct struct_XG_OFFS_USRH XG_OFFS_USRH;
+ struct struct_XG_OFFS_USRL XG_OFFS_USRL;
+ struct struct_YG_OFFS_USRH YG_OFFS_USRH;
+ struct struct_YG_OFFS_USRL YG_OFFS_USRL;
+ struct struct_ZG_OFFS_USRH ZG_OFFS_USRH;
+ struct struct_ZG_OFFS_USRL ZG_OFFS_USRL;
+
+ struct struct_SMPLRT_DIV SMPLRT_DIV;
+ struct struct_CONFIG CONFIG;
+
+ struct struct_GYRO_CONFIG GYRO_CONFIG;
+
+ struct struct_ACCEL_CONFIG ACCEL_CONFIG;
+ struct struct_ACCEL_CONFIG2 ACCEL_CONFIG2;
+
+ struct struct_LP_MODE_CFG LP_MODE_CFG;
+
+ struct struct_ACCEL_WOM_X_THR ACCEL_WOM_X_THR;
+ struct struct_ACCEL_WOM_Y_THR ACCEL_WOM_Y_THR;
+ struct struct_ACCEL_WOM_Z_THR ACCEL_WOM_Z_THR;
+
+ struct struct_FIFO_EN FIFO_EN;
+ struct struct_FSYNC_INT FSYNC_INT;
+ struct struct_INT_PIN_CFG INT_PIN_CFG;
+ struct struct_INT_ENABLE INT_ENABLE;
+ struct struct_FIFO_WM_INT_STATUS FIFO_WM_INT_STATUS;
+ struct struct_INT_STATUS INT_STATUS;
+
+ struct struct_ACCEL_XOUT_H ACCEL_XOUT_H;
+ struct struct_ACCEL_XOUT_L ACCEL_XOUT_L;
+ struct struct_ACCEL_YOUT_H ACCEL_YOUT_H;
+ struct struct_ACCEL_YOUT_L ACCEL_YOUT_L;
+ struct struct_ACCEL_ZOUT_H ACCEL_ZOUT_H;
+ struct struct_ACCEL_ZOUT_L ACCEL_ZOUT_L;
+
+ struct struct_TEMP_OUT_H TEMP_OUT_H;
+ struct struct_TEMP_OUT_L TEMP_OUT_L;
+
+ struct struct_GYRO_XOUT_H GYRO_XOUT_H;
+ struct struct_GYRO_XOUT_L GYRO_XOUT_L;
+ struct struct_GYRO_YOUT_H GYRO_YOUT_H;
+ struct struct_GYRO_YOUT_L GYRO_YOUT_L;
+ struct struct_GYRO_ZOUT_H GYRO_ZOUT_H;
+ struct struct_GYRO_ZOUT_L GYRO_ZOUT_L;
+
+ struct struct_SELF_TEST_X_GYRO SELF_TEST_X_GYRO;
+ struct struct_SELF_TEST_Y_GYRO SELF_TEST_Y_GYRO;
+ struct struct_SELF_TEST_Z_GYRO SELF_TEST_Z_GYRO;
+ struct struct_FIFO_WM_TH1 FIFO_WM_TH1;
+ struct struct_FIFO_WM_TH2 FIFO_WM_TH2;
+ struct struct_SIGNAL_PATH_RESET SIGNAL_PATH_RESET;
+ struct struct_ACCEL_INTEL_CTRL ACCEL_INTEL_CTRL;
+ struct struct_USER_CTRL USER_CTRL;
+
+ struct struct_PWR_MGMT_1 PWR_MGMT_1;
+ struct struct_PWR_MGMT_2 PWR_MGMT_2;
+
+ struct struct_I2C_IF I2C_IF;
+
+ struct struct_FIFO_COUNTH FIFO_COUNTH;
+ struct struct_FIFO_COUNTL FIFO_COUNTL;
+ struct struct_FIFO_R_W FIFO_R_W;
+
+ struct struct_WHO_AM_I WHO_AM_I;
+
+ struct struct_XA_OFFSET_H XA_OFFSET_H;
+ struct struct_XA_OFFSET_L XA_OFFSET_L;
+ struct struct_YA_OFFSET_H YA_OFFSET_H;
+ struct struct_YA_OFFSET_L YA_OFFSET_L;
+ struct struct_ZA_OFFSET_H ZA_OFFSET_H;
+ struct struct_ZA_OFFSET_L ZA_OFFSET_L;
+};
+
+
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
new file mode 100644
index 0000000..7dda14e
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include "inv_icm20602_iio.h"
+#include <linux/regulator/consumer.h>
+
+/* Attribute of icm20602 device init show */
+static ssize_t inv_icm20602_init_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static void inv_icm20602_def_config(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = st->config;
+
+ config->user_fps_in_ms = 20;
+ config->gyro_lpf = INV_ICM20602_GYRO_LFP_92HZ;
+ config->gyro_fsr = ICM20602_GYRO_FSR_1000DPS;
+ config->acc_lpf = ICM20602_ACCLFP_99;
+ config->acc_fsr = ICM20602_ACC_FSR_4G;
+ config->gyro_accel_sample_rate = ICM20602_SAMPLE_RATE_200HZ;
+ config->fifo_enabled = true;
+
+}
+
+static void inv_icm20602_load_config(struct inv_icm20602_state *st)
+{
+ struct icm20602_user_config *config = st->config;
+
+ if (config->user_fps_in_ms == 0)
+ inv_icm20602_def_config(st);
+ config->fifo_enabled = true;
+
+}
+
+static ssize_t inv_icm20602_init_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int result = MPU_SUCCESS;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ inv_icm20602_load_config(st);
+ result |= icm20602_detect(st);
+ result |= icm20602_init_device(st);
+ icm20602_start_fifo(st);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static IIO_DEVICE_ATTR(
+ inv_icm20602_init,
+ 0644,
+ inv_icm20602_init_show,
+ inv_icm20602_init_store,
+ 0);
+
+/* Attribute of gyro lpf base on the enum inv_icm20602_gyro_temp_lpf_e */
+static ssize_t inv_gyro_lpf_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, 4, "%d\n", st->config->gyro_lpf);
+}
+
+static ssize_t inv_gyro_lpf_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int gyro_lpf;
+
+ if (kstrtoint(buf, 10, &gyro_lpf))
+ return -EINVAL;
+ if (gyro_lpf > INV_ICM20602_GYRO_LFP_NUM)
+ return -EINVAL;
+ config->gyro_lpf = gyro_lpf;
+
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_gyro_lpf,
+ 0644,
+ inv_gyro_lpf_show,
+ inv_gyro_lpf_store,
+ 0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_gyro_temp_lpf_e */
+static ssize_t inv_gyro_fsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, 4, "%d\n", st->config->gyro_fsr);
+}
+
+static ssize_t inv_gyro_fsr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int gyro_fsr;
+
+ if (kstrtoint(buf, 10, &gyro_fsr))
+ return -EINVAL;
+ if (gyro_fsr > ICM20602_GYRO_FSR_NUM)
+ return -EINVAL;
+
+ config->gyro_fsr = gyro_fsr;
+ return count;
+}
+
+static IIO_DEVICE_ATTR(
+ inv_icm20602_gyro_fsr,
+ 0644,
+ inv_gyro_fsr_show,
+ inv_gyro_fsr_store,
+ 0);
+
+/* Attribute of self_test */
+static ssize_t inv_self_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t inv_self_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ icm20602_self_test(st);
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_self_test,
+ 0644,
+ inv_self_test_show,
+ inv_self_test_store,
+ 0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_acc_fsr_e */
+static ssize_t inv_gyro_acc_fsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, 4, "%d\n", st->config->acc_fsr);
+}
+
+static ssize_t inv_gyro_acc_fsr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int acc_fsr;
+
+ if (kstrtoint(buf, 10, &acc_fsr))
+ return -EINVAL;
+ if (acc_fsr > ICM20602_ACC_FSR_NUM)
+ return -EINVAL;
+
+ config->acc_fsr = acc_fsr;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_acc_fsr,
+ 0644,
+ inv_gyro_acc_fsr_show,
+ inv_gyro_acc_fsr_store,
+ 0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_acc_lpf_e */
+static ssize_t inv_gyro_acc_lpf_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, 4, "%d\n", st->config->acc_lpf);
+}
+
+static ssize_t inv_gyro_acc_lpf_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int acc_lpf;
+
+ if (kstrtoint(buf, 10, &acc_lpf))
+ return -EINVAL;
+ if (acc_lpf > ICM20602_ACCLPF_NUM)
+ return -EINVAL;
+
+ config->acc_fsr = acc_lpf;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_icm20602_acc_lpf,
+ 0644,
+ inv_gyro_acc_lpf_show,
+ inv_gyro_acc_lpf_store,
+ 0);
+
+/* Attribute of user_fps_in_ms */
+static ssize_t inv_user_fps_in_ms_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, 4, "%d\n", st->config->user_fps_in_ms);
+}
+
+static ssize_t inv_user_fps_in_ms_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int user_fps_in_ms;
+
+ if (kstrtoint(buf, 10, &user_fps_in_ms))
+ return -EINVAL;
+ if (user_fps_in_ms < 10)
+ return -EINVAL;
+
+ config->user_fps_in_ms = user_fps_in_ms;
+ return count;
+}
+static IIO_DEVICE_ATTR(
+ inv_user_fps_in_ms,
+ 0644,
+ inv_user_fps_in_ms_show,
+ inv_user_fps_in_ms_store,
+ 0);
+
+/* Attribute of gyro_accel_sample_rate */
+static ssize_t inv_sampling_frequency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, 4, "%d\n", st->config->gyro_accel_sample_rate);
+}
+
+static ssize_t inv_sampling_frequency_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int gyro_accel_sample_rate;
+
+ if (kstrtoint(buf, 10, &gyro_accel_sample_rate))
+ return -EINVAL;
+ if (gyro_accel_sample_rate < 10)
+ return -EINVAL;
+
+ config->gyro_accel_sample_rate = gyro_accel_sample_rate;
+ return count;
+}
+static IIO_DEV_ATTR_SAMP_FREQ(
+ 0644,
+ inv_sampling_frequency_show,
+ inv_sampling_frequency_store);
+
+static struct attribute *inv_icm20602_attributes[] = {
+ &iio_dev_attr_inv_icm20602_init.dev_attr.attr,
+
+ &iio_dev_attr_inv_icm20602_gyro_fsr.dev_attr.attr,
+ &iio_dev_attr_inv_icm20602_gyro_lpf.dev_attr.attr,
+
+ &iio_dev_attr_inv_icm20602_self_test.dev_attr.attr,
+
+ &iio_dev_attr_inv_icm20602_acc_fsr.dev_attr.attr,
+ &iio_dev_attr_inv_icm20602_acc_lpf.dev_attr.attr,
+
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+
+ &iio_dev_attr_inv_user_fps_in_ms.dev_attr.attr,
+ NULL,
+};
+
+#define INV_ICM20602_CHAN(_type, _channel2, _index) \
+{ \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec inv_icm20602_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
+
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_OFFSET)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = INV_ICM20602_SCAN_TEMP,
+ .channel2 = IIO_MOD_X,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .shift = 0,
+ .endianness = IIO_BE,
+ },
+ },
+ INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X),
+ INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y),
+ INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z),
+
+ INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X),
+ INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y),
+ INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z),
+};
+
+static const struct attribute_group inv_icm20602_attribute_group = {
+ .attrs = inv_icm20602_attributes,
+};
+
+static const struct iio_info icm20602_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = NULL,
+ .write_raw = NULL,
+ .attrs = &inv_icm20602_attribute_group,
+ .validate_trigger = inv_icm20602_validate_trigger,
+};
+
+static int icm20602_ldo_work(struct inv_icm20602_state *st, bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = regulator_set_voltage(st->reg_ldo,
+ ICM20602_LDO_VTG_MIN_UV, ICM20602_LDO_VTG_MAX_UV);
+ if (ret)
+ pr_err("Failed to request LDO voltage.\n");
+
+ ret = regulator_enable(st->reg_ldo);
+ if (ret)
+ pr_err("Failed to enable LDO %d\n", ret);
+ } else {
+ ret = regulator_disable(st->reg_ldo);
+ if (ret)
+ pr_err("Failed to disable LDO %d\n", ret);
+ regulator_set_load(st->reg_ldo, 0);
+ }
+
+ return MPU_SUCCESS;
+}
+
+static int icm20602_init_regulators(struct inv_icm20602_state *st)
+{
+ struct regulator *reg;
+
+ reg = regulator_get(&st->client->dev, "vdd-ldo");
+ if (IS_ERR_OR_NULL(reg)) {
+ pr_err("Unable to get regulator for LDO\n");
+ return -MPU_FAIL;
+ }
+
+ st->reg_ldo = reg;
+
+ return MPU_SUCCESS;
+}
+
+static int of_populate_icm20602_dt(struct inv_icm20602_state *st)
+{
+ int result = MPU_SUCCESS;
+
+ /* use client device irq */
+ st->gpio = of_get_named_gpio(st->client->dev.of_node,
+ "invensense,icm20602-gpio", 0);
+ result = gpio_is_valid(st->gpio);
+ if (!result) {
+ pr_err("gpio_is_valid %d failed\n", st->gpio);
+ return -MPU_FAIL;
+ }
+
+ result = gpio_request(st->gpio, "icm20602-irq");
+ if (result) {
+ pr_err("gpio_request failed\n");
+ return -MPU_FAIL;
+ }
+
+ result = gpio_direction_input(st->gpio);
+ if (result) {
+ pr_err("gpio_direction_input failed\n");
+ return -MPU_FAIL;
+ }
+
+ st->client->irq = gpio_to_irq(st->gpio);
+ if (st->client->irq < 0) {
+ pr_err("gpio_to_irq failed\n");
+ return -MPU_FAIL;
+ }
+
+ return MPU_SUCCESS;
+}
+
+/*
+ * inv_icm20602_probe() - probe function.
+ * @client: i2c client.
+ * @id: i2c device id.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ * The I2C address of the ICM-20602 is 0x68 or 0x69
+ * depending upon the value driven on AD0 pin.
+ */
+static int inv_icm20602_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct inv_icm20602_state *st;
+ struct iio_dev *indio_dev;
+ int result = MPU_SUCCESS;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ result = -ENOMEM;
+ goto out_remove_trigger;
+ }
+ st = iio_priv(indio_dev);
+ st->client = client;
+ st->interface = ICM20602_I2C;
+
+ pr_debug("i2c address is %x\n", client->addr);
+ result = of_populate_icm20602_dt(st);
+ if (result)
+ return result;
+
+ st->config = kzalloc(sizeof(struct icm20602_user_config), GFP_ATOMIC);
+ if (st->config == NULL)
+ return -ENOMEM;
+
+ icm20602_init_reg_map();
+
+ i2c_set_clientdata(client, indio_dev);
+
+ dev_set_drvdata(&client->dev, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = ICM20602_DEV_NAME;
+ indio_dev->channels = inv_icm20602_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
+
+ indio_dev->info = &icm20602_info;
+ indio_dev->modes = INDIO_BUFFER_TRIGGERED;
+ result = iio_triggered_buffer_setup(indio_dev,
+ inv_icm20602_irq_handler, inv_icm20602_read_fifo_fn, NULL);
+ if (result) {
+ dev_err(&st->client->dev, " configure buffer fail %d\n",
+ result);
+ goto out_remove_trigger;
+ }
+ icm20602_init_regulators(st);
+ icm20602_ldo_work(st, true);
+
+ result = inv_icm20602_probe_trigger(indio_dev);
+ if (result) {
+ dev_err(&st->client->dev, "trigger probe fail %d\n", result);
+ goto out_unreg_ring;
+ }
+
+ INIT_KFIFO(st->timestamps);
+ spin_lock_init(&st->time_stamp_lock);
+ result = iio_device_register(indio_dev);
+ if (result) {
+ dev_err(&st->client->dev, "IIO register fail %d\n", result);
+ goto out_remove_trigger;
+ }
+
+ return 0;
+
+out_remove_trigger:
+ inv_icm20602_remove_trigger(st);
+out_unreg_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_device_free(indio_dev);
+ gpio_free(st->gpio);
+
+ return 0;
+}
+
+static int inv_icm20602_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ gpio_free(st->gpio);
+ iio_device_unregister(indio_dev);
+ inv_icm20602_remove_trigger(st);
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static int inv_icm20602_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ icm20602_stop_fifo(st);
+ icm20602_ldo_work(st, false);
+ return 0;
+}
+
+static int inv_icm20602_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ icm20602_ldo_work(st, true);
+ icm20602_detect(st);
+ icm20602_init_device(st);
+ icm20602_start_fifo(st);
+
+ return 0;
+}
+
+static const struct dev_pm_ops icm20602_pm_ops = {
+ .resume = inv_icm20602_resume,
+ .suspend = inv_icm20602_suspend,
+};
+
+static const struct of_device_id icm20602_match_table[] = {
+ {.compatible = "invensense,icm20602"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, icm20602_match_table);
+
+static const struct i2c_device_id inv_icm20602_id[] = {
+ {"icm20602", 0},
+ {}
+};
+
+static struct i2c_driver icm20602_i2c_driver = {
+ .probe = inv_icm20602_probe,
+ .remove = inv_icm20602_remove,
+ .id_table = inv_icm20602_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "inv-icm20602",
+ .of_match_table = icm20602_match_table,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .pm = &icm20602_pm_ops,
+ },
+};
+
+static int __init inv_icm20602_init(void)
+{
+ return i2c_add_driver(&icm20602_i2c_driver);
+}
+module_init(inv_icm20602_init);
+
+static void __exit inv_icm20602_exit(void)
+{
+ i2c_del_driver(&icm20602_i2c_driver);
+}
+module_exit(inv_icm20602_exit);
+
+MODULE_DESCRIPTION("icm20602 IMU driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
new file mode 100644
index 0000000..b369ae4
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _INV_ICM20602_IIO_H_
+#define _INV_ICM20602_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/platform_data/invensense_mpu6050.h>
+
+/*
+ * it uses sensor irq to trigger
+ * if set INV20602_DEVICE_IRQ_TRIGGER as 1,
+ * otherwise use SMD IRQ to trigger
+ */
+#define INV20602_DEVICE_IRQ_TRIGGER 0
+
+#if INV20602_DEVICE_IRQ_TRIGGER
+#define INV20602_SMD_IRQ_TRIGGER 0
+#else
+#define INV20602_SMD_IRQ_TRIGGER 1
+#endif
+
+#define ICM20602_LDO_VTG_MIN_UV 3300000
+#define ICM20602_LDO_VTG_MAX_UV 3300000
+
+#define INV_ICM20602_TIME_STAMP_TOR 5
+#define ICM20602_PACKAGE_SIZE 14
+
+/* device enum */
+enum inv_devices {
+ INV_ICM20602,
+ INV_NUM_PARTS,
+};
+
+enum _mpu_err {
+ MPU_SUCCESS = 0,
+ MPU_FAIL = 1,
+ MPU_READ_FAIL = 2,
+ MPU_WRITE_FAIL = 3,
+};
+
+/* Gyro Full Scale Range Enum */
+enum inv_icm20602_gyro_fsr_e {
+ ICM20602_GYRO_FSR_250DPS = 0,
+ ICM20602_GYRO_FSR_500DPS,
+ ICM20602_GYRO_FSR_1000DPS,
+ ICM20602_GYRO_FSR_2000DPS,
+ ICM20602_GYRO_FSR_NUM,
+};
+
+/* Accelerometor Full Scale Range Enum */
+enum inv_icm20602_acc_fsr_e {
+ ICM20602_ACC_FSR_2G = 0,
+ ICM20602_ACC_FSR_4G,
+ ICM20602_ACC_FSR_8G,
+ ICM20602_ACC_FSR_16G,
+ ICM20602_ACC_FSR_NUM,
+};
+
+/* scan element definition */
+enum inv_icm20602_scan {
+ INV_ICM20602_SCAN_ACCL_X,
+ INV_ICM20602_SCAN_ACCL_Y,
+ INV_ICM20602_SCAN_ACCL_Z,
+ INV_ICM20602_SCAN_GYRO_X,
+ INV_ICM20602_SCAN_GYRO_Y,
+ INV_ICM20602_SCAN_GYRO_Z,
+ INV_ICM20602_SCAN_TEMP,
+ INV_ICM20602_SCAN_TIMESTAMP,
+};
+
+/* this is for CONFIGURATION reg bit[2:0] DLFP_CFG */
+enum inv_icm20602_gyro_temp_lpf_e {
+ INV_ICM20602_GLFP_250HZ = 0, /* 8KHz */
+ INV_ICM20602_GYRO_LFP_176HZ, /* 1KHz */
+ INV_ICM20602_GYRO_LFP_92HZ,
+ INV_ICM20602_GYRO_LFP_41HZ,
+ INV_ICM20602_GYRO_LFP_20HZ,
+ INV_ICM20602_GYRO_LFP_10HZ,
+ INV_ICM20602_GYRO_LFP_5HZ,
+ INV_ICM20602_GYRO_LFP_NUM,
+};
+
+enum inv_icm20602_gyro_sample_rate_e {
+ ICM20602_SAMPLE_RATE_100HZ = 100,
+ ICM20602_SAMPLE_RATE_200HZ = 200,
+ ICM20602_SAMPLE_RATE_500HZ = 500,
+ ICM20602_SAMPLE_RATE_1000HZ = 1000,
+};
+
+/* this is for ACCEL CONFIGURATION 2 reg */
+enum inv_icm20602_acc_lpf_e {
+ ICM20602_ACCLFP_218 = 1,
+ ICM20602_ACCLFP_99,
+ ICM20602_ACCLFP_44,
+ ICM20602_ACCLFP_21,
+ ICM20602_ACCLFP_10,
+ ICM20602_ACCLFP_5,
+ ICM20602_ACCLFP_420_NOLPF,
+ ICM20602_ACCLPF_NUM = 7,
+};
+
+/* IIO attribute address */
+enum INV_ICM20602_IIO_ATTR_ADDR {
+ ATTR_ICM20602_GYRO_MATRIX,
+ ATTR_ICM20602_ACCL_MATRIX,
+};
+
+/* this is for GYRO CONFIGURATION reg */
+enum inv_icm20602_fs_e {
+ INV_ICM20602_FS_250DPS = 0,
+ INV_ICM20602_FS_500DPS,
+ INV_ICM20602_FS_1000DPS,
+ INV_ICM20602_FS_2000DPS,
+ NUM_ICM20602_FS,
+};
+
+enum inv_icm20602_clock_sel_e {
+ INV_ICM20602_CLK_INTERNAL = 0,
+ INV_ICM20602_CLK_PLL,
+ INV_NUM_CLK,
+};
+
+enum inv_icm20602_spi_freq {
+ MPU_SPI_FREQUENCY_1MHZ = 960000UL,
+ MPU_SPI_FREQUENCY_5MHZ = 4800000UL,
+ MPU_SPI_FREQUENCY_8MHZ = 8000000UL,
+ MPU_SPI_FREQUENCY_10MHZ = 10000000UL,
+ MPU_SPI_FREQUENCY_15MHZ = 15000000UL,
+ MPU_SPI_FREQUENCY_20MHZ = 20000000UL,
+};
+
+#define MPU_SPI_BUF_LEN 512
+#define ICM20602_DEV_NAME "icm20602_iio"
+
+struct inv_icm20602_platform_data {
+ __s8 orientation[9];
+};
+
+struct X_Y_Z {
+ u8 X;
+ u8 Y;
+ u8 Z;
+};
+
+enum RAW_TYPE {
+ ACCEL = 1,
+ GYRO = 2,
+ TEMP = 4,
+};
+
+struct icm20602_user_config {
+ enum inv_icm20602_gyro_temp_lpf_e gyro_lpf;
+ enum inv_icm20602_gyro_fsr_e gyro_fsr;
+ struct X_Y_Z gyro_self_test;
+
+ enum inv_icm20602_acc_lpf_e acc_lpf;
+ enum inv_icm20602_acc_fsr_e acc_fsr;
+ struct X_Y_Z acc_self_test;
+
+ uint32_t gyro_accel_sample_rate;
+ uint32_t user_fps_in_ms;
+
+ bool fifo_enabled;
+ uint16_t fifo_waterlevel;
+ struct X_Y_Z wake_on_motion;
+};
+
+enum inv_icm20602_interface {
+ ICM20602_I2C = 0,
+ ICM20602_SPI
+};
+
+/*
+ * struct inv_icm20602_state - Driver state variables.
+ * @TIMESTAMP_FIFO_SIZE: fifo size for timestamp.
+ * @trig: IIO trigger.
+ * @chip_config: Cached attribute information.
+ * @reg: Map of important registers.
+ * @hw: Other hardware-specific information.
+ * @chip_type: chip type.
+ * @time_stamp_lock: spin lock to time stamp.
+ * @spi: spi devices
+ * @plat_data: platform data.
+ * @timestamps: kfifo queue to store time stamp.
+ */
+struct inv_icm20602_state {
+#define TIMESTAMP_FIFO_SIZE 32
+ enum inv_icm20602_interface interface;
+ struct iio_trigger *trig;
+ const struct inv_icm20602_reg_map *reg;
+ struct icm20602_user_config *config;
+ spinlock_t time_stamp_lock;
+ struct spi_device *spi;
+ struct i2c_client *client;
+ u8 fifo_packet_size;
+ int fifo_cnt_threshold;
+ char *buf;
+ struct struct_icm20602_data *data_push;
+ enum inv_devices chip_type;
+ int gpio;
+ struct regulator *reg_ldo;
+ DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
+};
+
+struct struct_icm20602_raw_data {
+ u8 ACCEL_XOUT_H;
+ u8 ACCEL_XOUT_L;
+
+ u8 ACCEL_YOUT_H;
+ u8 ACCEL_YOUT_L;
+
+ u8 ACCEL_ZOUT_H;
+ u8 ACCEL_ZOUT_L;
+
+ u8 TEMP_OUT_H;
+ u8 TEMP_OUT_L;
+
+ u8 GYRO_XOUT_H;
+ u8 GYRO_XOUT_L;
+
+ u8 GYRO_YOUT_H;
+ u8 GYRO_YOUT_L;
+
+ u8 GYRO_ZOUT_H;
+ u8 GYRO_ZOUT_L;
+};
+
+struct struct_icm20602_real_data {
+ u16 ACCEL_XOUT;
+ u16 ACCEL_YOUT;
+ u16 ACCEL_ZOUT;
+
+ u16 GYRO_XOUT;
+ u16 GYRO_YOUT;
+ u16 GYRO_ZOUT;
+
+ u16 TEMP_OUT;
+};
+
+struct struct_icm20602_data {
+ s64 timestamps;
+ u8 *raw_data;
+};
+
+extern struct iio_trigger *inv_trig;
+irqreturn_t inv_icm20602_irq_handler(int irq, void *p);
+irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p);
+
+int inv_icm20602_probe_trigger(struct iio_dev *indio_dev);
+void inv_icm20602_remove_trigger(struct inv_icm20602_state *st);
+int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig);
+int icm20602_int_status(struct inv_icm20602_state *st, u8 *int_status);
+int icm20602_int_wm_status(struct inv_icm20602_state *st, u8 *int_status);
+int icm20602_reset_fifo(struct inv_icm20602_state *st);
+int icm20602_fifo_count(struct inv_icm20602_state *st, u16 *fifo_count);
+
+int icm20602_read_raw(struct inv_icm20602_state *st,
+ struct struct_icm20602_real_data *real_data, uint32_t type);
+int icm20602_init_reg_map(void);
+int icm20602_init_device(struct inv_icm20602_state *st);
+int icm20602_detect(struct inv_icm20602_state *st);
+int icm20602_read_fifo(struct inv_icm20602_state *st,
+ void *buf, const int size);
+int icm20602_start_fifo(struct inv_icm20602_state *st);
+int icm20602_stop_fifo(struct inv_icm20602_state *st);
+bool icm20602_self_test(struct inv_icm20602_state *st);
+#endif
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
new file mode 100644
index 0000000..081bec9
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/spi/spi.h>
+#include "inv_icm20602_iio.h"
+#include <linux/i2c.h>
+
+#define combine_8_to_16(upper, lower) ((_upper << 8) | _lower)
+
+static void inv_clear_kfifo(struct inv_icm20602_state *st)
+{
+ unsigned long flags;
+
+ /* Take the spin lock sem to avoid interrupt kick in */
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+/*
+ * inv_icm20602_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+irqreturn_t inv_icm20602_irq_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ s64 timestamp;
+
+ timestamp = iio_get_time_ns(indio_dev);
+
+ kfifo_in_spinlocked(&st->timestamps, ×tamp, 1,
+ &st->time_stamp_lock);
+
+ return IRQ_WAKE_THREAD;
+}
+
+#define BIT_FIFO_OFLOW_INT 0x10
+#define BIT_FIFO_WM_INT 0x40
+static int inv_icm20602_read_data(struct iio_dev *indio_dev)
+{
+ int result = MPU_SUCCESS;
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+ struct icm20602_user_config *config = st->config;
+ int package_count;
+ char *buf = st->buf;
+ s64 timestamp;
+ u8 int_status;
+ u16 fifo_count;
+ int i;
+
+ if (!st)
+ return -MPU_FAIL;
+ package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
+ mutex_lock(&indio_dev->mlock);
+ icm20602_int_status(st, &int_status);
+ if (int_status & BIT_FIFO_OFLOW_INT) {
+ icm20602_fifo_count(st, &fifo_count);
+ pr_debug("fifo_count = %d\n", fifo_count);
+ inv_clear_kfifo(st);
+ icm20602_reset_fifo(st);
+ goto end_session;
+ }
+ if (config->fifo_enabled) {
+ result = kfifo_out(&st->timestamps,
+ ×tamp, 1);
+ /* when there is no timestamp, put it as 0 */
+ if (result == 0)
+ timestamp = 0;
+ for (i = 0; i < package_count; i++) {
+ result = icm20602_read_fifo(st,
+ buf, ICM20602_PACKAGE_SIZE);
+ memcpy(st->data_push[i].raw_data,
+ buf, ICM20602_PACKAGE_SIZE);
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ st->data_push[i].raw_data, timestamp);
+ buf += ICM20602_PACKAGE_SIZE;
+ }
+ memset(st->buf, 0, config->fifo_waterlevel);
+ }
+end_session:
+ mutex_unlock(&indio_dev->mlock);
+ iio_trigger_notify_done(indio_dev->trig);
+ return MPU_SUCCESS;
+}
+
+/*
+ * inv_icm20602_read_fifo() - Transfer data from hardware FIFO to KFIFO.
+ */
+irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+
+ inv_icm20602_read_data(indio_dev);
+ return IRQ_HANDLED;
+}
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c
new file mode 100644
index 0000000..f05a631
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/sched/rt.h>
+#include <linux/delay.h>
+#include "inv_icm20602_iio.h"
+#include <linux/i2c.h>
+
+static const struct iio_trigger_ops inv_icm20602_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ if (st->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+int inv_icm20602_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING,
+ "inv_icm20602",
+ st->trig);
+ if (ret) {
+ pr_err("request_irq failed\n");
+ goto error_free_trig;
+ }
+ st->trig->dev.parent = &st->client->dev;
+ st->trig->ops = &inv_icm20602_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+ if (ret) {
+ pr_err("iio_trigger_register failed\n");
+ goto error_free_irq;
+ }
+ indio_dev->trig = st->trig;
+
+ return 0;
+
+error_free_irq:
+ free_irq(st->client->irq, st->trig);
+error_free_trig:
+ iio_trigger_free(st->trig);
+error_ret:
+ return ret;
+}
+
+void inv_icm20602_remove_trigger(struct inv_icm20602_state *st)
+{
+ iio_trigger_unregister(st->trig);
+ free_irq(st->client->irq, st->trig);
+ iio_trigger_free(st->trig);
+}
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 158aaf4..5d05c38 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -174,7 +174,7 @@
struct iio_dev *indio_dev = filp->private_data;
struct iio_buffer *rb = indio_dev->buffer;
- if (!indio_dev->info)
+ if (!indio_dev->info || rb == NULL)
return 0;
poll_wait(filp, &rb->pollq, wait);
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 7de0f39..d23cf77 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -510,13 +510,26 @@
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
- return ret;
+ goto err_poweroff;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
- return iio_device_register(indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_pm_disable;
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+err_poweroff:
+ rpr0521_poweroff(data);
+
+ return ret;
}
static int rpr0521_remove(struct i2c_client *client)
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
index 6325e7d..f3cb4dc 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -48,8 +48,6 @@
}
static const struct spi_device_id st_magn_id_table[] = {
- { LSM303DLHC_MAGN_DEV_NAME },
- { LSM303DLM_MAGN_DEV_NAME },
{ LIS3MDL_MAGN_DEV_NAME },
{ LSM303AGR_MAGN_DEV_NAME },
{},
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 44e46c1..3458418 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -638,6 +638,8 @@
int st_press_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *press_data = iio_priv(indio_dev);
+ struct st_sensors_platform_data *pdata =
+ (struct st_sensors_platform_data *)press_data->dev->platform_data;
int irq = press_data->get_irq_data_ready(indio_dev);
int err;
@@ -673,12 +675,10 @@
press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz;
/* Some devices don't support a data ready pin. */
- if (!press_data->dev->platform_data &&
- press_data->sensor_settings->drdy_irq.addr)
- press_data->dev->platform_data =
- (struct st_sensors_platform_data *)&default_press_pdata;
+ if (!pdata && press_data->sensor_settings->drdy_irq.addr)
+ pdata = (struct st_sensors_platform_data *)&default_press_pdata;
- err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data);
+ err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0)
goto st_press_power_off;
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
index 19d2eb4..2a4a62e 100644
--- a/drivers/iio/pressure/zpa2326.c
+++ b/drivers/iio/pressure/zpa2326.c
@@ -871,12 +871,13 @@
{
int ret;
unsigned int val;
+ long timeout;
zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt");
- ret = wait_for_completion_interruptible_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&private->data_ready, ZPA2326_CONVERSION_JIFFIES);
- if (ret > 0)
+ if (timeout > 0)
/*
* Interrupt handler completed before timeout: return operation
* status.
@@ -886,13 +887,16 @@
/* Clear all interrupts just to be sure. */
regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val);
- if (!ret)
+ if (!timeout) {
/* Timed out. */
+ zpa2326_warn(indio_dev, "no one shot interrupt occurred (%ld)",
+ timeout);
ret = -ETIME;
-
- if (ret != -ERESTARTSYS)
- zpa2326_warn(indio_dev, "no one shot interrupt occurred (%d)",
- ret);
+ } else if (timeout < 0) {
+ zpa2326_warn(indio_dev,
+ "wait for one shot interrupt cancelled");
+ ret = -ERESTARTSYS;
+ }
return ret;
}
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index fb4ce03..978b8d9 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -209,6 +209,22 @@
}
EXPORT_SYMBOL(rdma_addr_size);
+int rdma_addr_size_in6(struct sockaddr_in6 *addr)
+{
+ int ret = rdma_addr_size((struct sockaddr *) addr);
+
+ return ret <= sizeof(*addr) ? ret : 0;
+}
+EXPORT_SYMBOL(rdma_addr_size_in6);
+
+int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr)
+{
+ int ret = rdma_addr_size((struct sockaddr *) addr);
+
+ return ret <= sizeof(*addr) ? ret : 0;
+}
+EXPORT_SYMBOL(rdma_addr_size_kss);
+
static struct rdma_addr_client self;
void rdma_addr_register_client(struct rdma_addr_client *client)
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 30f0161..cbe5324 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -4039,6 +4039,9 @@
struct cma_multicast *mc;
int ret;
+ if (!id->device)
+ return -EINVAL;
+
id_priv = container_of(id, struct rdma_id_private, id);
if (!cma_comp(id_priv, RDMA_CM_ADDR_BOUND) &&
!cma_comp(id_priv, RDMA_CM_ADDR_RESOLVED))
@@ -4336,7 +4339,7 @@
RDMA_NL_RDMA_CM_ATTR_SRC_ADDR))
goto out;
if (ibnl_put_attr(skb, nlh,
- rdma_addr_size(cma_src_addr(id_priv)),
+ rdma_addr_size(cma_dst_addr(id_priv)),
cma_dst_addr(id_priv),
RDMA_NL_RDMA_CM_ATTR_DST_ADDR))
goto out;
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 760ef60..15f4bdf 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -999,8 +999,7 @@
return -ENOMEM;
ib_comp_wq = alloc_workqueue("ib-comp-wq",
- WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
- WQ_UNBOUND_MAX_ACTIVE);
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
if (!ib_comp_wq) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c
index ade71e7..2fe4c2c 100644
--- a/drivers/infiniband/core/iwpm_util.c
+++ b/drivers/infiniband/core/iwpm_util.c
@@ -664,6 +664,7 @@
}
skb_num++;
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
+ ret = -EINVAL;
for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
hlist_for_each_entry(map_info, &iwpm_hash_bucket[i],
hlist_node) {
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 9520154..f2f1c9f 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -132,7 +132,7 @@
ctx = idr_find(&ctx_idr, id);
if (!ctx)
ctx = ERR_PTR(-ENOENT);
- else if (ctx->file != file)
+ else if (ctx->file != file || !ctx->cm_id)
ctx = ERR_PTR(-EINVAL);
return ctx;
}
@@ -454,6 +454,7 @@
struct rdma_ucm_create_id cmd;
struct rdma_ucm_create_id_resp resp;
struct ucma_context *ctx;
+ struct rdma_cm_id *cm_id;
enum ib_qp_type qp_type;
int ret;
@@ -474,10 +475,10 @@
return -ENOMEM;
ctx->uid = cmd.uid;
- ctx->cm_id = rdma_create_id(current->nsproxy->net_ns,
- ucma_event_handler, ctx, cmd.ps, qp_type);
- if (IS_ERR(ctx->cm_id)) {
- ret = PTR_ERR(ctx->cm_id);
+ cm_id = rdma_create_id(current->nsproxy->net_ns,
+ ucma_event_handler, ctx, cmd.ps, qp_type);
+ if (IS_ERR(cm_id)) {
+ ret = PTR_ERR(cm_id);
goto err1;
}
@@ -487,14 +488,19 @@
ret = -EFAULT;
goto err2;
}
+
+ ctx->cm_id = cm_id;
return 0;
err2:
- rdma_destroy_id(ctx->cm_id);
+ rdma_destroy_id(cm_id);
err1:
mutex_lock(&mut);
idr_remove(&ctx_idr, ctx->id);
mutex_unlock(&mut);
+ mutex_lock(&file->mut);
+ list_del(&ctx->list);
+ mutex_unlock(&file->mut);
kfree(ctx);
return ret;
}
@@ -624,6 +630,9 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if (!rdma_addr_size_in6(&cmd.addr))
+ return -EINVAL;
+
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
@@ -637,22 +646,21 @@
int in_len, int out_len)
{
struct rdma_ucm_bind cmd;
- struct sockaddr *addr;
struct ucma_context *ctx;
int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- addr = (struct sockaddr *) &cmd.addr;
- if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr)))
+ if (cmd.reserved || !cmd.addr_size ||
+ cmd.addr_size != rdma_addr_size_kss(&cmd.addr))
return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_bind_addr(ctx->cm_id, addr);
+ ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
ucma_put_ctx(ctx);
return ret;
}
@@ -668,13 +676,16 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if ((cmd.src_addr.sin6_family && !rdma_addr_size_in6(&cmd.src_addr)) ||
+ !rdma_addr_size_in6(&cmd.dst_addr))
+ return -EINVAL;
+
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
- (struct sockaddr *) &cmd.dst_addr,
- cmd.timeout_ms);
+ (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
ucma_put_ctx(ctx);
return ret;
}
@@ -684,24 +695,23 @@
int in_len, int out_len)
{
struct rdma_ucm_resolve_addr cmd;
- struct sockaddr *src, *dst;
struct ucma_context *ctx;
int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- src = (struct sockaddr *) &cmd.src_addr;
- dst = (struct sockaddr *) &cmd.dst_addr;
- if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) ||
- !cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
+ if (cmd.reserved ||
+ (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) ||
+ !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr)))
return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
+ ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
+ (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
ucma_put_ctx(ctx);
return ret;
}
@@ -1139,10 +1149,18 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if (cmd.qp_state > IB_QPS_ERR)
+ return -EINVAL;
+
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
+ if (!ctx->cm_id->device) {
+ ret = -EINVAL;
+ goto out;
+ }
+
resp.qp_attr_mask = 0;
memset(&qp_attr, 0, sizeof qp_attr);
qp_attr.qp_state = cmd.qp_state;
@@ -1213,6 +1231,9 @@
if (!optlen)
return -EINVAL;
+ if (!ctx->cm_id->device)
+ return -EINVAL;
+
memset(&sa_path, 0, sizeof(sa_path));
ib_sa_unpack_path(path_data->path_rec, &sa_path);
@@ -1275,6 +1296,9 @@
if (IS_ERR(ctx))
return PTR_ERR(ctx);
+ if (unlikely(cmd.optval > KMALLOC_MAX_SIZE))
+ return -EINVAL;
+
optval = memdup_user((void __user *) (unsigned long) cmd.optval,
cmd.optlen);
if (IS_ERR(optval)) {
@@ -1296,7 +1320,7 @@
{
struct rdma_ucm_notify cmd;
struct ucma_context *ctx;
- int ret;
+ int ret = -EINVAL;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
@@ -1305,7 +1329,9 @@
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_notify(ctx->cm_id, (enum ib_event_type) cmd.event);
+ if (ctx->cm_id->device)
+ ret = rdma_notify(ctx->cm_id, (enum ib_event_type)cmd.event);
+
ucma_put_ctx(ctx);
return ret;
}
@@ -1324,7 +1350,7 @@
return -ENOSPC;
addr = (struct sockaddr *) &cmd->addr;
- if (!cmd->addr_size || (cmd->addr_size != rdma_addr_size(addr)))
+ if (cmd->addr_size != rdma_addr_size(addr))
return -EINVAL;
if (cmd->join_flags == RDMA_MC_JOIN_FLAG_FULLMEMBER)
@@ -1391,7 +1417,10 @@
join_cmd.response = cmd.response;
join_cmd.uid = cmd.uid;
join_cmd.id = cmd.id;
- join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
+ join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr);
+ if (!join_cmd.addr_size)
+ return -EINVAL;
+
join_cmd.join_flags = RDMA_MC_JOIN_FLAG_FULLMEMBER;
memcpy(&join_cmd.addr, &cmd.addr, join_cmd.addr_size);
@@ -1407,6 +1436,9 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if (!rdma_addr_size_kss(&cmd.addr))
+ return -EINVAL;
+
return ucma_process_join(file, &cmd, out_len);
}
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index c22fde6..e74aa1d 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -193,7 +193,7 @@
sg_list_start = umem->sg_head.sgl;
while (npages) {
- ret = get_user_pages(cur_base,
+ ret = get_user_pages_longterm(cur_base,
min_t(unsigned long, npages,
PAGE_SIZE / sizeof (struct page *)),
gup_flags, page_list, vma_list);
@@ -357,7 +357,7 @@
return -EINVAL;
}
- ret = sg_pcopy_to_buffer(umem->sg_head.sgl, umem->nmap, dst, length,
+ ret = sg_pcopy_to_buffer(umem->sg_head.sgl, umem->npages, dst, length,
offset + ib_umem_offset(umem));
if (ret < 0)
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index d118ffe..4b717cf 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2491,9 +2491,13 @@
static void *alloc_wr(size_t wr_size, __u32 num_sge)
{
+ if (num_sge >= (U32_MAX - ALIGN(wr_size, sizeof (struct ib_sge))) /
+ sizeof (struct ib_sge))
+ return NULL;
+
return kmalloc(ALIGN(wr_size, sizeof (struct ib_sge)) +
num_sge * sizeof (struct ib_sge), GFP_KERNEL);
-};
+}
ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
struct ib_device *ib_dev,
@@ -2720,6 +2724,13 @@
goto err;
}
+ if (user_wr->num_sge >=
+ (U32_MAX - ALIGN(sizeof *next, sizeof (struct ib_sge))) /
+ sizeof (struct ib_sge)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
next = kmalloc(ALIGN(sizeof *next, sizeof (struct ib_sge)) +
user_wr->num_sge * sizeof (struct ib_sge),
GFP_KERNEL);
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 44b1104..c5e921b 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -735,12 +735,21 @@
return -1;
}
+static bool verify_command_idx(u32 command, bool extended)
+{
+ if (extended)
+ return command < ARRAY_SIZE(uverbs_ex_cmd_table);
+
+ return command < ARRAY_SIZE(uverbs_cmd_table);
+}
+
static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
size_t count, loff_t *pos)
{
struct ib_uverbs_file *file = filp->private_data;
struct ib_device *ib_dev;
struct ib_uverbs_cmd_hdr hdr;
+ bool extended_command;
__u32 command;
__u32 flags;
int srcu_key;
@@ -770,6 +779,15 @@
}
command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
+ flags = (hdr.command &
+ IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT;
+
+ extended_command = flags & IB_USER_VERBS_CMD_FLAG_EXTENDED;
+ if (!verify_command_idx(command, extended_command)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (verify_command_mask(ib_dev, command)) {
ret = -EOPNOTSUPP;
goto out;
@@ -781,12 +799,8 @@
goto out;
}
- flags = (hdr.command &
- IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT;
-
if (!flags) {
- if (command >= ARRAY_SIZE(uverbs_cmd_table) ||
- !uverbs_cmd_table[command]) {
+ if (!uverbs_cmd_table[command]) {
ret = -EINVAL;
goto out;
}
@@ -807,8 +821,7 @@
struct ib_udata uhw;
size_t written_count = count;
- if (command >= ARRAY_SIZE(uverbs_ex_cmd_table) ||
- !uverbs_ex_cmd_table[command]) {
+ if (!uverbs_ex_cmd_table[command]) {
ret = -ENOSYS;
goto out;
}
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 6512a55..dd18b74 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -488,6 +488,7 @@
ep = *((struct c4iw_ep **)(skb->cb + 2 * sizeof(void *)));
release_ep_resources(ep);
+ kfree_skb(skb);
return 0;
}
@@ -498,6 +499,7 @@
ep = *((struct c4iw_ep **)(skb->cb + 2 * sizeof(void *)));
c4iw_put_ep(&ep->parent_ep->com);
release_ep_resources(ep);
+ kfree_skb(skb);
return 0;
}
@@ -569,11 +571,13 @@
PDBG("%s rdev %p\n", __func__, rdev);
req->cmd = CPL_ABORT_NO_RST;
+ skb_get(skb);
ret = c4iw_ofld_send(rdev, skb);
if (ret) {
__state_set(&ep->com, DEAD);
queue_arp_failure_cpl(ep, skb, FAKE_CPL_PUT_EP_SAFE);
- }
+ } else
+ kfree_skb(skb);
}
static int send_flowc(struct c4iw_ep *ep)
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index b85a1a9..9e0f2cc 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -856,6 +856,11 @@
rdev->status_page->db_off = 0;
+ init_completion(&rdev->rqt_compl);
+ init_completion(&rdev->pbl_compl);
+ kref_init(&rdev->rqt_kref);
+ kref_init(&rdev->pbl_kref);
+
return 0;
err_free_status_page:
free_page((unsigned long)rdev->status_page);
@@ -872,12 +877,14 @@
static void c4iw_rdev_close(struct c4iw_rdev *rdev)
{
- destroy_workqueue(rdev->free_workq);
kfree(rdev->wr_log);
free_page((unsigned long)rdev->status_page);
c4iw_pblpool_destroy(rdev);
c4iw_rqtpool_destroy(rdev);
+ wait_for_completion(&rdev->pbl_compl);
+ wait_for_completion(&rdev->rqt_compl);
c4iw_destroy_resource(&rdev->resource);
+ destroy_workqueue(rdev->free_workq);
}
static void c4iw_dealloc(struct uld_ctx *ctx)
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 7d54066..896dff7 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -186,6 +186,10 @@
struct wr_log_entry *wr_log;
int wr_log_size;
struct workqueue_struct *free_workq;
+ struct completion rqt_compl;
+ struct completion pbl_compl;
+ struct kref rqt_kref;
+ struct kref pbl_kref;
};
static inline int c4iw_fatal_error(struct c4iw_rdev *rdev)
diff --git a/drivers/infiniband/hw/cxgb4/resource.c b/drivers/infiniband/hw/cxgb4/resource.c
index 67df71a..803c677 100644
--- a/drivers/infiniband/hw/cxgb4/resource.c
+++ b/drivers/infiniband/hw/cxgb4/resource.c
@@ -260,12 +260,22 @@
rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT);
if (rdev->stats.pbl.cur > rdev->stats.pbl.max)
rdev->stats.pbl.max = rdev->stats.pbl.cur;
+ kref_get(&rdev->pbl_kref);
} else
rdev->stats.pbl.fail++;
mutex_unlock(&rdev->stats.lock);
return (u32)addr;
}
+static void destroy_pblpool(struct kref *kref)
+{
+ struct c4iw_rdev *rdev;
+
+ rdev = container_of(kref, struct c4iw_rdev, pbl_kref);
+ gen_pool_destroy(rdev->pbl_pool);
+ complete(&rdev->pbl_compl);
+}
+
void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
{
PDBG("%s addr 0x%x size %d\n", __func__, addr, size);
@@ -273,6 +283,7 @@
rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT);
mutex_unlock(&rdev->stats.lock);
gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size);
+ kref_put(&rdev->pbl_kref, destroy_pblpool);
}
int c4iw_pblpool_create(struct c4iw_rdev *rdev)
@@ -312,7 +323,7 @@
void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
{
- gen_pool_destroy(rdev->pbl_pool);
+ kref_put(&rdev->pbl_kref, destroy_pblpool);
}
/*
@@ -333,12 +344,22 @@
rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT);
if (rdev->stats.rqt.cur > rdev->stats.rqt.max)
rdev->stats.rqt.max = rdev->stats.rqt.cur;
+ kref_get(&rdev->rqt_kref);
} else
rdev->stats.rqt.fail++;
mutex_unlock(&rdev->stats.lock);
return (u32)addr;
}
+static void destroy_rqtpool(struct kref *kref)
+{
+ struct c4iw_rdev *rdev;
+
+ rdev = container_of(kref, struct c4iw_rdev, rqt_kref);
+ gen_pool_destroy(rdev->rqt_pool);
+ complete(&rdev->rqt_compl);
+}
+
void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
{
PDBG("%s addr 0x%x size %d\n", __func__, addr, size << 6);
@@ -346,6 +367,7 @@
rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT);
mutex_unlock(&rdev->stats.lock);
gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);
+ kref_put(&rdev->rqt_kref, destroy_rqtpool);
}
int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
@@ -383,7 +405,7 @@
void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
{
- gen_pool_destroy(rdev->rqt_pool);
+ kref_put(&rdev->rqt_kref, destroy_rqtpool);
}
/*
diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c
index 4682909..7853b0c 100644
--- a/drivers/infiniband/hw/hfi1/chip.c
+++ b/drivers/infiniband/hw/hfi1/chip.c
@@ -6379,18 +6379,17 @@
*
* The expectation is that the caller of this routine would have taken
* care of properly transitioning the link into the correct state.
+ * NOTE: the caller needs to acquire the dd->dc8051_lock lock
+ * before calling this function.
*/
-static void dc_shutdown(struct hfi1_devdata *dd)
+static void _dc_shutdown(struct hfi1_devdata *dd)
{
- unsigned long flags;
+ lockdep_assert_held(&dd->dc8051_lock);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
- if (dd->dc_shutdown) {
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
+ if (dd->dc_shutdown)
return;
- }
+
dd->dc_shutdown = 1;
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
/* Shutdown the LCB */
lcb_shutdown(dd, 1);
/*
@@ -6401,35 +6400,45 @@
write_csr(dd, DC_DC8051_CFG_RST, 0x1);
}
+static void dc_shutdown(struct hfi1_devdata *dd)
+{
+ mutex_lock(&dd->dc8051_lock);
+ _dc_shutdown(dd);
+ mutex_unlock(&dd->dc8051_lock);
+}
+
/*
* Calling this after the DC has been brought out of reset should not
* do any damage.
+ * NOTE: the caller needs to acquire the dd->dc8051_lock lock
+ * before calling this function.
*/
-static void dc_start(struct hfi1_devdata *dd)
+static void _dc_start(struct hfi1_devdata *dd)
{
- unsigned long flags;
- int ret;
+ lockdep_assert_held(&dd->dc8051_lock);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
if (!dd->dc_shutdown)
- goto done;
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
+ return;
+
/* Take the 8051 out of reset */
write_csr(dd, DC_DC8051_CFG_RST, 0ull);
/* Wait until 8051 is ready */
- ret = wait_fm_ready(dd, TIMEOUT_8051_START);
- if (ret) {
+ if (wait_fm_ready(dd, TIMEOUT_8051_START))
dd_dev_err(dd, "%s: timeout starting 8051 firmware\n",
__func__);
- }
+
/* Take away reset for LCB and RX FPE (set in lcb_shutdown). */
write_csr(dd, DCC_CFG_RESET, 0x10);
/* lcb_shutdown() with abort=1 does not restore these */
write_csr(dd, DC_LCB_ERR_EN, dd->lcb_err_en);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
dd->dc_shutdown = 0;
-done:
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
+}
+
+static void dc_start(struct hfi1_devdata *dd)
+{
+ mutex_lock(&dd->dc8051_lock);
+ _dc_start(dd);
+ mutex_unlock(&dd->dc8051_lock);
}
/*
@@ -8418,16 +8427,11 @@
{
u64 reg, completed;
int return_code;
- unsigned long flags;
unsigned long timeout;
hfi1_cdbg(DC8051, "type %d, data 0x%012llx", type, in_data);
- /*
- * Alternative to holding the lock for a long time:
- * - keep busy wait - have other users bounce off
- */
- spin_lock_irqsave(&dd->dc8051_lock, flags);
+ mutex_lock(&dd->dc8051_lock);
/* We can't send any commands to the 8051 if it's in reset */
if (dd->dc_shutdown) {
@@ -8453,10 +8457,8 @@
return_code = -ENXIO;
goto fail;
}
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
- dc_shutdown(dd);
- dc_start(dd);
- spin_lock_irqsave(&dd->dc8051_lock, flags);
+ _dc_shutdown(dd);
+ _dc_start(dd);
}
/*
@@ -8534,8 +8536,7 @@
write_csr(dd, DC_DC8051_CFG_HOST_CMD_0, 0);
fail:
- spin_unlock_irqrestore(&dd->dc8051_lock, flags);
-
+ mutex_unlock(&dd->dc8051_lock);
return return_code;
}
@@ -9489,8 +9490,11 @@
int ret;
u8 status;
- /* report success if not a QSFP */
- if (ppd->port_type != PORT_TYPE_QSFP)
+ /*
+ * Report success if not a QSFP or, if it is a QSFP, but the cable is
+ * not present
+ */
+ if (ppd->port_type != PORT_TYPE_QSFP || !qsfp_mod_present(ppd))
return 0;
/* read byte 2, the status byte */
@@ -11846,6 +11850,10 @@
dd->scntrs = NULL;
kfree(dd->cntrnames);
dd->cntrnames = NULL;
+ if (dd->update_cntr_wq) {
+ destroy_workqueue(dd->update_cntr_wq);
+ dd->update_cntr_wq = NULL;
+ }
}
static u64 read_dev_port_cntr(struct hfi1_devdata *dd, struct cntr_entry *entry,
@@ -12001,7 +12009,7 @@
return write_dev_port_cntr(ppd->dd, entry, sval, ppd, vl, data);
}
-static void update_synth_timer(unsigned long opaque)
+static void do_update_synth_timer(struct work_struct *work)
{
u64 cur_tx;
u64 cur_rx;
@@ -12010,8 +12018,8 @@
int i, j, vl;
struct hfi1_pportdata *ppd;
struct cntr_entry *entry;
-
- struct hfi1_devdata *dd = (struct hfi1_devdata *)opaque;
+ struct hfi1_devdata *dd = container_of(work, struct hfi1_devdata,
+ update_cntr_work);
/*
* Rather than keep beating on the CSRs pick a minimal set that we can
@@ -12094,7 +12102,13 @@
} else {
hfi1_cdbg(CNTR, "[%d] No update necessary", dd->unit);
}
+}
+static void update_synth_timer(unsigned long opaque)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)opaque;
+
+ queue_work(dd->update_cntr_wq, &dd->update_cntr_work);
mod_timer(&dd->synth_stats_timer, jiffies + HZ * SYNTH_CNT_TIME);
}
@@ -12330,6 +12344,13 @@
if (init_cpu_counters(dd))
goto bail;
+ dd->update_cntr_wq = alloc_ordered_workqueue("hfi1_update_cntr_%d",
+ WQ_MEM_RECLAIM, dd->unit);
+ if (!dd->update_cntr_wq)
+ goto bail;
+
+ INIT_WORK(&dd->update_cntr_work, do_update_synth_timer);
+
mod_timer(&dd->synth_stats_timer, jiffies + HZ * SYNTH_CNT_TIME);
return 0;
bail:
diff --git a/drivers/infiniband/hw/hfi1/debugfs.c b/drivers/infiniband/hw/hfi1/debugfs.c
index 632ba21..0022660 100644
--- a/drivers/infiniband/hw/hfi1/debugfs.c
+++ b/drivers/infiniband/hw/hfi1/debugfs.c
@@ -67,13 +67,13 @@
loff_t *ppos)
{
struct dentry *d = file->f_path.dentry;
- int srcu_idx;
ssize_t r;
- r = debugfs_use_file_start(d, &srcu_idx);
- if (likely(!r))
- r = seq_read(file, buf, size, ppos);
- debugfs_use_file_finish(srcu_idx);
+ r = debugfs_file_get(d);
+ if (unlikely(r))
+ return r;
+ r = seq_read(file, buf, size, ppos);
+ debugfs_file_put(d);
return r;
}
@@ -83,13 +83,13 @@
int whence)
{
struct dentry *d = file->f_path.dentry;
- int srcu_idx;
loff_t r;
- r = debugfs_use_file_start(d, &srcu_idx);
- if (likely(!r))
- r = seq_lseek(file, offset, whence);
- debugfs_use_file_finish(srcu_idx);
+ r = debugfs_file_get(d);
+ if (unlikely(r))
+ return r;
+ r = seq_lseek(file, offset, whence);
+ debugfs_file_put(d);
return r;
}
diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h
index cc87fd4..a3279f3d 100644
--- a/drivers/infiniband/hw/hfi1/hfi.h
+++ b/drivers/infiniband/hw/hfi1/hfi.h
@@ -475,7 +475,7 @@
#define HFI1_PART_ENFORCE_OUT 0x2
/* how often we check for synthetic counter wrap around */
-#define SYNTH_CNT_TIME 2
+#define SYNTH_CNT_TIME 3
/* Counter flags */
#define CNTR_NORMAL 0x0 /* Normal counters, just read register */
@@ -929,8 +929,9 @@
spinlock_t rcvctrl_lock; /* protect changes to RcvCtrl */
/* around rcd and (user ctxts) ctxt_cnt use (intr vs free) */
spinlock_t uctxt_lock; /* rcd and user context changes */
- /* exclusive access to 8051 */
- spinlock_t dc8051_lock;
+ struct mutex dc8051_lock; /* exclusive access to 8051 */
+ struct workqueue_struct *update_cntr_wq;
+ struct work_struct update_cntr_work;
/* exclusive access to 8051 memory */
spinlock_t dc8051_memlock;
int dc8051_timed_out; /* remember if the 8051 timed out */
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index a3dd27b..ae1f90d 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -1049,6 +1049,8 @@
return ERR_PTR(-ENOMEM);
dd->num_pports = nports;
dd->pport = (struct hfi1_pportdata *)(dd + 1);
+ dd->pcidev = pdev;
+ pci_set_drvdata(pdev, dd);
INIT_LIST_HEAD(&dd->list);
idr_preload(GFP_KERNEL);
@@ -1078,11 +1080,11 @@
spin_lock_init(&dd->uctxt_lock);
spin_lock_init(&dd->hfi1_diag_trans_lock);
spin_lock_init(&dd->sc_init_lock);
- spin_lock_init(&dd->dc8051_lock);
spin_lock_init(&dd->dc8051_memlock);
seqlock_init(&dd->sc2vl_lock);
spin_lock_init(&dd->sde_map_lock);
spin_lock_init(&dd->pio_map_lock);
+ mutex_init(&dd->dc8051_lock);
init_waitqueue_head(&dd->event_queue);
dd->int_counter = alloc_percpu(u64);
diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c
index 335613a1..7176260 100644
--- a/drivers/infiniband/hw/hfi1/pcie.c
+++ b/drivers/infiniband/hw/hfi1/pcie.c
@@ -162,9 +162,6 @@
unsigned long len;
resource_size_t addr;
- dd->pcidev = pdev;
- pci_set_drvdata(pdev, dd);
-
addr = pci_resource_start(pdev, 0);
len = pci_resource_len(pdev, 0);
diff --git a/drivers/infiniband/hw/hfi1/sysfs.c b/drivers/infiniband/hw/hfi1/sysfs.c
index 919a547..621b60a 100644
--- a/drivers/infiniband/hw/hfi1/sysfs.c
+++ b/drivers/infiniband/hw/hfi1/sysfs.c
@@ -196,7 +196,8 @@
};
static struct attribute *port_cc_default_attributes[] = {
- &cc_prescan_attr.attr
+ &cc_prescan_attr.attr,
+ NULL
};
static struct kobj_type port_cc_ktype = {
diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
index 2c4b4d0..3b973cb 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
@@ -3644,8 +3644,10 @@
hmc_info->hmc_obj[I40IW_HMC_IW_APBVT_ENTRY].cnt = 1;
hmc_info->hmc_obj[I40IW_HMC_IW_MR].cnt = mrwanted;
- hmc_info->hmc_obj[I40IW_HMC_IW_XF].cnt = I40IW_MAX_WQ_ENTRIES * qpwanted;
- hmc_info->hmc_obj[I40IW_HMC_IW_Q1].cnt = 4 * I40IW_MAX_IRD_SIZE * qpwanted;
+ hmc_info->hmc_obj[I40IW_HMC_IW_XF].cnt =
+ roundup_pow_of_two(I40IW_MAX_WQ_ENTRIES * qpwanted);
+ hmc_info->hmc_obj[I40IW_HMC_IW_Q1].cnt =
+ roundup_pow_of_two(2 * I40IW_MAX_IRD_SIZE * qpwanted);
hmc_info->hmc_obj[I40IW_HMC_IW_XFFL].cnt =
hmc_info->hmc_obj[I40IW_HMC_IW_XF].cnt / hmc_fpm_misc->xf_block_size;
hmc_info->hmc_obj[I40IW_HMC_IW_Q1FL].cnt =
diff --git a/drivers/infiniband/hw/i40iw/i40iw_d.h b/drivers/infiniband/hw/i40iw/i40iw_d.h
index d1328a6..8ce599e 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_d.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_d.h
@@ -86,6 +86,7 @@
#define RDMA_OPCODE_MASK 0x0f
#define RDMA_READ_REQ_OPCODE 1
#define Q2_BAD_FRAME_OFFSET 72
+#define Q2_FPSN_OFFSET 64
#define CQE_MAJOR_DRV 0x8000
#define I40IW_TERM_SENT 0x01
diff --git a/drivers/infiniband/hw/i40iw/i40iw_puda.c b/drivers/infiniband/hw/i40iw/i40iw_puda.c
index c62d354..3ed4914 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_puda.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_puda.c
@@ -1320,7 +1320,7 @@
u32 *hw_host_ctx = (u32 *)qp->hw_host_ctx;
u32 rcv_wnd = hw_host_ctx[23];
/* first partial seq # in q2 */
- u32 fps = qp->q2_buf[16];
+ u32 fps = *(u32 *)(qp->q2_buf + Q2_FPSN_OFFSET);
struct list_head *rxlist = &pfpdu->rxlist;
struct list_head *plist;
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index c41c8d0..19bc1c2 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -1168,7 +1168,7 @@
/* need to protect from a race on closing the vma as part of
* mlx4_ib_vma_close().
*/
- down_read(&owning_mm->mmap_sem);
+ down_write(&owning_mm->mmap_sem);
for (i = 0; i < HW_BAR_COUNT; i++) {
vma = context->hw_bar_info[i].vma;
if (!vma)
@@ -1182,11 +1182,13 @@
BUG_ON(1);
}
+ context->hw_bar_info[i].vma->vm_flags &=
+ ~(VM_SHARED | VM_MAYSHARE);
/* context going to be destroyed, should not access ops any more */
context->hw_bar_info[i].vma->vm_ops = NULL;
}
- up_read(&owning_mm->mmap_sem);
+ up_write(&owning_mm->mmap_sem);
mmput(owning_mm);
put_task_struct(owning_process);
}
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index 5d73989..ae41623 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -406,7 +406,6 @@
goto err_free_mr;
mr->max_pages = max_num_sg;
-
err = mlx4_mr_enable(dev->dev, &mr->mmr);
if (err)
goto err_free_pl;
@@ -417,6 +416,7 @@
return &mr->ibmr;
err_free_pl:
+ mr->ibmr.device = pd->device;
mlx4_free_priv_pages(mr);
err_free_mr:
(void) mlx4_mr_free(dev->dev, &mr->mmr);
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index fcd04b8..fc62a7d 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -172,6 +172,8 @@
struct mlx5_ib_srq *srq;
struct mlx5_ib_wq *wq;
u16 wqe_ctr;
+ u8 roce_packet_type;
+ bool vlan_present;
u8 g;
if (qp->ibqp.srq || qp->ibqp.xrcd) {
@@ -223,7 +225,6 @@
break;
}
wc->slid = be16_to_cpu(cqe->slid);
- wc->sl = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0xf;
wc->src_qp = be32_to_cpu(cqe->flags_rqpn) & 0xffffff;
wc->dlid_path_bits = cqe->ml_path;
g = (be32_to_cpu(cqe->flags_rqpn) >> 28) & 3;
@@ -237,10 +238,22 @@
wc->pkey_index = 0;
}
- if (ll != IB_LINK_LAYER_ETHERNET)
+ if (ll != IB_LINK_LAYER_ETHERNET) {
+ wc->sl = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0xf;
return;
+ }
- switch (wc->sl & 0x3) {
+ vlan_present = cqe->l4_l3_hdr_type & 0x1;
+ roce_packet_type = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0x3;
+ if (vlan_present) {
+ wc->vlan_id = (be16_to_cpu(cqe->vlan_info)) & 0xfff;
+ wc->sl = (be16_to_cpu(cqe->vlan_info) >> 13) & 0x7;
+ wc->wc_flags |= IB_WC_WITH_VLAN;
+ } else {
+ wc->sl = 0;
+ }
+
+ switch (roce_packet_type) {
case MLX5_CQE_ROCE_L3_HEADER_TYPE_GRH:
wc->network_hdr_type = RDMA_NETWORK_IB;
break;
@@ -1117,7 +1130,12 @@
if (ucmd.reserved0 || ucmd.reserved1)
return -EINVAL;
- umem = ib_umem_get(context, ucmd.buf_addr, entries * ucmd.cqe_size,
+ /* check multiplication overflow */
+ if (ucmd.cqe_size && SIZE_MAX / ucmd.cqe_size <= entries - 1)
+ return -EINVAL;
+
+ umem = ib_umem_get(context, ucmd.buf_addr,
+ (size_t)ucmd.cqe_size * entries,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(umem)) {
err = PTR_ERR(umem);
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 5e29fbd..d7da1dc 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -1313,7 +1313,7 @@
/* need to protect from a race on closing the vma as part of
* mlx5_ib_vma_close.
*/
- down_read(&owning_mm->mmap_sem);
+ down_write(&owning_mm->mmap_sem);
list_for_each_entry_safe(vma_private, n, &context->vma_private_list,
list) {
vma = vma_private->vma;
@@ -1323,11 +1323,12 @@
/* context going to be destroyed, should
* not access ops any more.
*/
+ vma->vm_flags &= ~(VM_SHARED | VM_MAYSHARE);
vma->vm_ops = NULL;
list_del(&vma_private->list);
kfree(vma_private);
}
- up_read(&owning_mm->mmap_sem);
+ up_write(&owning_mm->mmap_sem);
mmput(owning_mm);
put_task_struct(owning_process);
}
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 0a260a0..7e466f0 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -1648,6 +1648,7 @@
MLX5_SET(mkc, mkc, access_mode, mr->access_mode);
MLX5_SET(mkc, mkc, umr_en, 1);
+ mr->ibmr.device = pd->device;
err = mlx5_core_create_mkey(dev->mdev, &mr->mmkey, in, inlen);
if (err)
goto err_destroy_psv;
@@ -1820,7 +1821,6 @@
mr->ibmr.iova = sg_dma_address(sg) + sg_offset;
mr->ibmr.length = 0;
- mr->ndescs = sg_nents;
for_each_sg(sgl, sg, sg_nents, i) {
if (unlikely(i >= mr->max_descs))
@@ -1832,6 +1832,7 @@
sg_offset = 0;
}
+ mr->ndescs = i;
if (sg_offset_p)
*sg_offset_p = sg_offset;
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index fdd1561..3cdcbfb 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -253,7 +253,11 @@
} else {
if (ucmd) {
qp->rq.wqe_cnt = ucmd->rq_wqe_count;
+ if (ucmd->rq_wqe_shift > BITS_PER_BYTE * sizeof(ucmd->rq_wqe_shift))
+ return -EINVAL;
qp->rq.wqe_shift = ucmd->rq_wqe_shift;
+ if ((1 << qp->rq.wqe_shift) / sizeof(struct mlx5_wqe_data_seg) < qp->wq_sig)
+ return -EINVAL;
qp->rq.max_gs = (1 << qp->rq.wqe_shift) / sizeof(struct mlx5_wqe_data_seg) - qp->wq_sig;
qp->rq.max_post = qp->rq.wqe_cnt;
} else {
@@ -1130,7 +1134,7 @@
ib_umem_release(sq->ubuffer.umem);
}
-static int get_rq_pas_size(void *qpc)
+static size_t get_rq_pas_size(void *qpc)
{
u32 log_page_size = MLX5_GET(qpc, qpc, log_page_size) + 12;
u32 log_rq_stride = MLX5_GET(qpc, qpc, log_rq_stride);
@@ -1146,7 +1150,8 @@
}
static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
- struct mlx5_ib_rq *rq, void *qpin)
+ struct mlx5_ib_rq *rq, void *qpin,
+ size_t qpinlen)
{
struct mlx5_ib_qp *mqp = rq->base.container_mibqp;
__be64 *pas;
@@ -1155,9 +1160,12 @@
void *rqc;
void *wq;
void *qpc = MLX5_ADDR_OF(create_qp_in, qpin, qpc);
- int inlen;
+ size_t rq_pas_size = get_rq_pas_size(qpc);
+ size_t inlen;
int err;
- u32 rq_pas_size = get_rq_pas_size(qpc);
+
+ if (qpinlen < rq_pas_size + MLX5_BYTE_OFF(create_qp_in, pas))
+ return -EINVAL;
inlen = MLX5_ST_SZ_BYTES(create_rq_in) + rq_pas_size;
in = mlx5_vzalloc(inlen);
@@ -1235,7 +1243,7 @@
}
static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
- u32 *in,
+ u32 *in, size_t inlen,
struct ib_pd *pd)
{
struct mlx5_ib_raw_packet_qp *raw_packet_qp = &qp->raw_packet_qp;
@@ -1262,7 +1270,7 @@
if (qp->rq.wqe_cnt) {
rq->base.container_mibqp = qp;
- err = create_raw_packet_qp_rq(dev, rq, in);
+ err = create_raw_packet_qp_rq(dev, rq, in, inlen);
if (err)
goto err_destroy_sq;
@@ -1753,10 +1761,15 @@
qp->flags |= MLX5_IB_QP_LSO;
}
+ if (inlen < 0) {
+ err = -EINVAL;
+ goto err;
+ }
+
if (init_attr->qp_type == IB_QPT_RAW_PACKET) {
qp->raw_packet_qp.sq.ubuffer.buf_addr = ucmd.sq_buf_addr;
raw_packet_qp_copy_info(qp, &qp->raw_packet_qp);
- err = create_raw_packet_qp(dev, qp, in, pd);
+ err = create_raw_packet_qp(dev, qp, in, inlen, pd);
} else {
err = mlx5_core_create_qp(dev->mdev, &base->mqp, in, inlen);
}
@@ -1796,6 +1809,7 @@
else if (qp->create_type == MLX5_QP_KERNEL)
destroy_qp_kernel(dev, qp);
+err:
kvfree(in);
return err;
}
@@ -2154,18 +2168,18 @@
static int ib_rate_to_mlx5(struct mlx5_ib_dev *dev, u8 rate)
{
- if (rate == IB_RATE_PORT_CURRENT) {
+ if (rate == IB_RATE_PORT_CURRENT)
return 0;
- } else if (rate < IB_RATE_2_5_GBPS || rate > IB_RATE_300_GBPS) {
- return -EINVAL;
- } else {
- while (rate != IB_RATE_2_5_GBPS &&
- !(1 << (rate + MLX5_STAT_RATE_OFFSET) &
- MLX5_CAP_GEN(dev->mdev, stat_rate_support)))
- --rate;
- }
- return rate + MLX5_STAT_RATE_OFFSET;
+ if (rate < IB_RATE_2_5_GBPS || rate > IB_RATE_300_GBPS)
+ return -EINVAL;
+
+ while (rate != IB_RATE_PORT_CURRENT &&
+ !(1 << (rate + MLX5_STAT_RATE_OFFSET) &
+ MLX5_CAP_GEN(dev->mdev, stat_rate_support)))
+ --rate;
+
+ return rate ? rate + MLX5_STAT_RATE_OFFSET : rate;
}
static int modify_raw_packet_eth_prio(struct mlx5_core_dev *dev,
@@ -2838,7 +2852,8 @@
* If we moved a kernel QP to RESET, clean up all old CQ
* entries and reinitialize the QP.
*/
- if (new_state == IB_QPS_RESET && !ibqp->uobject) {
+ if (new_state == IB_QPS_RESET &&
+ !ibqp->uobject && ibqp->qp_type != IB_QPT_XRC_TGT) {
mlx5_ib_cq_clean(recv_cq, base->mqp.qpn,
ibqp->srq ? to_msrq(ibqp->srq) : NULL);
if (send_cq != recv_cq)
diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c
index d61fd2c..5c1dbe2 100644
--- a/drivers/infiniband/hw/mlx5/srq.c
+++ b/drivers/infiniband/hw/mlx5/srq.c
@@ -243,8 +243,8 @@
{
struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct mlx5_ib_srq *srq;
- int desc_size;
- int buf_size;
+ size_t desc_size;
+ size_t buf_size;
int err;
struct mlx5_srq_attr in = {0};
__u32 max_srq_wqes = 1 << MLX5_CAP_GEN(dev->mdev, log_max_srq_sz);
@@ -268,15 +268,18 @@
desc_size = sizeof(struct mlx5_wqe_srq_next_seg) +
srq->msrq.max_gs * sizeof(struct mlx5_wqe_data_seg);
+ if (desc_size == 0 || srq->msrq.max_gs > desc_size)
+ return ERR_PTR(-EINVAL);
desc_size = roundup_pow_of_two(desc_size);
- desc_size = max_t(int, 32, desc_size);
+ desc_size = max_t(size_t, 32, desc_size);
+ if (desc_size < sizeof(struct mlx5_wqe_srq_next_seg))
+ return ERR_PTR(-EINVAL);
srq->msrq.max_avail_gather = (desc_size - sizeof(struct mlx5_wqe_srq_next_seg)) /
sizeof(struct mlx5_wqe_data_seg);
srq->msrq.wqe_shift = ilog2(desc_size);
buf_size = srq->msrq.max * desc_size;
- mlx5_ib_dbg(dev, "desc_size 0x%x, req wr 0x%x, srq size 0x%x, max_gs 0x%x, max_avail_gather 0x%x\n",
- desc_size, init_attr->attr.max_wr, srq->msrq.max, srq->msrq.max_gs,
- srq->msrq.max_avail_gather);
+ if (buf_size < desc_size)
+ return ERR_PTR(-EINVAL);
in.type = init_attr->srq_type;
if (pd->uobject)
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
index 8bef09a..2659430 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c
@@ -836,7 +836,7 @@
dev->reset_stats.type = OCRDMA_RESET_STATS;
dev->reset_stats.dev = dev;
- if (!debugfs_create_file("reset_stats", S_IRUSR, dev->dir,
+ if (!debugfs_create_file("reset_stats", 0200, dev->dir,
&dev->reset_stats, &ocrdma_dbg_ops))
goto err;
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index cedb447..228cb4c 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -150,7 +150,7 @@
.string = txselect_list,
.maxlen = MAX_ATTEN_LEN
};
-static int setup_txselect(const char *, struct kernel_param *);
+static int setup_txselect(const char *, const struct kernel_param *);
module_param_call(txselect, setup_txselect, param_get_string,
&kp_txselect, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(txselect,
@@ -6177,7 +6177,7 @@
}
/* handle the txselect parameter changing */
-static int setup_txselect(const char *str, struct kernel_param *kp)
+static int setup_txselect(const char *str, const struct kernel_param *kp)
{
struct qib_devdata *dd;
unsigned long val;
diff --git a/drivers/infiniband/sw/rdmavt/ah.c b/drivers/infiniband/sw/rdmavt/ah.c
index 16c4461..b0f09fb 100644
--- a/drivers/infiniband/sw/rdmavt/ah.c
+++ b/drivers/infiniband/sw/rdmavt/ah.c
@@ -119,7 +119,7 @@
spin_lock_irqsave(&dev->n_ahs_lock, flags);
if (dev->n_ahs_allocated == dev->dparms.props.max_ah) {
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
kfree(ah);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/infiniband/sw/rdmavt/cq.c b/drivers/infiniband/sw/rdmavt/cq.c
index 6d9904a..29dc527 100644
--- a/drivers/infiniband/sw/rdmavt/cq.c
+++ b/drivers/infiniband/sw/rdmavt/cq.c
@@ -197,7 +197,7 @@
return ERR_PTR(-EINVAL);
/* Allocate the completion queue structure. */
- cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+ cq = kzalloc_node(sizeof(*cq), GFP_KERNEL, rdi->dparms.node);
if (!cq)
return ERR_PTR(-ENOMEM);
@@ -213,7 +213,9 @@
sz += sizeof(struct ib_uverbs_wc) * (entries + 1);
else
sz += sizeof(struct ib_wc) * (entries + 1);
- wc = vmalloc_user(sz);
+ wc = udata ?
+ vmalloc_user(sz) :
+ vzalloc_node(sz, rdi->dparms.node);
if (!wc) {
ret = ERR_PTR(-ENOMEM);
goto bail_cq;
@@ -368,7 +370,9 @@
sz += sizeof(struct ib_uverbs_wc) * (cqe + 1);
else
sz += sizeof(struct ib_wc) * (cqe + 1);
- wc = vmalloc_user(sz);
+ wc = udata ?
+ vmalloc_user(sz) :
+ vzalloc_node(sz, rdi->dparms.node);
if (!wc)
return -ENOMEM;
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 39e31b2..2152c71 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -471,8 +471,6 @@
state = RESPST_ERR_LENGTH;
goto err;
}
-
- qp->resp.resid = mtu;
} else {
if (pktlen != resid) {
state = RESPST_ERR_LENGTH;
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c
index 59f37f4..ced416f 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.c
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.c
@@ -747,9 +747,8 @@
memcpy(wqe->dma.sge, ibwr->sg_list,
num_sge * sizeof(struct ib_sge));
- wqe->iova = (mask & WR_ATOMIC_MASK) ?
- atomic_wr(ibwr)->remote_addr :
- rdma_wr(ibwr)->remote_addr;
+ wqe->iova = mask & WR_ATOMIC_MASK ? atomic_wr(ibwr)->remote_addr :
+ mask & WR_READ_OR_WRITE_MASK ? rdma_wr(ibwr)->remote_addr : 0;
wqe->mask = mask;
wqe->dma.length = length;
wqe->dma.resid = length;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 335bd2c..34122c9 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -974,6 +974,19 @@
*/
priv->dev->broadcast[8] = priv->pkey >> 8;
priv->dev->broadcast[9] = priv->pkey & 0xff;
+
+ /*
+ * Update the broadcast address in the priv->broadcast object,
+ * in case it already exists, otherwise no one will do that.
+ */
+ if (priv->broadcast) {
+ spin_lock_irq(&priv->lock);
+ memcpy(priv->broadcast->mcmember.mgid.raw,
+ priv->dev->broadcast + 4,
+ sizeof(union ib_gid));
+ spin_unlock_irq(&priv->lock);
+ }
+
return 0;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 183db0c..0df7d45 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -799,6 +799,22 @@
spin_lock_irqsave(&priv->lock, flags);
if (!IS_ERR_OR_NULL(ah)) {
+ /*
+ * pathrec.dgid is used as the database key from the LLADDR,
+ * it must remain unchanged even if the SA returns a different
+ * GID to use in the AH.
+ */
+ if (memcmp(pathrec->dgid.raw, path->pathrec.dgid.raw,
+ sizeof(union ib_gid))) {
+ ipoib_dbg(
+ priv,
+ "%s got PathRec for gid %pI6 while asked for %pI6\n",
+ dev->name, pathrec->dgid.raw,
+ path->pathrec.dgid.raw);
+ memcpy(pathrec->dgid.raw, path->pathrec.dgid.raw,
+ sizeof(union ib_gid));
+ }
+
path->pathrec = *pathrec;
old_ah = path->ah;
@@ -919,8 +935,8 @@
return 0;
}
-static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
- struct net_device *dev)
+static struct ipoib_neigh *neigh_add_path(struct sk_buff *skb, u8 *daddr,
+ struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
@@ -933,7 +949,15 @@
spin_unlock_irqrestore(&priv->lock, flags);
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
- return;
+ return NULL;
+ }
+
+ /* To avoid race condition, make sure that the
+ * neigh will be added only once.
+ */
+ if (unlikely(!list_empty(&neigh->list))) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return neigh;
}
path = __path_find(dev, daddr + 4);
@@ -971,7 +995,7 @@
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_send(dev, skb, path->ah, IPOIB_QPN(daddr));
ipoib_neigh_put(neigh);
- return;
+ return NULL;
}
} else {
neigh->ah = NULL;
@@ -988,7 +1012,7 @@
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_neigh_put(neigh);
- return;
+ return NULL;
err_path:
ipoib_neigh_free(neigh);
@@ -998,6 +1022,8 @@
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_neigh_put(neigh);
+
+ return NULL;
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
@@ -1103,8 +1129,9 @@
case htons(ETH_P_TIPC):
neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (unlikely(!neigh)) {
- neigh_add_path(skb, phdr->hwaddr, dev);
- return NETDEV_TX_OK;
+ neigh = neigh_add_path(skb, phdr->hwaddr, dev);
+ if (likely(!neigh))
+ return NETDEV_TX_OK;
}
break;
case htons(ETH_P_ARP):
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index fddff40..8f79cae 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -487,6 +487,9 @@
!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags))
return -EINVAL;
+ init_completion(&mcast->done);
+ set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+
ipoib_dbg_mcast(priv, "joining MGID %pI6\n", mcast->mcmember.mgid.raw);
rec.mgid = mcast->mcmember.mgid;
@@ -645,8 +648,6 @@
if (mcast->backoff == 1 ||
time_after_eq(jiffies, mcast->delay_until)) {
/* Found the next unjoined group */
- init_completion(&mcast->done);
- set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
if (ipoib_mcast_join(dev, mcast)) {
spin_unlock_irq(&priv->lock);
return;
@@ -666,11 +667,9 @@
queue_delayed_work(priv->wq, &priv->mcast_task,
delay_until - jiffies);
}
- if (mcast) {
- init_completion(&mcast->done);
- set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+ if (mcast)
ipoib_mcast_join(dev, mcast);
- }
+
spin_unlock_irq(&priv->lock);
}
@@ -818,7 +817,10 @@
spin_lock_irqsave(&priv->lock, flags);
if (!neigh) {
neigh = ipoib_neigh_alloc(daddr, dev);
- if (neigh) {
+ /* Make sure that the neigh will be added only
+ * once to mcast list.
+ */
+ if (neigh && list_empty(&neigh->list)) {
kref_get(&mcast->ah->ref);
neigh->ah = mcast->ah;
list_add_tail(&neigh->list, &mcast->neigh_list);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 0983470..b879d21 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -2098,6 +2098,9 @@
u32 rkey, offset;
int ret;
+ if (cmd->ctx_init_done)
+ goto rdma_ctx_post;
+
if (dir == DMA_FROM_DEVICE) {
addr = cmd->write_va;
rkey = cmd->write_stag;
@@ -2125,11 +2128,15 @@
se_cmd->t_data_sg, se_cmd->t_data_nents,
offset, addr, rkey, dir);
}
+
if (ret < 0) {
isert_err("Cmd: %p failed to prepare RDMA res\n", cmd);
return ret;
}
+ cmd->ctx_init_done = true;
+
+rdma_ctx_post:
ret = rdma_rw_ctx_post(&cmd->rw, conn->qp, port_num, cqe, chain_wr);
if (ret < 0)
isert_err("Cmd: %p failed to post RDMA res\n", cmd);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index c02ada5..85bd197 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -124,6 +124,7 @@
struct rdma_rw_ctx rw;
struct work_struct comp_work;
struct scatterlist sg;
+ bool ctx_init_done;
};
static inline struct isert_cmd *tx_desc_to_cmd(struct iser_tx_desc *desc)
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 84f9185..463ea59 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -2626,9 +2626,11 @@
ret = FAST_IO_FAIL;
else
ret = FAILED;
- srp_free_req(ch, req, scmnd, 0);
- scmnd->result = DID_ABORT << 16;
- scmnd->scsi_done(scmnd);
+ if (ret == SUCCESS) {
+ srp_free_req(ch, req, scmnd, 0);
+ scmnd->result = DID_ABORT << 16;
+ scmnd->scsi_done(scmnd);
+ }
return ret;
}
@@ -3395,12 +3397,10 @@
num_online_nodes());
const int ch_end = ((node_idx + 1) * target->ch_count /
num_online_nodes());
- const int cv_start = (node_idx * ibdev->num_comp_vectors /
- num_online_nodes() + target->comp_vector)
- % ibdev->num_comp_vectors;
- const int cv_end = ((node_idx + 1) * ibdev->num_comp_vectors /
- num_online_nodes() + target->comp_vector)
- % ibdev->num_comp_vectors;
+ const int cv_start = node_idx * ibdev->num_comp_vectors /
+ num_online_nodes();
+ const int cv_end = (node_idx + 1) * ibdev->num_comp_vectors /
+ num_online_nodes();
int cpu_idx = 0;
for_each_online_cpu(cpu) {
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 29ab814..9888c9b 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -80,7 +80,7 @@
MODULE_PARM_DESC(srpt_srq_size,
"Shared receive queue (SRQ) size.");
-static int srpt_get_u64_x(char *buffer, struct kernel_param *kp)
+static int srpt_get_u64_x(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg);
}
@@ -2292,12 +2292,8 @@
}
spin_unlock_irqrestore(&ioctx->spinlock, flags);
- if (unlikely(transport_check_aborted_status(&ioctx->cmd, false)
- || WARN_ON_ONCE(state == SRPT_STATE_CMD_RSP_SENT))) {
- atomic_inc(&ch->req_lim_delta);
- srpt_abort_cmd(ioctx);
+ if (unlikely(WARN_ON_ONCE(state == SRPT_STATE_CMD_RSP_SENT)))
return;
- }
/* For read commands, transfer the data to the initiator. */
if (ioctx->cmd.data_direction == DMA_FROM_DEVICE &&
@@ -2670,7 +2666,8 @@
struct srpt_rdma_ch *ch = ioctx->ch;
unsigned long flags;
- WARN_ON(ioctx->state != SRPT_STATE_DONE);
+ WARN_ON_ONCE(ioctx->state != SRPT_STATE_DONE &&
+ !(ioctx->cmd.transport_state & CMD_T_ABORTED));
if (ioctx->n_rw_ctx) {
srpt_free_rw_ctxs(ch, ioctx);
diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c
index 766bf26..5f04b2d 100644
--- a/drivers/input/input-leds.c
+++ b/drivers/input/input-leds.c
@@ -88,6 +88,7 @@
const struct input_device_id *id)
{
struct input_leds *leds;
+ struct input_led *led;
unsigned int num_leds;
unsigned int led_code;
int led_no;
@@ -119,14 +120,13 @@
led_no = 0;
for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
- struct input_led *led = &leds->leds[led_no];
-
- led->handle = &leds->handle;
- led->code = led_code;
-
if (!input_led_info[led_code].name)
continue;
+ led = &leds->leds[led_no];
+ led->handle = &leds->handle;
+ led->code = led_code;
+
led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
dev_name(&dev->dev),
input_led_info[led_code].name);
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 7f12b65..795fa35 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -216,8 +216,10 @@
{
struct matrix_keypad *keypad = input_get_drvdata(dev);
+ spin_lock_irq(&keypad->lock);
keypad->stopped = true;
- mb();
+ spin_unlock_irq(&keypad->lock);
+
flush_work(&keypad->work.work);
/*
* matrix_keypad_scan() will leave IRQs enabled;
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index 5a57787..76bb513 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -274,9 +274,18 @@
};
MODULE_DEVICE_TABLE(i2c, qt1070_id);
+#ifdef CONFIG_OF
+static const struct of_device_id qt1070_of_match[] = {
+ { .compatible = "qt1070", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qt1070_of_match);
+#endif
+
static struct i2c_driver qt1070_driver = {
.driver = {
.name = "qt1070",
+ .of_match_table = of_match_ptr(qt1070_of_match),
.pm = &qt1070_pm_ops,
},
.id_table = qt1070_id,
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
index 3048ef3..a5e8998 100644
--- a/drivers/input/keyboard/tca8418_keypad.c
+++ b/drivers/input/keyboard/tca8418_keypad.c
@@ -189,8 +189,6 @@
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keymap[code], state);
- /* Read for next loop */
- error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®);
} while (1);
input_sync(input);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 3026c06..f8a987a 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -398,6 +398,18 @@
To compile this driver as a module, choose M here: the
module will be called keychord.
+
+config STMVL53L0X
+ tristate "STM VL53L0X Ranging Sensor"
+ depends on I2C
+ help
+ Say Y here if you want to enable the key chord driver
+ This is a Time-of-Flight (ToF) laser-ranging sensor, provide
+ the distance from obstacle.
+
+ To compile this driver as a module, choose M here: the module will
+ be called vl5310x.
+
config INPUT_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index c3af7c2..149273c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -82,3 +82,5 @@
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
+obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/
+obj-$(CONFIG_STMVL53L0X) += vl53l0x/
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 930424e..251d64c 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -521,7 +521,7 @@
if (!haptics)
return -ENOMEM;
- haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
+ haptics->overdrive_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
if (pdata) {
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
index fdcc146..82fefdf 100644
--- a/drivers/input/misc/keychord.c
+++ b/drivers/input/misc/keychord.c
@@ -276,7 +276,7 @@
size_t resid = count;
size_t key_bytes;
- if (count < sizeof(struct input_keychord))
+ if (count < sizeof(struct input_keychord) || count > PAGE_SIZE)
return -EINVAL;
keychords = kzalloc(count, GFP_KERNEL);
if (!keychords)
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index 603fc2f..12b2084 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -70,7 +70,7 @@
pwr->phys = "twl4030_pwrbutton/input0";
pwr->dev.parent = &pdev->dev;
- err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq,
+ err = devm_request_threaded_irq(&pdev->dev, irq, NULL, powerbutton_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
"twl4030_pwrbutton", pwr);
diff --git a/drivers/input/misc/vl53l0x/Makefile b/drivers/input/misc/vl53l0x/Makefile
new file mode 100644
index 0000000..af00414
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the vl53L0X drivers.
+#
+
+# Each configuration option enables a list of files.
+FEATURE_USE_CCI := false
+#FEATURE_USE_CCI := true
+
+ifeq ($(FEATURE_USE_CCI), true)
+ccflags-y += -Idrivers/input/misc/vl53l0x/inc -DCAMERA_CCI
+else
+ccflags-y += -Idrivers/input/misc/vl53l0x/inc -DSTM_TEST
+endif
+
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+obj-$(CONFIG_STMVL53L0X) += stmvl53l0x.o
+stmvl53l0x-objs := stmvl53l0x_module.o stmvl53l0x_module-i2c.o stmvl53l0x_module-cci.o src/vl53l0x_api_calibration.o src/vl53l0x_api_core.o src/vl53l0x_api_ranging.o src/vl53l0x_api_strings.o src/vl53l0x_api.o src/vl53l0x_platform.o src/vl53l0x_i2c_platform.o src/vl53l0x_port_i2c.o
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_api.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_api.h
new file mode 100644
index 0000000..a0e0e79
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_api.h
@@ -0,0 +1,1943 @@
+/*
+ * vl53l0x_api.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_API_H_
+#define _VL_API_H_
+
+#include "vl53l0x_api_strings.h"
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef _MSC_VER
+# ifdef VL_API_EXPORTS
+# define VL_API __declspec(dllexport)
+# else
+# define VL_API
+# endif
+#else
+# define VL_API
+#endif
+
+/** @defgroup VL_cut11_group VL53L0X cut1.1 Function Definition
+ * @brief VL53L0X cut1.1 Function Definition
+ * @{
+ */
+
+/** @defgroup VL_general_group VL53L0X General Functions
+ * @brief General functions and definitions
+ * @{
+ */
+
+/**
+ * @brief Return the VL53L0X PAL Implementation Version
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param pVersion Pointer to current PAL Implementation Version
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetVersion(struct VL_Version_t *pVersion);
+
+/**
+ * @brief Return the PAL Specification Version used for the current
+ * implementation.
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param pPalSpecVersion Pointer to current PAL Specification Version
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetPalSpecVersion(
+ struct VL_Version_t *pPalSpecVersion);
+
+/**
+ * @brief Reads the Product Revision for a for given Device
+ * This function can be used to distinguish cut1.0 from cut1.1.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pProductRevisionMajor Pointer to Product Revision Major
+ * for a given Device
+ * @param pProductRevisionMinor Pointer to Product Revision Minor
+ * for a given Device
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetProductRevision(struct vl_data *Dev,
+ uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor);
+
+/**
+ * @brief Reads the Device information for given Device
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pVL_DeviceInfo Pointer to current device info for a given
+ * Device
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetDeviceInfo(struct vl_data *Dev,
+ struct VL_DeviceInfo_t *pVL_DeviceInfo);
+
+/**
+ * @brief Read current status of the error register for the selected device
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pDeviceErrorStatus Pointer to current error code of the device
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetDeviceErrorStatus(struct vl_data *Dev,
+ uint8_t *pDeviceErrorStatus);
+
+/**
+ * @brief Human readable Range Status string for a given RangeStatus
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param RangeStatus The RangeStatus code as stored on
+ * @a struct VL_RangingMeasurementData_t
+ * @param pRangeStatusString The returned RangeStatus string.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetRangeStatusString(uint8_t RangeStatus,
+ char *pRangeStatusString);
+
+/**
+ * @brief Human readable error string for a given Error Code
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param ErrorCode The error code as stored on ::uint8_t
+ * @param pDeviceErrorString The error string corresponding to the ErrorCode
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetDeviceErrorString(
+ uint8_t ErrorCode, char *pDeviceErrorString);
+
+/**
+ * @brief Human readable error string for current PAL error status
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param PalErrorCode The error code as stored on @a int8_t
+ * @param pPalErrorString The error string corresponding to the
+ * PalErrorCode
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetPalErrorString(int8_t PalErrorCode,
+ char *pPalErrorString);
+
+/**
+ * @brief Human readable PAL State string
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param PalStateCode The State code as stored on @a uint8_t
+ * @param pPalStateString The State string corresponding to the
+ * PalStateCode
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetPalStateString(uint8_t PalStateCode,
+ char *pPalStateString);
+
+/**
+ * @brief Reads the internal state of the PAL for a given Device
+ *
+ * @note This function doesn't access to the device
+ *
+ * @param Dev Device Handle
+ * @param pPalState Pointer to current state of the PAL for a
+ * given Device
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetPalState(struct vl_data *Dev,
+ uint8_t *pPalState);
+
+/**
+ * @brief Set the power mode for a given Device
+ * The power mode can be Standby or Idle. Different level of both Standby and
+ * Idle can exists.
+ * This function should not be used when device is in Ranging state.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param PowerMode The value of the power mode to set.
+ * see ::uint8_t
+ * Valid values are:
+ * VL_POWERMODE_STANDBY_LEVEL1,
+ * VL_POWERMODE_IDLE_LEVEL1
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_MODE_NOT_SUPPORTED This error occurs when PowerMode
+ * is not in the supported list
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetPowerMode(struct vl_data *Dev,
+ uint8_t PowerMode);
+
+/**
+ * @brief Get the power mode for a given Device
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pPowerMode Pointer to the current value of the power
+ * mode. see ::uint8_t
+ * Valid values are:
+ * VL_POWERMODE_STANDBY_LEVEL1,
+ * VL_POWERMODE_IDLE_LEVEL1
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetPowerMode(struct vl_data *Dev,
+ uint8_t *pPowerMode);
+
+/**
+ * Set or over-hide part to part calibration offset
+ * \sa VL_DataInit() VL_GetOffsetCalibrationDataMicroMeter()
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param OffsetCalibrationDataMicroMeter Offset (microns)
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetOffsetCalibrationDataMicroMeter(
+ struct vl_data *Dev, int32_t OffsetCalibrationDataMicroMeter);
+
+/**
+ * @brief Get part to part calibration offset
+ *
+ * @par Function Description
+ * Should only be used after a successful call to @a VL_DataInit to backup
+ * device NVM value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pOffsetCalibrationDataMicroMeter Return part to part
+ * calibration offset from device (microns)
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetOffsetCalibrationDataMicroMeter(
+ struct vl_data *Dev, int32_t *pOffsetCalibrationDataMicroMeter);
+
+/**
+ * Set the linearity corrective gain
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LinearityCorrectiveGain Linearity corrective
+ * gain in x1000
+ * if value is 1000 then no modification is applied.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetLinearityCorrectiveGain(struct vl_data *Dev,
+ int16_t LinearityCorrectiveGain);
+
+/**
+ * @brief Get the linearity corrective gain
+ *
+ * @par Function Description
+ * Should only be used after a successful call to @a VL_DataInit to backup
+ * device NVM value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pLinearityCorrectiveGain Pointer to the linearity
+ * corrective gain in x1000
+ * if value is 1000 then no modification is applied.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetLinearityCorrectiveGain(struct vl_data *Dev,
+ uint16_t *pLinearityCorrectiveGain);
+
+/**
+ * Set Group parameter Hold state
+ *
+ * @par Function Description
+ * Set or remove device internal group parameter hold
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @param GroupParamHold Group parameter Hold state to be set (on/off)
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_SetGroupParamHold(struct vl_data *Dev,
+ uint8_t GroupParamHold);
+
+/**
+ * @brief Get the maximal distance for actual setup
+ * @par Function Description
+ * Device must be initialized through @a VL_SetParameters() prior calling
+ * this function.
+ *
+ * Any range value more than the value returned is to be considered as
+ * "no target detected" or
+ * "no target in detectable range"\n
+ * @warning The maximal distance depends on the setup
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @param pUpperLimitMilliMeter The maximal range limit for actual setup
+ * (in millimeter)
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_GetUpperLimitMilliMeter(struct vl_data *Dev,
+ uint16_t *pUpperLimitMilliMeter);
+
+
+/**
+ * @brief Get the Total Signal Rate
+ * @par Function Description
+ * This function will return the Total Signal Rate after a good ranging is done.
+ *
+ * @note This function access to Device
+ *
+ * @param Dev Device Handle
+ * @param pTotalSignalRate Total Signal Rate value in Mega count per second
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_GetTotalSignalRate(struct vl_data *Dev,
+ unsigned int *pTotalSignalRate);
+
+/** @} VL_general_group */
+
+/** @defgroup VL_init_group VL53L0X Init Functions
+ * @brief VL53L0X Init Functions
+ * @{
+ */
+
+/**
+ * @brief Set new device address
+ *
+ * After completion the device will answer to the new address programmed.
+ * This function should be called when several devices are used in parallel
+ * before start programming the sensor.
+ * When a single device us used, there is no need to call this function.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param DeviceAddress The new Device address
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetDeviceAddress(struct vl_data *Dev,
+ uint8_t DeviceAddress);
+
+/**
+ *
+ * @brief One time device initialization
+ *
+ * To be called once and only once after device is brought out of reset
+ * (Chip enable) and booted see @a VL_WaitDeviceBooted()
+ *
+ * @par Function Description
+ * When not used after a fresh device "power up" or reset, it may return
+ * @a #VL_ERROR_CALIBRATION_WARNING meaning wrong calibration data
+ * may have been fetched from device that can result in ranging offset error\n
+ * If application cannot execute device reset or need to run VL_DataInit
+ * multiple time then it must ensure proper offset calibration saving and
+ * restore on its own by using @a VL_GetOffsetCalibrationData() on first
+ * power up and then @a VL_SetOffsetCalibrationData() in all subsequent
+ * init.
+ * This function will change the uint8_t from VL_STATE_POWERDOWN to
+ * VL_STATE_WAIT_STATICINIT.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_DataInit(struct vl_data *Dev);
+
+/**
+ * @brief Set the tuning settings pointer
+ *
+ * This function is used to specify the Tuning settings buffer to be used
+ * for a given device. The buffer contains all the necessary data to permit
+ * the API to write tuning settings.
+ * This function permit to force the usage of either external or internal
+ * tuning settings.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pTuningSettingBuffer Pointer to tuning settings buffer.
+ * @param UseInternalTuningSettings Use internal tuning settings value.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetTuningSettingBuffer(struct vl_data *Dev,
+ uint8_t *pTuningSettingBuffer, uint8_t UseInternalTuningSettings);
+
+/**
+ * @brief Get the tuning settings pointer and the internal external switch
+ * value.
+ *
+ * This function is used to get the Tuning settings buffer pointer and the
+ * value.
+ * of the switch to select either external or internal tuning settings.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param ppTuningSettingBuffer Pointer to tuning settings buffer.
+ * @param pUseInternalTuningSettings Pointer to store Use internal tuning
+ * settings value.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetTuningSettingBuffer(struct vl_data *Dev,
+ uint8_t **ppTuningSettingBuffer, uint8_t *pUseInternalTuningSettings);
+
+/**
+ * @brief Do basic device init (and eventually patch loading)
+ * This function will change the uint8_t from
+ * VL_STATE_WAIT_STATICINIT to VL_STATE_IDLE.
+ * In this stage all default setting will be applied.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_StaticInit(struct vl_data *Dev);
+
+/**
+ * @brief Wait for device booted after chip enable (hardware standby)
+ * This function can be run only when uint8_t is VL_STATE_POWERDOWN.
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ *
+ */
+VL_API int8_t VL_WaitDeviceBooted(struct vl_data *Dev);
+
+/**
+ * @brief Do an hard reset or soft reset (depending on implementation) of the
+ * device \nAfter call of this function, device must be in same state as right
+ * after a power-up sequence.This function will change the uint8_t to
+ * VL_STATE_POWERDOWN.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_ResetDevice(struct vl_data *Dev);
+
+/** @} VL_init_group */
+
+/** @defgroup VL_parameters_group VL53L0X Parameters Functions
+ * @brief Functions used to prepare and setup the device
+ * @{
+ */
+
+/**
+ * @brief Prepare device for operation
+ * @par Function Description
+ * Update device with provided parameters
+ * @li Then start ranging operation.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pDeviceParameters Pointer to store current device parameters.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetDeviceParameters(struct vl_data *Dev,
+ const struct VL_DeviceParameters_t *pDeviceParameters);
+
+/**
+ * @brief Retrieve current device parameters
+ * @par Function Description
+ * Get actual parameters of the device
+ * @li Then start ranging operation.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pDeviceParameters Pointer to store current device parameters.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetDeviceParameters(struct vl_data *Dev,
+ struct VL_DeviceParameters_t *pDeviceParameters);
+
+/**
+ * @brief Set a new device mode
+ * @par Function Description
+ * Set device to a new mode (ranging, histogram ...)
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param DeviceMode New device mode to apply
+ * Valid values are:
+ * VL_DEVICEMODE_SINGLE_RANGING
+ * VL_DEVICEMODE_CONTINUOUS_RANGING
+ * VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING
+ * VL_DEVICEMODE_SINGLE_HISTOGRAM
+ * VL_HISTOGRAMMODE_REFERENCE_ONLY
+ * VL_HISTOGRAMMODE_RETURN_ONLY
+ * VL_HISTOGRAMMODE_BOTH
+ *
+ *
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_MODE_NOT_SUPPORTED This error occurs when DeviceMode is
+ * not in the supported list
+ */
+VL_API int8_t VL_SetDeviceMode(struct vl_data *Dev,
+ uint8_t DeviceMode);
+
+/**
+ * @brief Get current new device mode
+ * @par Function Description
+ * Get actual mode of the device(ranging, histogram ...)
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pDeviceMode Pointer to current apply mode value
+ * Valid values are:
+ * VL_DEVICEMODE_SINGLE_RANGING
+ * VL_DEVICEMODE_CONTINUOUS_RANGING
+ * VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING
+ * VL_DEVICEMODE_SINGLE_HISTOGRAM
+ * VL_HISTOGRAMMODE_REFERENCE_ONLY
+ * VL_HISTOGRAMMODE_RETURN_ONLY
+ * VL_HISTOGRAMMODE_BOTH
+ *
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_MODE_NOT_SUPPORTED This error occurs when
+ * DeviceMode is not in the supported list
+ */
+VL_API int8_t VL_GetDeviceMode(struct vl_data *Dev,
+ uint8_t *pDeviceMode);
+
+/**
+ * @brief Sets the resolution of range measurements.
+ * @par Function Description
+ * Set resolution of range measurements to either 0.25mm if
+ * fraction enabled or 1mm if not enabled.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param Enable Enable high resolution
+ *
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetRangeFractionEnable(struct vl_data *Dev,
+ uint8_t Enable);
+
+/**
+ * @brief Gets the fraction enable parameter indicating the resolution of
+ * range measurements.
+ *
+ * @par Function Description
+ * Gets the fraction enable state, which translates to the resolution of
+ * range measurements as follows :Enabled:=0.25mm resolution,
+ * Not Enabled:=1mm resolution.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param pEnable Output Parameter reporting the fraction enable state.
+ *
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetFractionEnable(struct vl_data *Dev,
+ uint8_t *pEnable);
+
+/**
+ * @brief Set a new Histogram mode
+ * @par Function Description
+ * Set device to a new Histogram mode
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param HistogramMode New device mode to apply
+ * Valid values are:
+ * VL_HISTOGRAMMODE_DISABLED
+ * struct vl_data *ICEMODE_SINGLE_HISTOGRAM
+ * VL_HISTOGRAMMODE_REFERENCE_ONLY
+ * VL_HISTOGRAMMODE_RETURN_ONLY
+ * VL_HISTOGRAMMODE_BOTH
+ *
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_MODE_NOT_SUPPORTED This error occurs when
+ * HistogramMode is not in the supported list
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetHistogramMode(struct vl_data *Dev,
+ uint8_t HistogramMode);
+
+/**
+ * @brief Get current new device mode
+ * @par Function Description
+ * Get current Histogram mode of a Device
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pHistogramMode Pointer to current Histogram Mode value
+ * Valid values are:
+ * VL_HISTOGRAMMODE_DISABLED
+ * struct vl_data *ICEMODE_SINGLE_HISTOGRAM
+ * VL_HISTOGRAMMODE_REFERENCE_ONLY
+ * VL_HISTOGRAMMODE_RETURN_ONLY
+ * VL_HISTOGRAMMODE_BOTH
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetHistogramMode(struct vl_data *Dev,
+ uint8_t *pHistogramMode);
+
+/**
+ * @brief Set Ranging Timing Budget in microseconds
+ *
+ * @par Function Description
+ * Defines the maximum time allowed by the user to the device to run a
+ * full ranging sequence for the current mode (ranging, histogram, ASL ...)
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param MeasurementTimingBudgetMicroSeconds Max measurement time in
+ * microseconds.
+ * Valid values are:
+ * >= 17000 microsecs when wraparound enabled
+ * >= 12000 microsecs when wraparound disabled
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned if
+ MeasurementTimingBudgetMicroSeconds out of range
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetMeasurementTimingBudgetMicroSeconds(
+ struct vl_data *Dev, uint32_t MeasurementTimingBudgetMicroSeconds);
+
+/**
+ * @brief Get Ranging Timing Budget in microseconds
+ *
+ * @par Function Description
+ * Returns the programmed the maximum time allowed by the user to the
+ * device to run a full ranging sequence for the current mode
+ * (ranging, histogram, ASL ...)
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pMeasurementTimingBudgetMicroSeconds Max measurement time in
+ * microseconds.
+ * Valid values are:
+ * >= 17000 microsecs when wraparound enabled
+ * >= 12000 microsecs when wraparound disabled
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetMeasurementTimingBudgetMicroSeconds(
+ struct vl_data *Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds);
+
+/**
+ * @brief Gets the VCSEL pulse period.
+ *
+ * @par Function Description
+ * This function retrieves the VCSEL pulse period for the given period type.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param VcselPeriodType VCSEL period identifier (pre-range|final).
+ * @param pVCSELPulsePeriod Pointer to VCSEL period value.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS Error VcselPeriodType parameter not
+ * supported.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetVcselPulsePeriod(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t *pVCSELPulsePeriod);
+
+/**
+ * @brief Sets the VCSEL pulse period.
+ *
+ * @par Function Description
+ * This function retrieves the VCSEL pulse period for the given period type.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param VcselPeriodType VCSEL period identifier (pre-range|final).
+ * @param VCSELPulsePeriod VCSEL period value
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS Error VcselPeriodType parameter not
+ * supported.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetVcselPulsePeriod(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t VCSELPulsePeriod);
+
+/**
+ * @brief Sets the (on/off) state of a requested sequence step.
+ *
+ * @par Function Description
+ * This function enables/disables a requested sequence step.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param SequenceStepId Sequence step identifier.
+ * @param SequenceStepEnabled Demanded state {0=Off,1=On}
+ * is enabled.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS Error SequenceStepId parameter not
+ * supported.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetSequenceStepEnable(struct vl_data *Dev,
+ uint8_t SequenceStepId, uint8_t SequenceStepEnabled);
+
+/**
+ * @brief Gets the (on/off) state of a requested sequence step.
+ *
+ * @par Function Description
+ * This function retrieves the state of a requested sequence step, i.e. on/off.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param SequenceStepId Sequence step identifier.
+ * @param pSequenceStepEnabled Out parameter reporting if the sequence step
+ * is enabled {0=Off,1=On}.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS Error SequenceStepId parameter not
+ * supported.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetSequenceStepEnable(struct vl_data *Dev,
+ uint8_t SequenceStepId, uint8_t *pSequenceStepEnabled);
+
+/**
+ * @brief Gets the (on/off) state of all sequence steps.
+ *
+ * @par Function Description
+ * This function retrieves the state of all sequence step in the scheduler.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param pSchedulerSequenceSteps Pointer to struct containing result.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetSequenceStepEnables(struct vl_data *Dev,
+ struct VL_SchedulerSequenceSteps_t *pSchedulerSequenceSteps);
+
+/**
+ * @brief Sets the timeout of a requested sequence step.
+ *
+ * @par Function Description
+ * This function sets the timeout of a requested sequence step.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param SequenceStepId Sequence step identifier.
+ * @param TimeOutMilliSecs Demanded timeout
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS Error SequenceStepId parameter not
+ * supported.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetSequenceStepTimeout(struct vl_data *Dev,
+ uint8_t SequenceStepId, unsigned int TimeOutMilliSecs);
+
+/**
+ * @brief Gets the timeout of a requested sequence step.
+ *
+ * @par Function Description
+ * This function retrieves the timeout of a requested sequence step.
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param SequenceStepId Sequence step identifier.
+ * @param pTimeOutMilliSecs Timeout value.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS Error SequenceStepId parameter not
+ * supported.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetSequenceStepTimeout(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ unsigned int *pTimeOutMilliSecs);
+
+/**
+ * @brief Gets number of sequence steps managed by the API.
+ *
+ * @par Function Description
+ * This function retrieves the number of sequence steps currently managed
+ * by the API
+ *
+ * @note This function Accesses the device
+ *
+ * @param Dev Device Handle
+ * @param pNumberOfSequenceSteps Out parameter reporting the number of
+ * sequence steps.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetNumberOfSequenceSteps(struct vl_data *Dev,
+ uint8_t *pNumberOfSequenceSteps);
+
+/**
+ * @brief Gets the name of a given sequence step.
+ *
+ * @par Function Description
+ * This function retrieves the name of sequence steps corresponding to
+ * SequenceStepId.
+ *
+ * @note This function doesn't Accesses the device
+ *
+ * @param SequenceStepId Sequence step identifier.
+ * @param pSequenceStepsString Pointer to Info string
+ *
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetSequenceStepsInfo(
+ uint8_t SequenceStepId, char *pSequenceStepsString);
+
+/**
+ * Program continuous mode Inter-Measurement period in milliseconds
+ *
+ * @par Function Description
+ * When trying to set too short time return INVALID_PARAMS minimal value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param InterMeasurementPeriodMilliSeconds Inter-Measurement Period in ms.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetInterMeasurementPeriodMilliSeconds(
+ struct vl_data *Dev, uint32_t InterMeasurementPeriodMilliSeconds);
+
+/**
+ * Get continuous mode Inter-Measurement period in milliseconds
+ *
+ * @par Function Description
+ * When trying to set too short time return INVALID_PARAMS minimal value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pInterMeasurementPeriodMilliSeconds Pointer to programmed
+ * Inter-Measurement Period in milliseconds.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetInterMeasurementPeriodMilliSeconds(
+ struct vl_data *Dev, uint32_t *pInterMeasurementPeriodMilliSeconds);
+
+/**
+ * @brief Enable/Disable Cross talk compensation feature
+ *
+ * @note This function is not Implemented.
+ * Enable/Disable Cross Talk by set to zero the Cross Talk value
+ * by using @a VL_SetXTalkCompensationRateMegaCps().
+ *
+ * @param Dev Device Handle
+ * @param XTalkCompensationEnable Cross talk compensation
+ * to be set 0=disabled else = enabled
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_SetXTalkCompensationEnable(struct vl_data *Dev,
+ uint8_t XTalkCompensationEnable);
+
+/**
+ * @brief Get Cross talk compensation rate
+ *
+ * @note This function is not Implemented.
+ * Enable/Disable Cross Talk by set to zero the Cross Talk value by
+ * using @a VL_SetXTalkCompensationRateMegaCps().
+ *
+ * @param Dev Device Handle
+ * @param pXTalkCompensationEnable Pointer to the Cross talk compensation
+ * state 0=disabled or 1 = enabled
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_GetXTalkCompensationEnable(struct vl_data *Dev,
+ uint8_t *pXTalkCompensationEnable);
+
+/**
+ * @brief Set Cross talk compensation rate
+ *
+ * @par Function Description
+ * Set Cross talk compensation rate.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param XTalkCompensationRateMegaCps Compensation rate in
+ * Mega counts per second (16.16 fix point) see datasheet for details
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetXTalkCompensationRateMegaCps(
+ struct vl_data *Dev, unsigned int XTalkCompensationRateMegaCps);
+
+/**
+ * @brief Get Cross talk compensation rate
+ *
+ * @par Function Description
+ * Get Cross talk compensation rate.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pXTalkCompensationRateMegaCps Pointer to Compensation rate
+ in Mega counts per second (16.16 fix point) see datasheet for details
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetXTalkCompensationRateMegaCps(
+ struct vl_data *Dev, unsigned int *pXTalkCompensationRateMegaCps);
+
+/**
+ * @brief Set Reference Calibration Parameters
+ *
+ * @par Function Description
+ * Set Reference Calibration Parameters.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param VhvSettings Parameter for VHV
+ * @param PhaseCal Parameter for PhaseCal
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetRefCalibration(struct vl_data *Dev,
+ uint8_t VhvSettings, uint8_t PhaseCal);
+
+/**
+ * @brief Get Reference Calibration Parameters
+ *
+ * @par Function Description
+ * Get Reference Calibration Parameters.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pVhvSettings Pointer to VHV parameter
+ * @param pPhaseCal Pointer to PhaseCal Parameter
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetRefCalibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal);
+
+/**
+ * @brief Get the number of the check limit managed by a given Device
+ *
+ * @par Function Description
+ * This function give the number of the check limit managed by the Device
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param pNumberOfLimitCheck Pointer to the number of check limit.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetNumberOfLimitCheck(
+ uint16_t *pNumberOfLimitCheck);
+
+/**
+ * @brief Return a description string for a given limit check number
+ *
+ * @par Function Description
+ * This function returns a description string for a given limit check number.
+ * The limit check is identified with the LimitCheckId.
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param pLimitCheckString Pointer to the
+ description string of the given check limit.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is
+ returned when LimitCheckId value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetLimitCheckInfo(struct vl_data *Dev,
+ uint16_t LimitCheckId, char *pLimitCheckString);
+
+/**
+ * @brief Return a the Status of the specified check limit
+ *
+ * @par Function Description
+ * This function returns the Status of the specified check limit.
+ * The value indicate if the check is fail or not.
+ * The limit check is identified with the LimitCheckId.
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param pLimitCheckStatus Pointer to the
+ Limit Check Status of the given check limit.
+ * LimitCheckStatus :
+ * 0 the check is not fail
+ * 1 the check if fail or not enabled
+ *
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is
+ returned when LimitCheckId value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetLimitCheckStatus(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t *pLimitCheckStatus);
+
+/**
+ * @brief Enable/Disable a specific limit check
+ *
+ * @par Function Description
+ * This function Enable/Disable a specific limit check.
+ * The limit check is identified with the LimitCheckId.
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ * (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param LimitCheckEnable if 1 the check limit
+ * corresponding to LimitCheckId is Enabled
+ * if 0 the check limit
+ * corresponding to LimitCheckId is disabled
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned
+ * when LimitCheckId value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetLimitCheckEnable(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t LimitCheckEnable);
+
+/**
+ * @brief Get specific limit check enable state
+ *
+ * @par Function Description
+ * This function get the enable state of a specific limit check.
+ * The limit check is identified with the LimitCheckId.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ * (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param pLimitCheckEnable Pointer to the check limit enable
+ * value.
+ * if 1 the check limit
+ * corresponding to LimitCheckId is Enabled
+ * if 0 the check limit
+ * corresponding to LimitCheckId is disabled
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned
+ * when LimitCheckId value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetLimitCheckEnable(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t *pLimitCheckEnable);
+
+/**
+ * @brief Set a specific limit check value
+ *
+ * @par Function Description
+ * This function set a specific limit check value.
+ * The limit check is identified with the LimitCheckId.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ * (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param LimitCheckValue Limit check Value for a given
+ * LimitCheckId
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned when either
+ * LimitCheckId or LimitCheckValue value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetLimitCheckValue(struct vl_data *Dev,
+ uint16_t LimitCheckId, unsigned int LimitCheckValue);
+
+/**
+ * @brief Get a specific limit check value
+ *
+ * @par Function Description
+ * This function get a specific limit check value from device then it updates
+ * internal values and check enables.
+ * The limit check is identified with the LimitCheckId.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ * (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param pLimitCheckValue Pointer to Limit
+ * check Value for a given LimitCheckId.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned
+ * when LimitCheckId value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetLimitCheckValue(struct vl_data *Dev,
+ uint16_t LimitCheckId, unsigned int *pLimitCheckValue);
+
+/**
+ * @brief Get the current value of the signal used for the limit check
+ *
+ * @par Function Description
+ * This function get a the current value of the signal used for the limit check.
+ * To obtain the latest value you should run a ranging before.
+ * The value reported is linked to the limit check identified with the
+ * LimitCheckId.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param LimitCheckId Limit Check ID
+ * (0<= LimitCheckId < VL_GetNumberOfLimitCheck() ).
+ * @param pLimitCheckCurrent Pointer to current Value for a
+ * given LimitCheckId.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned when
+ * LimitCheckId value is out of range.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetLimitCheckCurrent(struct vl_data *Dev,
+ uint16_t LimitCheckId, unsigned int *pLimitCheckCurrent);
+
+/**
+ * @brief Enable (or disable) Wrap around Check
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param WrapAroundCheckEnable Wrap around Check to be set
+ * 0=disabled, other = enabled
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetWrapAroundCheckEnable(struct vl_data *Dev,
+ uint8_t WrapAroundCheckEnable);
+
+/**
+ * @brief Get setup of Wrap around Check
+ *
+ * @par Function Description
+ * This function get the wrapAround check enable parameters
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pWrapAroundCheckEnable Pointer to the Wrap around Check state
+ * 0=disabled or 1 = enabled
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetWrapAroundCheckEnable(struct vl_data *Dev,
+ uint8_t *pWrapAroundCheckEnable);
+
+/**
+ * @brief Set Dmax Calibration Parameters for a given device
+ * When one of the parameter is zero, this function will get parameter
+ * from NVM.
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param RangeMilliMeter Calibration Distance
+ * @param SignalRateRtnMegaCps Signal rate return read at CalDistance
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetDmaxCalParameters(struct vl_data *Dev,
+ uint16_t RangeMilliMeter, unsigned int SignalRateRtnMegaCps);
+
+/**
+ * @brief Get Dmax Calibration Parameters for a given device
+ *
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pRangeMilliMeter Pointer to Calibration Distance
+ * @param pSignalRateRtnMegaCps Pointer to Signal rate return
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetDmaxCalParameters(struct vl_data *Dev,
+ uint16_t *pRangeMilliMeter, unsigned int *pSignalRateRtnMegaCps);
+
+/** @} VL_parameters_group */
+
+/** @defgroup VL_measurement_group VL53L0X Measurement Functions
+ * @brief Functions used for the measurements
+ * @{
+ */
+
+/**
+ * @brief Single shot measurement.
+ *
+ * @par Function Description
+ * Perform simple measurement sequence (Start measure, Wait measure to end,
+ * and returns when measurement is done).
+ * Once function returns, user can get valid data by calling
+ * VL_GetRangingMeasurement or VL_GetHistogramMeasurement
+ * depending on defined measurement mode
+ * User should Clear the interrupt in case this are enabled by using the
+ * function VL_ClearInterruptMask().
+ *
+ * @warning This function is a blocking function
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformSingleMeasurement(struct vl_data *Dev);
+
+/**
+ * @brief Perform Reference Calibration
+ *
+ * @details Perform a reference calibration of the Device.
+ * This function should be run from time to time before doing
+ * a ranging measurement.
+ * This function will launch a special ranging measurement, so
+ * if interrupt are enable an interrupt will be done.
+ * This function will clear the interrupt generated automatically.
+ *
+ * @warning This function is a blocking function
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pVhvSettings Pointer to vhv settings parameter.
+ * @param pPhaseCal Pointer to PhaseCal parameter.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformRefCalibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal);
+
+/**
+ * @brief Perform XTalk Measurement
+ *
+ * @details Measures the current cross talk from glass in front
+ * of the sensor.
+ * This functions performs a histogram measurement and uses the results
+ * to measure the crosstalk. For the function to be successful, there
+ * must be no target in front of the sensor.
+ *
+ * @warning This function is a blocking function
+ *
+ * @warning This function is not supported when the final range
+ * vcsel clock period is set below 10 PCLKS.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param TimeoutMs Histogram measurement duration.
+ * @param pXtalkPerSpad Output parameter containing the crosstalk
+ * measurement result, in MCPS/Spad. Format fixpoint 16:16.
+ * @param pAmbientTooHigh Output parameter which indicate that
+ * pXtalkPerSpad is not good if the Ambient is too high.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS vcsel clock period not supported
+ * for this operation. Must not be less than 10PCLKS.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformXTalkMeasurement(struct vl_data *Dev,
+ uint32_t TimeoutMs, unsigned int *pXtalkPerSpad,
+ uint8_t *pAmbientTooHigh);
+
+/**
+ * @brief Perform XTalk Calibration
+ *
+ * @details Perform a XTalk calibration of the Device.
+ * This function will launch a ranging measurement, if interrupts
+ * are enabled an interrupt will be done.
+ * This function will clear the interrupt generated automatically.
+ * This function will program a new value for the XTalk compensation
+ * and it will enable the cross talk before exit.
+ * This function will disable the VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD.
+ *
+ * @warning This function is a blocking function
+ *
+ * @note This function Access to the device
+ *
+ * @note This function change the device mode to
+ * struct vl_data *ICEMODE_SINGLE_RANGING
+ *
+ * @param Dev Device Handle
+ * @param XTalkCalDistance XTalkCalDistance value used for the XTalk
+ * computation.
+ * @param pXTalkCompensationRateMegaCps Pointer to new
+ * XTalkCompensation value.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformXTalkCalibration(struct vl_data *Dev,
+ unsigned int XTalkCalDistance,
+ unsigned int *pXTalkCompensationRateMegaCps);
+
+/**
+ * @brief Perform Offset Calibration
+ *
+ * @details Perform a Offset calibration of the Device.
+ * This function will launch a ranging measurement, if interrupts are
+ * enabled an interrupt will be done.
+ * This function will clear the interrupt generated automatically.
+ * This function will program a new value for the Offset calibration value
+ * This function will disable the VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD.
+ *
+ * @warning This function is a blocking function
+ *
+ * @note This function Access to the device
+ *
+ * @note This function does not change the device mode.
+ *
+ * @param Dev Device Handle
+ * @param CalDistanceMilliMeter Calibration distance value used for the
+ * offset compensation.
+ * @param pOffsetMicroMeter Pointer to new Offset value computed by the
+ * function.
+ *
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformOffsetCalibration(struct vl_data *Dev,
+ unsigned int CalDistanceMilliMeter, int32_t *pOffsetMicroMeter);
+
+/**
+ * @brief Start device measurement
+ *
+ * @details Started measurement will depend on device parameters set through
+ * @a VL_SetParameters()
+ * This is a non-blocking function.
+ * This function will change the uint8_t from VL_STATE_IDLE to
+ * VL_STATE_RUNNING.
+ *
+ * @note This function Access to the device
+ *
+
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_MODE_NOT_SUPPORTED This error occurs when
+ * DeviceMode programmed with @a VL_SetDeviceMode is not in the supported
+ * list:
+ * Supported mode are:
+ * struct vl_data *ICEMODE_SINGLE_RANGING,
+ * struct vl_data *ICEMODE_CONTINUOUS_RANGING,
+ * struct vl_data *ICEMODE_CONTINUOUS_TIMED_RANGING
+ * @return VL_ERROR_TIME_OUT Time out on start measurement
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_StartMeasurement(struct vl_data *Dev);
+
+/**
+ * @brief Stop device measurement
+ *
+ * @details Will set the device in standby mode at end of current measurement\n
+ * Not necessary in single mode as device shall return automatically
+ * in standby mode at end of measurement.
+ * This function will change the uint8_t from
+ * VL_STATE_RUNNING to VL_STATE_IDLE.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_StopMeasurement(struct vl_data *Dev);
+
+/**
+ * @brief Return Measurement Data Ready
+ *
+ * @par Function Description
+ * This function indicate that a measurement data is ready.
+ * This function check if interrupt mode is used then check is done accordingly.
+ * If perform function clear the interrupt, this function will not work,
+ * like in case of @a VL_PerformSingleRangingMeasurement().
+ * The previous function is blocking function, VL_GetMeasurementDataReady
+ * is used for non-blocking capture.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pMeasurementDataReady Pointer to Measurement Data Ready.
+ * 0=data not ready, 1 = data ready
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetMeasurementDataReady(struct vl_data *Dev,
+ uint8_t *pMeasurementDataReady);
+
+/**
+ * @brief Wait for device ready for a new measurement command.
+ * Blocking function.
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @param MaxLoop Max Number of polling loop (timeout).
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_WaitDeviceReadyForNewMeasurement(
+ struct vl_data *Dev, uint32_t MaxLoop);
+
+/**
+ * @brief Retrieve the Reference Signal after a measurements
+ *
+ * @par Function Description
+ * Get Reference Signal from last successful Ranging measurement
+ * This function return a valid value after that you call the
+ * @a VL_GetRangingMeasurementData().
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pMeasurementRefSignal Pointer to the Ref Signal to fill up.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetMeasurementRefSignal(struct vl_data *Dev,
+ unsigned int *pMeasurementRefSignal);
+
+/**
+ * @brief Retrieve the measurements from device for a given setup
+ *
+ * @par Function Description
+ * Get data from last successful Ranging measurement
+ * @warning USER should take care about @a VL_GetNumberOfROIZones()
+ * before get data.
+ * PAL will fill a NumberOfROIZones times the corresponding data
+ * structure used in the measurement function.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pRangingMeasurementData Pointer to the data structure to fill up.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetRangingMeasurementData(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData);
+
+/**
+ * @brief Retrieve the measurements from device for a given setup
+ *
+ * @par Function Description
+ * Get data from last successful Histogram measurement
+ * @warning USER should take care about @a VL_GetNumberOfROIZones()
+ * before get data.
+ * PAL will fill a NumberOfROIZones times the corresponding data structure
+ * used in the measurement function.
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @param pHistogramMeasurementData Pointer to the histogram data structure.
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_GetHistogramMeasurementData(struct vl_data *Dev,
+ struct VL_HistogramMeasurementData_t *pHistogramMeasurementData);
+
+/**
+ * @brief Performs a single ranging measurement and retrieve the ranging
+ * measurement data
+ *
+ * @par Function Description
+ * This function will change the device mode to
+ * struct vl_data *ICEMODE_SINGLE_RANGING with @a VL_SetDeviceMode(),
+ * It performs measurement with @a VL_PerformSingleMeasurement()
+ * It get data from last successful Ranging measurement with
+ * @a VL_GetRangingMeasurementData.
+ * Finally it clear the interrupt with @a VL_ClearInterruptMask().
+ *
+ * @note This function Access to the device
+ *
+ * @note This function change the device mode to
+ * struct vl_data *ICEMODE_SINGLE_RANGING
+ *
+ * @param Dev Device Handle
+ * @param pRangingMeasurementData Pointer to the data structure to fill up.
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformSingleRangingMeasurement(
+ struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData);
+
+/**
+ * @brief Performs a single histogram measurement and retrieve the histogram
+ * measurement data
+ * Is equivalent to VL_PerformSingleMeasurement +
+ * VL_GetHistogramMeasurementData
+ *
+ * @par Function Description
+ * Get data from last successful Ranging measurement.
+ * This function will clear the interrupt in case of these are enabled.
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @param pHistogramMeasurementData Pointer to the data structure to fill up.
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_PerformSingleHistogramMeasurement(
+ struct vl_data *Dev,
+ struct VL_HistogramMeasurementData_t *pHistogramMeasurementData);
+
+/**
+ * @brief Set the number of ROI Zones to be used for a specific Device
+ *
+ * @par Function Description
+ * Set the number of ROI Zones to be used for a specific Device.
+ * The programmed value should be less than the max number of ROI Zones given
+ * with @a VL_GetMaxNumberOfROIZones().
+ * This version of API manage only one zone.
+ *
+ * @param Dev Device Handle
+ * @param NumberOfROIZones Number of ROI Zones to be used for a
+ * specific Device.
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INVALID_PARAMS This error is returned if
+ * NumberOfROIZones != 1
+ */
+VL_API int8_t VL_SetNumberOfROIZones(struct vl_data *Dev,
+ uint8_t NumberOfROIZones);
+
+/**
+ * @brief Get the number of ROI Zones managed by the Device
+ *
+ * @par Function Description
+ * Get number of ROI Zones managed by the Device
+ * USER should take care about @a VL_GetNumberOfROIZones()
+ * before get data after a perform measurement.
+ * PAL will fill a NumberOfROIZones times the corresponding data
+ * structure used in the measurement function.
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pNumberOfROIZones Pointer to the Number of ROI Zones value.
+ * @return VL_ERROR_NONE Success
+ */
+VL_API int8_t VL_GetNumberOfROIZones(struct vl_data *Dev,
+ uint8_t *pNumberOfROIZones);
+
+/**
+ * @brief Get the Maximum number of ROI Zones managed by the Device
+ *
+ * @par Function Description
+ * Get Maximum number of ROI Zones managed by the Device.
+ *
+ * @note This function doesn't Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pMaxNumberOfROIZones Pointer to the Maximum Number
+ * of ROI Zones value.
+ * @return VL_ERROR_NONE Success
+ */
+VL_API int8_t VL_GetMaxNumberOfROIZones(struct vl_data *Dev,
+ uint8_t *pMaxNumberOfROIZones);
+
+/** @} VL_measurement_group */
+
+/** @defgroup VL_interrupt_group VL53L0X Interrupt Functions
+ * @brief Functions used for interrupt managements
+ * @{
+ */
+
+/**
+ * @brief Set the configuration of GPIO pin for a given device
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param Pin ID of the GPIO Pin
+ * @param Functionality Select Pin functionality.
+ * Refer to ::uint8_t
+ * @param DeviceMode Device Mode associated to the Gpio.
+ * @param Polarity Set interrupt polarity. Active high
+ * or active low see ::uint8_t
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted.
+ * @return VL_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs
+ * when Functionality programmed is not in the supported list:
+ * Supported value are:
+ * VL_GPIOFUNCTIONALITY_OFF,
+ * VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW,
+ * VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH,
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT,
+ * VL_GPIOFUNCTIONALITY_NEW_MEASURE_READY
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetGpioConfig(struct vl_data *Dev, uint8_t Pin,
+ uint8_t DeviceMode, uint8_t Functionality,
+ uint8_t Polarity);
+
+/**
+ * @brief Get current configuration for GPIO pin for a given device
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param Pin ID of the GPIO Pin
+ * @param pDeviceMode Pointer to Device Mode associated to the Gpio.
+ * @param pFunctionality Pointer to Pin functionality.
+ * Refer to ::uint8_t
+ * @param pPolarity Pointer to interrupt polarity.
+ * Active high or active low see ::uint8_t
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted.
+ * @return VL_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs
+ * when Functionality programmed is not in the supported list:
+ * Supported value are:
+ * VL_GPIOFUNCTIONALITY_OFF,
+ * VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW,
+ * VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH,
+ * VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT,
+ * VL_GPIOFUNCTIONALITY_NEW_MEASURE_READY
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetGpioConfig(struct vl_data *Dev, uint8_t Pin,
+ uint8_t *pDeviceMode,
+ uint8_t *pFunctionality,
+ uint8_t *pPolarity);
+
+/**
+ * @brief Set low and high Interrupt thresholds for a given mode
+ * (ranging, ALS, ...) for a given device
+ *
+ * @par Function Description
+ * Set low and high Interrupt thresholds for a given mode (ranging, ALS, ...)
+ * for a given device
+ *
+ * @note This function Access to the device
+ *
+ * @note DeviceMode is ignored for the current device
+ *
+ * @param Dev Device Handle
+ * @param DeviceMode Device Mode for which change thresholds
+ * @param ThresholdLow Low threshold (mm, lux ..., depending on the mode)
+ * @param ThresholdHigh High threshold (mm, lux ..., depending on the mode)
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetInterruptThresholds(struct vl_data *Dev,
+ uint8_t DeviceMode, unsigned int ThresholdLow,
+ unsigned int ThresholdHigh);
+
+/**
+ * @brief Get high and low Interrupt thresholds for a given mode
+ * (ranging, ALS, ...) for a given device
+ *
+ * @par Function Description
+ * Get high and low Interrupt thresholds for a given mode (ranging, ALS, ...)
+ * for a given device
+ *
+ * @note This function Access to the device
+ *
+ * @note DeviceMode is ignored for the current device
+ *
+ * @param Dev Device Handle
+ * @param DeviceMode Device Mode from which read thresholds
+ * @param pThresholdLow Low threshold (mm, lux ..., depending on the mode)
+ * @param pThresholdHigh High threshold (mm, lux ..., depending on the mode)
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetInterruptThresholds(struct vl_data *Dev,
+ uint8_t DeviceMode, unsigned int *pThresholdLow,
+ unsigned int *pThresholdHigh);
+
+/**
+ * @brief Return device stop completion status
+ *
+ * @par Function Description
+ * Returns stop completiob status.
+ * User shall call this function after a stop command
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pStopStatus Pointer to status variable to update
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetStopCompletedStatus(struct vl_data *Dev,
+ uint32_t *pStopStatus);
+
+
+/**
+ * @brief Clear given system interrupt condition
+ *
+ * @par Function Description
+ * Clear given interrupt(s).
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param InterruptMask Mask of interrupts to clear
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_INTERRUPT_NOT_CLEARED Cannot clear interrupts
+ *
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_ClearInterruptMask(struct vl_data *Dev,
+ uint32_t InterruptMask);
+
+/**
+ * @brief Return device interrupt status
+ *
+ * @par Function Description
+ * Returns currently raised interrupts by the device.
+ * User shall be able to activate/deactivate interrupts through
+ * @a VL_SetGpioConfig()
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pInterruptMaskStatus Pointer to status variable to update
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetInterruptMaskStatus(struct vl_data *Dev,
+ uint32_t *pInterruptMaskStatus);
+
+/**
+ * @brief Configure ranging interrupt reported to system
+ *
+ * @note This function is not Implemented
+ *
+ * @param Dev Device Handle
+ * @param InterruptMask Mask of interrupt to Enable/disable
+ * (0:interrupt disabled or 1: interrupt enabled)
+ * @return VL_ERROR_NOT_IMPLEMENTED Not implemented
+ */
+VL_API int8_t VL_EnableInterruptMask(struct vl_data *Dev,
+ uint32_t InterruptMask);
+
+/** @} VL_interrupt_group */
+
+/** @defgroup VL_SPADfunctions_group VL53L0X SPAD Functions
+ * @brief Functions used for SPAD managements
+ * @{
+ */
+
+/**
+ * @brief Set the SPAD Ambient Damper Threshold value
+ *
+ * @par Function Description
+ * This function set the SPAD Ambient Damper Threshold value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param SpadAmbientDamperThreshold SPAD Ambient Damper Threshold value
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetSpadAmbientDamperThreshold(struct vl_data *Dev,
+ uint16_t SpadAmbientDamperThreshold);
+
+/**
+ * @brief Get the current SPAD Ambient Damper Threshold value
+ *
+ * @par Function Description
+ * This function get the SPAD Ambient Damper Threshold value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pSpadAmbientDamperThreshold Pointer to programmed
+ * SPAD Ambient Damper Threshold value
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetSpadAmbientDamperThreshold(struct vl_data *Dev,
+ uint16_t *pSpadAmbientDamperThreshold);
+
+/**
+ * @brief Set the SPAD Ambient Damper Factor value
+ *
+ * @par Function Description
+ * This function set the SPAD Ambient Damper Factor value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param SpadAmbientDamperFactor SPAD Ambient Damper Factor value
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetSpadAmbientDamperFactor(struct vl_data *Dev,
+ uint16_t SpadAmbientDamperFactor);
+
+/**
+ * @brief Get the current SPAD Ambient Damper Factor value
+ *
+ * @par Function Description
+ * This function get the SPAD Ambient Damper Factor value
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param pSpadAmbientDamperFactor Pointer to programmed SPAD Ambient
+ * Damper Factor value
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetSpadAmbientDamperFactor(struct vl_data *Dev,
+ uint16_t *pSpadAmbientDamperFactor);
+
+/**
+ * @brief Performs Reference Spad Management
+ *
+ * @par Function Description
+ * The reference SPAD initialization procedure determines the minimum amount
+ * of reference spads to be enables to achieve a target reference signal rate
+ * and should be performed once during initialization.
+ *
+ * @note This function Access to the device
+ *
+ * @note This function change the device mode to
+ * struct vl_data *ICEMODE_SINGLE_RANGING
+ *
+ * @param Dev Device Handle
+ * @param refSpadCount Reports ref Spad Count
+ * @param isApertureSpads Reports if spads are of type
+ * aperture or non-aperture.
+ * 1:=aperture, 0:=Non-Aperture
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_REF_SPAD_INIT Error in the Ref Spad procedure.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_PerformRefSpadManagement(struct vl_data *Dev,
+ uint32_t *refSpadCount, uint8_t *isApertureSpads);
+
+/**
+ * @brief Applies Reference SPAD configuration
+ *
+ * @par Function Description
+ * This function applies a given number of reference spads, identified as
+ * either Aperture or Non-Aperture.
+ * The requested spad count and type are stored within the device specific
+ * parameters data for access by the host.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param refSpadCount Number of ref spads.
+ * @param isApertureSpads Defines if spads are of type
+ * aperture or non-aperture.
+ * 1:=aperture, 0:=Non-Aperture
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_REF_SPAD_INIT Error in the in the reference
+ * spad configuration.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_SetReferenceSpads(struct vl_data *Dev,
+ uint32_t refSpadCount, uint8_t isApertureSpads);
+
+/**
+ * @brief Retrieves SPAD configuration
+ *
+ * @par Function Description
+ * This function retrieves the current number of applied reference spads
+ * and also their type : Aperture or Non-Aperture.
+ *
+ * @note This function Access to the device
+ *
+ * @param Dev Device Handle
+ * @param refSpadCount Number ref Spad Count
+ * @param isApertureSpads Reports if spads are of type
+ * aperture or non-aperture.
+ * 1:=aperture, 0:=Non-Aperture
+ * @return VL_ERROR_NONE Success
+ * @return VL_ERROR_REF_SPAD_INIT Error in the in the reference
+ * spad configuration.
+ * @return "Other error code" See ::int8_t
+ */
+VL_API int8_t VL_GetReferenceSpads(struct vl_data *Dev,
+ uint32_t *refSpadCount, uint8_t *isApertureSpads);
+
+/** @} VL_SPADfunctions_group */
+
+/** @} VL_cut11_group */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VL_API_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_api_calibration.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_calibration.h
new file mode 100644
index 0000000..5964e0b
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_calibration.h
@@ -0,0 +1,75 @@
+/*
+ * vl53l0x_api_calibration.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_API_CALIBRATION_H_
+#define _VL_API_CALIBRATION_H_
+
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int8_t VL_perform_xtalk_calibration(struct vl_data *Dev,
+ unsigned int XTalkCalDistance,
+ unsigned int *pXTalkCompensationRateMegaCps);
+
+int8_t VL_perform_offset_calibration(struct vl_data *Dev,
+ unsigned int CalDistanceMilliMeter,
+ int32_t *pOffsetMicroMeter);
+
+int8_t VL_set_offset_calibration_data_micro_meter(struct vl_data *Dev,
+ int32_t OffsetCalibrationDataMicroMeter);
+
+int8_t VL_get_offset_calibration_data_micro_meter(struct vl_data *Dev,
+ int32_t *pOffsetCalibrationDataMicroMeter);
+
+int8_t VL_apply_offset_adjustment(struct vl_data *Dev);
+
+int8_t VL_perform_ref_spad_management(struct vl_data *Dev,
+ uint32_t *refSpadCount, uint8_t *isApertureSpads);
+
+int8_t VL_set_reference_spads(struct vl_data *Dev,
+ uint32_t count, uint8_t isApertureSpads);
+
+int8_t VL_get_reference_spads(struct vl_data *Dev,
+ uint32_t *pSpadCount, uint8_t *pIsApertureSpads);
+
+int8_t VL_perform_phase_calibration(struct vl_data *Dev,
+ uint8_t *pPhaseCal, const uint8_t get_data_enable,
+ const uint8_t restore_config);
+
+int8_t VL_perform_ref_calibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal, uint8_t get_data_enable);
+
+int8_t VL_set_ref_calibration(struct vl_data *Dev,
+ uint8_t VhvSettings, uint8_t PhaseCal);
+
+int8_t VL_get_ref_calibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal);
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VL_API_CALIBRATION_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_api_core.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_core.h
new file mode 100644
index 0000000..ac368cf
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_core.h
@@ -0,0 +1,98 @@
+/*
+ * vl53l0x_api_core.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_API_CORE_H_
+#define _VL_API_CORE_H_
+
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+int8_t VL_reverse_bytes(uint8_t *data, uint32_t size);
+
+int8_t VL_measurement_poll_for_completion(struct vl_data *Dev);
+
+uint8_t VL_encode_vcsel_period(uint8_t vcsel_period_pclks);
+
+uint8_t VL_decode_vcsel_period(uint8_t vcsel_period_reg);
+
+uint32_t VL_isqrt(uint32_t num);
+
+uint32_t VL_quadrature_sum(uint32_t a, uint32_t b);
+
+int8_t VL_get_info_from_device(struct vl_data *Dev, uint8_t option);
+
+int8_t VL_set_vcsel_pulse_period(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t VCSELPulsePeriodPCLK);
+
+int8_t VL_get_vcsel_pulse_period(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK);
+
+uint32_t VL_decode_timeout(uint16_t encoded_timeout);
+
+int8_t get_sequence_step_timeout(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ uint32_t *pTimeOutMicroSecs);
+
+int8_t set_sequence_step_timeout(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ uint32_t TimeOutMicroSecs);
+
+int8_t VL_set_measurement_timing_budget_micro_seconds(
+ struct vl_data *Dev, uint32_t MeasurementTimingBudgetMicroSeconds);
+
+int8_t VL_get_measurement_timing_budget_micro_seconds(
+ struct vl_data *Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds);
+
+int8_t VL_load_tuning_settings(struct vl_data *Dev,
+ uint8_t *pTuningSettingBuffer);
+
+int8_t VL_calc_sigma_estimate(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ unsigned int *pSigmaEstimate, uint32_t *pDmax_mm);
+
+int8_t VL_get_total_xtalk_rate(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ unsigned int *ptotal_xtalk_rate_mcps);
+
+int8_t VL_get_total_signal_rate(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ unsigned int *ptotal_signal_rate_mcps);
+
+int8_t VL_get_pal_range_status(struct vl_data *Dev,
+ uint8_t DeviceRangeStatus,
+ unsigned int SignalRate,
+ uint16_t EffectiveSpadRtnCount,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ uint8_t *pPalRangeStatus);
+
+uint32_t VL_calc_timeout_mclks(struct vl_data *Dev,
+ uint32_t timeout_period_us, uint8_t vcsel_period_pclks);
+
+uint16_t VL_encode_timeout(uint32_t timeout_macro_clks);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VL_API_CORE_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_api_ranging.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_ranging.h
new file mode 100644
index 0000000..e6b48fc
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_ranging.h
@@ -0,0 +1,37 @@
+/*
+ * vl53l0x_api_ranging.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_API_RANGING_H_
+#define _VL_API_RANGING_H_
+
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VL_API_RANGING_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_api_strings.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_strings.h
new file mode 100644
index 0000000..78a2dcd
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_api_strings.h
@@ -0,0 +1,268 @@
+/*
+ * vl53l0x_api_strings.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef VL_API_STRINGS_H_
+#define VL_API_STRINGS_H_
+
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+int8_t VL_get_device_info(struct vl_data *Dev,
+ struct VL_DeviceInfo_t *pVL_DeviceInfo);
+
+int8_t VL_get_device_error_string(uint8_t ErrorCode,
+ char *pDeviceErrorString);
+
+int8_t VL_get_range_status_string(uint8_t RangeStatus,
+ char *pRangeStatusString);
+
+int8_t VL_get_pal_error_string(int8_t PalErrorCode,
+ char *pPalErrorString);
+
+int8_t VL_get_pal_state_string(uint8_t PalStateCode,
+ char *pPalStateString);
+
+int8_t VL_get_sequence_steps_info(
+ uint8_t SequenceStepId,
+ char *pSequenceStepsString);
+
+int8_t VL_get_limit_check_info(struct vl_data *Dev,
+ uint16_t LimitCheckId, char *pLimitCheckString);
+
+
+#ifdef USE_EMPTY_STRING
+ #define VL_STRING_DEVICE_INFO_NAME ""
+ #define VL_STRING_DEVICE_INFO_NAME_TS0 ""
+ #define VL_STRING_DEVICE_INFO_NAME_TS1 ""
+ #define VL_STRING_DEVICE_INFO_NAME_TS2 ""
+ #define VL_STRING_DEVICE_INFO_NAME_ES1 ""
+ #define VL_STRING_DEVICE_INFO_TYPE ""
+
+ /* PAL ERROR strings */
+ #define VL_STRING_ERROR_NONE ""
+ #define VL_STRING_ERROR_CALIBRATION_WARNING ""
+ #define VL_STRING_ERROR_MIN_CLIPPED ""
+ #define VL_STRING_ERROR_UNDEFINED ""
+ #define VL_STRING_ERROR_INVALID_PARAMS ""
+ #define VL_STRING_ERROR_NOT_SUPPORTED ""
+ #define VL_STRING_ERROR_RANGE_ERROR ""
+ #define VL_STRING_ERROR_TIME_OUT ""
+ #define VL_STRING_ERROR_MODE_NOT_SUPPORTED ""
+ #define VL_STRING_ERROR_BUFFER_TOO_SMALL ""
+ #define VL_STRING_ERROR_GPIO_NOT_EXISTING ""
+ #define VL_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ""
+ #define VL_STRING_ERROR_CONTROL_INTERFACE ""
+ #define VL_STRING_ERROR_INVALID_COMMAND ""
+ #define VL_STRING_ERROR_DIVISION_BY_ZERO ""
+ #define VL_STRING_ERROR_REF_SPAD_INIT ""
+ #define VL_STRING_ERROR_NOT_IMPLEMENTED ""
+
+ #define VL_STRING_UNKNOWN_ERROR_CODE ""
+
+
+
+ /* Range Status */
+ #define VL_STRING_RANGESTATUS_NONE ""
+ #define VL_STRING_RANGESTATUS_RANGEVALID ""
+ #define VL_STRING_RANGESTATUS_SIGMA ""
+ #define VL_STRING_RANGESTATUS_SIGNAL ""
+ #define VL_STRING_RANGESTATUS_MINRANGE ""
+ #define VL_STRING_RANGESTATUS_PHASE ""
+ #define VL_STRING_RANGESTATUS_HW ""
+
+
+ /* Range Status */
+ #define VL_STRING_STATE_POWERDOWN ""
+ #define VL_STRING_STATE_WAIT_STATICINIT ""
+ #define VL_STRING_STATE_STANDBY ""
+ #define VL_STRING_STATE_IDLE ""
+ #define VL_STRING_STATE_RUNNING ""
+ #define VL_STRING_STATE_UNKNOWN ""
+ #define VL_STRING_STATE_ERROR ""
+
+
+ /* Device Specific */
+ #define VL_STRING_DEVICEERROR_NONE ""
+ #define VL_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE ""
+ #define VL_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE ""
+ #define VL_STRING_DEVICEERROR_NOVHVVALUEFOUND ""
+ #define VL_STRING_DEVICEERROR_MSRCNOTARGET ""
+ #define VL_STRING_DEVICEERROR_SNRCHECK ""
+ #define VL_STRING_DEVICEERROR_RANGEPHASECHECK ""
+ #define VL_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK ""
+ #define VL_STRING_DEVICEERROR_TCC ""
+ #define VL_STRING_DEVICEERROR_PHASECONSISTENCY ""
+ #define VL_STRING_DEVICEERROR_MINCLIP ""
+ #define VL_STRING_DEVICEERROR_RANGECOMPLETE ""
+ #define VL_STRING_DEVICEERROR_ALGOUNDERFLOW ""
+ #define VL_STRING_DEVICEERROR_ALGOOVERFLOW ""
+ #define VL_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD ""
+ #define VL_STRING_DEVICEERROR_UNKNOWN ""
+
+ /* Check Enable */
+ #define VL_STRING_CHECKENABLE_SIGMA_FINAL_RANGE ""
+ #define VL_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE ""
+ #define VL_STRING_CHECKENABLE_SIGNAL_REF_CLIP ""
+ #define VL_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD ""
+
+ /* Sequence Step */
+ #define VL_STRING_SEQUENCESTEP_TCC ""
+ #define VL_STRING_SEQUENCESTEP_DSS ""
+ #define VL_STRING_SEQUENCESTEP_MSRC ""
+ #define VL_STRING_SEQUENCESTEP_PRE_RANGE ""
+ #define VL_STRING_SEQUENCESTEP_FINAL_RANGE ""
+#else
+ #define VL_STRING_DEVICE_INFO_NAME "VL53L0X cut1.0"
+ #define VL_STRING_DEVICE_INFO_NAME_TS0 "VL53L0X TS0"
+ #define VL_STRING_DEVICE_INFO_NAME_TS1 "VL53L0X TS1"
+ #define VL_STRING_DEVICE_INFO_NAME_TS2 "VL53L0X TS2"
+ #define VL_STRING_DEVICE_INFO_NAME_ES1 "VL53L0X ES1 or later"
+ #define VL_STRING_DEVICE_INFO_TYPE "VL53L0X"
+
+ /* PAL ERROR strings */
+ #define VL_STRING_ERROR_NONE \
+ "No Error"
+ #define VL_STRING_ERROR_CALIBRATION_WARNING \
+ "Calibration Warning Error"
+ #define VL_STRING_ERROR_MIN_CLIPPED \
+ "Min clipped error"
+ #define VL_STRING_ERROR_UNDEFINED \
+ "Undefined error"
+ #define VL_STRING_ERROR_INVALID_PARAMS \
+ "Invalid parameters error"
+ #define VL_STRING_ERROR_NOT_SUPPORTED \
+ "Not supported error"
+ #define VL_STRING_ERROR_RANGE_ERROR \
+ "Range error"
+ #define VL_STRING_ERROR_TIME_OUT \
+ "Time out error"
+ #define VL_STRING_ERROR_MODE_NOT_SUPPORTED \
+ "Mode not supported error"
+ #define VL_STRING_ERROR_BUFFER_TOO_SMALL \
+ "Buffer too small"
+ #define VL_STRING_ERROR_GPIO_NOT_EXISTING \
+ "GPIO not existing"
+ #define VL_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED \
+ "GPIO funct not supported"
+ #define VL_STRING_ERROR_INTERRUPT_NOT_CLEARED \
+ "Interrupt not Cleared"
+ #define VL_STRING_ERROR_CONTROL_INTERFACE \
+ "Control Interface Error"
+ #define VL_STRING_ERROR_INVALID_COMMAND \
+ "Invalid Command Error"
+ #define VL_STRING_ERROR_DIVISION_BY_ZERO \
+ "Division by zero Error"
+ #define VL_STRING_ERROR_REF_SPAD_INIT \
+ "Reference Spad Init Error"
+ #define VL_STRING_ERROR_NOT_IMPLEMENTED \
+ "Not implemented error"
+
+ #define VL_STRING_UNKNOWN_ERROR_CODE \
+ "Unknown Error Code"
+
+
+
+ /* Range Status */
+ #define VL_STRING_RANGESTATUS_NONE "No Update"
+ #define VL_STRING_RANGESTATUS_RANGEVALID "Range Valid"
+ #define VL_STRING_RANGESTATUS_SIGMA "Sigma Fail"
+ #define VL_STRING_RANGESTATUS_SIGNAL "Signal Fail"
+ #define VL_STRING_RANGESTATUS_MINRANGE "Min Range Fail"
+ #define VL_STRING_RANGESTATUS_PHASE "Phase Fail"
+ #define VL_STRING_RANGESTATUS_HW "Hardware Fail"
+
+
+ /* Range Status */
+ #define VL_STRING_STATE_POWERDOWN "POWERDOWN State"
+ #define VL_STRING_STATE_WAIT_STATICINIT \
+ "Wait for staticinit State"
+ #define VL_STRING_STATE_STANDBY "STANDBY State"
+ #define VL_STRING_STATE_IDLE "IDLE State"
+ #define VL_STRING_STATE_RUNNING "RUNNING State"
+ #define VL_STRING_STATE_UNKNOWN "UNKNOWN State"
+ #define VL_STRING_STATE_ERROR "ERROR State"
+
+
+ /* Device Specific */
+ #define VL_STRING_DEVICEERROR_NONE "No Update"
+ #define VL_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \
+ "VCSEL Continuity Test Failure"
+ #define VL_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \
+ "VCSEL Watchdog Test Failure"
+ #define VL_STRING_DEVICEERROR_NOVHVVALUEFOUND \
+ "No VHV Value found"
+ #define VL_STRING_DEVICEERROR_MSRCNOTARGET \
+ "MSRC No Target Error"
+ #define VL_STRING_DEVICEERROR_SNRCHECK \
+ "SNR Check Exit"
+ #define VL_STRING_DEVICEERROR_RANGEPHASECHECK \
+ "Range Phase Check Error"
+ #define VL_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK \
+ "Sigma Threshold Check Error"
+ #define VL_STRING_DEVICEERROR_TCC \
+ "TCC Error"
+ #define VL_STRING_DEVICEERROR_PHASECONSISTENCY \
+ "Phase Consistency Error"
+ #define VL_STRING_DEVICEERROR_MINCLIP \
+ "Min Clip Error"
+ #define VL_STRING_DEVICEERROR_RANGECOMPLETE \
+ "Range Complete"
+ #define VL_STRING_DEVICEERROR_ALGOUNDERFLOW \
+ "Range Algo Underflow Error"
+ #define VL_STRING_DEVICEERROR_ALGOOVERFLOW \
+ "Range Algo Overlow Error"
+ #define VL_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD \
+ "Range Ignore Threshold Error"
+ #define VL_STRING_DEVICEERROR_UNKNOWN \
+ "Unknown error code"
+
+ /* Check Enable */
+ #define VL_STRING_CHECKENABLE_SIGMA_FINAL_RANGE \
+ "SIGMA FINAL RANGE"
+ #define VL_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE \
+ "SIGNAL RATE FINAL RANGE"
+ #define VL_STRING_CHECKENABLE_SIGNAL_REF_CLIP \
+ "SIGNAL REF CLIP"
+ #define VL_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD \
+ "RANGE IGNORE THRESHOLD"
+ #define VL_STRING_CHECKENABLE_SIGNAL_RATE_MSRC \
+ "SIGNAL RATE MSRC"
+ #define VL_STRING_CHECKENABLE_SIGNAL_RATE_PRE_RANGE \
+ "SIGNAL RATE PRE RANGE"
+
+ /* Sequence Step */
+ #define VL_STRING_SEQUENCESTEP_TCC "TCC"
+ #define VL_STRING_SEQUENCESTEP_DSS "DSS"
+ #define VL_STRING_SEQUENCESTEP_MSRC "MSRC"
+ #define VL_STRING_SEQUENCESTEP_PRE_RANGE "PRE RANGE"
+ #define VL_STRING_SEQUENCESTEP_FINAL_RANGE "FINAL RANGE"
+#endif /* USE_EMPTY_STRING */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_def.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_def.h
new file mode 100644
index 0000000..50495a0
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_def.h
@@ -0,0 +1,619 @@
+/*
+ * vl53l0x_def.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * @file VL_def.h
+ *
+ * @brief Type definitions for VL53L0X API.
+ *
+ */
+
+
+#ifndef _VL_DEF_H_
+#define _VL_DEF_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup VL_globaldefine_group VL53L0X Defines
+ * @brief VL53L0X Defines
+ * @{
+ */
+
+
+/** PAL SPECIFICATION major version */
+#define VL53L0X10_SPECIFICATION_VER_MAJOR 1
+/** PAL SPECIFICATION minor version */
+#define VL53L0X10_SPECIFICATION_VER_MINOR 2
+/** PAL SPECIFICATION sub version */
+#define VL53L0X10_SPECIFICATION_VER_SUB 7
+/** PAL SPECIFICATION sub version */
+#define VL53L0X10_SPECIFICATION_VER_REVISION 1440
+
+/** VL53L0X PAL IMPLEMENTATION major version */
+#define VL53L0X10_IMPLEMENTATION_VER_MAJOR 1
+/** VL53L0X PAL IMPLEMENTATION minor version */
+#define VL53L0X10_IMPLEMENTATION_VER_MINOR 0
+/** VL53L0X PAL IMPLEMENTATION sub version */
+#define VL53L0X10_IMPLEMENTATION_VER_SUB 9
+/** VL53L0X PAL IMPLEMENTATION sub version */
+#define VL53L0X10_IMPLEMENTATION_VER_REVISION 3673
+
+/** PAL SPECIFICATION major version */
+#define VL_SPECIFICATION_VER_MAJOR 1
+/** PAL SPECIFICATION minor version */
+#define VL_SPECIFICATION_VER_MINOR 2
+/** PAL SPECIFICATION sub version */
+#define VL_SPECIFICATION_VER_SUB 7
+/** PAL SPECIFICATION sub version */
+#define VL_SPECIFICATION_VER_REVISION 1440
+
+/** VL53L0X PAL IMPLEMENTATION major version */
+#define VL_IMPLEMENTATION_VER_MAJOR 1
+/** VL53L0X PAL IMPLEMENTATION minor version */
+#define VL_IMPLEMENTATION_VER_MINOR 0
+/** VL53L0X PAL IMPLEMENTATION sub version */
+#define VL_IMPLEMENTATION_VER_SUB 2
+/** VL53L0X PAL IMPLEMENTATION sub version */
+#define VL_IMPLEMENTATION_VER_REVISION 4823
+#define VL_DEFAULT_MAX_LOOP 2000
+#define VL_MAX_STRING_LENGTH 32
+
+
+#include "vl53l0x_device.h"
+#include "vl53l0x_types.h"
+
+
+/****************************************
+ * PRIVATE define do not edit
+ ****************************************/
+
+/** @brief Defines the parameters of the Get Version Functions
+ */
+struct VL_Version_t {
+ uint32_t revision; /*!< revision number */
+ uint8_t major; /*!< major number */
+ uint8_t minor; /*!< minor number */
+ uint8_t build; /*!< build number */
+};
+
+
+/** @brief Defines the parameters of the Get Device Info Functions
+ */
+struct VL_DeviceInfo_t {
+ char Name[VL_MAX_STRING_LENGTH];
+ /*!< Name of the Device e.g. Left_Distance */
+ char Type[VL_MAX_STRING_LENGTH];
+ /*!< Type of the Device e.g VL53L0X */
+ char ProductId[VL_MAX_STRING_LENGTH];
+ /*!< Product Identifier String */
+ uint8_t ProductType;
+ /*!< Product Type, VL53L0X = 1, VL53L1 = 2 */
+ uint8_t ProductRevisionMajor;
+ /*!< Product revision major */
+ uint8_t ProductRevisionMinor;
+ /*!< Product revision minor */
+};
+
+
+/** @defgroup VL_define_Error_group Error and Warning code returned by API
+ * The following DEFINE are used to identify the PAL ERROR
+ * @{
+ */
+
+#define VL_ERROR_NONE ((int8_t) 0)
+#define VL_ERROR_CALIBRATION_WARNING ((int8_t) -1)
+ /*!< Warning invalid calibration data may be in used*/
+ /*\a VL_InitData()*/
+ /*\a VL_GetOffsetCalibrationData*/
+ /*\a VL_SetOffsetCalibrationData */
+#define VL_ERROR_MIN_CLIPPED ((int8_t) -2)
+ /*!< Warning parameter passed was clipped to min before to be applied */
+
+#define VL_ERROR_UNDEFINED ((int8_t) -3)
+ /*!< Unqualified error */
+#define VL_ERROR_INVALID_PARAMS ((int8_t) -4)
+ /*!< Parameter passed is invalid or out of range */
+#define VL_ERROR_NOT_SUPPORTED ((int8_t) -5)
+ /*!< Function is not supported in current mode or configuration */
+#define VL_ERROR_RANGE_ERROR ((int8_t) -6)
+ /*!< Device report a ranging error interrupt status */
+#define VL_ERROR_TIME_OUT ((int8_t) -7)
+ /*!< Aborted due to time out */
+#define VL_ERROR_MODE_NOT_SUPPORTED ((int8_t) -8)
+ /*!< Asked mode is not supported by the device */
+#define VL_ERROR_BUFFER_TOO_SMALL ((int8_t) -9)
+ /*!< ... */
+#define VL_ERROR_GPIO_NOT_EXISTING ((int8_t) -10)
+ /*!< User tried to setup a non-existing GPIO pin */
+#define VL_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ((int8_t) -11)
+ /*!< unsupported GPIO functionality */
+#define VL_ERROR_INTERRUPT_NOT_CLEARED ((int8_t) -12)
+ /*!< Error during interrupt clear */
+#define VL_ERROR_CONTROL_INTERFACE ((int8_t) -20)
+ /*!< error reported from IO functions */
+#define VL_ERROR_INVALID_COMMAND ((int8_t) -30)
+ /*!< The command is not allowed in the current device state*/
+ /* * (power down) */
+#define VL_ERROR_DIVISION_BY_ZERO ((int8_t) -40)
+ /*!< In the function a division by zero occurs */
+#define VL_ERROR_REF_SPAD_INIT ((int8_t) -50)
+ /*!< Error during reference SPAD initialization */
+#define VL_ERROR_NOT_IMPLEMENTED ((int8_t) -99)
+ /*!< Tells requested functionality has not been implemented yet or*/
+ /* * not compatible with the device */
+/** @} VL_define_Error_group */
+
+
+/** @defgroup VL_define_DeviceModes_group Defines Device modes
+ * Defines all possible modes for the device
+ * @{
+ */
+
+#define VL_DEVICEMODE_SINGLE_RANGING ((uint8_t) 0)
+#define VL_DEVICEMODE_CONTINUOUS_RANGING ((uint8_t) 1)
+#define VL_DEVICEMODE_SINGLE_HISTOGRAM ((uint8_t) 2)
+#define VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING ((uint8_t) 3)
+#define VL_DEVICEMODE_SINGLE_ALS ((uint8_t) 10)
+#define VL_DEVICEMODE_GPIO_DRIVE ((uint8_t) 20)
+#define VL_DEVICEMODE_GPIO_OSC ((uint8_t) 21)
+ /* ... Modes to be added depending on device */
+/** @} VL_define_DeviceModes_group */
+
+
+
+/** @defgroup VL_define_HistogramModes_group Defines Histogram modes
+ * Defines all possible Histogram modes for the device
+ * @{
+ */
+
+#define VL_HISTOGRAMMODE_DISABLED ((uint8_t) 0)
+ /*!< Histogram Disabled */
+#define VL_HISTOGRAMMODE_REFERENCE_ONLY ((uint8_t) 1)
+ /*!< Histogram Reference array only */
+#define VL_HISTOGRAMMODE_RETURN_ONLY ((uint8_t) 2)
+ /*!< Histogram Return array only */
+#define VL_HISTOGRAMMODE_BOTH ((uint8_t) 3)
+ /*!< Histogram both Reference and Return Arrays */
+ /* ... Modes to be added depending on device */
+/** @} VL_define_HistogramModes_group */
+
+
+/** @defgroup VL_define_PowerModes_group List of available Power Modes
+ * List of available Power Modes
+ * @{
+ */
+
+#define VL_POWERMODE_STANDBY_LEVEL1 ((uint8_t) 0)
+ /*!< Standby level 1 */
+#define VL_POWERMODE_STANDBY_LEVEL2 ((uint8_t) 1)
+ /*!< Standby level 2 */
+#define VL_POWERMODE_IDLE_LEVEL1 ((uint8_t) 2)
+ /*!< Idle level 1 */
+#define VL_POWERMODE_IDLE_LEVEL2 ((uint8_t) 3)
+ /*!< Idle level 2 */
+
+/** @} VL_define_PowerModes_group */
+
+
+/** @brief Defines all parameters for the device
+ */
+struct VL_DeviceParameters_t {
+ uint8_t DeviceMode;
+ /*!< Defines type of measurement to be done for the next measure */
+ uint8_t HistogramMode;
+ /*!< Defines type of histogram measurement to be done for the next*/
+ /* * measure */
+ uint32_t MeasurementTimingBudgetMicroSeconds;
+ /*!< Defines the allowed total time for a single measurement */
+ uint32_t InterMeasurementPeriodMilliSeconds;
+ /*!< Defines time between two consecutive measurements (between two*/
+ /* * measurement starts). If set to 0 means back-to-back mode */
+ uint8_t XTalkCompensationEnable;
+ /*!< Tells if Crosstalk compensation shall be enable or not */
+ uint16_t XTalkCompensationRangeMilliMeter;
+ /*!< CrossTalk compensation range in millimeter */
+ unsigned int XTalkCompensationRateMegaCps;
+ /*!< CrossTalk compensation rate in Mega counts per seconds.*/
+ /* * Expressed in 16.16 fixed point format. */
+ int32_t RangeOffsetMicroMeters;
+ /*!< Range offset adjustment (mm). */
+
+ uint8_t LimitChecksEnable[VL_CHECKENABLE_NUMBER_OF_CHECKS];
+ /*!< This Array store all the Limit Check enable for this device. */
+ uint8_t LimitChecksStatus[VL_CHECKENABLE_NUMBER_OF_CHECKS];
+ /*!< This Array store all the Status of the check linked to last*/
+ /** measurement. */
+ unsigned int LimitChecksValue[VL_CHECKENABLE_NUMBER_OF_CHECKS];
+ /*!< This Array store all the Limit Check value for this device */
+
+ uint8_t WrapAroundCheckEnable;
+ /*!< Tells if Wrap Around Check shall be enable or not */
+};
+
+
+/** @defgroup VL_define_State_group Defines the current status
+ * of the device Defines the current status of the device
+ * @{
+ */
+
+#define VL_STATE_POWERDOWN ((uint8_t) 0)
+ /*!< Device is in HW reset */
+#define VL_STATE_WAIT_STATICINIT ((uint8_t) 1)
+ /*!< Device is initialized and wait for static initialization */
+#define VL_STATE_STANDBY ((uint8_t) 2)
+ /*!< Device is in Low power Standby mode */
+#define VL_STATE_IDLE ((uint8_t) 3)
+ /*!< Device has been initialized and ready to do measurements */
+#define VL_STATE_RUNNING ((uint8_t) 4)
+ /*!< Device is performing measurement */
+#define VL_STATE_UNKNOWN ((uint8_t) 98)
+ /*!< Device is in unknown state and need to be rebooted */
+#define VL_STATE_ERROR ((uint8_t) 99)
+ /*!< Device is in error state and need to be rebooted */
+
+/** @} VL_define_State_group */
+
+
+/** @brief Structure containing the Dmax computation parameters and data
+ */
+struct VL_DMaxData_t {
+ int32_t AmbTuningWindowFactor_K;
+ /*!< internal algo tuning (*1000) */
+ int32_t RetSignalAt0mm;
+ /*!< intermediate dmax computation value caching */
+};
+
+/**
+ * @struct VL_RangeData_t
+ * @brief Range measurement data.
+ */
+struct VL_RangingMeasurementData_t {
+ uint32_t TimeStamp; /*!< 32-bit time stamp. */
+ uint32_t MeasurementTimeUsec;
+ /*!< Give the Measurement time needed by the device to do the */
+ /** measurement.*/
+
+
+ uint16_t RangeMilliMeter; /*!< range distance in millimeter. */
+
+ uint16_t RangeDMaxMilliMeter;
+ /*!< Tells what is the maximum detection distance of */
+ /* the device */
+ /* * in current setup and environment conditions (Filled when */
+ /* * applicable) */
+
+ unsigned int SignalRateRtnMegaCps;
+ /*!< Return signal rate (MCPS)\n these is a 16.16 fix point */
+ /* * value, which is effectively a measure of target */
+ /* * reflectance.*/
+ unsigned int AmbientRateRtnMegaCps;
+ /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point */
+ /* * value, which is effectively a measure of the ambien */
+ /* * t light.*/
+
+ uint16_t EffectiveSpadRtnCount;
+ /*!< Return the effective SPAD count for the return signal. */
+ /* * To obtain Real value it should be divided by 256 */
+
+ uint8_t ZoneId;
+ /*!< Denotes which zone and range scheduler stage the range */
+ /* * data relates to. */
+ uint8_t RangeFractionalPart;
+ /*!< Fractional part of range distance. Final value is a */
+ /* * FixPoint168 value. */
+ uint8_t RangeStatus;
+ /*!< Range Status for the current measurement. This is device */
+ /* * dependent. Value = 0 means value is valid. */
+ /* * See \ref RangeStatusPage */
+};
+
+
+#define VL_HISTOGRAM_BUFFER_SIZE 24
+
+/**
+ * @struct VL_HistogramData_t
+ * @brief Histogram measurement data.
+ */
+struct VL_HistogramMeasurementData_t {
+ /* Histogram Measurement data */
+ uint32_t HistogramData[VL_HISTOGRAM_BUFFER_SIZE];
+ /*!< Histogram data */
+ uint8_t HistogramType; /*!< Indicate the types of histogram data : */
+ /*Return only, Reference only, both Return and Reference */
+ uint8_t FirstBin; /*!< First Bin value */
+ uint8_t BufferSize; /*!< Buffer Size - Set by the user.*/
+ uint8_t NumberOfBins;
+ /*!< Number of bins filled by the histogram measurement */
+
+ uint8_t ErrorStatus;
+ /*!< Error status of the current measurement. \n */
+ /* see @a ::uint8_t @a VL_GetStatusErrorString() */
+};
+
+#define VL_REF_SPAD_BUFFER_SIZE 6
+
+/**
+ * @struct VL_SpadData_t
+ * @brief Spad Configuration Data.
+ */
+struct VL_SpadData_t {
+ uint8_t RefSpadEnables[VL_REF_SPAD_BUFFER_SIZE];
+ /*!< Reference Spad Enables */
+ uint8_t RefGoodSpadMap[VL_REF_SPAD_BUFFER_SIZE];
+ /*!< Reference Spad Good Spad Map */
+};
+
+struct VL_DeviceSpecificParameters_t {
+ unsigned int OscFrequencyMHz; /* Frequency used */
+
+ uint16_t LastEncodedTimeout;
+ /* last encoded Time out used for timing budget*/
+
+ uint8_t Pin0GpioFunctionality;
+ /* store the functionality of the GPIO: pin0 */
+
+ uint32_t FinalRangeTimeoutMicroSecs;
+ /*!< Execution time of the final range*/
+ uint8_t FinalRangeVcselPulsePeriod;
+ /*!< Vcsel pulse period (pll clocks) for the final range measurement*/
+ uint32_t PreRangeTimeoutMicroSecs;
+ /*!< Execution time of the final range*/
+ uint8_t PreRangeVcselPulsePeriod;
+ /*!< Vcsel pulse period (pll clocks) for the pre-range measurement*/
+
+ uint16_t SigmaEstRefArray;
+ /*!< Reference array sigma value in 1/100th of [mm] e.g. 100 = 1mm */
+ uint16_t SigmaEstEffPulseWidth;
+ /*!< Effective Pulse width for sigma estimate in 1/100th */
+ /* * of ns e.g. 900 = 9.0ns */
+ uint16_t SigmaEstEffAmbWidth;
+ /*!< Effective Ambient width for sigma estimate in 1/100th of ns */
+ /* * e.g. 500 = 5.0ns */
+
+
+ uint8_t ReadDataFromDeviceDone; /* Indicate if read from device has */
+ /*been done (==1) or not (==0) */
+ uint8_t ModuleId; /* Module ID */
+ uint8_t Revision; /* test Revision */
+ char ProductId[VL_MAX_STRING_LENGTH];
+ /* Product Identifier String */
+ uint8_t ReferenceSpadCount; /* used for ref spad management */
+ uint8_t ReferenceSpadType; /* used for ref spad management */
+ uint8_t RefSpadsInitialised; /* reports if ref spads are initialised. */
+ uint32_t PartUIDUpper; /*!< Unique Part ID Upper */
+ uint32_t PartUIDLower; /*!< Unique Part ID Lower */
+ unsigned int SignalRateMeasFixed400mm; /*!< Peek Signal rate at 400 mm*/
+
+};
+
+/**
+ * @struct VL_DevData_t
+ *
+ * @brief VL53L0X PAL device ST private data structure \n
+ * End user should never access any of these field directly
+ *
+ * These must never access directly but only via macro
+ */
+struct VL_DevData_t {
+ struct VL_DMaxData_t DMaxData;
+ /*!< Dmax Data */
+ int32_t Part2PartOffsetNVMMicroMeter;
+ /*!< backed up NVM value */
+ int32_t Part2PartOffsetAdjustmentNVMMicroMeter;
+ /*!< backed up NVM value representing additional offset adjustment */
+ struct VL_DeviceParameters_t CurrentParameters;
+ /*!< Current Device Parameter */
+ struct VL_RangingMeasurementData_t LastRangeMeasure;
+ /*!< Ranging Data */
+ struct VL_HistogramMeasurementData_t LastHistogramMeasure;
+ /*!< Histogram Data */
+ struct VL_DeviceSpecificParameters_t DeviceSpecificParameters;
+ /*!< Parameters specific to the device */
+ struct VL_SpadData_t SpadData;
+ /*!< Spad Data */
+ uint8_t SequenceConfig;
+ /*!< Internal value for the sequence config */
+ uint8_t RangeFractionalEnable;
+ /*!< Enable/Disable fractional part of ranging data */
+ uint8_t PalState;
+ /*!< Current state of the PAL for this device */
+ uint8_t PowerMode;
+ /*!< Current Power Mode */
+ uint16_t SigmaEstRefArray;
+ /*!< Reference array sigma value in 1/100th of [mm] e.g. 100 = 1mm */
+ uint16_t SigmaEstEffPulseWidth;
+ /*!< Effective Pulse width for sigma estimate in 1/100th */
+ /* of ns e.g. 900 = 9.0ns */
+ uint16_t SigmaEstEffAmbWidth;
+ /*!< Effective Ambient width for sigma estimate in 1/100th of ns */
+ /* * e.g. 500 = 5.0ns */
+ uint8_t StopVariable;
+ /*!< StopVariable used during the stop sequence */
+ uint16_t targetRefRate;
+ /*!< Target Ambient Rate for Ref spad management */
+ unsigned int SigmaEstimate;
+ /*!< Sigma Estimate - based on ambient & VCSEL rates and */
+ /** signal_total_events */
+ unsigned int SignalEstimate;
+ /*!< Signal Estimate - based on ambient & VCSEL rates and cross talk */
+ unsigned int LastSignalRefMcps;
+ /*!< Latest Signal ref in Mcps */
+ uint8_t *pTuningSettingsPointer;
+ /*!< Pointer for Tuning Settings table */
+ uint8_t UseInternalTuningSettings;
+ /*!< Indicate if we use Tuning Settings table */
+ uint16_t LinearityCorrectiveGain;
+ /*!< Linearity Corrective Gain value in x1000 */
+ uint16_t DmaxCalRangeMilliMeter;
+ /*!< Dmax Calibration Range millimeter */
+ unsigned int DmaxCalSignalRateRtnMegaCps;
+ /*!< Dmax Calibration Signal Rate Return MegaCps */
+
+};
+
+
+/** @defgroup VL_define_InterruptPolarity_group Defines the Polarity
+ * of the Interrupt
+ * Defines the Polarity of the Interrupt
+ * @{
+ */
+
+#define VL_INTERRUPTPOLARITY_LOW ((uint8_t) 0)
+/*!< Set active low polarity best setup for falling edge. */
+#define VL_INTERRUPTPOLARITY_HIGH ((uint8_t) 1)
+/*!< Set active high polarity best setup for rising edge. */
+
+/** @} VL_define_InterruptPolarity_group */
+
+
+/** @defgroup VL_define_VcselPeriod_group Vcsel Period Defines
+ * Defines the range measurement for which to access the vcsel period.
+ * @{
+ */
+
+#define VL_VCSEL_PERIOD_PRE_RANGE ((uint8_t) 0)
+/*!<Identifies the pre-range vcsel period. */
+#define VL_VCSEL_PERIOD_FINAL_RANGE ((uint8_t) 1)
+/*!<Identifies the final range vcsel period. */
+
+/** @} VL_define_VcselPeriod_group */
+
+/** @defgroup VL_define_SchedulerSequence_group Defines the steps
+ * carried out by the scheduler during a range measurement.
+ * @{
+ * Defines the states of all the steps in the scheduler
+ * i.e. enabled/disabled.
+ */
+struct VL_SchedulerSequenceSteps_t {
+ uint8_t TccOn; /*!<Reports if Target Centre Check On */
+ uint8_t MsrcOn; /*!<Reports if MSRC On */
+ uint8_t DssOn; /*!<Reports if DSS On */
+ uint8_t PreRangeOn; /*!<Reports if Pre-Range On */
+ uint8_t FinalRangeOn; /*!<Reports if Final-Range On */
+};
+
+/** @} VL_define_SchedulerSequence_group */
+
+/** @defgroup VL_define_SequenceStepId_group Defines the Polarity
+ * of the Interrupt
+ * Defines the the sequence steps performed during ranging..
+ * @{
+ */
+
+#define VL_SEQUENCESTEP_TCC ((uint8_t) 0)
+/*!<Target CentreCheck identifier. */
+#define VL_SEQUENCESTEP_DSS ((uint8_t) 1)
+/*!<Dynamic Spad Selection function Identifier. */
+#define VL_SEQUENCESTEP_MSRC ((uint8_t) 2)
+/*!<Minimum Signal Rate Check function Identifier. */
+#define VL_SEQUENCESTEP_PRE_RANGE ((uint8_t) 3)
+/*!<Pre-Range check Identifier. */
+#define VL_SEQUENCESTEP_FINAL_RANGE ((uint8_t) 4)
+/*!<Final Range Check Identifier. */
+
+#define VL_SEQUENCESTEP_NUMBER_OF_CHECKS 5
+/*!<Number of Sequence Step Managed by the API. */
+
+/** @} VL_define_SequenceStepId_group */
+
+
+/* MACRO Definitions */
+/** @defgroup VL_define_GeneralMacro_group General Macro Defines
+ * General Macro Defines
+ * @{
+ */
+
+/* Defines */
+#define VL_SETPARAMETERFIELD(Dev, field, value) \
+ PALDevDataSet(Dev, CurrentParameters.field, value)
+
+#define VL_GETPARAMETERFIELD(Dev, field, variable) \
+ (variable = PALDevDataGet(Dev, CurrentParameters).field)
+
+
+#define VL_SETARRAYPARAMETERFIELD(Dev, field, index, value) \
+ PALDevDataSet(Dev, CurrentParameters.field[index], value)
+
+#define VL_GETARRAYPARAMETERFIELD(Dev, field, index, variable) \
+ (variable = PALDevDataGet(Dev, CurrentParameters).field[index])
+
+
+#define VL_SETDEVICESPECIFICPARAMETER(Dev, field, value) \
+ PALDevDataSet(Dev, DeviceSpecificParameters.field, value)
+
+#define VL_GETDEVICESPECIFICPARAMETER(Dev, field) \
+ PALDevDataGet(Dev, DeviceSpecificParameters).field
+
+
+#define VL_FIXPOINT1616TOFIXPOINT97(Value) \
+ (uint16_t)((Value>>9)&0xFFFF)
+#define VL_FIXPOINT97TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<9)
+
+#define VL_FIXPOINT1616TOFIXPOINT88(Value) \
+ (uint16_t)((Value>>8)&0xFFFF)
+#define VL_FIXPOINT88TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<8)
+
+#define VL_FIXPOINT1616TOFIXPOINT412(Value) \
+ (uint16_t)((Value>>4)&0xFFFF)
+#define VL_FIXPOINT412TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<4)
+
+#define VL_FIXPOINT1616TOFIXPOINT313(Value) \
+ (uint16_t)((Value>>3)&0xFFFF)
+#define VL_FIXPOINT313TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<3)
+
+#define VL_FIXPOINT1616TOFIXPOINT08(Value) \
+ (uint8_t)((Value>>8)&0x00FF)
+#define VL_FIXPOINT08TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<8)
+
+#define VL_FIXPOINT1616TOFIXPOINT53(Value) \
+ (uint8_t)((Value>>13)&0x00FF)
+#define VL_FIXPOINT53TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<13)
+
+#define VL_FIXPOINT1616TOFIXPOINT102(Value) \
+ (uint16_t)((Value>>14)&0x0FFF)
+#define VL_FIXPOINT102TOFIXPOINT1616(Value) \
+ (unsigned int)(Value<<12)
+
+#define VL_MAKEUINT16(lsb, msb) (uint16_t)((((uint16_t)msb)<<8) + \
+ (uint16_t)lsb)
+
+/** @} VL_define_GeneralMacro_group */
+
+/** @} VL_globaldefine_group */
+
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _VL_DEF_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_device.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_device.h
new file mode 100644
index 0000000..b296ff8
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_device.h
@@ -0,0 +1,246 @@
+/*
+ * vl53l0x_device.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Device specific defines. To be adapted by implementer for the targeted
+ * device.
+ */
+
+#ifndef _VL_DEVICE_H_
+#define _VL_DEVICE_H_
+
+#include "vl53l0x_types.h"
+
+
+/** @defgroup VL_DevSpecDefines_group VL53L0X cut1.1
+ * Device Specific Defines
+ * @brief VL53L0X cut1.1 Device Specific Defines
+ * @{
+ */
+
+
+/** @defgroup uint8_t_group Device Error
+ * @brief Device Error code
+ *
+ * This enum is Device specific it should be updated in the implementation
+ * Use @a VL_GetStatusErrorString() to get the string.
+ * It is related to Status Register of the Device.
+ * @{
+ */
+
+#define VL_DEVICEERROR_NONE ((uint8_t) 0)
+ /*!< 0 NoError */
+#define VL_DEVICEERROR_VCSELCONTINUITYTESTFAILURE ((uint8_t) 1)
+#define VL_DEVICEERROR_VCSELWATCHDOGTESTFAILURE ((uint8_t) 2)
+#define VL_DEVICEERROR_NOVHVVALUEFOUND ((uint8_t) 3)
+#define VL_DEVICEERROR_MSRCNOTARGET ((uint8_t) 4)
+#define VL_DEVICEERROR_SNRCHECK ((uint8_t) 5)
+#define VL_DEVICEERROR_RANGEPHASECHECK ((uint8_t) 6)
+#define VL_DEVICEERROR_SIGMATHRESHOLDCHECK ((uint8_t) 7)
+#define VL_DEVICEERROR_TCC ((uint8_t) 8)
+#define VL_DEVICEERROR_PHASECONSISTENCY ((uint8_t) 9)
+#define VL_DEVICEERROR_MINCLIP ((uint8_t) 10)
+#define VL_DEVICEERROR_RANGECOMPLETE ((uint8_t) 11)
+#define VL_DEVICEERROR_ALGOUNDERFLOW ((uint8_t) 12)
+#define VL_DEVICEERROR_ALGOOVERFLOW ((uint8_t) 13)
+#define VL_DEVICEERROR_RANGEIGNORETHRESHOLD ((uint8_t) 14)
+
+/** @} end of uint8_t_group */
+
+
+/** @defgroup VL_CheckEnable_group Check Enable list
+ * @brief Check Enable code
+ *
+ * Define used to specify the LimitCheckId.
+ * Use @a VL_GetLimitCheckInfo() to get the string.
+ * @{
+ */
+
+#define VL_CHECKENABLE_SIGMA_FINAL_RANGE 0
+#define VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE 1
+#define VL_CHECKENABLE_SIGNAL_REF_CLIP 2
+#define VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD 3
+#define VL_CHECKENABLE_SIGNAL_RATE_MSRC 4
+#define VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE 5
+
+#define VL_CHECKENABLE_NUMBER_OF_CHECKS 6
+
+/** @} end of VL_CheckEnable_group */
+
+
+/** @defgroup uint8_t_group Gpio Functionality
+ * @brief Defines the different functionalities for the device GPIO(s)
+ * @{
+ */
+
+#define VL_GPIOFUNCTIONALITY_OFF \
+ ((uint8_t) 0) /*!< NO Interrupt */
+#define VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW \
+ ((uint8_t) 1) /*!< Level Low (value < thresh_low) */
+#define VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH \
+ ((uint8_t) 2)/*!< Level High (value > thresh_high)*/
+#define VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT \
+ ((uint8_t) 3)
+ /*!< Out Of Window (value < thresh_low OR value > thresh_high) */
+#define VL_GPIOFUNCTIONALITY_NEW_MEASURE_READY \
+ ((uint8_t) 4) /*!< New Sample Ready */
+
+/** @} end of uint8_t_group */
+
+
+/* Device register map */
+
+/** @defgroup VL_DefineRegisters_group Define Registers
+ * @brief List of all the defined registers
+ * @{
+ */
+#define VL_REG_SYSRANGE_START 0x000
+ /** mask existing bit in #VL_REG_SYSRANGE_START*/
+ #define VL_REG_SYSRANGE_MODE_MASK 0x0F
+ /** bit 0 in #VL_REG_SYSRANGE_START write 1 toggle state in */
+ /* continuous mode and arm next shot in single shot mode */
+ #define VL_REG_SYSRANGE_MODE_START_STOP 0x01
+ /** bit 1 write 0 in #VL_REG_SYSRANGE_START set single shot mode */
+ #define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00
+ /** bit 1 write 1 in #VL_REG_SYSRANGE_START set back-to-back */
+ /* operation mode */
+ #define VL_REG_SYSRANGE_MODE_BACKTOBACK 0x02
+ /** bit 2 write 1 in #VL_REG_SYSRANGE_START set timed operation */
+ /* * mode */
+ #define VL_REG_SYSRANGE_MODE_TIMED 0x04
+ /** bit 3 write 1 in #VL_REG_SYSRANGE_START set histogram operation */
+ /* * mode */
+ #define VL_REG_SYSRANGE_MODE_HISTOGRAM 0x08
+
+
+#define VL_REG_SYSTEM_THRESH_HIGH 0x000C
+#define VL_REG_SYSTEM_THRESH_LOW 0x000E
+
+
+#define VL_REG_SYSTEM_SEQUENCE_CONFIG 0x0001
+#define VL_REG_SYSTEM_RANGE_CONFIG 0x0009
+#define VL_REG_SYSTEM_INTERMEASUREMENT_PERIOD 0x0004
+
+
+#define VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO 0x000A
+ #define VL_REG_SYSTEM_INTERRUPT_GPIO_DISABLED 0x00
+ #define VL_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_LOW 0x01
+ #define VL_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_HIGH 0x02
+ #define VL_REG_SYSTEM_INTERRUPT_GPIO_OUT_OF_WINDOW 0x03
+ #define VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY 0x04
+
+#define VL_REG_GPIO_HV_MUX_ACTIVE_HIGH 0x0084
+
+
+#define VL_REG_SYSTEM_INTERRUPT_CLEAR 0x000B
+
+/* Result registers */
+#define VL_REG_RESULT_INTERRUPT_STATUS 0x0013
+#define VL_REG_RESULT_RANGE_STATUS 0x0014
+
+#define VL_REG_RESULT_CORE_PAGE 1
+#define VL_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN 0x00BC
+#define VL_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN 0x00C0
+#define VL_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF 0x00D0
+#define VL_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF 0x00D4
+#define VL_REG_RESULT_PEAK_SIGNAL_RATE_REF 0x00B6
+
+/* Algo register */
+
+#define VL_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM 0x0028
+
+#define VL_REG_I2C_SLAVE_DEVICE_ADDRESS 0x008a
+
+/* Check Limit registers */
+#define VL_REG_MSRC_CONFIG_CONTROL 0x0060
+
+#define VL_REG_PRE_RANGE_CONFIG_MIN_SNR 0X0027
+#define VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW 0x0056
+#define VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH 0x0057
+#define VL_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT 0x0064
+
+#define VL_REG_FINAL_RANGE_CONFIG_MIN_SNR 0X0067
+#define VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW 0x0047
+#define VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH 0x0048
+#define VL_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT 0x0044
+
+
+#define VL_REG_PRE_RANGE_CONFIG_SIGMA_THRESH_HI 0X0061
+#define VL_REG_PRE_RANGE_CONFIG_SIGMA_THRESH_LO 0X0062
+
+/* PRE RANGE registers */
+#define VL_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD 0x0050
+#define VL_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI 0x0051
+#define VL_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO 0x0052
+
+#define VL_REG_SYSTEM_HISTOGRAM_BIN 0x0081
+#define VL_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT 0x0033
+#define VL_REG_HISTOGRAM_CONFIG_READOUT_CTRL 0x0055
+
+#define VL_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x0070
+#define VL_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI 0x0071
+#define VL_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO 0x0072
+#define VL_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS 0x0020
+
+#define VL_REG_MSRC_CONFIG_TIMEOUT_MACROP 0x0046
+
+
+#define VL_REG_SOFT_RESET_GO2_SOFT_RESET_N 0x00bf
+#define VL_REG_IDENTIFICATION_MODEL_ID 0x00c0
+#define VL_REG_IDENTIFICATION_REVISION_ID 0x00c2
+
+#define VL_REG_OSC_CALIBRATE_VAL 0x00f8
+
+
+#define VL_SIGMA_ESTIMATE_MAX_VALUE 65535
+/* equivalent to a range sigma of 655.35mm */
+
+#define VL_REG_GLOBAL_CONFIG_VCSEL_WIDTH 0x032
+#define VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0 0x0B0
+#define VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_1 0x0B1
+#define VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_2 0x0B2
+#define VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_3 0x0B3
+#define VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_4 0x0B4
+#define VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_5 0x0B5
+
+#define VL_REG_GLOBAL_CONFIG_REF_EN_START_SELECT 0xB6
+#define VL_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD 0x4E /* 0x14E */
+#define VL_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET 0x4F /* 0x14F */
+#define VL_REG_POWER_MANAGEMENT_GO1_POWER_FORCE 0x80
+
+/*
+ * Speed of light in um per 1E-10 Seconds
+ */
+
+#define VL_SPEED_OF_LIGHT_IN_AIR 2997
+
+#define VL_REG_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV 0x0089
+
+#define VL_REG_ALGO_PHASECAL_LIM 0x0030 /* 0x130 */
+#define VL_REG_ALGO_PHASECAL_CONFIG_TIMEOUT 0x0030
+
+/** @} VL_DefineRegisters_group */
+
+/** @} VL_DevSpecDefines_group */
+
+
+#endif
+
+/* _VL_DEVICE_H_ */
+
+
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_i2c_platform.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_i2c_platform.h
new file mode 100644
index 0000000..3cd5d69
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_i2c_platform.h
@@ -0,0 +1,397 @@
+/*
+ * vl53l0x_i2c_platform.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * @file VL_i2c_platform.h
+ * @brief Function prototype definitions for EWOK Platform layer.
+ *
+ */
+
+
+#ifndef _VL_I2C_PLATFORM_H_
+#define _VL_I2C_PLATFORM_H_
+
+#include "vl53l0x_def.h"
+
+
+/** Maximum buffer size to be used in i2c */
+#define VL_MAX_I2C_XFER_SIZE 64
+
+/**
+ * @brief Typedef defining .\n
+ * The developer should modify this to suit the platform being deployed.
+ *
+ */
+
+/**
+ * @brief Typedef defining 8 bit unsigned char type.\n
+ * The developer should modify this to suit the platform being deployed.
+ *
+ */
+#define I2C 0x01
+#define SPI 0x00
+
+#define COMMS_BUFFER_SIZE 64
+/*MUST be the same size as the SV task buffer */
+
+#define BYTES_PER_WORD 2
+#define BYTES_PER_DWORD 4
+
+#define VL_MAX_STRING_LENGTH_PLT 256
+
+/**
+ * @brief Initialise platform comms.
+ *
+ * @param comms_type - selects between I2C and SPI
+ * @param comms_speed_khz - unsigned short containing the I2C speed in kHz
+ *
+ * @return status - status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_comms_initialise(uint8_t comms_type,
+ uint16_t comms_speed_khz);
+
+/**
+ * @brief Close platform comms.
+ *
+ * @return status - status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_comms_close(void);
+
+/**
+ * @brief Cycle Power to Device
+ *
+ * @return status - status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_cycle_power(void);
+
+int32_t VL_set_page(struct vl_data *dev, uint8_t page_data);
+
+/**
+ * @brief Writes the supplied byte buffer to the device
+ *
+ * Wrapper for SystemVerilog Write Multi task
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint8_t *spad_enables;
+ *
+ * int status = VL_write_multi(RET_SPAD_EN_0, spad_enables, 36);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param pdata - pointer to uint8_t buffer containing the data to be written
+ * @param count - number of bytes in the supplied byte buffer
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_write_multi(struct vl_data *dev, uint8_t index, uint8_t *pdata,
+ int32_t count);
+
+
+/**
+ * @brief Reads the requested number of bytes from the device
+ *
+ * Wrapper for SystemVerilog Read Multi task
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint8_t buffer[COMMS_BUFFER_SIZE];
+ *
+ * int status = status = VL_read_multi(DEVICE_ID, buffer, 2)
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param pdata - pointer to the uint8_t buffer to store read data
+ * @param count - number of uint8_t's to read
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_read_multi(struct vl_data *dev, uint8_t index, uint8_t *pdata,
+ int32_t count);
+
+
+/**
+ * @brief Writes a single byte to the device
+ *
+ * Wrapper for SystemVerilog Write Byte task
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint8_t page_number = MAIN_SELECT_PAGE;
+ *
+ * int status = VL_write_byte(PAGE_SELECT, page_number);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param data - uint8_t data value to write
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_write_byte(struct vl_data *dev, uint8_t index, uint8_t data);
+
+
+/**
+ * @brief Writes a single word (16-bit unsigned) to the device
+ *
+ * Manages the big-endian nature of the device (first byte written is the
+ * MS byte).
+ * Uses SystemVerilog Write Multi task.
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint16_t nvm_ctrl_pulse_width = 0x0004;
+ *
+ * int status = VL_write_word(NVM_CTRL__PULSE_WIDTH_MSB,
+ * nvm_ctrl_pulse_width);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param data - uin16_t data value write
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_write_word(struct vl_data *dev, uint8_t index, uint16_t data);
+
+
+/**
+ * @brief Writes a single dword (32-bit unsigned) to the device
+ *
+ * Manages the big-endian nature of the device (first byte written is the
+ * MS byte).
+ * Uses SystemVerilog Write Multi task.
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint32_t nvm_data = 0x0004;
+ *
+ * int status = VL_write_dword(NVM_CTRL__DATAIN_MMM, nvm_data);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param data - uint32_t data value to write
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_write_dword(struct vl_data *dev, uint8_t index, uint32_t data);
+
+
+
+/**
+ * @brief Reads a single byte from the device
+ *
+ * Uses SystemVerilog Read Byte task.
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint8_t device_status = 0;
+ *
+ * int status = VL_read_byte(STATUS, &device_status);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param pdata - pointer to uint8_t data value
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_read_byte(struct vl_data *dev, uint8_t index, uint8_t *pdata);
+
+
+/**
+ * @brief Reads a single word (16-bit unsigned) from the device
+ *
+ * Manages the big-endian nature of the device (first byte read is the MS byte).
+ * Uses SystemVerilog Read Multi task.
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint16_t timeout = 0;
+ *
+ * int status = VL_read_word(TIMEOUT_OVERALL_PERIODS_MSB, &timeout);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param pdata - pointer to uint16_t data value
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_read_word(struct vl_data *dev, uint8_t index, uint16_t *pdata);
+
+
+/**
+ * @brief Reads a single dword (32-bit unsigned) from the device
+ *
+ * Manages the big-endian nature of the device (first byte read is the MS byte).
+ * Uses SystemVerilog Read Multi task.
+ *
+ * @code
+ *
+ * Example:
+ *
+ * uint32_t range_1 = 0;
+ *
+ * int status = VL_read_dword(RANGE_1_MMM, &range_1);
+ *
+ * @endcode
+ *
+ * @param address - uint8_t device address value
+ * @param index - uint8_t register index value
+ * @param pdata - pointer to uint32_t data value
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_read_dword(struct vl_data *dev, uint8_t index, uint32_t *pdata);
+
+
+/**
+ * @brief Implements a programmable wait in us
+ *
+ * Wrapper for SystemVerilog Wait in micro seconds task
+ *
+ * @param wait_us - integer wait in micro seconds
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_platform_wait_us(int32_t wait_us);
+
+
+/**
+ * @brief Implements a programmable wait in ms
+ *
+ * Wrapper for SystemVerilog Wait in milli seconds task
+ *
+ * @param wait_ms - integer wait in milli seconds
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_wait_ms(int32_t wait_ms);
+
+
+/**
+ * @brief Set GPIO value
+ *
+ * @param level - input level - either 0 or 1
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_set_gpio(uint8_t level);
+
+
+/**
+ * @brief Get GPIO value
+ *
+ * @param plevel - uint8_t pointer to store GPIO level (0 or 1)
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_get_gpio(uint8_t *plevel);
+
+/**
+ * @brief Release force on GPIO
+ *
+ * @return status - SystemVerilog status 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_release_gpio(void);
+
+
+/**
+ * @brief Get the frequency of the timer used for ranging results time stamps
+ *
+ * @param[out] ptimer_freq_hz : pointer for timer frequency
+ *
+ * @return status : 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_get_timer_frequency(int32_t *ptimer_freq_hz);
+
+/**
+ * @brief Get the timer value in units of timer_freq_hz
+ * (see VL_get_timestamp_frequency())
+ *
+ * @param[out] ptimer_count : pointer for timer count value
+ *
+ * @return status : 0 = ok, 1 = error
+ *
+ */
+
+int32_t VL_get_timer_value(int32_t *ptimer_count);
+int VL_I2CWrite(struct vl_data *dev, uint8_t *buff, uint8_t len);
+int VL_I2CRead(struct vl_data *dev, uint8_t *buff, uint8_t len);
+
+#endif /* _VL_I2C_PLATFORM_H_ */
+
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_interrupt_threshold_settings.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_interrupt_threshold_settings.h
new file mode 100644
index 0000000..ecfc994
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_interrupt_threshold_settings.h
@@ -0,0 +1,183 @@
+/*
+ * vl53l0x_interrupt_threshold_settings.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_INTERRUPT_THRESHOLD_SETTINGS_H_
+#define _VL_INTERRUPT_THRESHOLD_SETTINGS_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+uint8_t InterruptThresholdSettings[] = {
+
+ /* Start of Interrupt Threshold Settings */
+ 0x1, 0xff, 0x00,
+ 0x1, 0x80, 0x01,
+ 0x1, 0xff, 0x01,
+ 0x1, 0x00, 0x00,
+ 0x1, 0xff, 0x01,
+ 0x1, 0x4f, 0x02,
+ 0x1, 0xFF, 0x0E,
+ 0x1, 0x00, 0x03,
+ 0x1, 0x01, 0x84,
+ 0x1, 0x02, 0x0A,
+ 0x1, 0x03, 0x03,
+ 0x1, 0x04, 0x08,
+ 0x1, 0x05, 0xC8,
+ 0x1, 0x06, 0x03,
+ 0x1, 0x07, 0x8D,
+ 0x1, 0x08, 0x08,
+ 0x1, 0x09, 0xC6,
+ 0x1, 0x0A, 0x01,
+ 0x1, 0x0B, 0x02,
+ 0x1, 0x0C, 0x00,
+ 0x1, 0x0D, 0xD5,
+ 0x1, 0x0E, 0x18,
+ 0x1, 0x0F, 0x12,
+ 0x1, 0x10, 0x01,
+ 0x1, 0x11, 0x82,
+ 0x1, 0x12, 0x00,
+ 0x1, 0x13, 0xD5,
+ 0x1, 0x14, 0x18,
+ 0x1, 0x15, 0x13,
+ 0x1, 0x16, 0x03,
+ 0x1, 0x17, 0x86,
+ 0x1, 0x18, 0x0A,
+ 0x1, 0x19, 0x09,
+ 0x1, 0x1A, 0x08,
+ 0x1, 0x1B, 0xC2,
+ 0x1, 0x1C, 0x03,
+ 0x1, 0x1D, 0x8F,
+ 0x1, 0x1E, 0x0A,
+ 0x1, 0x1F, 0x06,
+ 0x1, 0x20, 0x01,
+ 0x1, 0x21, 0x02,
+ 0x1, 0x22, 0x00,
+ 0x1, 0x23, 0xD5,
+ 0x1, 0x24, 0x18,
+ 0x1, 0x25, 0x22,
+ 0x1, 0x26, 0x01,
+ 0x1, 0x27, 0x82,
+ 0x1, 0x28, 0x00,
+ 0x1, 0x29, 0xD5,
+ 0x1, 0x2A, 0x18,
+ 0x1, 0x2B, 0x0B,
+ 0x1, 0x2C, 0x28,
+ 0x1, 0x2D, 0x78,
+ 0x1, 0x2E, 0x28,
+ 0x1, 0x2F, 0x91,
+ 0x1, 0x30, 0x00,
+ 0x1, 0x31, 0x0B,
+ 0x1, 0x32, 0x00,
+ 0x1, 0x33, 0x0B,
+ 0x1, 0x34, 0x00,
+ 0x1, 0x35, 0xA1,
+ 0x1, 0x36, 0x00,
+ 0x1, 0x37, 0xA0,
+ 0x1, 0x38, 0x00,
+ 0x1, 0x39, 0x04,
+ 0x1, 0x3A, 0x28,
+ 0x1, 0x3B, 0x30,
+ 0x1, 0x3C, 0x0C,
+ 0x1, 0x3D, 0x04,
+ 0x1, 0x3E, 0x0F,
+ 0x1, 0x3F, 0x79,
+ 0x1, 0x40, 0x28,
+ 0x1, 0x41, 0x1E,
+ 0x1, 0x42, 0x2F,
+ 0x1, 0x43, 0x87,
+ 0x1, 0x44, 0x00,
+ 0x1, 0x45, 0x0B,
+ 0x1, 0x46, 0x00,
+ 0x1, 0x47, 0x0B,
+ 0x1, 0x48, 0x00,
+ 0x1, 0x49, 0xA7,
+ 0x1, 0x4A, 0x00,
+ 0x1, 0x4B, 0xA6,
+ 0x1, 0x4C, 0x00,
+ 0x1, 0x4D, 0x04,
+ 0x1, 0x4E, 0x01,
+ 0x1, 0x4F, 0x00,
+ 0x1, 0x50, 0x00,
+ 0x1, 0x51, 0x80,
+ 0x1, 0x52, 0x09,
+ 0x1, 0x53, 0x08,
+ 0x1, 0x54, 0x01,
+ 0x1, 0x55, 0x00,
+ 0x1, 0x56, 0x0F,
+ 0x1, 0x57, 0x79,
+ 0x1, 0x58, 0x09,
+ 0x1, 0x59, 0x05,
+ 0x1, 0x5A, 0x00,
+ 0x1, 0x5B, 0x60,
+ 0x1, 0x5C, 0x05,
+ 0x1, 0x5D, 0xD1,
+ 0x1, 0x5E, 0x0C,
+ 0x1, 0x5F, 0x3C,
+ 0x1, 0x60, 0x00,
+ 0x1, 0x61, 0xD0,
+ 0x1, 0x62, 0x0B,
+ 0x1, 0x63, 0x03,
+ 0x1, 0x64, 0x28,
+ 0x1, 0x65, 0x10,
+ 0x1, 0x66, 0x2A,
+ 0x1, 0x67, 0x39,
+ 0x1, 0x68, 0x0B,
+ 0x1, 0x69, 0x02,
+ 0x1, 0x6A, 0x28,
+ 0x1, 0x6B, 0x10,
+ 0x1, 0x6C, 0x2A,
+ 0x1, 0x6D, 0x61,
+ 0x1, 0x6E, 0x0C,
+ 0x1, 0x6F, 0x00,
+ 0x1, 0x70, 0x0F,
+ 0x1, 0x71, 0x79,
+ 0x1, 0x72, 0x00,
+ 0x1, 0x73, 0x0B,
+ 0x1, 0x74, 0x00,
+ 0x1, 0x75, 0x0B,
+ 0x1, 0x76, 0x00,
+ 0x1, 0x77, 0xA1,
+ 0x1, 0x78, 0x00,
+ 0x1, 0x79, 0xA0,
+ 0x1, 0x7A, 0x00,
+ 0x1, 0x7B, 0x04,
+ 0x1, 0xFF, 0x04,
+ 0x1, 0x79, 0x1D,
+ 0x1, 0x7B, 0x27,
+ 0x1, 0x96, 0x0E,
+ 0x1, 0x97, 0xFE,
+ 0x1, 0x98, 0x03,
+ 0x1, 0x99, 0xEF,
+ 0x1, 0x9A, 0x02,
+ 0x1, 0x9B, 0x44,
+ 0x1, 0x73, 0x07,
+ 0x1, 0x70, 0x01,
+ 0x1, 0xff, 0x01,
+ 0x1, 0x00, 0x01,
+ 0x1, 0xff, 0x00,
+ 0x00, 0x00, 0x00
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VL_INTERRUPT_THRESHOLD_SETTINGS_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_platform.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_platform.h
new file mode 100644
index 0000000..d896fe8
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_platform.h
@@ -0,0 +1,213 @@
+/*
+ * vl53l0x_platform.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_PLATFORM_H_
+#define _VL_PLATFORM_H_
+
+#include <linux/delay.h>
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform_log.h"
+
+#include "stmvl53l0x-i2c.h"
+#include "stmvl53l0x-cci.h"
+#include "stmvl53l0x.h"
+
+/**
+ * @file vl53l0x_platform.h
+ *
+ * @brief All end user OS/platform/application porting
+ */
+
+/**
+ * @defgroup VL_platform_group VL53L0 Platform Functions
+ * @brief VL53L0 Platform Functions
+ * @{
+ */
+
+/**
+ * @def PALDevDataGet
+ * @brief Get ST private structure @a struct VL_DevData_t data access
+ *
+ * @param Dev Device Handle
+ * @param field ST structure field name
+ * It maybe used and as real data "ref" not just as "get" for sub-structure item
+ * like PALDevDataGet(FilterData.field)[i] or
+ * PALDevDataGet(FilterData.MeasurementIndex)++
+ */
+#define PALDevDataGet(Dev, field) (Dev->Data.field)
+
+/**
+ * @def PALDevDataSet(Dev, field, data)
+ * @brief Set ST private structure @a struct VL_DevData_t data field
+ * @param Dev Device Handle
+ * @param field ST structure field na*me
+ * @param data Data to be set
+ */
+#define PALDevDataSet(Dev, field, data) ((Dev->Data.field) = (data))
+
+
+/**
+ * @defgroup VL_registerAccess_group PAL Register Access Functions
+ * @brief PAL Register Access Functions
+ * @{
+ */
+
+/**
+ * Lock comms interface to serialize all commands to a shared I2C interface
+ * for a specific device
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_LockSequenceAccess(struct vl_data *Dev);
+
+/**
+ * Unlock comms interface to serialize all commands to a shared I2C interface
+ * for a specific device
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_UnlockSequenceAccess(struct vl_data *Dev);
+
+
+/**
+ * Writes the supplied byte buffer to the device
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param pdata Pointer to uint8_t buffer containing the data
+ * to be written
+ * @param count Number of bytes in the supplied byte buffer
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_WriteMulti(struct vl_data *Dev, uint8_t index,
+ uint8_t *pdata, uint32_t count);
+
+/**
+ * Reads the requested number of bytes from the device
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param pdata Pointer to the uint8_t buffer to store read data
+ * @param count Number of uint8_t's to read
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_ReadMulti(struct vl_data *Dev, uint8_t index,
+ uint8_t *pdata, uint32_t count);
+
+/**
+ * Write single byte register
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param data 8 bit register data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_WrByte(struct vl_data *Dev, uint8_t index, uint8_t data);
+
+/**
+ * Write word register
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param data 16 bit register data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_WrWord(struct vl_data *Dev, uint8_t index, uint16_t data);
+
+/**
+ * Write double word (4 byte) register
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param data 32 bit register data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_WrDWord(struct vl_data *Dev, uint8_t index, uint32_t data);
+
+/**
+ * Read single byte register
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param data pointer to 8 bit data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_RdByte(struct vl_data *Dev, uint8_t index, uint8_t *data);
+
+/**
+ * Read word (2byte) register
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param data pointer to 16 bit data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_RdWord(struct vl_data *Dev, uint8_t index, uint16_t *data);
+
+/**
+ * Read dword (4byte) register
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param data pointer to 32 bit data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_RdDWord(struct vl_data *Dev, uint8_t index, uint32_t *data);
+
+/**
+ * Threat safe Update (read/modify/write) single byte register
+ *
+ * Final_reg = (Initial_reg & and_data) |or_data
+ *
+ * @param Dev Device Handle
+ * @param index The register index
+ * @param AndData 8 bit and data
+ * @param OrData 8 bit or data
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_UpdateByte(struct vl_data *Dev, uint8_t index,
+ uint8_t AndData, uint8_t OrData);
+
+/** @} end of VL_registerAccess_group */
+
+
+/**
+ * @brief execute delay in all polling API call
+ *
+ * A typical multi-thread or RTOs implementation is to sleep the task for
+ * some 5ms (with 100Hz max rate faster polling is not needed)
+ * if nothing specific is need you can define it as an empty/void macro
+ * @code
+ * #define VL_PollingDelay(...) (void)0
+ * @endcode
+ * @param Dev Device Handle
+ * @return VL_ERROR_NONE Success
+ * @return "Other error code" See ::int8_t
+ */
+int8_t VL_PollingDelay(struct vl_data *Dev);
+/* usually best implemented as a real function */
+
+/** @} end of VL_platform_group */
+
+#endif /* _VL_PLATFORM_H_ */
+
+
+
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_platform_log.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_platform_log.h
new file mode 100644
index 0000000..a8f22c5
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_platform_log.h
@@ -0,0 +1,99 @@
+/*
+ * vl53l0x_platform_log.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_PLATFORM_LOG_H_
+#define _VL_PLATFORM_LOG_H_
+
+#include <linux/string.h>
+/* LOG Functions */
+
+
+/**
+ * @file vl53l0x_platform_log.h
+ *
+ * @brief platform log function definition
+ */
+
+/* #define VL_LOG_ENABLE */
+
+enum {
+ TRACE_LEVEL_NONE,
+ TRACE_LEVEL_ERRORS,
+ TRACE_LEVEL_WARNING,
+ TRACE_LEVEL_INFO,
+ TRACE_LEVEL_DEBUG,
+ TRACE_LEVEL_ALL,
+ TRACE_LEVEL_IGNORE
+};
+
+enum {
+ TRACE_FUNCTION_NONE = 0,
+ TRACE_FUNCTION_I2C = 1,
+ TRACE_FUNCTION_ALL = 0x7fffffff /* all bits except sign */
+};
+
+enum {
+ TRACE_MODULE_NONE = 0x0,
+ TRACE_MODULE_API = 0x1,
+ TRACE_MODULE_PLATFORM = 0x2,
+ TRACE_MODULE_ALL = 0x7fffffff /* all bits except sign */
+};
+
+
+#ifdef VL_LOG_ENABLE
+
+#include <linux/module.h>
+
+
+extern uint32_t _trace_level;
+
+
+
+int32_t VL_trace_config(char *filename, uint32_t modules,
+ uint32_t level, uint32_t functions);
+
+#define trace_print_module_function(...)
+
+#define LOG_GET_TIME() 0
+#define _LOG_FUNCTION_START(module, fmt, ...) \
+ dbg("beg %s start @%d\t" fmt "\n", \
+ __func__, LOG_GET_TIME(), ##__VA_ARGS__)
+
+#define _LOG_FUNCTION_END(module, status, ...)\
+ dbg("end %s start @%d Status %d\n", \
+ __func__, LOG_GET_TIME(), (int)status)
+
+#define _LOG_FUNCTION_END_FMT(module, status, fmt, ...)\
+ dbg("End %s @%d %d\t"fmt"\n", \
+ __func__, LOG_GET_TIME(), (int)status, ##__VA_ARGS__)
+
+
+#else /* VL_LOG_ENABLE no logging */
+ #define VL_ErrLog(...) (void)0
+ #define _LOG_FUNCTION_START(module, fmt, ...) (void)0
+ #define _LOG_FUNCTION_END(module, status, ...) (void)0
+ #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) (void)0
+#endif /* else */
+
+#define VL_COPYSTRING(str, ...) strlcpy(str, ##__VA_ARGS__, sizeof(str))
+
+
+#endif /* _VL_PLATFORM_LOG_H_ */
+
+
+
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_tuning.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_tuning.h
new file mode 100644
index 0000000..ba2998a
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_tuning.h
@@ -0,0 +1,135 @@
+/*
+ * vl53l0x_tuning.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VL_TUNING_H_
+#define _VL_TUNING_H_
+
+#include "vl53l0x_def.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+uint8_t DefaultTuningSettings[] = {
+
+ /* update 02/11/2015_v36 */
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x00, 0x00,
+
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x09, 0x00,
+ 0x01, 0x10, 0x00,
+ 0x01, 0x11, 0x00,
+
+ 0x01, 0x24, 0x01,
+ 0x01, 0x25, 0xff,
+ 0x01, 0x75, 0x00,
+
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x4e, 0x2c,
+ 0x01, 0x48, 0x00,
+ 0x01, 0x30, 0x20,
+
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x30, 0x09, /* mja changed from 0x64. */
+ 0x01, 0x54, 0x00,
+ 0x01, 0x31, 0x04,
+ 0x01, 0x32, 0x03,
+ 0x01, 0x40, 0x83,
+ 0x01, 0x46, 0x25,
+ 0x01, 0x60, 0x00,
+ 0x01, 0x27, 0x00,
+ 0x01, 0x50, 0x06,
+ 0x01, 0x51, 0x00,
+ 0x01, 0x52, 0x96,
+ 0x01, 0x56, 0x08,
+ 0x01, 0x57, 0x30,
+ 0x01, 0x61, 0x00,
+ 0x01, 0x62, 0x00,
+ 0x01, 0x64, 0x00,
+ 0x01, 0x65, 0x00,
+ 0x01, 0x66, 0xa0,
+
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x22, 0x32,
+ 0x01, 0x47, 0x14,
+ 0x01, 0x49, 0xff,
+ 0x01, 0x4a, 0x00,
+
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x7a, 0x0a,
+ 0x01, 0x7b, 0x00,
+ 0x01, 0x78, 0x21,
+
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x23, 0x34,
+ 0x01, 0x42, 0x00,
+ 0x01, 0x44, 0xff,
+ 0x01, 0x45, 0x26,
+ 0x01, 0x46, 0x05,
+ 0x01, 0x40, 0x40,
+ 0x01, 0x0E, 0x06,
+ 0x01, 0x20, 0x1a,
+ 0x01, 0x43, 0x40,
+
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x34, 0x03,
+ 0x01, 0x35, 0x44,
+
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x31, 0x04,
+ 0x01, 0x4b, 0x09,
+ 0x01, 0x4c, 0x05,
+ 0x01, 0x4d, 0x04,
+
+
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x44, 0x00,
+ 0x01, 0x45, 0x20,
+ 0x01, 0x47, 0x08,
+ 0x01, 0x48, 0x28,
+ 0x01, 0x67, 0x00,
+ 0x01, 0x70, 0x04,
+ 0x01, 0x71, 0x01,
+ 0x01, 0x72, 0xfe,
+ 0x01, 0x76, 0x00,
+ 0x01, 0x77, 0x00,
+
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x0d, 0x01,
+
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x80, 0x01,
+ 0x01, 0x01, 0xF8,
+
+ 0x01, 0xFF, 0x01,
+ 0x01, 0x8e, 0x01,
+ 0x01, 0x00, 0x01,
+ 0x01, 0xFF, 0x00,
+ 0x01, 0x80, 0x00,
+
+ 0x00, 0x00, 0x00
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VL_TUNING_H_ */
diff --git a/drivers/input/misc/vl53l0x/inc/vl53l0x_types.h b/drivers/input/misc/vl53l0x/inc/vl53l0x_types.h
new file mode 100644
index 0000000..be40e83
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/inc/vl53l0x_types.h
@@ -0,0 +1,54 @@
+/*
+ * vl53l0x_types.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef VL_TYPES_H_
+#define VL_TYPES_H_
+
+#include <linux/types.h>
+
+#ifndef NULL
+#error "TODO review NULL definition or add required include "
+#define NULL 0
+#endif
+
+#if !defined(STDINT_H) && !defined(_GCC_STDINT_H) \
+ && !defined(_STDINT_H) && !defined(_LINUX_TYPES_H)
+
+#pragma message(
+"Review type definition of STDINT define for your platform and add to above")
+
+/*
+ * target platform do not provide stdint or use a different #define than above
+ * to avoid seeing the message below addapt the #define list above or implement
+ * all type and delete these pragma
+ */
+
+unsigned int uint32_t;
+int int32_t;
+
+unsigned short uint16_t;
+short int16_t;
+
+unsigned char uint8_t;
+
+signed char int8_t;
+
+
+#endif /* _STDINT_H */
+
+#endif /* VL_TYPES_H_ */
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_api.c b/drivers/input/misc/vl53l0x/src/vl53l0x_api.c
new file mode 100644
index 0000000..888947f
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_api.c
@@ -0,0 +1,3096 @@
+/*
+ * vl53l0x_api.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "vl53l0x_api.h"
+#include "vl53l0x_tuning.h"
+#include "vl53l0x_interrupt_threshold_settings.h"
+#include "vl53l0x_api_core.h"
+#include "vl53l0x_api_calibration.h"
+#include "vl53l0x_api_strings.h"
+
+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
+#define LOG_FUNCTION_START(fmt, ...) \
+ _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__)
+#define LOG_FUNCTION_END(status, ...) \
+ _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__)
+#define LOG_FUNCTION_END_FMT(status, fmt, ...) \
+ _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__)
+
+#ifdef VL_LOG_ENABLE
+#define trace_print(level, ...) trace_print_module_function(TRACE_MODULE_API, \
+ level, TRACE_FUNCTION_NONE, ##__VA_ARGS__)
+#endif
+
+/* Group PAL General Functions */
+
+int8_t VL_GetVersion(struct VL_Version_t *pVersion)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ pVersion->major = VL_IMPLEMENTATION_VER_MAJOR;
+ pVersion->minor = VL_IMPLEMENTATION_VER_MINOR;
+ pVersion->build = VL_IMPLEMENTATION_VER_SUB;
+
+ pVersion->revision = VL_IMPLEMENTATION_VER_REVISION;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetPalSpecVersion(struct VL_Version_t *pPalSpecVersion)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ pPalSpecVersion->major = VL_SPECIFICATION_VER_MAJOR;
+ pPalSpecVersion->minor = VL_SPECIFICATION_VER_MINOR;
+ pPalSpecVersion->build = VL_SPECIFICATION_VER_SUB;
+
+ pPalSpecVersion->revision = VL_SPECIFICATION_VER_REVISION;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetProductRevision(struct vl_data *Dev,
+ uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t revision_id;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_IDENTIFICATION_REVISION_ID,
+ &revision_id);
+ *pProductRevisionMajor = 1;
+ *pProductRevisionMinor = (revision_id & 0xF0) >> 4;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+
+}
+
+int8_t VL_GetDeviceInfo(struct vl_data *Dev,
+ struct VL_DeviceInfo_t *pVL_DeviceInfo)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_device_info(Dev, pVL_DeviceInfo);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetDeviceErrorStatus(struct vl_data *Dev,
+ uint8_t *pDeviceErrorStatus)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t RangeStatus;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_RESULT_RANGE_STATUS,
+ &RangeStatus);
+
+ *pDeviceErrorStatus = (uint8_t)((RangeStatus & 0x78) >> 3);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+
+int8_t VL_GetDeviceErrorString(uint8_t ErrorCode,
+ char *pDeviceErrorString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_device_error_string(ErrorCode, pDeviceErrorString);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetRangeStatusString(uint8_t RangeStatus,
+ char *pRangeStatusString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_range_status_string(RangeStatus,
+ pRangeStatusString);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetPalErrorString(int8_t PalErrorCode,
+ char *pPalErrorString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_pal_error_string(PalErrorCode, pPalErrorString);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetPalStateString(uint8_t PalStateCode,
+ char *pPalStateString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_pal_state_string(PalStateCode, pPalStateString);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetPalState(struct vl_data *Dev, uint8_t *pPalState)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pPalState = PALDevDataGet(Dev, PalState);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetPowerMode(struct vl_data *Dev,
+ uint8_t PowerMode)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ /* Only level1 of Power mode exists */
+ if ((PowerMode != VL_POWERMODE_STANDBY_LEVEL1)
+ && (PowerMode != VL_POWERMODE_IDLE_LEVEL1)) {
+ Status = VL_ERROR_MODE_NOT_SUPPORTED;
+ } else if (PowerMode == VL_POWERMODE_STANDBY_LEVEL1) {
+ /* set the standby level1 of power mode */
+ Status = VL_WrByte(Dev, 0x80, 0x00);
+ if (Status == VL_ERROR_NONE) {
+ /* Set PAL State to standby */
+ PALDevDataSet(Dev, PalState, VL_STATE_STANDBY);
+ PALDevDataSet(Dev, PowerMode,
+ VL_POWERMODE_STANDBY_LEVEL1);
+ }
+
+ } else {
+ /* VL_POWERMODE_IDLE_LEVEL1 */
+ Status = VL_WrByte(Dev, 0x80, 0x00);
+ if (Status == VL_ERROR_NONE)
+ Status = VL_StaticInit(Dev);
+
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, PowerMode,
+ VL_POWERMODE_IDLE_LEVEL1);
+
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetPowerMode(struct vl_data *Dev,
+ uint8_t *pPowerMode)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte;
+
+ LOG_FUNCTION_START("");
+
+ /* Only level1 of Power mode exists */
+ Status = VL_RdByte(Dev, 0x80, &Byte);
+
+ if (Status == VL_ERROR_NONE) {
+ if (Byte == 1) {
+ PALDevDataSet(Dev, PowerMode,
+ VL_POWERMODE_IDLE_LEVEL1);
+ } else {
+ PALDevDataSet(Dev, PowerMode,
+ VL_POWERMODE_STANDBY_LEVEL1);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetOffsetCalibrationDataMicroMeter(struct vl_data *Dev,
+ int32_t OffsetCalibrationDataMicroMeter)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_set_offset_calibration_data_micro_meter(Dev,
+ OffsetCalibrationDataMicroMeter);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetOffsetCalibrationDataMicroMeter(struct vl_data *Dev,
+ int32_t *pOffsetCalibrationDataMicroMeter)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_offset_calibration_data_micro_meter(Dev,
+ pOffsetCalibrationDataMicroMeter);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetLinearityCorrectiveGain(struct vl_data *Dev,
+ int16_t LinearityCorrectiveGain)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ if ((LinearityCorrectiveGain < 0) || (LinearityCorrectiveGain > 1000))
+ Status = VL_ERROR_INVALID_PARAMS;
+ else {
+ PALDevDataSet(Dev, LinearityCorrectiveGain,
+ LinearityCorrectiveGain);
+
+ if (LinearityCorrectiveGain != 1000) {
+ /* Disable FW Xtalk */
+ Status = VL_WrWord(Dev,
+ VL_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, 0);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetLinearityCorrectiveGain(struct vl_data *Dev,
+ uint16_t *pLinearityCorrectiveGain)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pLinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetGroupParamHold(struct vl_data *Dev, uint8_t GroupParamHold)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetUpperLimitMilliMeter(struct vl_data *Dev,
+ uint16_t *pUpperLimitMilliMeter)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetTotalSignalRate(struct vl_data *Dev,
+ unsigned int *pTotalSignalRate)
+{
+ int8_t Status = VL_ERROR_NONE;
+ struct VL_RangingMeasurementData_t LastRangeDataBuffer;
+
+ LOG_FUNCTION_START("");
+
+ LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure);
+
+ Status = VL_get_total_signal_rate(
+ Dev, &LastRangeDataBuffer, pTotalSignalRate);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/* End Group PAL General Functions */
+
+/* Group PAL Init Functions */
+int8_t VL_SetDeviceAddress(struct vl_data *Dev, uint8_t DeviceAddress)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_WrByte(Dev, VL_REG_I2C_SLAVE_DEVICE_ADDRESS,
+ DeviceAddress / 2);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_DataInit(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ struct VL_DeviceParameters_t CurrentParameters;
+ int i;
+ uint8_t StopVariable;
+
+ LOG_FUNCTION_START("");
+
+ /* by default the I2C is running at 1V8 if you want to change it you */
+ /* need to include this define at compilation level. */
+#ifdef USE_I2C_2V8
+ Status = VL_UpdateByte(Dev,
+ VL_REG_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV,
+ 0xFE,
+ 0x01);
+#endif
+
+ /* Set I2C standard mode */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, 0x88, 0x00);
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone, 0);
+
+#ifdef USE_IQC_STATION
+ if (Status == VL_ERROR_NONE)
+ Status = VL_apply_offset_adjustment(Dev);
+#endif
+
+ /* Default value is 1000 for Linearity Corrective Gain */
+ PALDevDataSet(Dev, LinearityCorrectiveGain, 1000);
+
+ /* Dmax default Parameter */
+ PALDevDataSet(Dev, DmaxCalRangeMilliMeter, 400);
+ PALDevDataSet(Dev, DmaxCalSignalRateRtnMegaCps,
+ (unsigned int)((0x00016B85))); /* 1.42 No Cover Glass*/
+
+ /* Set Default static parameters */
+ /* *set first temporary values 9.44MHz * 65536 = 618660 */
+ VL_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, 618660);
+
+ /* Set Default XTalkCompensationRateMegaCps to 0 */
+ VL_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, 0);
+
+ /* Get default parameters */
+ Status = VL_GetDeviceParameters(Dev, &CurrentParameters);
+ if (Status == VL_ERROR_NONE) {
+ /* initialize PAL values */
+ CurrentParameters.DeviceMode = VL_DEVICEMODE_SINGLE_RANGING;
+ CurrentParameters.HistogramMode = VL_HISTOGRAMMODE_DISABLED;
+ PALDevDataSet(Dev, CurrentParameters, CurrentParameters);
+ }
+
+ /* Sigma estimator variable */
+ PALDevDataSet(Dev, SigmaEstRefArray, 100);
+ PALDevDataSet(Dev, SigmaEstEffPulseWidth, 900);
+ PALDevDataSet(Dev, SigmaEstEffAmbWidth, 500);
+ PALDevDataSet(Dev, targetRefRate, 0x0A00); /* 20 MCPS in 9:7 format */
+
+ /* Use internal default settings */
+ PALDevDataSet(Dev, UseInternalTuningSettings, 1);
+
+ Status |= VL_WrByte(Dev, 0x80, 0x01);
+ Status |= VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x00);
+ Status |= VL_RdByte(Dev, 0x91, &StopVariable);
+ PALDevDataSet(Dev, StopVariable, StopVariable);
+ Status |= VL_WrByte(Dev, 0x00, 0x01);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+ Status |= VL_WrByte(Dev, 0x80, 0x00);
+
+ /* Enable all check */
+ for (i = 0; i < VL_CHECKENABLE_NUMBER_OF_CHECKS; i++) {
+ if (Status == VL_ERROR_NONE)
+ Status |= VL_SetLimitCheckEnable(Dev, i, 1);
+ else
+ break;
+
+ }
+
+ /* Disable the following checks */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP, 0);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGNAL_RATE_MSRC, 0);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE, 0);
+
+ /* Limit default values */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_SetLimitCheckValue(Dev,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ (unsigned int)(18 * 65536));
+ }
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_SetLimitCheckValue(Dev,
+ VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
+ (unsigned int)(25 * 65536 / 100));
+ /* 0.25 * 65536 */
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_SetLimitCheckValue(Dev,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP,
+ (unsigned int)(35 * 65536));
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_SetLimitCheckValue(Dev,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD,
+ (unsigned int)(0 * 65536));
+ }
+
+ if (Status == VL_ERROR_NONE) {
+
+ PALDevDataSet(Dev, SequenceConfig, 0xFF);
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ 0xFF);
+
+ /* Set PAL state to tell that we are waiting for call to */
+ /* * VL_StaticInit */
+ PALDevDataSet(Dev, PalState, VL_STATE_WAIT_STATICINIT);
+ }
+
+ if (Status == VL_ERROR_NONE)
+ VL_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 0);
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetTuningSettingBuffer(struct vl_data *Dev,
+ uint8_t *pTuningSettingBuffer, uint8_t UseInternalTuningSettings)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ if (UseInternalTuningSettings == 1) {
+ /* Force use internal settings */
+ PALDevDataSet(Dev, UseInternalTuningSettings, 1);
+ } else {
+
+ /* check that the first byte is not 0 */
+ if (*pTuningSettingBuffer != 0) {
+ PALDevDataSet(Dev, pTuningSettingsPointer,
+ pTuningSettingBuffer);
+ PALDevDataSet(Dev, UseInternalTuningSettings, 0);
+
+ } else {
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetTuningSettingBuffer(struct vl_data *Dev,
+ uint8_t **ppTuningSettingBuffer, uint8_t *pUseInternalTuningSettings)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *ppTuningSettingBuffer = PALDevDataGet(Dev, pTuningSettingsPointer);
+ *pUseInternalTuningSettings = PALDevDataGet(Dev,
+ UseInternalTuningSettings);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_StaticInit(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ struct VL_DeviceParameters_t CurrentParameters = {0};
+ uint8_t *pTuningSettingBuffer;
+ uint16_t tempword = 0;
+ uint8_t tempbyte = 0;
+ uint8_t UseInternalTuningSettings = 0;
+ uint32_t count = 0;
+ uint8_t isApertureSpads = 0;
+ uint32_t refSpadCount = 0;
+ uint8_t ApertureSpads = 0;
+ uint8_t vcselPulsePeriodPCLK;
+ uint32_t seqTimeoutMicroSecs;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_info_from_device(Dev, 1);
+
+ /* set the ref spad from NVM */
+ count = (uint32_t)VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadCount);
+ ApertureSpads = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadType);
+
+ /* NVM value invalid */
+ if ((ApertureSpads > 1) ||
+ ((ApertureSpads == 1) && (count > 32)) ||
+ ((ApertureSpads == 0) && (count > 12)))
+ Status = VL_perform_ref_spad_management(Dev, &refSpadCount,
+ &isApertureSpads);
+ else
+ Status = VL_set_reference_spads(Dev, count, ApertureSpads);
+
+
+ /* Initialize tuning settings buffer to prevent compiler warning. */
+ pTuningSettingBuffer = DefaultTuningSettings;
+
+ if (Status == VL_ERROR_NONE) {
+ UseInternalTuningSettings = PALDevDataGet(Dev,
+ UseInternalTuningSettings);
+
+ if (UseInternalTuningSettings == 0)
+ pTuningSettingBuffer = PALDevDataGet(Dev,
+ pTuningSettingsPointer);
+ else
+ pTuningSettingBuffer = DefaultTuningSettings;
+
+ }
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_load_tuning_settings(Dev,
+ pTuningSettingBuffer);
+
+
+ /* Set interrupt config to new sample ready */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_SetGpioConfig(Dev, 0, 0,
+ VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY,
+ VL_INTERRUPTPOLARITY_LOW);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_RdWord(Dev, 0x84, &tempword);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz,
+ VL_FIXPOINT412TOFIXPOINT1616(tempword));
+ }
+
+ /* After static init, some device parameters may be changed, */
+ /* * so update them */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetDeviceParameters(Dev, &CurrentParameters);
+
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetFractionEnable(Dev, &tempbyte);
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, RangeFractionalEnable, tempbyte);
+
+ }
+
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, CurrentParameters, CurrentParameters);
+
+
+ /* read the sequence config and save it */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdByte(Dev,
+ VL_REG_SYSTEM_SEQUENCE_CONFIG, &tempbyte);
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, SequenceConfig, tempbyte);
+
+ }
+
+ /* Disable MSRC and TCC by default */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetSequenceStepEnable(Dev,
+ VL_SEQUENCESTEP_TCC, 0);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetSequenceStepEnable(Dev,
+ VL_SEQUENCESTEP_MSRC, 0);
+
+
+ /* Set PAL State to standby */
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, PalState, VL_STATE_IDLE);
+
+
+
+ /* Store pre-range vcsel period */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetVcselPulsePeriod(
+ Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &vcselPulsePeriodPCLK);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev, PreRangeVcselPulsePeriod,
+ vcselPulsePeriodPCLK);
+ }
+
+ /* Store final-range vcsel period */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetVcselPulsePeriod(
+ Dev,
+ VL_VCSEL_PERIOD_FINAL_RANGE,
+ &vcselPulsePeriodPCLK);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev, FinalRangeVcselPulsePeriod,
+ vcselPulsePeriodPCLK);
+ }
+
+ /* Store pre-range timeout */
+ if (Status == VL_ERROR_NONE) {
+ Status = get_sequence_step_timeout(
+ Dev,
+ VL_SEQUENCESTEP_PRE_RANGE,
+ &seqTimeoutMicroSecs);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev,
+ PreRangeTimeoutMicroSecs,
+ seqTimeoutMicroSecs);
+ }
+
+ /* Store final-range timeout */
+ if (Status == VL_ERROR_NONE) {
+ Status = get_sequence_step_timeout(
+ Dev,
+ VL_SEQUENCESTEP_FINAL_RANGE,
+ &seqTimeoutMicroSecs);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev,
+ FinalRangeTimeoutMicroSecs,
+ seqTimeoutMicroSecs);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_WaitDeviceBooted(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_ResetDevice(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte;
+
+ LOG_FUNCTION_START("");
+
+ /* Set reset bit */
+ Status = VL_WrByte(Dev, VL_REG_SOFT_RESET_GO2_SOFT_RESET_N,
+ 0x00);
+
+ /* Wait for some time */
+ if (Status == VL_ERROR_NONE) {
+ do {
+ Status = VL_RdByte(Dev,
+ VL_REG_IDENTIFICATION_MODEL_ID, &Byte);
+ } while (Byte != 0x00);
+ }
+
+ VL_PollingDelay(Dev);
+
+ /* Release reset */
+ Status = VL_WrByte(Dev, VL_REG_SOFT_RESET_GO2_SOFT_RESET_N,
+ 0x01);
+
+ /* Wait until correct boot-up of the device */
+ if (Status == VL_ERROR_NONE) {
+ do {
+ Status = VL_RdByte(Dev,
+ VL_REG_IDENTIFICATION_MODEL_ID, &Byte);
+ } while (Byte == 0x00);
+ }
+
+ VL_PollingDelay(Dev);
+
+ /* Set PAL State to VL_STATE_POWERDOWN */
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, PalState, VL_STATE_POWERDOWN);
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+/* End Group PAL Init Functions */
+
+/* Group PAL Parameters Functions */
+int8_t VL_SetDeviceParameters(struct vl_data *Dev,
+ const struct VL_DeviceParameters_t *pDeviceParameters)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int i;
+
+ LOG_FUNCTION_START("");
+ Status = VL_SetDeviceMode(Dev, pDeviceParameters->DeviceMode);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetInterMeasurementPeriodMilliSeconds(Dev,
+ pDeviceParameters->InterMeasurementPeriodMilliSeconds);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetXTalkCompensationRateMegaCps(Dev,
+ pDeviceParameters->XTalkCompensationRateMegaCps);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetOffsetCalibrationDataMicroMeter(Dev,
+ pDeviceParameters->RangeOffsetMicroMeters);
+
+
+ for (i = 0; i < VL_CHECKENABLE_NUMBER_OF_CHECKS; i++) {
+ if (Status == VL_ERROR_NONE)
+ Status |= VL_SetLimitCheckEnable(Dev, i,
+ pDeviceParameters->LimitChecksEnable[i]);
+ else
+ break;
+
+ if (Status == VL_ERROR_NONE)
+ Status |= VL_SetLimitCheckValue(Dev, i,
+ pDeviceParameters->LimitChecksValue[i]);
+ else
+ break;
+
+ }
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetWrapAroundCheckEnable(Dev,
+ pDeviceParameters->WrapAroundCheckEnable);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetMeasurementTimingBudgetMicroSeconds(Dev,
+ pDeviceParameters->MeasurementTimingBudgetMicroSeconds);
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetDeviceParameters(struct vl_data *Dev,
+ struct VL_DeviceParameters_t *pDeviceParameters)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int i;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_GetDeviceMode(Dev, &(pDeviceParameters->DeviceMode));
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetInterMeasurementPeriodMilliSeconds(Dev,
+ &(pDeviceParameters->InterMeasurementPeriodMilliSeconds));
+
+
+ if (Status == VL_ERROR_NONE)
+ pDeviceParameters->XTalkCompensationEnable = 0;
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetXTalkCompensationRateMegaCps(Dev,
+ &(pDeviceParameters->XTalkCompensationRateMegaCps));
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetOffsetCalibrationDataMicroMeter(Dev,
+ &(pDeviceParameters->RangeOffsetMicroMeters));
+
+
+ if (Status == VL_ERROR_NONE) {
+ for (i = 0; i < VL_CHECKENABLE_NUMBER_OF_CHECKS; i++) {
+ /* get first the values, then the enables.
+ * VL_GetLimitCheckValue will modify the enable
+ * flags
+ */
+ if (Status == VL_ERROR_NONE) {
+ Status |= VL_GetLimitCheckValue(Dev, i,
+ &(pDeviceParameters->LimitChecksValue[i]));
+ } else {
+ break;
+ }
+ if (Status == VL_ERROR_NONE) {
+ Status |= VL_GetLimitCheckEnable(Dev, i,
+ &(pDeviceParameters->LimitChecksEnable[i]));
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetWrapAroundCheckEnable(Dev,
+ &(pDeviceParameters->WrapAroundCheckEnable));
+ }
+
+ /* Need to be done at the end as it uses VCSELPulsePeriod */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetMeasurementTimingBudgetMicroSeconds(Dev,
+ &(pDeviceParameters->MeasurementTimingBudgetMicroSeconds));
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetDeviceMode(struct vl_data *Dev,
+ uint8_t DeviceMode)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("%d", (int)DeviceMode);
+
+ switch (DeviceMode) {
+ case VL_DEVICEMODE_SINGLE_RANGING:
+ case VL_DEVICEMODE_CONTINUOUS_RANGING:
+ case VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING:
+ case VL_DEVICEMODE_GPIO_DRIVE:
+ case VL_DEVICEMODE_GPIO_OSC:
+ /* Supported modes */
+ VL_SETPARAMETERFIELD(Dev, DeviceMode, DeviceMode);
+ break;
+ default:
+ /* Unsupported mode */
+ Status = VL_ERROR_MODE_NOT_SUPPORTED;
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetDeviceMode(struct vl_data *Dev,
+ uint8_t *pDeviceMode)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ VL_GETPARAMETERFIELD(Dev, DeviceMode, *pDeviceMode);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetRangeFractionEnable(struct vl_data *Dev, uint8_t Enable)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("%d", (int)Enable);
+
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_RANGE_CONFIG, Enable);
+
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, RangeFractionalEnable, Enable);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetFractionEnable(struct vl_data *Dev, uint8_t *pEnabled)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_SYSTEM_RANGE_CONFIG, pEnabled);
+
+ if (Status == VL_ERROR_NONE)
+ *pEnabled = (*pEnabled & 1);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetHistogramMode(struct vl_data *Dev,
+ uint8_t HistogramMode)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetHistogramMode(struct vl_data *Dev,
+ uint8_t *pHistogramMode)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetMeasurementTimingBudgetMicroSeconds(struct vl_data *Dev,
+ uint32_t MeasurementTimingBudgetMicroSeconds)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_set_measurement_timing_budget_micro_seconds(Dev,
+ MeasurementTimingBudgetMicroSeconds);
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_GetMeasurementTimingBudgetMicroSeconds(struct vl_data *Dev,
+ uint32_t *pMeasurementTimingBudgetMicroSeconds)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_measurement_timing_budget_micro_seconds(Dev,
+ pMeasurementTimingBudgetMicroSeconds);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetVcselPulsePeriod(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t VCSELPulsePeriodPCLK)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_set_vcsel_pulse_period(Dev, VcselPeriodType,
+ VCSELPulsePeriodPCLK);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetVcselPulsePeriod(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_vcsel_pulse_period(Dev, VcselPeriodType,
+ pVCSELPulsePeriodPCLK);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetSequenceStepEnable(struct vl_data *Dev,
+ uint8_t SequenceStepId, uint8_t SequenceStepEnabled)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SequenceConfig = 0;
+ uint8_t SequenceConfigNew = 0;
+ uint32_t MeasurementTimingBudgetMicroSeconds;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ &SequenceConfig);
+
+ SequenceConfigNew = SequenceConfig;
+
+ if (Status == VL_ERROR_NONE) {
+ if (SequenceStepEnabled == 1) {
+
+ /* Enable requested sequence step
+ */
+ switch (SequenceStepId) {
+ case VL_SEQUENCESTEP_TCC:
+ SequenceConfigNew |= 0x10;
+ break;
+ case VL_SEQUENCESTEP_DSS:
+ SequenceConfigNew |= 0x28;
+ break;
+ case VL_SEQUENCESTEP_MSRC:
+ SequenceConfigNew |= 0x04;
+ break;
+ case VL_SEQUENCESTEP_PRE_RANGE:
+ SequenceConfigNew |= 0x40;
+ break;
+ case VL_SEQUENCESTEP_FINAL_RANGE:
+ SequenceConfigNew |= 0x80;
+ break;
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ } else {
+ /* Disable requested sequence step
+ */
+ switch (SequenceStepId) {
+ case VL_SEQUENCESTEP_TCC:
+ SequenceConfigNew &= 0xef;
+ break;
+ case VL_SEQUENCESTEP_DSS:
+ SequenceConfigNew &= 0xd7;
+ break;
+ case VL_SEQUENCESTEP_MSRC:
+ SequenceConfigNew &= 0xfb;
+ break;
+ case VL_SEQUENCESTEP_PRE_RANGE:
+ SequenceConfigNew &= 0xbf;
+ break;
+ case VL_SEQUENCESTEP_FINAL_RANGE:
+ SequenceConfigNew &= 0x7f;
+ break;
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+ }
+
+ if (SequenceConfigNew != SequenceConfig) {
+ /* Apply New Setting */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_WrByte(Dev,
+ VL_REG_SYSTEM_SEQUENCE_CONFIG, SequenceConfigNew);
+ }
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, SequenceConfig, SequenceConfigNew);
+
+
+ /* Recalculate timing budget */
+ if (Status == VL_ERROR_NONE) {
+ VL_GETPARAMETERFIELD(Dev,
+ MeasurementTimingBudgetMicroSeconds,
+ MeasurementTimingBudgetMicroSeconds);
+
+ VL_SetMeasurementTimingBudgetMicroSeconds(Dev,
+ MeasurementTimingBudgetMicroSeconds);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t sequence_step_enabled(struct vl_data *Dev,
+ uint8_t SequenceStepId, uint8_t SequenceConfig,
+ uint8_t *pSequenceStepEnabled)
+{
+ int8_t Status = VL_ERROR_NONE;
+ *pSequenceStepEnabled = 0;
+
+ LOG_FUNCTION_START("");
+
+ switch (SequenceStepId) {
+ case VL_SEQUENCESTEP_TCC:
+ *pSequenceStepEnabled = (SequenceConfig & 0x10) >> 4;
+ break;
+ case VL_SEQUENCESTEP_DSS:
+ *pSequenceStepEnabled = (SequenceConfig & 0x08) >> 3;
+ break;
+ case VL_SEQUENCESTEP_MSRC:
+ *pSequenceStepEnabled = (SequenceConfig & 0x04) >> 2;
+ break;
+ case VL_SEQUENCESTEP_PRE_RANGE:
+ *pSequenceStepEnabled = (SequenceConfig & 0x40) >> 6;
+ break;
+ case VL_SEQUENCESTEP_FINAL_RANGE:
+ *pSequenceStepEnabled = (SequenceConfig & 0x80) >> 7;
+ break;
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetSequenceStepEnable(struct vl_data *Dev,
+ uint8_t SequenceStepId, uint8_t *pSequenceStepEnabled)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SequenceConfig = 0;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ &SequenceConfig);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = sequence_step_enabled(Dev, SequenceStepId,
+ SequenceConfig, pSequenceStepEnabled);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetSequenceStepEnables(struct vl_data *Dev,
+ struct VL_SchedulerSequenceSteps_t *pSchedulerSequenceSteps)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SequenceConfig = 0;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ &SequenceConfig);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = sequence_step_enabled(Dev,
+ VL_SEQUENCESTEP_TCC, SequenceConfig,
+ &pSchedulerSequenceSteps->TccOn);
+ }
+ if (Status == VL_ERROR_NONE) {
+ Status = sequence_step_enabled(Dev,
+ VL_SEQUENCESTEP_DSS, SequenceConfig,
+ &pSchedulerSequenceSteps->DssOn);
+ }
+ if (Status == VL_ERROR_NONE) {
+ Status = sequence_step_enabled(Dev,
+ VL_SEQUENCESTEP_MSRC, SequenceConfig,
+ &pSchedulerSequenceSteps->MsrcOn);
+ }
+ if (Status == VL_ERROR_NONE) {
+ Status = sequence_step_enabled(Dev,
+ VL_SEQUENCESTEP_PRE_RANGE, SequenceConfig,
+ &pSchedulerSequenceSteps->PreRangeOn);
+ }
+ if (Status == VL_ERROR_NONE) {
+ Status = sequence_step_enabled(Dev,
+ VL_SEQUENCESTEP_FINAL_RANGE, SequenceConfig,
+ &pSchedulerSequenceSteps->FinalRangeOn);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetNumberOfSequenceSteps(struct vl_data *Dev,
+ uint8_t *pNumberOfSequenceSteps)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pNumberOfSequenceSteps = VL_SEQUENCESTEP_NUMBER_OF_CHECKS;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetSequenceStepsInfo(
+ uint8_t SequenceStepId, char *pSequenceStepsString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_sequence_steps_info(
+ SequenceStepId,
+ pSequenceStepsString);
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_SetSequenceStepTimeout(struct vl_data *Dev,
+ uint8_t SequenceStepId, unsigned int TimeOutMilliSecs)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int8_t Status1 = VL_ERROR_NONE;
+ uint32_t TimeoutMicroSeconds = ((TimeOutMilliSecs * 1000) + 0x8000)
+ >> 16;
+ uint32_t MeasurementTimingBudgetMicroSeconds;
+ unsigned int OldTimeOutMicroSeconds;
+
+ LOG_FUNCTION_START("");
+
+ /* Read back the current value in case we need to revert back to this.
+ */
+ Status = get_sequence_step_timeout(Dev, SequenceStepId,
+ &OldTimeOutMicroSeconds);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = set_sequence_step_timeout(Dev, SequenceStepId,
+ TimeoutMicroSeconds);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_GETPARAMETERFIELD(Dev,
+ MeasurementTimingBudgetMicroSeconds,
+ MeasurementTimingBudgetMicroSeconds);
+
+ /* At this point we don't know if the requested */
+ /* value is valid, */
+ /* therefore proceed to update the entire timing budget and */
+ /* if this fails, revert back to the previous value. */
+ Status = VL_SetMeasurementTimingBudgetMicroSeconds(Dev,
+ MeasurementTimingBudgetMicroSeconds);
+
+ if (Status != VL_ERROR_NONE) {
+ Status1 = set_sequence_step_timeout(Dev, SequenceStepId,
+ OldTimeOutMicroSeconds);
+
+ if (Status1 == VL_ERROR_NONE) {
+ Status1 =
+ VL_SetMeasurementTimingBudgetMicroSeconds(
+ Dev,
+ MeasurementTimingBudgetMicroSeconds);
+ }
+
+ Status = Status1;
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_GetSequenceStepTimeout(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ unsigned int *pTimeOutMilliSecs)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint32_t TimeoutMicroSeconds;
+
+ LOG_FUNCTION_START("");
+
+ Status = get_sequence_step_timeout(Dev, SequenceStepId,
+ &TimeoutMicroSeconds);
+ if (Status == VL_ERROR_NONE) {
+ TimeoutMicroSeconds <<= 8;
+ *pTimeOutMilliSecs = (TimeoutMicroSeconds + 500)/1000;
+ *pTimeOutMilliSecs <<= 8;
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetInterMeasurementPeriodMilliSeconds(struct vl_data *Dev,
+ uint32_t InterMeasurementPeriodMilliSeconds)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t osc_calibrate_val;
+ uint32_t IMPeriodMilliSeconds;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdWord(Dev, VL_REG_OSC_CALIBRATE_VAL,
+ &osc_calibrate_val);
+
+ if (Status == VL_ERROR_NONE) {
+ if (osc_calibrate_val != 0) {
+ IMPeriodMilliSeconds =
+ InterMeasurementPeriodMilliSeconds
+ * osc_calibrate_val;
+ } else {
+ IMPeriodMilliSeconds =
+ InterMeasurementPeriodMilliSeconds;
+ }
+ Status = VL_WrDWord(Dev,
+ VL_REG_SYSTEM_INTERMEASUREMENT_PERIOD,
+ IMPeriodMilliSeconds);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETPARAMETERFIELD(Dev,
+ InterMeasurementPeriodMilliSeconds,
+ InterMeasurementPeriodMilliSeconds);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetInterMeasurementPeriodMilliSeconds(struct vl_data *Dev,
+ uint32_t *pInterMeasurementPeriodMilliSeconds)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t osc_calibrate_val;
+ uint32_t IMPeriodMilliSeconds;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdWord(Dev, VL_REG_OSC_CALIBRATE_VAL,
+ &osc_calibrate_val);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdDWord(Dev,
+ VL_REG_SYSTEM_INTERMEASUREMENT_PERIOD,
+ &IMPeriodMilliSeconds);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ if (osc_calibrate_val != 0) {
+ *pInterMeasurementPeriodMilliSeconds =
+ IMPeriodMilliSeconds / osc_calibrate_val;
+ }
+ VL_SETPARAMETERFIELD(Dev,
+ InterMeasurementPeriodMilliSeconds,
+ *pInterMeasurementPeriodMilliSeconds);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetXTalkCompensationEnable(struct vl_data *Dev,
+ uint8_t XTalkCompensationEnable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ unsigned int TempFix1616;
+ uint16_t LinearityCorrectiveGain;
+
+ LOG_FUNCTION_START("");
+
+ LinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain);
+
+ if ((XTalkCompensationEnable == 0)
+ || (LinearityCorrectiveGain != 1000)) {
+ TempFix1616 = 0;
+ } else {
+ VL_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps,
+ TempFix1616);
+ }
+
+ /* the following register has a format 3.13 */
+ Status = VL_WrWord(Dev,
+ VL_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS,
+ VL_FIXPOINT1616TOFIXPOINT313(TempFix1616));
+
+ if (Status == VL_ERROR_NONE) {
+ if (XTalkCompensationEnable == 0) {
+ VL_SETPARAMETERFIELD(Dev, XTalkCompensationEnable,
+ 0);
+ } else {
+ VL_SETPARAMETERFIELD(Dev, XTalkCompensationEnable,
+ 1);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetXTalkCompensationEnable(struct vl_data *Dev,
+ uint8_t *pXTalkCompensationEnable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Temp8;
+
+ LOG_FUNCTION_START("");
+
+ VL_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp8);
+ *pXTalkCompensationEnable = Temp8;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetXTalkCompensationRateMegaCps(struct vl_data *Dev,
+ unsigned int XTalkCompensationRateMegaCps)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Temp8;
+ uint16_t LinearityCorrectiveGain;
+ uint16_t data;
+
+ LOG_FUNCTION_START("");
+
+ VL_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp8);
+ LinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain);
+
+ if (Temp8 == 0) { /* disabled write only internal value */
+ VL_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps,
+ XTalkCompensationRateMegaCps);
+ } else {
+ /* the following register has a format 3.13 */
+ if (LinearityCorrectiveGain == 1000) {
+ data = VL_FIXPOINT1616TOFIXPOINT313(
+ XTalkCompensationRateMegaCps);
+ } else {
+ data = 0;
+ }
+
+ Status = VL_WrWord(Dev,
+ VL_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, data);
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETPARAMETERFIELD(Dev,
+ XTalkCompensationRateMegaCps,
+ XTalkCompensationRateMegaCps);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetXTalkCompensationRateMegaCps(struct vl_data *Dev,
+ unsigned int *pXTalkCompensationRateMegaCps)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t Value;
+ unsigned int TempFix1616;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdWord(Dev,
+ VL_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, (uint16_t *)&Value);
+ if (Status == VL_ERROR_NONE) {
+ if (Value == 0) {
+ /* the Xtalk is disabled return value from memory */
+ VL_GETPARAMETERFIELD(Dev,
+ XTalkCompensationRateMegaCps, TempFix1616);
+ *pXTalkCompensationRateMegaCps = TempFix1616;
+ VL_SETPARAMETERFIELD(Dev, XTalkCompensationEnable,
+ 0);
+ } else {
+ TempFix1616 = VL_FIXPOINT313TOFIXPOINT1616(Value);
+ *pXTalkCompensationRateMegaCps = TempFix1616;
+ VL_SETPARAMETERFIELD(Dev,
+ XTalkCompensationRateMegaCps, TempFix1616);
+ VL_SETPARAMETERFIELD(Dev, XTalkCompensationEnable,
+ 1);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetRefCalibration(struct vl_data *Dev, uint8_t VhvSettings,
+ uint8_t PhaseCal)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_set_ref_calibration(Dev, VhvSettings, PhaseCal);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetRefCalibration(struct vl_data *Dev, uint8_t *pVhvSettings,
+ uint8_t *pPhaseCal)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_ref_calibration(Dev, pVhvSettings, pPhaseCal);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/*
+ * CHECK LIMIT FUNCTIONS
+ */
+
+int8_t VL_GetNumberOfLimitCheck(uint16_t *pNumberOfLimitCheck)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pNumberOfLimitCheck = VL_CHECKENABLE_NUMBER_OF_CHECKS;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetLimitCheckInfo(struct vl_data *Dev, uint16_t LimitCheckId,
+ char *pLimitCheckString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_limit_check_info(Dev, LimitCheckId,
+ pLimitCheckString);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetLimitCheckStatus(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t *pLimitCheckStatus)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Temp8;
+
+ LOG_FUNCTION_START("");
+
+ if (LimitCheckId >= VL_CHECKENABLE_NUMBER_OF_CHECKS) {
+ Status = VL_ERROR_INVALID_PARAMS;
+ } else {
+
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksStatus,
+ LimitCheckId, Temp8);
+
+ *pLimitCheckStatus = Temp8;
+
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetLimitCheckEnable(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t LimitCheckEnable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ unsigned int TempFix1616 = 0;
+ uint8_t LimitCheckEnableInt = 0;
+ uint8_t LimitCheckDisable = 0;
+ uint8_t Temp8;
+
+ LOG_FUNCTION_START("");
+
+ if (LimitCheckId >= VL_CHECKENABLE_NUMBER_OF_CHECKS) {
+ Status = VL_ERROR_INVALID_PARAMS;
+ } else {
+ if (LimitCheckEnable == 0) {
+ TempFix1616 = 0;
+ LimitCheckEnableInt = 0;
+ LimitCheckDisable = 1;
+
+ } else {
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ LimitCheckId, TempFix1616);
+ LimitCheckDisable = 0;
+ /* this to be sure to have either 0 or 1 */
+ LimitCheckEnableInt = 1;
+ }
+
+ switch (LimitCheckId) {
+
+ case VL_CHECKENABLE_SIGMA_FINAL_RANGE:
+ /* internal computation: */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ LimitCheckEnableInt);
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE:
+
+ Status = VL_WrWord(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT,
+ VL_FIXPOINT1616TOFIXPOINT97(TempFix1616));
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_REF_CLIP:
+
+ /* internal computation: */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP,
+ LimitCheckEnableInt);
+
+ break;
+
+ case VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD:
+
+ /* internal computation: */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD,
+ LimitCheckEnableInt);
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_MSRC:
+
+ Temp8 = (uint8_t)(LimitCheckDisable << 1);
+ Status = VL_UpdateByte(Dev,
+ VL_REG_MSRC_CONFIG_CONTROL,
+ 0xFE, Temp8);
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE:
+
+ Temp8 = (uint8_t)(LimitCheckDisable << 4);
+ Status = VL_UpdateByte(Dev,
+ VL_REG_MSRC_CONFIG_CONTROL,
+ 0xEF, Temp8);
+
+ break;
+
+
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+
+ }
+
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ if (LimitCheckEnable == 0) {
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable,
+ LimitCheckId, 0);
+ } else {
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable,
+ LimitCheckId, 1);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetLimitCheckEnable(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t *pLimitCheckEnable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Temp8;
+
+ LOG_FUNCTION_START("");
+
+ if (LimitCheckId >= VL_CHECKENABLE_NUMBER_OF_CHECKS) {
+ Status = VL_ERROR_INVALID_PARAMS;
+ *pLimitCheckEnable = 0;
+ } else {
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable,
+ LimitCheckId, Temp8);
+ *pLimitCheckEnable = Temp8;
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetLimitCheckValue(struct vl_data *Dev, uint16_t LimitCheckId,
+ unsigned int LimitCheckValue)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Temp8;
+
+ LOG_FUNCTION_START("");
+
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, LimitCheckId,
+ Temp8);
+
+ if (Temp8 == 0) { /* disabled write only internal value */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ LimitCheckId, LimitCheckValue);
+ } else {
+
+ switch (LimitCheckId) {
+
+ case VL_CHECKENABLE_SIGMA_FINAL_RANGE:
+ /* internal computation: */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ LimitCheckValue);
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE:
+
+ Status = VL_WrWord(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT,
+ VL_FIXPOINT1616TOFIXPOINT97(
+ LimitCheckValue));
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_REF_CLIP:
+
+ /* internal computation: */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP,
+ LimitCheckValue);
+
+ break;
+
+ case VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD:
+
+ /* internal computation: */
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD,
+ LimitCheckValue);
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_MSRC:
+ case VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE:
+
+ Status = VL_WrWord(Dev,
+ VL_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT,
+ VL_FIXPOINT1616TOFIXPOINT97(
+ LimitCheckValue));
+
+ break;
+
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ LimitCheckId, LimitCheckValue);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetLimitCheckValue(struct vl_data *Dev, uint16_t LimitCheckId,
+ unsigned int *pLimitCheckValue)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t EnableZeroValue = 0;
+ uint16_t Temp16;
+ unsigned int TempFix1616;
+
+ LOG_FUNCTION_START("");
+
+ switch (LimitCheckId) {
+
+ case VL_CHECKENABLE_SIGMA_FINAL_RANGE:
+ /* internal computation: */
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE, TempFix1616);
+ EnableZeroValue = 0;
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE:
+ Status = VL_RdWord(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT,
+ &Temp16);
+ if (Status == VL_ERROR_NONE)
+ TempFix1616 = VL_FIXPOINT97TOFIXPOINT1616(Temp16);
+
+
+ EnableZeroValue = 1;
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_REF_CLIP:
+ /* internal computation: */
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP, TempFix1616);
+ EnableZeroValue = 0;
+ break;
+
+ case VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD:
+ /* internal computation: */
+ VL_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD, TempFix1616);
+ EnableZeroValue = 0;
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_MSRC:
+ case VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE:
+ Status = VL_RdWord(Dev,
+ VL_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT,
+ &Temp16);
+ if (Status == VL_ERROR_NONE)
+ TempFix1616 = VL_FIXPOINT97TOFIXPOINT1616(Temp16);
+
+
+ EnableZeroValue = 0;
+ break;
+
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+
+ }
+
+ if (Status == VL_ERROR_NONE) {
+
+ if (EnableZeroValue == 1) {
+
+ if (TempFix1616 == 0) {
+ /* disabled: return value from memory */
+ VL_GETARRAYPARAMETERFIELD(Dev,
+ LimitChecksValue, LimitCheckId,
+ TempFix1616);
+ *pLimitCheckValue = TempFix1616;
+ VL_SETARRAYPARAMETERFIELD(Dev,
+ LimitChecksEnable, LimitCheckId, 0);
+ } else {
+ *pLimitCheckValue = TempFix1616;
+ VL_SETARRAYPARAMETERFIELD(Dev,
+ LimitChecksValue, LimitCheckId,
+ TempFix1616);
+ VL_SETARRAYPARAMETERFIELD(Dev,
+ LimitChecksEnable, LimitCheckId, 1);
+ }
+ } else {
+ *pLimitCheckValue = TempFix1616;
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+
+}
+
+int8_t VL_GetLimitCheckCurrent(struct vl_data *Dev,
+ uint16_t LimitCheckId, unsigned int *pLimitCheckCurrent)
+{
+ int8_t Status = VL_ERROR_NONE;
+ struct VL_RangingMeasurementData_t LastRangeDataBuffer;
+
+ LOG_FUNCTION_START("");
+
+ if (LimitCheckId >= VL_CHECKENABLE_NUMBER_OF_CHECKS) {
+ Status = VL_ERROR_INVALID_PARAMS;
+ } else {
+ switch (LimitCheckId) {
+ case VL_CHECKENABLE_SIGMA_FINAL_RANGE:
+ /* Need to run a ranging to have the latest values */
+ *pLimitCheckCurrent = PALDevDataGet(Dev, SigmaEstimate);
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE:
+ /* Need to run a ranging to have the latest values */
+ LastRangeDataBuffer = PALDevDataGet(Dev,
+ LastRangeMeasure);
+ *pLimitCheckCurrent =
+ LastRangeDataBuffer.SignalRateRtnMegaCps;
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_REF_CLIP:
+ /* Need to run a ranging to have the latest values */
+ *pLimitCheckCurrent = PALDevDataGet(Dev,
+ LastSignalRefMcps);
+
+ break;
+
+ case VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD:
+ /* Need to run a ranging to have the latest values */
+ LastRangeDataBuffer = PALDevDataGet(Dev,
+ LastRangeMeasure);
+ *pLimitCheckCurrent =
+ LastRangeDataBuffer.SignalRateRtnMegaCps;
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_MSRC:
+ /* Need to run a ranging to have the latest values */
+ LastRangeDataBuffer = PALDevDataGet(Dev,
+ LastRangeMeasure);
+ *pLimitCheckCurrent =
+ LastRangeDataBuffer.SignalRateRtnMegaCps;
+
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE:
+ /* Need to run a ranging to have the latest values */
+ LastRangeDataBuffer = PALDevDataGet(Dev,
+ LastRangeMeasure);
+ *pLimitCheckCurrent =
+ LastRangeDataBuffer.SignalRateRtnMegaCps;
+
+ break;
+
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+
+}
+
+/*
+ * WRAPAROUND Check
+ */
+int8_t VL_SetWrapAroundCheckEnable(struct vl_data *Dev,
+ uint8_t WrapAroundCheckEnable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte;
+ uint8_t WrapAroundCheckEnableInt;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG, &Byte);
+ if (WrapAroundCheckEnable == 0) {
+ /* Disable wraparound */
+ Byte = Byte & 0x7F;
+ WrapAroundCheckEnableInt = 0;
+ } else {
+ /*Enable wraparound */
+ Byte = Byte | 0x80;
+ WrapAroundCheckEnableInt = 1;
+ }
+
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG, Byte);
+
+ if (Status == VL_ERROR_NONE) {
+ PALDevDataSet(Dev, SequenceConfig, Byte);
+ VL_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable,
+ WrapAroundCheckEnableInt);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetWrapAroundCheckEnable(struct vl_data *Dev,
+ uint8_t *pWrapAroundCheckEnable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t data;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG, &data);
+ if (Status == VL_ERROR_NONE) {
+ PALDevDataSet(Dev, SequenceConfig, data);
+ if (data & (0x01 << 7))
+ *pWrapAroundCheckEnable = 0x01;
+ else
+ *pWrapAroundCheckEnable = 0x00;
+ }
+ if (Status == VL_ERROR_NONE) {
+ VL_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable,
+ *pWrapAroundCheckEnable);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetDmaxCalParameters(struct vl_data *Dev,
+ uint16_t RangeMilliMeter, unsigned int SignalRateRtnMegaCps)
+{
+ int8_t Status = VL_ERROR_NONE;
+ unsigned int SignalRateRtnMegaCpsTemp = 0;
+
+ LOG_FUNCTION_START("");
+
+ /* Check if one of input parameter is zero, in that case the */
+ /* value are get from NVM */
+ if ((RangeMilliMeter == 0) || (SignalRateRtnMegaCps == 0)) {
+ /* NVM parameters */
+ /* Run VL_get_info_from_device wit option 4 to get */
+ /* signal rate at 400 mm if the value have been already */
+ /* get this function will return with no access to device */
+ VL_get_info_from_device(Dev, 4);
+
+ SignalRateRtnMegaCpsTemp = VL_GETDEVICESPECIFICPARAMETER(
+ Dev, SignalRateMeasFixed400mm);
+
+ PALDevDataSet(Dev, DmaxCalRangeMilliMeter, 400);
+ PALDevDataSet(Dev, DmaxCalSignalRateRtnMegaCps,
+ SignalRateRtnMegaCpsTemp);
+ } else {
+ /* User parameters */
+ PALDevDataSet(Dev, DmaxCalRangeMilliMeter, RangeMilliMeter);
+ PALDevDataSet(Dev, DmaxCalSignalRateRtnMegaCps,
+ SignalRateRtnMegaCps);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetDmaxCalParameters(struct vl_data *Dev,
+ uint16_t *pRangeMilliMeter, unsigned int *pSignalRateRtnMegaCps)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pRangeMilliMeter = PALDevDataGet(Dev, DmaxCalRangeMilliMeter);
+ *pSignalRateRtnMegaCps = PALDevDataGet(Dev,
+ DmaxCalSignalRateRtnMegaCps);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/* End Group PAL Parameters Functions */
+
+/* Group PAL Measurement Functions */
+int8_t VL_PerformSingleMeasurement(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t DeviceMode;
+
+ LOG_FUNCTION_START("");
+
+ /* Get Current DeviceMode */
+ Status = VL_GetDeviceMode(Dev, &DeviceMode);
+
+ /* Start immediately to run a single ranging measurement in case of */
+ /* single ranging or single histogram */
+ if (Status == VL_ERROR_NONE
+ && DeviceMode == VL_DEVICEMODE_SINGLE_RANGING)
+ Status = VL_StartMeasurement(Dev);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_measurement_poll_for_completion(Dev);
+
+
+ /* Change PAL State in case of single ranging or single histogram */
+ if (Status == VL_ERROR_NONE
+ && DeviceMode == VL_DEVICEMODE_SINGLE_RANGING)
+ PALDevDataSet(Dev, PalState, VL_STATE_IDLE);
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_PerformSingleHistogramMeasurement(struct vl_data *Dev,
+ struct VL_HistogramMeasurementData_t *pHistogramMeasurementData)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_PerformRefCalibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_perform_ref_calibration(Dev, pVhvSettings,
+ pPhaseCal, 1);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_PerformXTalkMeasurement(struct vl_data *Dev,
+ uint32_t TimeoutMs, unsigned int *pXtalkPerSpad,
+ uint8_t *pAmbientTooHigh)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented on VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_PerformXTalkCalibration(struct vl_data *Dev,
+ unsigned int XTalkCalDistance,
+ unsigned int *pXTalkCompensationRateMegaCps)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_perform_xtalk_calibration(Dev, XTalkCalDistance,
+ pXTalkCompensationRateMegaCps);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_PerformOffsetCalibration(struct vl_data *Dev,
+ unsigned int CalDistanceMilliMeter, int32_t *pOffsetMicroMeter)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_perform_offset_calibration(Dev, CalDistanceMilliMeter,
+ pOffsetMicroMeter);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_CheckAndLoadInterruptSettings(struct vl_data *Dev,
+ uint8_t StartNotStopFlag)
+{
+ uint8_t InterruptConfig;
+ unsigned int ThresholdLow;
+ unsigned int ThresholdHigh;
+ int8_t Status = VL_ERROR_NONE;
+
+ InterruptConfig = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ Pin0GpioFunctionality);
+
+ if ((InterruptConfig ==
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW) ||
+ (InterruptConfig ==
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH) ||
+ (InterruptConfig ==
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT)) {
+
+ Status = VL_GetInterruptThresholds(Dev,
+ VL_DEVICEMODE_CONTINUOUS_RANGING,
+ &ThresholdLow, &ThresholdHigh);
+
+ if (((ThresholdLow > 255*65536) ||
+ (ThresholdHigh > 255*65536)) &&
+ (Status == VL_ERROR_NONE)) {
+
+ if (StartNotStopFlag != 0) {
+ Status = VL_load_tuning_settings(Dev,
+ InterruptThresholdSettings);
+ } else {
+ Status |= VL_WrByte(Dev, 0xFF, 0x04);
+ Status |= VL_WrByte(Dev, 0x70, 0x00);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+ Status |= VL_WrByte(Dev, 0x80, 0x00);
+ }
+
+ }
+
+
+ }
+
+ return Status;
+
+}
+
+
+int8_t VL_StartMeasurement(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t DeviceMode;
+ uint8_t Byte;
+ uint8_t StartStopByte = VL_REG_SYSRANGE_MODE_START_STOP;
+ uint32_t LoopNb;
+
+ LOG_FUNCTION_START("");
+
+ /* Get Current DeviceMode */
+ VL_GetDeviceMode(Dev, &DeviceMode);
+
+ Status = VL_WrByte(Dev, 0x80, 0x01);
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status = VL_WrByte(Dev, 0x00, 0x00);
+ Status = VL_WrByte(Dev, 0x91, PALDevDataGet(Dev, StopVariable));
+ Status = VL_WrByte(Dev, 0x00, 0x01);
+ Status = VL_WrByte(Dev, 0xFF, 0x00);
+ Status = VL_WrByte(Dev, 0x80, 0x00);
+
+ switch (DeviceMode) {
+ case VL_DEVICEMODE_SINGLE_RANGING:
+ Status = VL_WrByte(Dev, VL_REG_SYSRANGE_START, 0x01);
+
+ Byte = StartStopByte;
+ if (Status == VL_ERROR_NONE) {
+ /* Wait until start bit has been cleared */
+ LoopNb = 0;
+ do {
+ if (LoopNb > 0)
+ Status = VL_RdByte(Dev,
+ VL_REG_SYSRANGE_START, &Byte);
+ LoopNb = LoopNb + 1;
+ } while (((Byte & StartStopByte) == StartStopByte)
+ && (Status == VL_ERROR_NONE)
+ && (LoopNb < VL_DEFAULT_MAX_LOOP));
+
+ if (LoopNb >= VL_DEFAULT_MAX_LOOP)
+ Status = VL_ERROR_TIME_OUT;
+
+ }
+
+ break;
+ case VL_DEVICEMODE_CONTINUOUS_RANGING:
+ /* Back-to-back mode */
+
+ /* Check if need to apply interrupt settings */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_CheckAndLoadInterruptSettings(Dev, 1);
+
+ Status = VL_WrByte(Dev,
+ VL_REG_SYSRANGE_START,
+ VL_REG_SYSRANGE_MODE_BACKTOBACK);
+ if (Status == VL_ERROR_NONE) {
+ /* Set PAL State to Running */
+ PALDevDataSet(Dev, PalState, VL_STATE_RUNNING);
+ }
+ break;
+ case VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING:
+ /* Continuous mode */
+ /* Check if need to apply interrupt settings */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_CheckAndLoadInterruptSettings(Dev, 1);
+
+ Status = VL_WrByte(Dev,
+ VL_REG_SYSRANGE_START,
+ VL_REG_SYSRANGE_MODE_TIMED);
+
+ if (Status == VL_ERROR_NONE) {
+ /* Set PAL State to Running */
+ PALDevDataSet(Dev, PalState, VL_STATE_RUNNING);
+ }
+ break;
+ default:
+ /* Selected mode not supported */
+ Status = VL_ERROR_MODE_NOT_SUPPORTED;
+ }
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_StopMeasurement(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_WrByte(Dev, VL_REG_SYSRANGE_START,
+ VL_REG_SYSRANGE_MODE_SINGLESHOT);
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status = VL_WrByte(Dev, 0x00, 0x00);
+ Status = VL_WrByte(Dev, 0x91, 0x00);
+ Status = VL_WrByte(Dev, 0x00, 0x01);
+ Status = VL_WrByte(Dev, 0xFF, 0x00);
+
+ if (Status == VL_ERROR_NONE) {
+ /* Set PAL State to Idle */
+ PALDevDataSet(Dev, PalState, VL_STATE_IDLE);
+ }
+
+ /* Check if need to apply interrupt settings */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_CheckAndLoadInterruptSettings(Dev, 0);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetMeasurementDataReady(struct vl_data *Dev,
+ uint8_t *pMeasurementDataReady)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SysRangeStatusRegister;
+ uint8_t InterruptConfig;
+ uint32_t InterruptMask;
+
+ LOG_FUNCTION_START("");
+
+ InterruptConfig = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ Pin0GpioFunctionality);
+
+ if (InterruptConfig ==
+ VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) {
+ Status = VL_GetInterruptMaskStatus(Dev, &InterruptMask);
+ if (InterruptMask ==
+ VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY)
+ *pMeasurementDataReady = 1;
+ else
+ *pMeasurementDataReady = 0;
+ } else {
+ Status = VL_RdByte(Dev, VL_REG_RESULT_RANGE_STATUS,
+ &SysRangeStatusRegister);
+ if (Status == VL_ERROR_NONE) {
+ if (SysRangeStatusRegister & 0x01)
+ *pMeasurementDataReady = 1;
+ else
+ *pMeasurementDataReady = 0;
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_WaitDeviceReadyForNewMeasurement(struct vl_data *Dev,
+ uint32_t MaxLoop)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented for VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+
+int8_t VL_GetRangingMeasurementData(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t DeviceRangeStatus;
+ uint8_t RangeFractionalEnable;
+ uint8_t PalRangeStatus;
+ uint8_t XTalkCompensationEnable;
+ uint16_t AmbientRate;
+ unsigned int SignalRate;
+ uint16_t XTalkCompensationRateMegaCps;
+ uint16_t EffectiveSpadRtnCount;
+ uint16_t tmpuint16;
+ uint16_t XtalkRangeMilliMeter;
+ uint16_t LinearityCorrectiveGain;
+ uint8_t localBuffer[12];
+ struct VL_RangingMeasurementData_t LastRangeDataBuffer;
+
+ LOG_FUNCTION_START("");
+
+ /*
+ * use multi read even if some registers are not useful, result will
+ * be more efficient
+ * start reading at 0x14 dec20
+ * end reading at 0x21 dec33 total 14 bytes to read
+ */
+ Status = VL_ReadMulti(Dev, 0x14, localBuffer, 12);
+
+ if (Status == VL_ERROR_NONE) {
+
+ pRangingMeasurementData->ZoneId = 0; /* Only one zone */
+ pRangingMeasurementData->TimeStamp = 0; /* Not Implemented */
+
+ tmpuint16 = VL_MAKEUINT16(localBuffer[11],
+ localBuffer[10]);
+ /* cut1.1 if SYSTEM__RANGE_CONFIG if 1 range is 2bits fractional
+ *(format 11.2) else no fractional
+ */
+
+ pRangingMeasurementData->MeasurementTimeUsec = 0;
+
+ SignalRate = VL_FIXPOINT97TOFIXPOINT1616(
+ VL_MAKEUINT16(localBuffer[7], localBuffer[6]));
+ /* peak_signal_count_rate_rtn_mcps */
+ pRangingMeasurementData->SignalRateRtnMegaCps = SignalRate;
+
+ AmbientRate = VL_MAKEUINT16(localBuffer[9],
+ localBuffer[8]);
+ pRangingMeasurementData->AmbientRateRtnMegaCps =
+ VL_FIXPOINT97TOFIXPOINT1616(AmbientRate);
+
+ EffectiveSpadRtnCount = VL_MAKEUINT16(localBuffer[3],
+ localBuffer[2]);
+ /* EffectiveSpadRtnCount is 8.8 format */
+ pRangingMeasurementData->EffectiveSpadRtnCount =
+ EffectiveSpadRtnCount;
+
+ DeviceRangeStatus = localBuffer[0];
+
+ /* Get Linearity Corrective Gain */
+ LinearityCorrectiveGain = PALDevDataGet(Dev,
+ LinearityCorrectiveGain);
+
+ /* Get ranging configuration */
+ RangeFractionalEnable = PALDevDataGet(Dev,
+ RangeFractionalEnable);
+
+ if (LinearityCorrectiveGain != 1000) {
+
+ tmpuint16 = (uint16_t)((LinearityCorrectiveGain
+ * tmpuint16 + 500) / 1000);
+
+ /* Implement Xtalk */
+ VL_GETPARAMETERFIELD(Dev,
+ XTalkCompensationRateMegaCps,
+ XTalkCompensationRateMegaCps);
+ VL_GETPARAMETERFIELD(Dev, XTalkCompensationEnable,
+ XTalkCompensationEnable);
+
+ if (XTalkCompensationEnable) {
+
+ if ((SignalRate
+ - ((XTalkCompensationRateMegaCps
+ * EffectiveSpadRtnCount) >> 8))
+ <= 0) {
+ if (RangeFractionalEnable)
+ XtalkRangeMilliMeter = 8888;
+ else
+ XtalkRangeMilliMeter = 8888
+ << 2;
+ } else {
+ XtalkRangeMilliMeter =
+ (tmpuint16 * SignalRate)
+ / (SignalRate
+ - ((XTalkCompensationRateMegaCps
+ * EffectiveSpadRtnCount)
+ >> 8));
+ }
+
+ tmpuint16 = XtalkRangeMilliMeter;
+ }
+
+ }
+
+ if (RangeFractionalEnable) {
+ pRangingMeasurementData->RangeMilliMeter =
+ (uint16_t)((tmpuint16) >> 2);
+ pRangingMeasurementData->RangeFractionalPart =
+ (uint8_t)((tmpuint16 & 0x03) << 6);
+ } else {
+ pRangingMeasurementData->RangeMilliMeter = tmpuint16;
+ pRangingMeasurementData->RangeFractionalPart = 0;
+ }
+
+ /*
+ * For a standard definition of RangeStatus, this should
+ * return 0 in case of good result after a ranging
+ * The range status depends on the device so call a device
+ * specific function to obtain the right Status.
+ */
+ Status |= VL_get_pal_range_status(Dev, DeviceRangeStatus,
+ SignalRate, EffectiveSpadRtnCount,
+ pRangingMeasurementData, &PalRangeStatus);
+
+ if (Status == VL_ERROR_NONE)
+ pRangingMeasurementData->RangeStatus = PalRangeStatus;
+
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ /* Copy last read data into Dev buffer */
+ LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure);
+
+ LastRangeDataBuffer.RangeMilliMeter =
+ pRangingMeasurementData->RangeMilliMeter;
+ LastRangeDataBuffer.RangeFractionalPart =
+ pRangingMeasurementData->RangeFractionalPart;
+ LastRangeDataBuffer.RangeDMaxMilliMeter =
+ pRangingMeasurementData->RangeDMaxMilliMeter;
+ LastRangeDataBuffer.MeasurementTimeUsec =
+ pRangingMeasurementData->MeasurementTimeUsec;
+ LastRangeDataBuffer.SignalRateRtnMegaCps =
+ pRangingMeasurementData->SignalRateRtnMegaCps;
+ LastRangeDataBuffer.AmbientRateRtnMegaCps =
+ pRangingMeasurementData->AmbientRateRtnMegaCps;
+ LastRangeDataBuffer.EffectiveSpadRtnCount =
+ pRangingMeasurementData->EffectiveSpadRtnCount;
+ LastRangeDataBuffer.RangeStatus =
+ pRangingMeasurementData->RangeStatus;
+
+ PALDevDataSet(Dev, LastRangeMeasure, LastRangeDataBuffer);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetMeasurementRefSignal(struct vl_data *Dev,
+ unsigned int *pMeasurementRefSignal)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SignalRefClipLimitCheckEnable = 0;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_GetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP,
+ &SignalRefClipLimitCheckEnable);
+ if (SignalRefClipLimitCheckEnable != 0)
+ *pMeasurementRefSignal = PALDevDataGet(Dev, LastSignalRefMcps);
+ else
+ Status = VL_ERROR_INVALID_COMMAND;
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_GetHistogramMeasurementData(struct vl_data *Dev,
+ struct VL_HistogramMeasurementData_t *pHistogramMeasurementData)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_PerformSingleRangingMeasurement(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ /* This function will do a complete single ranging */
+ /* Here we fix the mode! */
+ Status = VL_SetDeviceMode(Dev, VL_DEVICEMODE_SINGLE_RANGING);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_PerformSingleMeasurement(Dev);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetRangingMeasurementData(Dev,
+ pRangingMeasurementData);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_ClearInterruptMask(Dev, 0);
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetNumberOfROIZones(struct vl_data *Dev,
+ uint8_t NumberOfROIZones)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ if (NumberOfROIZones != 1)
+ Status = VL_ERROR_INVALID_PARAMS;
+
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetNumberOfROIZones(struct vl_data *Dev,
+ uint8_t *pNumberOfROIZones)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pNumberOfROIZones = 1;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetMaxNumberOfROIZones(struct vl_data *Dev,
+ uint8_t *pMaxNumberOfROIZones)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ *pMaxNumberOfROIZones = 1;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/* End Group PAL Measurement Functions */
+
+int8_t VL_SetGpioConfig(struct vl_data *Dev, uint8_t Pin,
+ uint8_t DeviceMode, uint8_t Functionality,
+ uint8_t Polarity)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t data;
+
+ LOG_FUNCTION_START("");
+
+ if (Pin != 0) {
+ Status = VL_ERROR_GPIO_NOT_EXISTING;
+ } else if (DeviceMode == VL_DEVICEMODE_GPIO_DRIVE) {
+ if (Polarity == VL_INTERRUPTPOLARITY_LOW)
+ data = 0x10;
+ else
+ data = 1;
+
+ Status = VL_WrByte(Dev,
+ VL_REG_GPIO_HV_MUX_ACTIVE_HIGH, data);
+
+ } else if (DeviceMode == VL_DEVICEMODE_GPIO_OSC) {
+
+ Status |= VL_WrByte(Dev, 0xff, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x00);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x00);
+ Status |= VL_WrByte(Dev, 0x80, 0x01);
+ Status |= VL_WrByte(Dev, 0x85, 0x02);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x04);
+ Status |= VL_WrByte(Dev, 0xcd, 0x00);
+ Status |= VL_WrByte(Dev, 0xcc, 0x11);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x07);
+ Status |= VL_WrByte(Dev, 0xbe, 0x00);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x06);
+ Status |= VL_WrByte(Dev, 0xcc, 0x09);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x00);
+ Status |= VL_WrByte(Dev, 0xff, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x00);
+
+ } else {
+
+ if (Status == VL_ERROR_NONE) {
+ switch (Functionality) {
+ case VL_GPIOFUNCTIONALITY_OFF:
+ data = 0x00;
+ break;
+ case VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW:
+ data = 0x01;
+ break;
+ case VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH:
+ data = 0x02;
+ break;
+ case VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT:
+ data = 0x03;
+ break;
+ case VL_GPIOFUNCTIONALITY_NEW_MEASURE_READY:
+ data = 0x04;
+ break;
+ default:
+ Status =
+ VL_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED;
+ }
+ }
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, data);
+
+ if (Status == VL_ERROR_NONE) {
+ if (Polarity == VL_INTERRUPTPOLARITY_LOW)
+ data = 0;
+ else
+ data = (uint8_t)(1 << 4);
+
+ Status = VL_UpdateByte(Dev,
+ VL_REG_GPIO_HV_MUX_ACTIVE_HIGH, 0xEF, data);
+ }
+
+ if (Status == VL_ERROR_NONE)
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ Pin0GpioFunctionality, Functionality);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_ClearInterruptMask(Dev, 0);
+
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetGpioConfig(struct vl_data *Dev, uint8_t Pin,
+ uint8_t *pDeviceMode,
+ uint8_t *pFunctionality,
+ uint8_t *pPolarity)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t GpioFunctionality;
+ uint8_t data;
+
+ LOG_FUNCTION_START("");
+
+ /* pDeviceMode not managed by Ewok it return the current mode */
+
+ Status = VL_GetDeviceMode(Dev, pDeviceMode);
+
+ if (Status == VL_ERROR_NONE) {
+ if (Pin != 0) {
+ Status = VL_ERROR_GPIO_NOT_EXISTING;
+ } else {
+ Status = VL_RdByte(Dev,
+ VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, &data);
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ switch (data & 0x07) {
+ case 0x00:
+ GpioFunctionality = VL_GPIOFUNCTIONALITY_OFF;
+ break;
+ case 0x01:
+ GpioFunctionality =
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW;
+ break;
+ case 0x02:
+ GpioFunctionality =
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH;
+ break;
+ case 0x03:
+ GpioFunctionality =
+ VL_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT;
+ break;
+ case 0x04:
+ GpioFunctionality =
+ VL_GPIOFUNCTIONALITY_NEW_MEASURE_READY;
+ break;
+ default:
+ Status = VL_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED;
+ }
+ }
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_RdByte(Dev,
+ VL_REG_GPIO_HV_MUX_ACTIVE_HIGH, &data);
+
+ if (Status == VL_ERROR_NONE) {
+ if ((data & (uint8_t)(1 << 4)) == 0)
+ *pPolarity = VL_INTERRUPTPOLARITY_LOW;
+ else
+ *pPolarity = VL_INTERRUPTPOLARITY_HIGH;
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ *pFunctionality = GpioFunctionality;
+ VL_SETDEVICESPECIFICPARAMETER(Dev, Pin0GpioFunctionality,
+ GpioFunctionality);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetInterruptThresholds(struct vl_data *Dev,
+ uint8_t DeviceMode, unsigned int ThresholdLow,
+ unsigned int ThresholdHigh)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t Threshold16;
+
+ LOG_FUNCTION_START("");
+
+ /* no dependency on DeviceMode for Ewok */
+ /* Need to divide by 2 because the FW will apply a x2 */
+ Threshold16 = (uint16_t)((ThresholdLow >> 17) & 0x00fff);
+ Status = VL_WrWord(Dev, VL_REG_SYSTEM_THRESH_LOW,
+ Threshold16);
+
+ if (Status == VL_ERROR_NONE) {
+ /* Need to divide by 2 because the FW will apply a x2 */
+ Threshold16 = (uint16_t)((ThresholdHigh >> 17) & 0x00fff);
+ Status = VL_WrWord(Dev, VL_REG_SYSTEM_THRESH_HIGH,
+ Threshold16);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetInterruptThresholds(struct vl_data *Dev,
+ uint8_t DeviceMode, unsigned int *pThresholdLow,
+ unsigned int *pThresholdHigh)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t Threshold16;
+
+ LOG_FUNCTION_START("");
+
+ /* no dependency on DeviceMode for Ewok */
+
+ Status = VL_RdWord(Dev, VL_REG_SYSTEM_THRESH_LOW,
+ &Threshold16);
+ /* Need to multiply by 2 because the FW will apply a x2 */
+ *pThresholdLow = (unsigned int)((0x00fff & Threshold16) << 17);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdWord(Dev, VL_REG_SYSTEM_THRESH_HIGH,
+ &Threshold16);
+ /* Need to multiply by 2 because the FW will apply a x2 */
+ *pThresholdHigh =
+ (unsigned int)((0x00fff & Threshold16) << 17);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetStopCompletedStatus(struct vl_data *Dev,
+ uint32_t *pStopStatus)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte = 0;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_RdByte(Dev, 0x04, &Byte);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, 0xFF, 0x0);
+
+ *pStopStatus = Byte;
+
+ if (Byte == 0) {
+ Status = VL_WrByte(Dev, 0x80, 0x01);
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status = VL_WrByte(Dev, 0x00, 0x00);
+ Status = VL_WrByte(Dev, 0x91,
+ PALDevDataGet(Dev, StopVariable));
+ Status = VL_WrByte(Dev, 0x00, 0x01);
+ Status = VL_WrByte(Dev, 0xFF, 0x00);
+ Status = VL_WrByte(Dev, 0x80, 0x00);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/* Group PAL Interrupt Functions */
+int8_t VL_ClearInterruptMask(struct vl_data *Dev,
+ uint32_t InterruptMask)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t LoopCount;
+ uint8_t Byte;
+
+ LOG_FUNCTION_START("");
+
+ /* clear bit 0 range interrupt, bit 1 error interrupt */
+ LoopCount = 0;
+ do {
+ Status = VL_WrByte(Dev,
+ VL_REG_SYSTEM_INTERRUPT_CLEAR, 0x01);
+ Status |= VL_WrByte(Dev,
+ VL_REG_SYSTEM_INTERRUPT_CLEAR, 0x00);
+ Status |= VL_RdByte(Dev,
+ VL_REG_RESULT_INTERRUPT_STATUS, &Byte);
+ LoopCount++;
+ } while (((Byte & 0x07) != 0x00)
+ && (LoopCount < 3)
+ && (Status == VL_ERROR_NONE));
+
+
+ if (LoopCount >= 3)
+ Status = VL_ERROR_INTERRUPT_NOT_CLEARED;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetInterruptMaskStatus(struct vl_data *Dev,
+ uint32_t *pInterruptMaskStatus)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_RdByte(Dev, VL_REG_RESULT_INTERRUPT_STATUS,
+ &Byte);
+ *pInterruptMaskStatus = Byte & 0x07;
+
+ if (Byte & 0x18)
+ Status = VL_ERROR_RANGE_ERROR;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_EnableInterruptMask(struct vl_data *Dev,
+ uint32_t InterruptMask)
+{
+ int8_t Status = VL_ERROR_NOT_IMPLEMENTED;
+
+ LOG_FUNCTION_START("");
+
+ /* not implemented for VL53L0X */
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/* End Group PAL Interrupt Functions */
+
+/* Group SPAD functions */
+
+int8_t VL_SetSpadAmbientDamperThreshold(struct vl_data *Dev,
+ uint16_t SpadAmbientDamperThreshold)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrWord(Dev, 0x40, SpadAmbientDamperThreshold);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetSpadAmbientDamperThreshold(struct vl_data *Dev,
+ uint16_t *pSpadAmbientDamperThreshold)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_RdWord(Dev, 0x40, pSpadAmbientDamperThreshold);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_SetSpadAmbientDamperFactor(struct vl_data *Dev,
+ uint16_t SpadAmbientDamperFactor)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte;
+
+ LOG_FUNCTION_START("");
+
+ Byte = (uint8_t)(SpadAmbientDamperFactor & 0x00FF);
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrByte(Dev, 0x42, Byte);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_GetSpadAmbientDamperFactor(struct vl_data *Dev,
+ uint16_t *pSpadAmbientDamperFactor)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t Byte;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_RdByte(Dev, 0x42, &Byte);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+ *pSpadAmbientDamperFactor = (uint16_t)Byte;
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+/* END Group SPAD functions */
+
+/*****************************************************************************
+ * Internal functions
+ *****************************************************************************/
+
+int8_t VL_SetReferenceSpads(struct vl_data *Dev, uint32_t count,
+ uint8_t isApertureSpads)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_set_reference_spads(Dev, count, isApertureSpads);
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_GetReferenceSpads(struct vl_data *Dev, uint32_t *pSpadCount,
+ uint8_t *pIsApertureSpads)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_reference_spads(Dev, pSpadCount, pIsApertureSpads);
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_PerformRefSpadManagement(struct vl_data *Dev,
+ uint32_t *refSpadCount, uint8_t *isApertureSpads)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_perform_ref_spad_management(Dev, refSpadCount,
+ isApertureSpads);
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_api_calibration.c b/drivers/input/misc/vl53l0x/src/vl53l0x_api_calibration.c
new file mode 100644
index 0000000..3905bd2
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_api_calibration.c
@@ -0,0 +1,1266 @@
+/*
+ * vl53l0x_api_calibration.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "vl53l0x_api.h"
+#include "vl53l0x_api_core.h"
+#include "vl53l0x_api_calibration.h"
+
+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
+
+#define LOG_FUNCTION_START(fmt, ...) \
+ _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__)
+#define LOG_FUNCTION_END(status, ...) \
+ _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__)
+#define LOG_FUNCTION_END_FMT(status, fmt, ...) \
+ _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__)
+
+#define REF_ARRAY_SPAD_0 0
+#define REF_ARRAY_SPAD_5 5
+#define REF_ARRAY_SPAD_10 10
+
+uint32_t refArrayQuadrants[4] = {REF_ARRAY_SPAD_10, REF_ARRAY_SPAD_5,
+ REF_ARRAY_SPAD_0, REF_ARRAY_SPAD_5 };
+
+int8_t VL_perform_xtalk_calibration(struct vl_data *Dev,
+ unsigned int XTalkCalDistance,
+ unsigned int *pXTalkCompensationRateMegaCps)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t sum_ranging = 0;
+ uint16_t sum_spads = 0;
+ unsigned int sum_signalRate = 0;
+ unsigned int total_count = 0;
+ uint8_t xtalk_meas = 0;
+ struct VL_RangingMeasurementData_t RangingMeasurementData;
+ unsigned int xTalkStoredMeanSignalRate;
+ unsigned int xTalkStoredMeanRange;
+ unsigned int xTalkStoredMeanRtnSpads;
+ uint32_t signalXTalkTotalPerSpad;
+ uint32_t xTalkStoredMeanRtnSpadsAsInt;
+ uint32_t xTalkCalDistanceAsInt;
+ unsigned int XTalkCompensationRateMegaCps;
+
+ if (XTalkCalDistance <= 0)
+ Status = VL_ERROR_INVALID_PARAMS;
+
+ /* Disable the XTalk compensation */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetXTalkCompensationEnable(Dev, 0);
+
+ /* Disable the RIT */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_SetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0);
+ }
+
+ /* Perform 50 measurements and compute the averages */
+ if (Status == VL_ERROR_NONE) {
+ sum_ranging = 0;
+ sum_spads = 0;
+ sum_signalRate = 0;
+ total_count = 0;
+ for (xtalk_meas = 0; xtalk_meas < 50; xtalk_meas++) {
+ Status = VL_PerformSingleRangingMeasurement(Dev,
+ &RangingMeasurementData);
+
+ if (Status != VL_ERROR_NONE)
+ break;
+
+ /* The range is valid when RangeStatus = 0 */
+ if (RangingMeasurementData.RangeStatus == 0) {
+ sum_ranging = sum_ranging +
+ RangingMeasurementData.RangeMilliMeter;
+ sum_signalRate = sum_signalRate +
+ RangingMeasurementData.SignalRateRtnMegaCps;
+ sum_spads = sum_spads +
+ RangingMeasurementData.EffectiveSpadRtnCount
+ / 256;
+ total_count = total_count + 1;
+ }
+ }
+
+ /* no valid values found */
+ if (total_count == 0)
+ Status = VL_ERROR_RANGE_ERROR;
+
+ }
+
+
+ if (Status == VL_ERROR_NONE) {
+ /* unsigned int / uint16_t = unsigned int */
+ xTalkStoredMeanSignalRate = sum_signalRate / total_count;
+ xTalkStoredMeanRange = (unsigned int)((uint32_t)(
+ sum_ranging << 16) / total_count);
+ xTalkStoredMeanRtnSpads = (unsigned int)((uint32_t)(
+ sum_spads << 16) / total_count);
+
+ /* Round Mean Spads to Whole Number.
+ * Typically the calculated mean SPAD count is a whole number
+ * or very close to a whole
+ * number, therefore any truncation will not result in a
+ * significant loss in accuracy.
+ * Also, for a grey target at a typical distance of around
+ * 400mm, around 220 SPADs will
+ * be enabled, therefore, any truncation will result in a loss
+ * of accuracy of less than
+ * 0.5%.
+ */
+ xTalkStoredMeanRtnSpadsAsInt = (xTalkStoredMeanRtnSpads +
+ 0x8000) >> 16;
+
+ /* Round Cal Distance to Whole Number. */
+ /* Note that the cal distance is in mm, */
+ /* therefore no resolution is lost.*/
+ xTalkCalDistanceAsInt = (XTalkCalDistance + 0x8000) >> 16;
+
+ if (xTalkStoredMeanRtnSpadsAsInt == 0 ||
+ xTalkCalDistanceAsInt == 0 ||
+ xTalkStoredMeanRange >= XTalkCalDistance) {
+ XTalkCompensationRateMegaCps = 0;
+ } else {
+ /* Round Cal Distance to Whole Number. */
+ /* Note that the cal distance is in mm, therefore no */
+ /* resolution is lost.*/
+ xTalkCalDistanceAsInt = (XTalkCalDistance +
+ 0x8000) >> 16;
+
+ /* Apply division by mean spad count early in the */
+ /* calculation to keep the numbers small. */
+ /* This ensures we can maintain a 32bit calculation. */
+ /* Fixed1616 / int := Fixed1616 */
+ signalXTalkTotalPerSpad = (xTalkStoredMeanSignalRate) /
+ xTalkStoredMeanRtnSpadsAsInt;
+
+ /* Complete the calculation for total Signal */
+ /* XTalk per SPAD */
+ /* Fixed1616 * (Fixed1616 - Fixed1616/int) := */
+ /* (2^16 * Fixed1616) */
+
+ signalXTalkTotalPerSpad *= ((1 << 16) -
+ (xTalkStoredMeanRange / xTalkCalDistanceAsInt));
+
+ /* Round from 2^16 * Fixed1616, to Fixed1616. */
+ XTalkCompensationRateMegaCps = (signalXTalkTotalPerSpad
+ + 0x8000) >> 16;
+ }
+
+ *pXTalkCompensationRateMegaCps = XTalkCompensationRateMegaCps;
+
+ /* Enable the XTalk compensation */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetXTalkCompensationEnable(Dev, 1);
+
+ /* Enable the XTalk compensation */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetXTalkCompensationRateMegaCps(Dev,
+ XTalkCompensationRateMegaCps);
+
+ }
+
+ return Status;
+}
+
+int8_t VL_perform_offset_calibration(struct vl_data *Dev,
+ unsigned int CalDistanceMilliMeter,
+ int32_t *pOffsetMicroMeter)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t sum_ranging = 0;
+ unsigned int total_count = 0;
+ struct VL_RangingMeasurementData_t RangingMeasurementData;
+ unsigned int StoredMeanRange;
+ uint32_t StoredMeanRangeAsInt;
+ uint32_t CalDistanceAsInt_mm;
+ uint8_t SequenceStepEnabled;
+ int meas = 0;
+
+ if (CalDistanceMilliMeter <= 0)
+ Status = VL_ERROR_INVALID_PARAMS;
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetOffsetCalibrationDataMicroMeter(Dev, 0);
+
+
+ /* Get the value of the TCC */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetSequenceStepEnable(Dev,
+ VL_SEQUENCESTEP_TCC, &SequenceStepEnabled);
+
+
+ /* Disable the TCC */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetSequenceStepEnable(Dev,
+ VL_SEQUENCESTEP_TCC, 0);
+
+
+ /* Disable the RIT */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_SetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0);
+
+ /* Perform 50 measurements and compute the averages */
+ if (Status == VL_ERROR_NONE) {
+ sum_ranging = 0;
+ total_count = 0;
+ for (meas = 0; meas < 50; meas++) {
+ Status = VL_PerformSingleRangingMeasurement(Dev,
+ &RangingMeasurementData);
+
+ if (Status != VL_ERROR_NONE)
+ break;
+
+ /* The range is valid when RangeStatus = 0 */
+ if (RangingMeasurementData.RangeStatus == 0) {
+ sum_ranging = sum_ranging +
+ RangingMeasurementData.RangeMilliMeter;
+ total_count = total_count + 1;
+ }
+ }
+
+ /* no valid values found */
+ if (total_count == 0)
+ Status = VL_ERROR_RANGE_ERROR;
+ }
+
+
+ if (Status == VL_ERROR_NONE) {
+ /* unsigned int / uint16_t = unsigned int */
+ StoredMeanRange = (unsigned int)((uint32_t)(sum_ranging << 16)
+ / total_count);
+
+ StoredMeanRangeAsInt = (StoredMeanRange + 0x8000) >> 16;
+
+ /* Round Cal Distance to Whole Number. */
+ /* Note that the cal distance is in mm, */
+ /* therefore no resolution is lost.*/
+ CalDistanceAsInt_mm = (CalDistanceMilliMeter + 0x8000) >> 16;
+
+ *pOffsetMicroMeter = (CalDistanceAsInt_mm -
+ StoredMeanRangeAsInt) * 1000;
+
+ /* Apply the calculated offset */
+ if (Status == VL_ERROR_NONE) {
+ VL_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters,
+ *pOffsetMicroMeter);
+ Status = VL_SetOffsetCalibrationDataMicroMeter(Dev,
+ *pOffsetMicroMeter);
+ }
+
+ }
+
+ /* Restore the TCC */
+ if (Status == VL_ERROR_NONE) {
+ if (SequenceStepEnabled != 0)
+ Status = VL_SetSequenceStepEnable(Dev,
+ VL_SEQUENCESTEP_TCC, 1);
+ }
+
+ return Status;
+}
+
+
+int8_t VL_set_offset_calibration_data_micro_meter(struct vl_data *Dev,
+ int32_t OffsetCalibrationDataMicroMeter)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t cMaxOffsetMicroMeter = 511000;
+ int32_t cMinOffsetMicroMeter = -512000;
+ int16_t cOffsetRange = 4096;
+ uint32_t encodedOffsetVal;
+
+ LOG_FUNCTION_START("");
+
+ if (OffsetCalibrationDataMicroMeter > cMaxOffsetMicroMeter)
+ OffsetCalibrationDataMicroMeter = cMaxOffsetMicroMeter;
+ else if (OffsetCalibrationDataMicroMeter < cMinOffsetMicroMeter)
+ OffsetCalibrationDataMicroMeter = cMinOffsetMicroMeter;
+
+ /* The offset register is 10.2 format and units are mm
+ * therefore conversion is applied by a division of
+ * 250.
+ */
+ if (OffsetCalibrationDataMicroMeter >= 0) {
+ encodedOffsetVal =
+ OffsetCalibrationDataMicroMeter/250;
+ } else {
+ encodedOffsetVal =
+ cOffsetRange +
+ OffsetCalibrationDataMicroMeter/250;
+ }
+
+ Status = VL_WrWord(Dev,
+ VL_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM,
+ encodedOffsetVal);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_offset_calibration_data_micro_meter(struct vl_data *Dev,
+ int32_t *pOffsetCalibrationDataMicroMeter)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint16_t RangeOffsetRegister;
+ int16_t cMaxOffset = 2047;
+ int16_t cOffsetRange = 4096;
+
+ /* Note that offset has 10.2 format */
+
+ Status = VL_RdWord(Dev,
+ VL_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM,
+ &RangeOffsetRegister);
+
+ if (Status == VL_ERROR_NONE) {
+ RangeOffsetRegister = (RangeOffsetRegister & 0x0fff);
+
+ /* Apply 12 bit 2's compliment conversion */
+ if (RangeOffsetRegister > cMaxOffset)
+ *pOffsetCalibrationDataMicroMeter =
+ (int16_t)(RangeOffsetRegister - cOffsetRange)
+ * 250;
+ else
+ *pOffsetCalibrationDataMicroMeter =
+ (int16_t)RangeOffsetRegister * 250;
+
+ }
+
+ return Status;
+}
+
+
+int8_t VL_apply_offset_adjustment(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t CorrectedOffsetMicroMeters;
+ int32_t CurrentOffsetMicroMeters;
+
+ /* if we run on this function we can read all the NVM info */
+ /* used by the API */
+ Status = VL_get_info_from_device(Dev, 7);
+
+ /* Read back current device offset */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetOffsetCalibrationDataMicroMeter(Dev,
+ &CurrentOffsetMicroMeters);
+ }
+
+ /* Apply Offset Adjustment derived from 400mm measurements */
+ if (Status == VL_ERROR_NONE) {
+
+ /* Store initial device offset */
+ PALDevDataSet(Dev, Part2PartOffsetNVMMicroMeter,
+ CurrentOffsetMicroMeters);
+
+ CorrectedOffsetMicroMeters = CurrentOffsetMicroMeters +
+ (int32_t)PALDevDataGet(Dev,
+ Part2PartOffsetAdjustmentNVMMicroMeter);
+
+ Status = VL_SetOffsetCalibrationDataMicroMeter(Dev,
+ CorrectedOffsetMicroMeters);
+
+ /* store current, adjusted offset */
+ if (Status == VL_ERROR_NONE) {
+ VL_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters,
+ CorrectedOffsetMicroMeters);
+ }
+ }
+
+ return Status;
+}
+
+void get_next_good_spad(uint8_t goodSpadArray[], uint32_t size,
+ uint32_t curr, int32_t *next)
+{
+ uint32_t startIndex;
+ uint32_t fineOffset;
+ uint32_t cSpadsPerByte = 8;
+ uint32_t coarseIndex;
+ uint32_t fineIndex;
+ uint8_t dataByte;
+ uint8_t success = 0;
+
+ /*
+ * Starting with the current good spad, loop through the array to find
+ * the next. i.e. the next bit set in the sequence.
+ *
+ * The coarse index is the byte index of the array and the fine index is
+ * the index of the bit within each byte.
+ */
+
+ *next = -1;
+
+ startIndex = curr / cSpadsPerByte;
+ fineOffset = curr % cSpadsPerByte;
+
+ for (coarseIndex = startIndex; ((coarseIndex < size) && !success);
+ coarseIndex++) {
+ fineIndex = 0;
+ dataByte = goodSpadArray[coarseIndex];
+
+ if (coarseIndex == startIndex) {
+ /* locate the bit position of the provided current */
+ /* spad bit before iterating */
+ dataByte >>= fineOffset;
+ fineIndex = fineOffset;
+ }
+
+ while (fineIndex < cSpadsPerByte) {
+ if ((dataByte & 0x1) == 1) {
+ success = 1;
+ *next = coarseIndex * cSpadsPerByte + fineIndex;
+ break;
+ }
+ dataByte >>= 1;
+ fineIndex++;
+ }
+ }
+}
+
+
+uint8_t is_aperture(uint32_t spadIndex)
+{
+ /*
+ * This function reports if a given spad index is an aperture SPAD by
+ * deriving the quadrant.
+ */
+ uint32_t quadrant;
+ uint8_t isAperture = 1;
+
+ quadrant = spadIndex >> 6;
+ if (refArrayQuadrants[quadrant] == REF_ARRAY_SPAD_0)
+ isAperture = 0;
+
+ return isAperture;
+}
+
+
+int8_t enable_spad_bit(uint8_t spadArray[], uint32_t size,
+ uint32_t spadIndex)
+{
+ int8_t status = VL_ERROR_NONE;
+ uint32_t cSpadsPerByte = 8;
+ uint32_t coarseIndex;
+ uint32_t fineIndex;
+
+ coarseIndex = spadIndex / cSpadsPerByte;
+ fineIndex = spadIndex % cSpadsPerByte;
+ if (coarseIndex >= size)
+ status = VL_ERROR_REF_SPAD_INIT;
+ else
+ spadArray[coarseIndex] |= (1 << fineIndex);
+
+ return status;
+}
+
+int8_t count_enabled_spads(uint8_t spadArray[],
+ uint32_t byteCount, uint32_t maxSpads,
+ uint32_t *pTotalSpadsEnabled, uint8_t *pIsAperture)
+{
+ int8_t status = VL_ERROR_NONE;
+ uint32_t cSpadsPerByte = 8;
+ uint32_t lastByte;
+ uint32_t lastBit;
+ uint32_t byteIndex = 0;
+ uint32_t bitIndex = 0;
+ uint8_t tempByte;
+ uint8_t spadTypeIdentified = 0;
+
+ /* The entire array will not be used for spads, therefore the last
+ * byte and last bit is determined from the max spads value.
+ */
+
+ lastByte = maxSpads / cSpadsPerByte;
+ lastBit = maxSpads % cSpadsPerByte;
+
+ /* Check that the max spads value does not exceed the array bounds. */
+ if (lastByte >= byteCount)
+ status = VL_ERROR_REF_SPAD_INIT;
+
+ *pTotalSpadsEnabled = 0;
+
+ /* Count the bits enabled in the whole bytes */
+ for (byteIndex = 0; byteIndex <= (lastByte - 1); byteIndex++) {
+ tempByte = spadArray[byteIndex];
+
+ for (bitIndex = 0; bitIndex <= cSpadsPerByte; bitIndex++) {
+ if ((tempByte & 0x01) == 1) {
+ (*pTotalSpadsEnabled)++;
+ if (!spadTypeIdentified) {
+ *pIsAperture = 1;
+ if ((byteIndex < 2) && (bitIndex < 4))
+ *pIsAperture = 0;
+ spadTypeIdentified = 1;
+ }
+ }
+ tempByte >>= 1;
+ }
+ }
+
+ /* Count the number of bits enabled in the last byte accounting
+ * for the fact that not all bits in the byte may be used.
+ */
+ tempByte = spadArray[lastByte];
+
+ for (bitIndex = 0; bitIndex <= lastBit; bitIndex++) {
+ if ((tempByte & 0x01) == 1)
+ (*pTotalSpadsEnabled)++;
+ }
+
+ return status;
+}
+
+int8_t set_ref_spad_map(struct vl_data *Dev, uint8_t *refSpadArray)
+{
+ int8_t status = VL_WriteMulti(Dev,
+ VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0,
+ refSpadArray, 6);
+ return status;
+}
+
+int8_t get_ref_spad_map(struct vl_data *Dev, uint8_t *refSpadArray)
+{
+ int8_t status = VL_ReadMulti(Dev,
+ VL_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0,
+ refSpadArray,
+ 6);
+ return status;
+}
+
+int8_t enable_ref_spads(struct vl_data *Dev,
+ uint8_t apertureSpads,
+ uint8_t goodSpadArray[],
+ uint8_t spadArray[],
+ uint32_t size,
+ uint32_t start,
+ uint32_t offset,
+ uint32_t spadCount,
+ uint32_t *lastSpad)
+{
+ int8_t status = VL_ERROR_NONE;
+ uint32_t index;
+ uint32_t i;
+ int32_t nextGoodSpad = offset;
+ uint32_t currentSpad;
+ uint8_t checkSpadArray[6];
+
+ /*
+ * This function takes in a spad array which may or may not have SPADS
+ * already enabled and appends from a given offset a requested number
+ * of new SPAD enables. The 'good spad map' is applied to
+ * determine the next SPADs to enable.
+ *
+ * This function applies to only aperture or only non-aperture spads.
+ * Checks are performed to ensure this.
+ */
+
+ currentSpad = offset;
+ for (index = 0; index < spadCount; index++) {
+ get_next_good_spad(goodSpadArray, size, currentSpad,
+ &nextGoodSpad);
+
+ if (nextGoodSpad == -1) {
+ status = VL_ERROR_REF_SPAD_INIT;
+ break;
+ }
+
+ /* Confirm that the next good SPAD is non-aperture */
+ if (is_aperture(start + nextGoodSpad) != apertureSpads) {
+ /* if we can't get the required number of good aperture
+ * spads from the current quadrant then this is an error
+ */
+ status = VL_ERROR_REF_SPAD_INIT;
+ break;
+ }
+ currentSpad = (uint32_t)nextGoodSpad;
+ enable_spad_bit(spadArray, size, currentSpad);
+ currentSpad++;
+ }
+ *lastSpad = currentSpad;
+
+ if (status == VL_ERROR_NONE)
+ status = set_ref_spad_map(Dev, spadArray);
+
+
+ if (status == VL_ERROR_NONE) {
+ status = get_ref_spad_map(Dev, checkSpadArray);
+
+ i = 0;
+
+ /* Compare spad maps. If not equal report error. */
+ while (i < size) {
+ if (spadArray[i] != checkSpadArray[i]) {
+ status = VL_ERROR_REF_SPAD_INIT;
+ break;
+ }
+ i++;
+ }
+ }
+ return status;
+}
+
+
+int8_t perform_ref_signal_measurement(struct vl_data *Dev,
+ uint16_t *refSignalRate)
+{
+ int8_t status = VL_ERROR_NONE;
+ struct VL_RangingMeasurementData_t rangingMeasurementData;
+
+ uint8_t SequenceConfig = 0;
+
+ /* store the value of the sequence config,
+ * this will be reset before the end of the function
+ */
+
+ SequenceConfig = PALDevDataGet(Dev, SequenceConfig);
+
+ /*
+ * This function performs a reference signal rate measurement.
+ */
+ if (status == VL_ERROR_NONE)
+ status = VL_WrByte(Dev,
+ VL_REG_SYSTEM_SEQUENCE_CONFIG, 0xC0);
+
+ if (status == VL_ERROR_NONE)
+ status = VL_PerformSingleRangingMeasurement(Dev,
+ &rangingMeasurementData);
+
+ if (status == VL_ERROR_NONE)
+ status = VL_WrByte(Dev, 0xFF, 0x01);
+
+ if (status == VL_ERROR_NONE)
+ status = VL_RdWord(Dev,
+ VL_REG_RESULT_PEAK_SIGNAL_RATE_REF,
+ refSignalRate);
+
+ if (status == VL_ERROR_NONE)
+ status = VL_WrByte(Dev, 0xFF, 0x00);
+
+ if (status == VL_ERROR_NONE) {
+ /* restore the previous Sequence Config */
+ status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ SequenceConfig);
+ if (status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, SequenceConfig, SequenceConfig);
+ }
+
+ return status;
+}
+
+int8_t VL_perform_ref_spad_management(struct vl_data *Dev,
+ uint32_t *refSpadCount,
+ uint8_t *isApertureSpads)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t lastSpadArray[6];
+ uint8_t startSelect = 0xB4;
+ uint32_t minimumSpadCount = 3;
+ uint32_t maxSpadCount = 44;
+ uint32_t currentSpadIndex = 0;
+ uint32_t lastSpadIndex = 0;
+ int32_t nextGoodSpad = 0;
+ uint16_t targetRefRate = 0x0A00; /* 20 MCPS in 9:7 format */
+ uint16_t peakSignalRateRef;
+ uint32_t needAptSpads = 0;
+ uint32_t index = 0;
+ uint32_t spadArraySize = 6;
+ uint32_t signalRateDiff = 0;
+ uint32_t lastSignalRateDiff = 0;
+ uint8_t complete = 0;
+ uint8_t VhvSettings = 0;
+ uint8_t PhaseCal = 0;
+ uint32_t refSpadCount_int = 0;
+ uint8_t isApertureSpads_int = 0;
+
+ /*
+ * The reference SPAD initialization procedure determines the minimum
+ * amount of reference spads to be enables to achieve a target reference
+ * signal rate and should be performed once during initialization.
+ *
+ * Either aperture or non-aperture spads are applied but never both.
+ * Firstly non-aperture spads are set, beginning with 5 spads, and
+ * increased one spad at a time until the closest measurement to the
+ * target rate is achieved.
+ *
+ * If the target rate is exceeded when 5 non-aperture spads are enabled,
+ * initialization is performed instead with aperture spads.
+ *
+ * When setting spads, a 'Good Spad Map' is applied.
+ *
+ * This procedure operates within a SPAD window of interest of a maximum
+ * 44 spads.
+ * The start point is currently fixed to 180, which lies towards the end
+ * of the non-aperture quadrant and runs in to the adjacent aperture
+ * quadrant.
+ */
+
+
+ targetRefRate = PALDevDataGet(Dev, targetRefRate);
+
+ /*
+ * Initialize Spad arrays.
+ * Currently the good spad map is initialised to 'All good'.
+ * This is a short term implementation. The good spad map will be
+ * provided as an input.
+ * Note that there are 6 bytes. Only the first 44 bits will be used to
+ * represent spads.
+ */
+ for (index = 0; index < spadArraySize; index++)
+ Dev->Data.SpadData.RefSpadEnables[index] = 0;
+
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, 0xFF, 0x00);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_GLOBAL_CONFIG_REF_EN_START_SELECT,
+ startSelect);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_POWER_MANAGEMENT_GO1_POWER_FORCE, 0);
+
+ /* Perform ref calibration */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_perform_ref_calibration(Dev, &VhvSettings,
+ &PhaseCal, 0);
+
+ if (Status == VL_ERROR_NONE) {
+ /* Enable Minimum NON-APERTURE Spads */
+ currentSpadIndex = 0;
+ lastSpadIndex = currentSpadIndex;
+ needAptSpads = 0;
+ Status = enable_ref_spads(Dev,
+ needAptSpads,
+ Dev->Data.SpadData.RefGoodSpadMap,
+ Dev->Data.SpadData.RefSpadEnables,
+ spadArraySize,
+ startSelect,
+ currentSpadIndex,
+ minimumSpadCount,
+ &lastSpadIndex);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ currentSpadIndex = lastSpadIndex;
+
+ Status = perform_ref_signal_measurement(Dev,
+ &peakSignalRateRef);
+ if ((Status == VL_ERROR_NONE) &&
+ (peakSignalRateRef > targetRefRate)) {
+ /* Signal rate measurement too high, */
+ /* switch to APERTURE SPADs */
+
+ for (index = 0; index < spadArraySize; index++)
+ Dev->Data.SpadData.RefSpadEnables[index] = 0;
+
+
+ /* Increment to the first APERTURE spad */
+ while ((is_aperture(startSelect + currentSpadIndex)
+ == 0) && (currentSpadIndex < maxSpadCount)) {
+ currentSpadIndex++;
+ }
+
+ needAptSpads = 1;
+
+ Status = enable_ref_spads(Dev,
+ needAptSpads,
+ Dev->Data.SpadData.RefGoodSpadMap,
+ Dev->Data.SpadData.RefSpadEnables,
+ spadArraySize,
+ startSelect,
+ currentSpadIndex,
+ minimumSpadCount,
+ &lastSpadIndex);
+
+ if (Status == VL_ERROR_NONE) {
+ currentSpadIndex = lastSpadIndex;
+ Status = perform_ref_signal_measurement(Dev,
+ &peakSignalRateRef);
+
+ if ((Status == VL_ERROR_NONE) &&
+ (peakSignalRateRef > targetRefRate)) {
+ /* Signal rate still too high after
+ * setting the minimum number of
+ * APERTURE spads. Can do no more
+ * therefore set the min number of
+ * aperture spads as the result.
+ */
+ isApertureSpads_int = 1;
+ refSpadCount_int = minimumSpadCount;
+ }
+ }
+ } else {
+ needAptSpads = 0;
+ }
+ }
+
+ if ((Status == VL_ERROR_NONE) &&
+ (peakSignalRateRef < targetRefRate)) {
+ /* At this point, the minimum number of either aperture
+ * or non-aperture spads have been set. Proceed to add
+ * spads and perform measurements until the target
+ * reference is reached.
+ */
+ isApertureSpads_int = needAptSpads;
+ refSpadCount_int = minimumSpadCount;
+
+ memcpy(lastSpadArray, Dev->Data.SpadData.RefSpadEnables,
+ spadArraySize);
+ lastSignalRateDiff = abs(peakSignalRateRef -
+ targetRefRate);
+ complete = 0;
+
+ while (!complete) {
+ get_next_good_spad(
+ Dev->Data.SpadData.RefGoodSpadMap,
+ spadArraySize, currentSpadIndex,
+ &nextGoodSpad);
+
+ if (nextGoodSpad == -1) {
+ Status = VL_ERROR_REF_SPAD_INIT;
+ break;
+ }
+
+ /* Cannot combine Aperture and Non-Aperture spads, so
+ * ensure the current spad is of the correct type.
+ */
+ if (is_aperture((uint32_t)startSelect + nextGoodSpad) !=
+ needAptSpads) {
+ /* At this point we have enabled the maximum
+ * number of Aperture spads.
+ */
+ complete = 1;
+ break;
+ }
+
+ (refSpadCount_int)++;
+
+ currentSpadIndex = nextGoodSpad;
+ Status = enable_spad_bit(
+ Dev->Data.SpadData.RefSpadEnables,
+ spadArraySize, currentSpadIndex);
+
+ if (Status == VL_ERROR_NONE) {
+ currentSpadIndex++;
+ /* Proceed to apply the additional spad and */
+ /* perform measurement. */
+ Status = set_ref_spad_map(Dev,
+ Dev->Data.SpadData.RefSpadEnables);
+ }
+
+ if (Status != VL_ERROR_NONE)
+ break;
+
+ Status = perform_ref_signal_measurement(Dev,
+ &peakSignalRateRef);
+
+ if (Status != VL_ERROR_NONE)
+ break;
+
+ signalRateDiff = abs(peakSignalRateRef - targetRefRate);
+
+ if (peakSignalRateRef > targetRefRate) {
+ /* Select the spad map that provides the */
+ /* measurement closest to the target rate, */
+ /* either above or below it. */
+
+ if (signalRateDiff > lastSignalRateDiff) {
+ /* Previous spad map produced a closer */
+ /* measurement, so choose this. */
+ Status = set_ref_spad_map(Dev,
+ lastSpadArray);
+ memcpy(
+ Dev->Data.SpadData.RefSpadEnables,
+ lastSpadArray, spadArraySize);
+
+ (refSpadCount_int)--;
+ }
+ complete = 1;
+ } else {
+ /* Continue to add spads */
+ lastSignalRateDiff = signalRateDiff;
+ memcpy(lastSpadArray,
+ Dev->Data.SpadData.RefSpadEnables,
+ spadArraySize);
+ }
+
+ } /* while */
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ *refSpadCount = refSpadCount_int;
+ *isApertureSpads = isApertureSpads_int;
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 1);
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadCount, (uint8_t)(*refSpadCount));
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadType, *isApertureSpads);
+ }
+
+ return Status;
+}
+
+int8_t VL_set_reference_spads(struct vl_data *Dev,
+ uint32_t count, uint8_t isApertureSpads)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint32_t currentSpadIndex = 0;
+ uint8_t startSelect = 0xB4;
+ uint32_t spadArraySize = 6;
+ uint32_t maxSpadCount = 44;
+ uint32_t lastSpadIndex;
+ uint32_t index;
+
+ /*
+ * This function applies a requested number of reference spads, either
+ * aperture or
+ * non-aperture, as requested.
+ * The good spad map will be applied.
+ */
+
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, 0xFF, 0x00);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_GLOBAL_CONFIG_REF_EN_START_SELECT,
+ startSelect);
+
+ for (index = 0; index < spadArraySize; index++)
+ Dev->Data.SpadData.RefSpadEnables[index] = 0;
+
+ if (isApertureSpads) {
+ /* Increment to the first APERTURE spad */
+ while ((is_aperture(startSelect + currentSpadIndex) == 0) &&
+ (currentSpadIndex < maxSpadCount)) {
+ currentSpadIndex++;
+ }
+ }
+ Status = enable_ref_spads(Dev,
+ isApertureSpads,
+ Dev->Data.SpadData.RefGoodSpadMap,
+ Dev->Data.SpadData.RefSpadEnables,
+ spadArraySize,
+ startSelect,
+ currentSpadIndex,
+ count,
+ &lastSpadIndex);
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 1);
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadCount, (uint8_t)(count));
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadType, isApertureSpads);
+ }
+
+ return Status;
+}
+
+int8_t VL_get_reference_spads(struct vl_data *Dev,
+ uint32_t *pSpadCount, uint8_t *pIsApertureSpads)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t refSpadsInitialised;
+ uint8_t refSpadArray[6];
+ uint32_t cMaxSpadCount = 44;
+ uint32_t cSpadArraySize = 6;
+ uint32_t spadsEnabled;
+ uint8_t isApertureSpads = 0;
+
+ refSpadsInitialised = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ RefSpadsInitialised);
+
+ if (refSpadsInitialised == 1) {
+
+ *pSpadCount = (uint32_t)VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadCount);
+ *pIsApertureSpads = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadType);
+ } else {
+
+ /* obtain spad info from device.*/
+ Status = get_ref_spad_map(Dev, refSpadArray);
+
+ if (Status == VL_ERROR_NONE) {
+ /* count enabled spads within spad map array and
+ * determine if Aperture or Non-Aperture.
+ */
+ Status = count_enabled_spads(refSpadArray,
+ cSpadArraySize,
+ cMaxSpadCount,
+ &spadsEnabled,
+ &isApertureSpads);
+
+ if (Status == VL_ERROR_NONE) {
+
+ *pSpadCount = spadsEnabled;
+ *pIsApertureSpads = isApertureSpads;
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ RefSpadsInitialised, 1);
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadCount,
+ (uint8_t)spadsEnabled);
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadType, isApertureSpads);
+ }
+ }
+ }
+
+ return Status;
+}
+
+
+int8_t VL_perform_single_ref_calibration(struct vl_data *Dev,
+ uint8_t vhv_init_byte)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, VL_REG_SYSRANGE_START,
+ VL_REG_SYSRANGE_MODE_START_STOP |
+ vhv_init_byte);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_measurement_poll_for_completion(Dev);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_ClearInterruptMask(Dev, 0);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, VL_REG_SYSRANGE_START, 0x00);
+
+ return Status;
+}
+
+
+int8_t VL_ref_calibration_io(struct vl_data *Dev,
+ uint8_t read_not_write, uint8_t VhvSettings, uint8_t PhaseCal,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal,
+ const uint8_t vhv_enable, const uint8_t phase_enable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t PhaseCalint = 0;
+
+ /* Read VHV from device */
+ Status |= VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x00);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+
+ if (read_not_write) {
+ if (vhv_enable)
+ Status |= VL_RdByte(Dev, 0xCB, pVhvSettings);
+ if (phase_enable)
+ Status |= VL_RdByte(Dev, 0xEE, &PhaseCalint);
+ } else {
+ if (vhv_enable)
+ Status |= VL_WrByte(Dev, 0xCB, VhvSettings);
+ if (phase_enable)
+ Status |= VL_UpdateByte(Dev, 0xEE, 0x80, PhaseCal);
+ }
+
+ Status |= VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x01);
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+
+ *pPhaseCal = (uint8_t)(PhaseCalint&0xEF);
+
+ return Status;
+}
+
+
+int8_t VL_perform_vhv_calibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, const uint8_t get_data_enable,
+ const uint8_t restore_config)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SequenceConfig = 0;
+ uint8_t VhvSettings = 0;
+ uint8_t PhaseCal = 0;
+ uint8_t PhaseCalInt = 0;
+
+ /* store the value of the sequence config,
+ * this will be reset before the end of the function
+ */
+
+ if (restore_config)
+ SequenceConfig = PALDevDataGet(Dev, SequenceConfig);
+
+ /* Run VHV */
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG, 0x01);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_perform_single_ref_calibration(Dev, 0x40);
+
+ /* Read VHV from device */
+ if ((Status == VL_ERROR_NONE) && (get_data_enable == 1)) {
+ Status = VL_ref_calibration_io(Dev, 1,
+ VhvSettings, PhaseCal, /* Not used here */
+ pVhvSettings, &PhaseCalInt,
+ 1, 0);
+ } else
+ *pVhvSettings = 0;
+
+
+ if ((Status == VL_ERROR_NONE) && restore_config) {
+ /* restore the previous Sequence Config */
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ SequenceConfig);
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, SequenceConfig, SequenceConfig);
+
+ }
+
+ return Status;
+}
+
+int8_t VL_perform_phase_calibration(struct vl_data *Dev,
+ uint8_t *pPhaseCal, const uint8_t get_data_enable,
+ const uint8_t restore_config)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SequenceConfig = 0;
+ uint8_t VhvSettings = 0;
+ uint8_t PhaseCal = 0;
+ uint8_t VhvSettingsint;
+
+ /* store the value of the sequence config,
+ * this will be reset before the end of the function
+ */
+
+ if (restore_config)
+ SequenceConfig = PALDevDataGet(Dev, SequenceConfig);
+
+ /* Run PhaseCal */
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG, 0x02);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_perform_single_ref_calibration(Dev, 0x0);
+
+ /* Read PhaseCal from device */
+ if ((Status == VL_ERROR_NONE) && (get_data_enable == 1)) {
+ Status = VL_ref_calibration_io(Dev, 1,
+ VhvSettings, PhaseCal, /* Not used here */
+ &VhvSettingsint, pPhaseCal,
+ 0, 1);
+ } else
+ *pPhaseCal = 0;
+
+
+ if ((Status == VL_ERROR_NONE) && restore_config) {
+ /* restore the previous Sequence Config */
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ SequenceConfig);
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, SequenceConfig, SequenceConfig);
+
+ }
+
+ return Status;
+}
+
+int8_t VL_perform_ref_calibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal, uint8_t get_data_enable)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t SequenceConfig = 0;
+
+ /* store the value of the sequence config,
+ * this will be reset before the end of the function
+ */
+
+ SequenceConfig = PALDevDataGet(Dev, SequenceConfig);
+
+ /* In the following function we don't save the config to optimize */
+ /* writes on device. Config is saved and restored only once. */
+ Status = VL_perform_vhv_calibration(
+ Dev, pVhvSettings, get_data_enable, 0);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_perform_phase_calibration(
+ Dev, pPhaseCal, get_data_enable, 0);
+
+
+ if (Status == VL_ERROR_NONE) {
+ /* restore the previous Sequence Config */
+ Status = VL_WrByte(Dev, VL_REG_SYSTEM_SEQUENCE_CONFIG,
+ SequenceConfig);
+ if (Status == VL_ERROR_NONE)
+ PALDevDataSet(Dev, SequenceConfig, SequenceConfig);
+
+ }
+
+ return Status;
+}
+
+int8_t VL_set_ref_calibration(struct vl_data *Dev,
+ uint8_t VhvSettings, uint8_t PhaseCal)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t pVhvSettings;
+ uint8_t pPhaseCal;
+
+ Status = VL_ref_calibration_io(Dev, 0,
+ VhvSettings, PhaseCal,
+ &pVhvSettings, &pPhaseCal,
+ 1, 1);
+
+ return Status;
+}
+
+int8_t VL_get_ref_calibration(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t VhvSettings = 0;
+ uint8_t PhaseCal = 0;
+
+ Status = VL_ref_calibration_io(Dev, 1,
+ VhvSettings, PhaseCal,
+ pVhvSettings, pPhaseCal,
+ 1, 1);
+
+ return Status;
+}
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_api_core.c b/drivers/input/misc/vl53l0x/src/vl53l0x_api_core.c
new file mode 100644
index 0000000..6824c02
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_api_core.c
@@ -0,0 +1,2220 @@
+/*
+ * vl53l0x_api_core.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "vl53l0x_api.h"
+#include "vl53l0x_api_core.h"
+#include "vl53l0x_api_calibration.h"
+
+
+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
+#define LOG_FUNCTION_START(fmt, ...) \
+ _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__)
+#define LOG_FUNCTION_END(status, ...) \
+ _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__)
+#define LOG_FUNCTION_END_FMT(status, fmt, ...) \
+ _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__)
+
+int8_t VL_reverse_bytes(uint8_t *data, uint32_t size)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t tempData;
+ uint32_t mirrorIndex;
+ uint32_t middle = size/2;
+ uint32_t index;
+
+ for (index = 0; index < middle; index++) {
+ mirrorIndex = size - index - 1;
+ tempData = data[index];
+ data[index] = data[mirrorIndex];
+ data[mirrorIndex] = tempData;
+ }
+ return Status;
+}
+
+int8_t VL_measurement_poll_for_completion(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t NewDataReady = 0;
+ uint32_t LoopNb;
+
+ LOG_FUNCTION_START("");
+
+ LoopNb = 0;
+
+ do {
+ Status = VL_GetMeasurementDataReady(Dev, &NewDataReady);
+ if (Status != 0)
+ break; /* the error is set */
+
+ if (NewDataReady == 1)
+ break; /* done note that status == 0 */
+
+ LoopNb++;
+ if (LoopNb >= VL_DEFAULT_MAX_LOOP) {
+ Status = VL_ERROR_TIME_OUT;
+ break;
+ }
+
+ VL_PollingDelay(Dev);
+ } while (1);
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+
+uint8_t VL_decode_vcsel_period(uint8_t vcsel_period_reg)
+{
+ /*!
+ * Converts the encoded VCSEL period register value into the real
+ * period in PLL clocks
+ */
+
+ uint8_t vcsel_period_pclks = 0;
+
+ vcsel_period_pclks = (vcsel_period_reg + 1) << 1;
+
+ return vcsel_period_pclks;
+}
+
+uint8_t VL_encode_vcsel_period(uint8_t vcsel_period_pclks)
+{
+ /*!
+ * Converts the encoded VCSEL period register value into the real period
+ * in PLL clocks
+ */
+
+ uint8_t vcsel_period_reg = 0;
+
+ vcsel_period_reg = (vcsel_period_pclks >> 1) - 1;
+
+ return vcsel_period_reg;
+}
+
+
+uint32_t VL_isqrt(uint32_t num)
+{
+ /*
+ * Implements an integer square root
+ *
+ * From: http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
+ */
+
+ uint32_t res = 0;
+ uint32_t bit = 1 << 30;
+ /* The second-to-top bit is set: */
+ /* 1 << 14 for 16-bits, 1 << 30 for 32 bits */
+
+ /* "bit" starts at the highest power of four <= the argument. */
+ while (bit > num)
+ bit >>= 2;
+
+
+ while (bit != 0) {
+ if (num >= res + bit) {
+ num -= res + bit;
+ res = (res >> 1) + bit;
+ } else
+ res >>= 1;
+
+ bit >>= 2;
+ }
+
+ return res;
+}
+
+
+uint32_t VL_quadrature_sum(uint32_t a, uint32_t b)
+{
+ /*
+ * Implements a quadrature sum
+ *
+ * rea = sqrt(a^2 + b^2)
+ *
+ * Trap overflow case max input value is 65535 (16-bit value)
+ * as internal calc are 32-bit wide
+ *
+ * If overflow then seta output to maximum
+ */
+ uint32_t res = 0;
+
+ if (a > 65535 || b > 65535)
+ res = 65535;
+ else
+ res = VL_isqrt(a * a + b * b);
+
+ return res;
+}
+
+
+int8_t VL_device_read_strobe(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t strobe;
+ uint32_t LoopNb;
+
+ LOG_FUNCTION_START("");
+
+ Status |= VL_WrByte(Dev, 0x83, 0x00);
+
+ /* polling use timeout to avoid deadlock*/
+ if (Status == VL_ERROR_NONE) {
+ LoopNb = 0;
+ do {
+ Status = VL_RdByte(Dev, 0x83, &strobe);
+ if ((strobe != 0x00) || Status != VL_ERROR_NONE)
+ break;
+ LoopNb = LoopNb + 1;
+ } while (LoopNb < VL_DEFAULT_MAX_LOOP);
+
+ if (LoopNb >= VL_DEFAULT_MAX_LOOP)
+ Status = VL_ERROR_TIME_OUT;
+
+ }
+
+ Status |= VL_WrByte(Dev, 0x83, 0x01);
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+
+}
+
+int8_t VL_get_info_from_device(struct vl_data *Dev, uint8_t option)
+{
+
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t byte;
+ uint32_t TmpDWord;
+ uint8_t ModuleId;
+ uint8_t Revision;
+ uint8_t ReferenceSpadCount = 0;
+ uint8_t ReferenceSpadType = 0;
+ uint32_t PartUIDUpper = 0;
+ uint32_t PartUIDLower = 0;
+ uint32_t OffsetFixed1104_mm = 0;
+ int16_t OffsetMicroMeters = 0;
+ uint32_t DistMeasTgtFixed1104_mm = 400 << 4;
+ uint32_t DistMeasFixed1104_400_mm = 0;
+ uint32_t SignalRateMeasFixed1104_400_mm = 0;
+ char ProductId[19];
+ char *ProductId_tmp;
+ uint8_t ReadDataFromDeviceDone;
+ unsigned int SignalRateMeasFixed400mmFix = 0;
+ uint8_t NvmRefGoodSpadMap[VL_REF_SPAD_BUFFER_SIZE];
+ int i;
+
+
+ LOG_FUNCTION_START("");
+
+ ReadDataFromDeviceDone = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ReadDataFromDeviceDone);
+
+ /* This access is done only once after that a GetDeviceInfo or */
+ /* datainit is done*/
+ if (ReadDataFromDeviceDone != 7) {
+
+ Status |= VL_WrByte(Dev, 0x80, 0x01);
+ Status |= VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x00);
+
+ Status |= VL_WrByte(Dev, 0xFF, 0x06);
+ Status |= VL_RdByte(Dev, 0x83, &byte);
+ Status |= VL_WrByte(Dev, 0x83, byte|4);
+ Status |= VL_WrByte(Dev, 0xFF, 0x07);
+ Status |= VL_WrByte(Dev, 0x81, 0x01);
+
+ Status |= VL_PollingDelay(Dev);
+
+ Status |= VL_WrByte(Dev, 0x80, 0x01);
+
+ if (((option & 1) == 1) &&
+ ((ReadDataFromDeviceDone & 1) == 0)) {
+ Status |= VL_WrByte(Dev, 0x94, 0x6b);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ ReferenceSpadCount = (uint8_t)((TmpDWord >> 8) & 0x07f);
+ ReferenceSpadType = (uint8_t)((TmpDWord >> 15) & 0x01);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x24);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+
+ NvmRefGoodSpadMap[0] = (uint8_t)((TmpDWord >> 24)
+ & 0xff);
+ NvmRefGoodSpadMap[1] = (uint8_t)((TmpDWord >> 16)
+ & 0xff);
+ NvmRefGoodSpadMap[2] = (uint8_t)((TmpDWord >> 8)
+ & 0xff);
+ NvmRefGoodSpadMap[3] = (uint8_t)(TmpDWord & 0xff);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x25);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ NvmRefGoodSpadMap[4] = (uint8_t)((TmpDWord >> 24)
+ & 0xff);
+ NvmRefGoodSpadMap[5] = (uint8_t)((TmpDWord >> 16)
+ & 0xff);
+ }
+
+ if (((option & 2) == 2) &&
+ ((ReadDataFromDeviceDone & 2) == 0)) {
+
+ Status |= VL_WrByte(Dev, 0x94, 0x02);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdByte(Dev, 0x90, &ModuleId);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x7B);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdByte(Dev, 0x90, &Revision);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x77);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ ProductId[0] = (char)((TmpDWord >> 25) & 0x07f);
+ ProductId[1] = (char)((TmpDWord >> 18) & 0x07f);
+ ProductId[2] = (char)((TmpDWord >> 11) & 0x07f);
+ ProductId[3] = (char)((TmpDWord >> 4) & 0x07f);
+
+ byte = (uint8_t)((TmpDWord & 0x00f) << 3);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x78);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ ProductId[4] = (char)(byte +
+ ((TmpDWord >> 29) & 0x07f));
+ ProductId[5] = (char)((TmpDWord >> 22) & 0x07f);
+ ProductId[6] = (char)((TmpDWord >> 15) & 0x07f);
+ ProductId[7] = (char)((TmpDWord >> 8) & 0x07f);
+ ProductId[8] = (char)((TmpDWord >> 1) & 0x07f);
+
+ byte = (uint8_t)((TmpDWord & 0x001) << 6);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x79);
+
+ Status |= VL_device_read_strobe(Dev);
+
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ ProductId[9] = (char)(byte +
+ ((TmpDWord >> 26) & 0x07f));
+ ProductId[10] = (char)((TmpDWord >> 19) & 0x07f);
+ ProductId[11] = (char)((TmpDWord >> 12) & 0x07f);
+ ProductId[12] = (char)((TmpDWord >> 5) & 0x07f);
+
+ byte = (uint8_t)((TmpDWord & 0x01f) << 2);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x7A);
+
+ Status |= VL_device_read_strobe(Dev);
+
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ ProductId[13] = (char)(byte +
+ ((TmpDWord >> 30) & 0x07f));
+ ProductId[14] = (char)((TmpDWord >> 23) & 0x07f);
+ ProductId[15] = (char)((TmpDWord >> 16) & 0x07f);
+ ProductId[16] = (char)((TmpDWord >> 9) & 0x07f);
+ ProductId[17] = (char)((TmpDWord >> 2) & 0x07f);
+ ProductId[18] = '\0';
+
+ }
+
+ if (((option & 4) == 4) &&
+ ((ReadDataFromDeviceDone & 4) == 0)) {
+
+ Status |= VL_WrByte(Dev, 0x94, 0x7B);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &PartUIDUpper);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x7C);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &PartUIDLower);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x73);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ SignalRateMeasFixed1104_400_mm = (TmpDWord &
+ 0x0000000ff) << 8;
+
+ Status |= VL_WrByte(Dev, 0x94, 0x74);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ SignalRateMeasFixed1104_400_mm |= ((TmpDWord &
+ 0xff000000) >> 24);
+
+ Status |= VL_WrByte(Dev, 0x94, 0x75);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ DistMeasFixed1104_400_mm = (TmpDWord & 0x0000000ff)
+ << 8;
+
+ Status |= VL_WrByte(Dev, 0x94, 0x76);
+ Status |= VL_device_read_strobe(Dev);
+ Status |= VL_RdDWord(Dev, 0x90, &TmpDWord);
+
+ DistMeasFixed1104_400_mm |= ((TmpDWord & 0xff000000)
+ >> 24);
+ }
+
+ Status |= VL_WrByte(Dev, 0x81, 0x00);
+ Status |= VL_WrByte(Dev, 0xFF, 0x06);
+ Status |= VL_RdByte(Dev, 0x83, &byte);
+ Status |= VL_WrByte(Dev, 0x83, byte&0xfb);
+ Status |= VL_WrByte(Dev, 0xFF, 0x01);
+ Status |= VL_WrByte(Dev, 0x00, 0x01);
+
+ Status |= VL_WrByte(Dev, 0xFF, 0x00);
+ Status |= VL_WrByte(Dev, 0x80, 0x00);
+ }
+
+ if ((Status == VL_ERROR_NONE) &&
+ (ReadDataFromDeviceDone != 7)) {
+ /* Assign to variable if status is ok */
+ if (((option & 1) == 1) &&
+ ((ReadDataFromDeviceDone & 1) == 0)) {
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadCount, ReferenceSpadCount);
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ReferenceSpadType, ReferenceSpadType);
+
+ for (i = 0; i < VL_REF_SPAD_BUFFER_SIZE; i++) {
+ Dev->Data.SpadData.RefGoodSpadMap[i] =
+ NvmRefGoodSpadMap[i];
+ }
+ }
+
+ if (((option & 2) == 2) &&
+ ((ReadDataFromDeviceDone & 2) == 0)) {
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ ModuleId, ModuleId);
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ Revision, Revision);
+
+ ProductId_tmp = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ProductId);
+ VL_COPYSTRING(ProductId_tmp, ProductId);
+
+ }
+
+ if (((option & 4) == 4) &&
+ ((ReadDataFromDeviceDone & 4) == 0)) {
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ PartUIDUpper, PartUIDUpper);
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ PartUIDLower, PartUIDLower);
+
+ SignalRateMeasFixed400mmFix =
+ VL_FIXPOINT97TOFIXPOINT1616(
+ SignalRateMeasFixed1104_400_mm);
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ SignalRateMeasFixed400mm,
+ SignalRateMeasFixed400mmFix);
+
+ OffsetMicroMeters = 0;
+ if (DistMeasFixed1104_400_mm != 0) {
+ OffsetFixed1104_mm = DistMeasFixed1104_400_mm -
+ DistMeasTgtFixed1104_mm;
+ OffsetMicroMeters = (OffsetFixed1104_mm
+ * 1000) >> 4;
+ OffsetMicroMeters *= -1;
+ }
+
+ PALDevDataSet(Dev,
+ Part2PartOffsetAdjustmentNVMMicroMeter,
+ OffsetMicroMeters);
+ }
+ byte = (uint8_t)(ReadDataFromDeviceDone|option);
+ VL_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone,
+ byte);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+
+uint32_t VL_calc_macro_period_ps(struct vl_data *Dev,
+ uint8_t vcsel_period_pclks)
+{
+ uint64_t PLL_period_ps;
+ uint32_t macro_period_vclks;
+ uint32_t macro_period_ps;
+
+ LOG_FUNCTION_START("");
+
+ /* The above calculation will produce rounding errors, */
+ /* therefore set fixed value */
+ PLL_period_ps = 1655;
+
+ macro_period_vclks = 2304;
+ macro_period_ps = (uint32_t)(macro_period_vclks
+ * vcsel_period_pclks * PLL_period_ps);
+
+ LOG_FUNCTION_END("");
+ return macro_period_ps;
+}
+
+uint16_t VL_encode_timeout(uint32_t timeout_macro_clks)
+{
+ /*!
+ * Encode timeout in macro periods in (LSByte * 2^MSByte) + 1 format
+ */
+
+ uint16_t encoded_timeout = 0;
+ uint32_t ls_byte = 0;
+ uint16_t ms_byte = 0;
+
+ if (timeout_macro_clks > 0) {
+ ls_byte = timeout_macro_clks - 1;
+
+ while ((ls_byte & 0xFFFFFF00) > 0) {
+ ls_byte = ls_byte >> 1;
+ ms_byte++;
+ }
+
+ encoded_timeout = (ms_byte << 8)
+ + (uint16_t) (ls_byte & 0x000000FF);
+ }
+
+ return encoded_timeout;
+
+}
+
+uint32_t VL_decode_timeout(uint16_t encoded_timeout)
+{
+ /*!
+ * Decode 16-bit timeout register value - format (LSByte * 2^MSByte) + 1
+ */
+
+ uint32_t timeout_macro_clks = 0;
+
+ timeout_macro_clks = ((uint32_t) (encoded_timeout & 0x00FF)
+ << (uint32_t) ((encoded_timeout & 0xFF00) >> 8)) + 1;
+
+ return timeout_macro_clks;
+}
+
+
+/* To convert ms into register value */
+uint32_t VL_calc_timeout_mclks(struct vl_data *Dev,
+ uint32_t timeout_period_us,
+ uint8_t vcsel_period_pclks)
+{
+ uint32_t macro_period_ps;
+ uint32_t macro_period_ns;
+ uint32_t timeout_period_mclks = 0;
+
+ macro_period_ps = VL_calc_macro_period_ps(Dev, vcsel_period_pclks);
+ macro_period_ns = (macro_period_ps + 500) / 1000;
+
+ timeout_period_mclks =
+ (uint32_t) (((timeout_period_us * 1000)
+ + (macro_period_ns / 2)) / macro_period_ns);
+
+ return timeout_period_mclks;
+}
+
+/* To convert register value into us */
+uint32_t VL_calc_timeout_us(struct vl_data *Dev,
+ uint16_t timeout_period_mclks,
+ uint8_t vcsel_period_pclks)
+{
+ uint32_t macro_period_ps;
+ uint32_t macro_period_ns;
+ uint32_t actual_timeout_period_us = 0;
+
+ macro_period_ps = VL_calc_macro_period_ps(Dev, vcsel_period_pclks);
+ macro_period_ns = (macro_period_ps + 500) / 1000;
+
+ actual_timeout_period_us =
+ ((timeout_period_mclks * macro_period_ns) + 500) / 1000;
+
+ return actual_timeout_period_us;
+}
+
+
+int8_t get_sequence_step_timeout(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ uint32_t *pTimeOutMicroSecs)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t CurrentVCSELPulsePeriodPClk;
+ uint8_t EncodedTimeOutByte = 0;
+ uint32_t TimeoutMicroSeconds = 0;
+ uint16_t PreRangeEncodedTimeOut = 0;
+ uint16_t MsrcTimeOutMClks;
+ uint16_t PreRangeTimeOutMClks;
+ uint16_t FinalRangeTimeOutMClks = 0;
+ uint16_t FinalRangeEncodedTimeOut;
+ struct VL_SchedulerSequenceSteps_t SchedulerSequenceSteps;
+
+ if ((SequenceStepId == VL_SEQUENCESTEP_TCC) ||
+ (SequenceStepId == VL_SEQUENCESTEP_DSS) ||
+ (SequenceStepId == VL_SEQUENCESTEP_MSRC)) {
+
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdByte(Dev,
+ VL_REG_MSRC_CONFIG_TIMEOUT_MACROP,
+ &EncodedTimeOutByte);
+ }
+ MsrcTimeOutMClks = VL_decode_timeout(EncodedTimeOutByte);
+
+ TimeoutMicroSeconds = VL_calc_timeout_us(Dev,
+ MsrcTimeOutMClks,
+ CurrentVCSELPulsePeriodPClk);
+ } else if (SequenceStepId == VL_SEQUENCESTEP_PRE_RANGE) {
+ /* Retrieve PRE-RANGE VCSEL Period */
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+
+ /* Retrieve PRE-RANGE Timeout in Macro periods (MCLKS) */
+ if (Status == VL_ERROR_NONE) {
+
+ /* Retrieve PRE-RANGE VCSEL Period */
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdWord(Dev,
+ VL_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ &PreRangeEncodedTimeOut);
+ }
+
+ PreRangeTimeOutMClks = VL_decode_timeout(
+ PreRangeEncodedTimeOut);
+
+ TimeoutMicroSeconds = VL_calc_timeout_us(Dev,
+ PreRangeTimeOutMClks,
+ CurrentVCSELPulsePeriodPClk);
+ }
+ } else if (SequenceStepId == VL_SEQUENCESTEP_FINAL_RANGE) {
+
+ VL_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps);
+ PreRangeTimeOutMClks = 0;
+
+ if (SchedulerSequenceSteps.PreRangeOn) {
+ /* Retrieve PRE-RANGE VCSEL Period */
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+
+ /* Retrieve PRE-RANGE Timeout in Macro periods */
+ /* (MCLKS) */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdWord(Dev,
+ VL_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ &PreRangeEncodedTimeOut);
+ PreRangeTimeOutMClks = VL_decode_timeout(
+ PreRangeEncodedTimeOut);
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ /* Retrieve FINAL-RANGE VCSEL Period */
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_FINAL_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+ }
+
+ /* Retrieve FINAL-RANGE Timeout in Macro periods (MCLKS) */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdWord(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ &FinalRangeEncodedTimeOut);
+ FinalRangeTimeOutMClks = VL_decode_timeout(
+ FinalRangeEncodedTimeOut);
+ }
+
+ FinalRangeTimeOutMClks -= PreRangeTimeOutMClks;
+ TimeoutMicroSeconds = VL_calc_timeout_us(Dev,
+ FinalRangeTimeOutMClks,
+ CurrentVCSELPulsePeriodPClk);
+ }
+
+ *pTimeOutMicroSecs = TimeoutMicroSeconds;
+
+ return Status;
+}
+
+
+int8_t set_sequence_step_timeout(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ uint32_t TimeOutMicroSecs)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t CurrentVCSELPulsePeriodPClk;
+ uint8_t MsrcEncodedTimeOut;
+ uint16_t PreRangeEncodedTimeOut;
+ uint16_t PreRangeTimeOutMClks;
+ uint16_t MsrcRangeTimeOutMClks;
+ uint32_t FinalRangeTimeOutMClks;
+ uint16_t FinalRangeEncodedTimeOut;
+ struct VL_SchedulerSequenceSteps_t SchedulerSequenceSteps;
+
+ if ((SequenceStepId == VL_SEQUENCESTEP_TCC) ||
+ (SequenceStepId == VL_SEQUENCESTEP_DSS) ||
+ (SequenceStepId == VL_SEQUENCESTEP_MSRC)) {
+
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+
+ if (Status == VL_ERROR_NONE) {
+ MsrcRangeTimeOutMClks = VL_calc_timeout_mclks(Dev,
+ TimeOutMicroSecs,
+ (uint8_t)CurrentVCSELPulsePeriodPClk);
+
+ if (MsrcRangeTimeOutMClks > 256)
+ MsrcEncodedTimeOut = 255;
+ else
+ MsrcEncodedTimeOut =
+ (uint8_t)MsrcRangeTimeOutMClks - 1;
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ LastEncodedTimeout,
+ MsrcEncodedTimeOut);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_WrByte(Dev,
+ VL_REG_MSRC_CONFIG_TIMEOUT_MACROP,
+ MsrcEncodedTimeOut);
+ }
+ } else {
+
+ if (SequenceStepId == VL_SEQUENCESTEP_PRE_RANGE) {
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+ PreRangeTimeOutMClks =
+ VL_calc_timeout_mclks(Dev,
+ TimeOutMicroSecs,
+ (uint8_t)CurrentVCSELPulsePeriodPClk);
+ PreRangeEncodedTimeOut = VL_encode_timeout(
+ PreRangeTimeOutMClks);
+
+ VL_SETDEVICESPECIFICPARAMETER(Dev,
+ LastEncodedTimeout,
+ PreRangeEncodedTimeOut);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_WrWord(Dev,
+ VL_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ PreRangeEncodedTimeOut);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev,
+ PreRangeTimeoutMicroSecs,
+ TimeOutMicroSecs);
+ }
+ } else if (SequenceStepId == VL_SEQUENCESTEP_FINAL_RANGE) {
+
+ /* For the final range timeout, the pre-range timeout
+ * must be added. To do this both final and pre-range
+ * timeouts must be expressed in macro periods MClks
+ * because they have different vcsel periods.
+ */
+
+ VL_GetSequenceStepEnables(Dev,
+ &SchedulerSequenceSteps);
+ PreRangeTimeOutMClks = 0;
+ if (SchedulerSequenceSteps.PreRangeOn) {
+
+ /* Retrieve PRE-RANGE VCSEL Period */
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_PRE_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+
+ /* Retrieve PRE-RANGE Timeout in Macro */
+ /* periods (MCLKS) */
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdWord(Dev, 0x51,
+ &PreRangeEncodedTimeOut);
+ PreRangeTimeOutMClks =
+ VL_decode_timeout(
+ PreRangeEncodedTimeOut);
+ }
+ }
+
+ /* Calculate FINAL RANGE Timeout in Macro Periods */
+ /* (MCLKS) and add PRE-RANGE value */
+ if (Status == VL_ERROR_NONE) {
+
+ Status = VL_GetVcselPulsePeriod(Dev,
+ VL_VCSEL_PERIOD_FINAL_RANGE,
+ &CurrentVCSELPulsePeriodPClk);
+ }
+ if (Status == VL_ERROR_NONE) {
+
+ FinalRangeTimeOutMClks =
+ VL_calc_timeout_mclks(Dev,
+ TimeOutMicroSecs,
+ (uint8_t) CurrentVCSELPulsePeriodPClk);
+
+ FinalRangeTimeOutMClks += PreRangeTimeOutMClks;
+
+ FinalRangeEncodedTimeOut =
+ VL_encode_timeout(FinalRangeTimeOutMClks);
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_WrWord(Dev, 0x71,
+ FinalRangeEncodedTimeOut);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev,
+ FinalRangeTimeoutMicroSecs,
+ TimeOutMicroSecs);
+ }
+ }
+ } else
+ Status = VL_ERROR_INVALID_PARAMS;
+
+ }
+ return Status;
+}
+
+int8_t VL_set_vcsel_pulse_period(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t VCSELPulsePeriodPCLK)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t vcsel_period_reg;
+ uint8_t MinPreVcselPeriodPCLK = 12;
+ uint8_t MaxPreVcselPeriodPCLK = 18;
+ uint8_t MinFinalVcselPeriodPCLK = 8;
+ uint8_t MaxFinalVcselPeriodPCLK = 14;
+ uint32_t MeasurementTimingBudgetMicroSeconds;
+ uint32_t FinalRangeTimeoutMicroSeconds;
+ uint32_t PreRangeTimeoutMicroSeconds;
+ uint32_t MsrcTimeoutMicroSeconds;
+ uint8_t PhaseCalInt = 0;
+
+ /* Check if valid clock period requested */
+
+ if ((VCSELPulsePeriodPCLK % 2) != 0) {
+ /* Value must be an even number */
+ Status = VL_ERROR_INVALID_PARAMS;
+ } else if (VcselPeriodType == VL_VCSEL_PERIOD_PRE_RANGE &&
+ (VCSELPulsePeriodPCLK < MinPreVcselPeriodPCLK ||
+ VCSELPulsePeriodPCLK > MaxPreVcselPeriodPCLK)) {
+ Status = VL_ERROR_INVALID_PARAMS;
+ } else if (VcselPeriodType == VL_VCSEL_PERIOD_FINAL_RANGE &&
+ (VCSELPulsePeriodPCLK < MinFinalVcselPeriodPCLK ||
+ VCSELPulsePeriodPCLK > MaxFinalVcselPeriodPCLK)) {
+
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+
+ /* Apply specific settings for the requested clock period */
+
+ if (Status != VL_ERROR_NONE)
+ return Status;
+
+
+ if (VcselPeriodType == VL_VCSEL_PERIOD_PRE_RANGE) {
+
+ /* Set phase check limits */
+ if (VCSELPulsePeriodPCLK == 12) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x18);
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+ } else if (VCSELPulsePeriodPCLK == 14) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x30);
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+ } else if (VCSELPulsePeriodPCLK == 16) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x40);
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+ } else if (VCSELPulsePeriodPCLK == 18) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x50);
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+ }
+ } else if (VcselPeriodType == VL_VCSEL_PERIOD_FINAL_RANGE) {
+
+ if (VCSELPulsePeriodPCLK == 8) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x10);
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+
+ Status |= VL_WrByte(Dev,
+ VL_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x02);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x01);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_LIM,
+ 0x30);
+ Status |= VL_WrByte(Dev, 0xff, 0x00);
+ } else if (VCSELPulsePeriodPCLK == 10) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x28);
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+
+ Status |= VL_WrByte(Dev,
+ VL_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x01);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_LIM,
+ 0x20);
+ Status |= VL_WrByte(Dev, 0xff, 0x00);
+ } else if (VCSELPulsePeriodPCLK == 12) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x38);
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+
+ Status |= VL_WrByte(Dev,
+ VL_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x01);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_LIM,
+ 0x20);
+ Status |= VL_WrByte(Dev, 0xff, 0x00);
+ } else if (VCSELPulsePeriodPCLK == 14) {
+
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH,
+ 0x048);
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW,
+ 0x08);
+
+ Status |= VL_WrByte(Dev,
+ VL_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07);
+
+ Status |= VL_WrByte(Dev, 0xff, 0x01);
+ Status |= VL_WrByte(Dev,
+ VL_REG_ALGO_PHASECAL_LIM,
+ 0x20);
+ Status |= VL_WrByte(Dev, 0xff, 0x00);
+ }
+ }
+
+
+ /* Re-calculate and apply timeouts, in macro periods */
+
+ if (Status == VL_ERROR_NONE) {
+ vcsel_period_reg = VL_encode_vcsel_period((uint8_t)
+ VCSELPulsePeriodPCLK);
+
+ /* When the VCSEL period for the pre or final range is changed, */
+ /* the corresponding timeout must be read from the device using */
+ /* the current VCSEL period, then the new VCSEL period can be */
+ /* applied. The timeout then must be written back to the device */
+ /* using the new VCSEL period. */
+ /* For the MSRC timeout, the same applies - this timeout being */
+ /* dependent on the pre-range vcsel period. */
+ switch (VcselPeriodType) {
+ case VL_VCSEL_PERIOD_PRE_RANGE:
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_PRE_RANGE,
+ &PreRangeTimeoutMicroSeconds);
+
+ if (Status == VL_ERROR_NONE)
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_MSRC,
+ &MsrcTimeoutMicroSeconds);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD,
+ vcsel_period_reg);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = set_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_PRE_RANGE,
+ PreRangeTimeoutMicroSeconds);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = set_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_MSRC,
+ MsrcTimeoutMicroSeconds);
+
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev,
+ PreRangeVcselPulsePeriod,
+ VCSELPulsePeriodPCLK);
+ break;
+ case VL_VCSEL_PERIOD_FINAL_RANGE:
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_FINAL_RANGE,
+ &FinalRangeTimeoutMicroSeconds);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD,
+ vcsel_period_reg);
+
+
+ if (Status == VL_ERROR_NONE)
+ Status = set_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_FINAL_RANGE,
+ FinalRangeTimeoutMicroSeconds);
+
+ VL_SETDEVICESPECIFICPARAMETER(
+ Dev,
+ FinalRangeVcselPulsePeriod,
+ VCSELPulsePeriodPCLK);
+ break;
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+ /* Finally, the timing budget must be re-applied */
+ if (Status == VL_ERROR_NONE) {
+ VL_GETPARAMETERFIELD(Dev,
+ MeasurementTimingBudgetMicroSeconds,
+ MeasurementTimingBudgetMicroSeconds);
+
+ Status = VL_SetMeasurementTimingBudgetMicroSeconds(Dev,
+ MeasurementTimingBudgetMicroSeconds);
+ }
+
+ /* Perform the phase calibration. This is needed after changing on */
+ /* vcsel period. */
+ /* get_data_enable = 0, restore_config = 1 */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_perform_phase_calibration(
+ Dev, &PhaseCalInt, 0, 1);
+
+ return Status;
+}
+
+int8_t VL_get_vcsel_pulse_period(struct vl_data *Dev,
+ uint8_t VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t vcsel_period_reg;
+
+ switch (VcselPeriodType) {
+ case VL_VCSEL_PERIOD_PRE_RANGE:
+ Status = VL_RdByte(Dev,
+ VL_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD,
+ &vcsel_period_reg);
+ break;
+ case VL_VCSEL_PERIOD_FINAL_RANGE:
+ Status = VL_RdByte(Dev,
+ VL_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD,
+ &vcsel_period_reg);
+ break;
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+
+ if (Status == VL_ERROR_NONE)
+ *pVCSELPulsePeriodPCLK =
+ VL_decode_vcsel_period(vcsel_period_reg);
+
+ return Status;
+}
+
+
+
+int8_t VL_set_measurement_timing_budget_micro_seconds(
+ struct vl_data *Dev, uint32_t MeasurementTimingBudgetMicroSeconds)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint32_t FinalRangeTimingBudgetMicroSeconds;
+ struct VL_SchedulerSequenceSteps_t SchedulerSequenceSteps;
+ uint32_t MsrcDccTccTimeoutMicroSeconds = 2000;
+ uint32_t StartOverheadMicroSeconds = 1910;
+ uint32_t EndOverheadMicroSeconds = 960;
+ uint32_t MsrcOverheadMicroSeconds = 660;
+ uint32_t TccOverheadMicroSeconds = 590;
+ uint32_t DssOverheadMicroSeconds = 690;
+ uint32_t PreRangeOverheadMicroSeconds = 660;
+ uint32_t FinalRangeOverheadMicroSeconds = 550;
+ uint32_t PreRangeTimeoutMicroSeconds = 0;
+ uint32_t cMinTimingBudgetMicroSeconds = 20000;
+ uint32_t SubTimeout = 0;
+
+ LOG_FUNCTION_START("");
+
+ if (MeasurementTimingBudgetMicroSeconds
+ < cMinTimingBudgetMicroSeconds) {
+ Status = VL_ERROR_INVALID_PARAMS;
+ return Status;
+ }
+
+ FinalRangeTimingBudgetMicroSeconds =
+ MeasurementTimingBudgetMicroSeconds -
+ (StartOverheadMicroSeconds + EndOverheadMicroSeconds);
+
+ Status = VL_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps);
+
+ if (Status == VL_ERROR_NONE &&
+ (SchedulerSequenceSteps.TccOn ||
+ SchedulerSequenceSteps.MsrcOn ||
+ SchedulerSequenceSteps.DssOn)) {
+
+ /* TCC, MSRC and DSS all share the same timeout */
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_MSRC,
+ &MsrcDccTccTimeoutMicroSeconds);
+
+ /* Subtract the TCC, MSRC and DSS timeouts if they are */
+ /* enabled. */
+
+ if (Status != VL_ERROR_NONE)
+ return Status;
+
+ /* TCC */
+ if (SchedulerSequenceSteps.TccOn) {
+
+ SubTimeout = MsrcDccTccTimeoutMicroSeconds
+ + TccOverheadMicroSeconds;
+
+ if (SubTimeout <
+ FinalRangeTimingBudgetMicroSeconds) {
+ FinalRangeTimingBudgetMicroSeconds -=
+ SubTimeout;
+ } else {
+ /* Requested timeout too big. */
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+ if (Status != VL_ERROR_NONE) {
+ LOG_FUNCTION_END(Status);
+ return Status;
+ }
+
+ /* DSS */
+ if (SchedulerSequenceSteps.DssOn) {
+
+ SubTimeout = 2 * (MsrcDccTccTimeoutMicroSeconds +
+ DssOverheadMicroSeconds);
+
+ if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) {
+ FinalRangeTimingBudgetMicroSeconds
+ -= SubTimeout;
+ } else {
+ /* Requested timeout too big. */
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ } else if (SchedulerSequenceSteps.MsrcOn) {
+ /* MSRC */
+ SubTimeout = MsrcDccTccTimeoutMicroSeconds +
+ MsrcOverheadMicroSeconds;
+
+ if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) {
+ FinalRangeTimingBudgetMicroSeconds
+ -= SubTimeout;
+ } else {
+ /* Requested timeout too big. */
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+ }
+
+ if (Status != VL_ERROR_NONE) {
+ LOG_FUNCTION_END(Status);
+ return Status;
+ }
+
+ if (SchedulerSequenceSteps.PreRangeOn) {
+
+ /* Subtract the Pre-range timeout if enabled. */
+
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_PRE_RANGE,
+ &PreRangeTimeoutMicroSeconds);
+
+ SubTimeout = PreRangeTimeoutMicroSeconds +
+ PreRangeOverheadMicroSeconds;
+
+ if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) {
+ FinalRangeTimingBudgetMicroSeconds -= SubTimeout;
+ } else {
+ /* Requested timeout too big. */
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+
+ if (Status == VL_ERROR_NONE &&
+ SchedulerSequenceSteps.FinalRangeOn) {
+
+ FinalRangeTimingBudgetMicroSeconds -=
+ FinalRangeOverheadMicroSeconds;
+
+ /* Final Range Timeout
+ * Note that the final range timeout is determined by the timing
+ * budget and the sum of all other timeouts within the sequence.
+ * If there is no room for the final range timeout,then an error
+ * will be set. Otherwise the remaining time will be applied to
+ * the final range.
+ */
+ Status = set_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_FINAL_RANGE,
+ FinalRangeTimingBudgetMicroSeconds);
+
+ VL_SETPARAMETERFIELD(Dev,
+ MeasurementTimingBudgetMicroSeconds,
+ MeasurementTimingBudgetMicroSeconds);
+ }
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+int8_t VL_get_measurement_timing_budget_micro_seconds(
+ struct vl_data *Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds)
+{
+ int8_t Status = VL_ERROR_NONE;
+ struct VL_SchedulerSequenceSteps_t SchedulerSequenceSteps;
+ uint32_t FinalRangeTimeoutMicroSeconds;
+ uint32_t MsrcDccTccTimeoutMicroSeconds = 2000;
+ uint32_t StartOverheadMicroSeconds = 1910;
+ uint32_t EndOverheadMicroSeconds = 960;
+ uint32_t MsrcOverheadMicroSeconds = 660;
+ uint32_t TccOverheadMicroSeconds = 590;
+ uint32_t DssOverheadMicroSeconds = 690;
+ uint32_t PreRangeOverheadMicroSeconds = 660;
+ uint32_t FinalRangeOverheadMicroSeconds = 550;
+ uint32_t PreRangeTimeoutMicroSeconds = 0;
+
+ LOG_FUNCTION_START("");
+
+ /* Start and end overhead times always present */
+ *pMeasurementTimingBudgetMicroSeconds
+ = StartOverheadMicroSeconds + EndOverheadMicroSeconds;
+
+ Status = VL_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps);
+
+ if (Status != VL_ERROR_NONE) {
+ LOG_FUNCTION_END(Status);
+ return Status;
+ }
+
+
+ if (SchedulerSequenceSteps.TccOn ||
+ SchedulerSequenceSteps.MsrcOn ||
+ SchedulerSequenceSteps.DssOn) {
+
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_MSRC,
+ &MsrcDccTccTimeoutMicroSeconds);
+
+ if (Status == VL_ERROR_NONE) {
+ if (SchedulerSequenceSteps.TccOn) {
+ *pMeasurementTimingBudgetMicroSeconds +=
+ MsrcDccTccTimeoutMicroSeconds +
+ TccOverheadMicroSeconds;
+ }
+
+ if (SchedulerSequenceSteps.DssOn) {
+ *pMeasurementTimingBudgetMicroSeconds +=
+ 2 * (MsrcDccTccTimeoutMicroSeconds +
+ DssOverheadMicroSeconds);
+ } else if (SchedulerSequenceSteps.MsrcOn) {
+ *pMeasurementTimingBudgetMicroSeconds +=
+ MsrcDccTccTimeoutMicroSeconds +
+ MsrcOverheadMicroSeconds;
+ }
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ if (SchedulerSequenceSteps.PreRangeOn) {
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_PRE_RANGE,
+ &PreRangeTimeoutMicroSeconds);
+ *pMeasurementTimingBudgetMicroSeconds +=
+ PreRangeTimeoutMicroSeconds +
+ PreRangeOverheadMicroSeconds;
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ if (SchedulerSequenceSteps.FinalRangeOn) {
+ Status = get_sequence_step_timeout(Dev,
+ VL_SEQUENCESTEP_FINAL_RANGE,
+ &FinalRangeTimeoutMicroSeconds);
+ *pMeasurementTimingBudgetMicroSeconds +=
+ (FinalRangeTimeoutMicroSeconds +
+ FinalRangeOverheadMicroSeconds);
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ VL_SETPARAMETERFIELD(Dev,
+ MeasurementTimingBudgetMicroSeconds,
+ *pMeasurementTimingBudgetMicroSeconds);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+
+
+int8_t VL_load_tuning_settings(struct vl_data *Dev,
+ uint8_t *pTuningSettingBuffer)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int i;
+ int Index;
+ uint8_t msb;
+ uint8_t lsb;
+ uint8_t SelectParam;
+ uint8_t NumberOfWrites;
+ uint8_t Address;
+ uint8_t localBuffer[4]; /* max */
+ uint16_t Temp16;
+
+ LOG_FUNCTION_START("");
+
+ Index = 0;
+
+ while ((*(pTuningSettingBuffer + Index) != 0) &&
+ (Status == VL_ERROR_NONE)) {
+ NumberOfWrites = *(pTuningSettingBuffer + Index);
+ Index++;
+ if (NumberOfWrites == 0xFF) {
+ /* internal parameters */
+ SelectParam = *(pTuningSettingBuffer + Index);
+ Index++;
+ switch (SelectParam) {
+ case 0: /* uint16_t SigmaEstRefArray -> 2 bytes */
+ msb = *(pTuningSettingBuffer + Index);
+ Index++;
+ lsb = *(pTuningSettingBuffer + Index);
+ Index++;
+ Temp16 = VL_MAKEUINT16(lsb, msb);
+ PALDevDataSet(Dev, SigmaEstRefArray, Temp16);
+ break;
+ case 1: /* uint16_t SigmaEstEffPulseWidth -> 2 bytes */
+ msb = *(pTuningSettingBuffer + Index);
+ Index++;
+ lsb = *(pTuningSettingBuffer + Index);
+ Index++;
+ Temp16 = VL_MAKEUINT16(lsb, msb);
+ PALDevDataSet(Dev, SigmaEstEffPulseWidth,
+ Temp16);
+ break;
+ case 2: /* uint16_t SigmaEstEffAmbWidth -> 2 bytes */
+ msb = *(pTuningSettingBuffer + Index);
+ Index++;
+ lsb = *(pTuningSettingBuffer + Index);
+ Index++;
+ Temp16 = VL_MAKEUINT16(lsb, msb);
+ PALDevDataSet(Dev, SigmaEstEffAmbWidth, Temp16);
+ break;
+ case 3: /* uint16_t targetRefRate -> 2 bytes */
+ msb = *(pTuningSettingBuffer + Index);
+ Index++;
+ lsb = *(pTuningSettingBuffer + Index);
+ Index++;
+ Temp16 = VL_MAKEUINT16(lsb, msb);
+ PALDevDataSet(Dev, targetRefRate, Temp16);
+ break;
+ default: /* invalid parameter */
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+
+ } else if (NumberOfWrites <= 4) {
+ Address = *(pTuningSettingBuffer + Index);
+ Index++;
+
+ for (i = 0; i < NumberOfWrites; i++) {
+ localBuffer[i] = *(pTuningSettingBuffer +
+ Index);
+ Index++;
+ }
+
+ Status = VL_WriteMulti(Dev, Address, localBuffer,
+ NumberOfWrites);
+
+ } else {
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_total_xtalk_rate(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ unsigned int *ptotal_xtalk_rate_mcps)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ uint8_t xtalkCompEnable;
+ unsigned int totalXtalkMegaCps;
+ unsigned int xtalkPerSpadMegaCps;
+
+ *ptotal_xtalk_rate_mcps = 0;
+
+ Status = VL_GetXTalkCompensationEnable(Dev, &xtalkCompEnable);
+ if (Status == VL_ERROR_NONE) {
+
+ if (xtalkCompEnable) {
+
+ VL_GETPARAMETERFIELD(
+ Dev,
+ XTalkCompensationRateMegaCps,
+ xtalkPerSpadMegaCps);
+
+ /* FixPoint1616 * FixPoint 8:8 = FixPoint0824 */
+ totalXtalkMegaCps =
+ pRangingMeasurementData->EffectiveSpadRtnCount *
+ xtalkPerSpadMegaCps;
+
+ /* FixPoint0824 >> 8 = FixPoint1616 */
+ *ptotal_xtalk_rate_mcps =
+ (totalXtalkMegaCps + 0x80) >> 8;
+ }
+ }
+
+ return Status;
+}
+
+int8_t VL_get_total_signal_rate(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ unsigned int *ptotal_signal_rate_mcps)
+{
+ int8_t Status = VL_ERROR_NONE;
+ unsigned int totalXtalkMegaCps;
+
+ LOG_FUNCTION_START("");
+
+ *ptotal_signal_rate_mcps =
+ pRangingMeasurementData->SignalRateRtnMegaCps;
+
+ Status = VL_get_total_xtalk_rate(
+ Dev, pRangingMeasurementData, &totalXtalkMegaCps);
+
+ if (Status == VL_ERROR_NONE)
+ *ptotal_signal_rate_mcps += totalXtalkMegaCps;
+
+ return Status;
+}
+
+int8_t VL_calc_dmax(
+ struct vl_data *Dev,
+ unsigned int totalSignalRate_mcps,
+ unsigned int totalCorrSignalRate_mcps,
+ unsigned int pwMult,
+ uint32_t sigmaEstimateP1,
+ unsigned int sigmaEstimateP2,
+ uint32_t peakVcselDuration_us,
+ uint32_t *pdmax_mm)
+{
+ const uint32_t cSigmaLimit = 18;
+ const unsigned int cSignalLimit = 0x4000; /* 0.25 */
+ const unsigned int cSigmaEstRef = 0x00000042; /* 0.001 */
+ const uint32_t cAmbEffWidthSigmaEst_ns = 6;
+ const uint32_t cAmbEffWidthDMax_ns = 7;
+ uint32_t dmaxCalRange_mm;
+ unsigned int dmaxCalSignalRateRtn_mcps;
+ unsigned int minSignalNeeded;
+ unsigned int minSignalNeeded_p1;
+ unsigned int minSignalNeeded_p2;
+ unsigned int minSignalNeeded_p3;
+ unsigned int minSignalNeeded_p4;
+ unsigned int sigmaLimitTmp;
+ unsigned int sigmaEstSqTmp;
+ unsigned int signalLimitTmp;
+ unsigned int SignalAt0mm;
+ unsigned int dmaxDark;
+ unsigned int dmaxAmbient;
+ unsigned int dmaxDarkTmp;
+ unsigned int sigmaEstP2Tmp;
+ uint32_t signalRateTemp_mcps;
+
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ dmaxCalRange_mm =
+ PALDevDataGet(Dev, DmaxCalRangeMilliMeter);
+
+ dmaxCalSignalRateRtn_mcps =
+ PALDevDataGet(Dev, DmaxCalSignalRateRtnMegaCps);
+
+ /* uint32 * FixPoint1616 = FixPoint1616 */
+ SignalAt0mm = dmaxCalRange_mm * dmaxCalSignalRateRtn_mcps;
+
+ /* FixPoint1616 >> 8 = FixPoint2408 */
+ SignalAt0mm = (SignalAt0mm + 0x80) >> 8;
+ SignalAt0mm *= dmaxCalRange_mm;
+
+ minSignalNeeded_p1 = 0;
+ if (totalCorrSignalRate_mcps > 0) {
+
+ /* Shift by 10 bits to increase resolution prior to the */
+ /* division */
+ signalRateTemp_mcps = totalSignalRate_mcps << 10;
+
+ /* Add rounding value prior to division */
+ minSignalNeeded_p1 = signalRateTemp_mcps +
+ (totalCorrSignalRate_mcps/2);
+
+ /* FixPoint0626/FixPoint1616 = FixPoint2210 */
+ minSignalNeeded_p1 /= totalCorrSignalRate_mcps;
+
+ /* Apply a factored version of the speed of light. */
+ /* Correction to be applied at the end */
+ minSignalNeeded_p1 *= 3;
+
+ /* FixPoint2210 * FixPoint2210 = FixPoint1220 */
+ minSignalNeeded_p1 *= minSignalNeeded_p1;
+
+ /* FixPoint1220 >> 16 = FixPoint2804 */
+ minSignalNeeded_p1 = (minSignalNeeded_p1 + 0x8000) >> 16;
+ }
+
+ minSignalNeeded_p2 = pwMult * sigmaEstimateP1;
+
+ /* FixPoint1616 >> 16 = uint32 */
+ minSignalNeeded_p2 = (minSignalNeeded_p2 + 0x8000) >> 16;
+
+ /* uint32 * uint32 = uint32 */
+ minSignalNeeded_p2 *= minSignalNeeded_p2;
+
+ /* Check sigmaEstimateP2
+ * If this value is too high there is not enough signal rate
+ * to calculate dmax value so set a suitable value to ensure
+ * a very small dmax.
+ */
+ sigmaEstP2Tmp = (sigmaEstimateP2 + 0x8000) >> 16;
+ sigmaEstP2Tmp = (sigmaEstP2Tmp + cAmbEffWidthSigmaEst_ns/2)/
+ cAmbEffWidthSigmaEst_ns;
+ sigmaEstP2Tmp *= cAmbEffWidthDMax_ns;
+
+ if (sigmaEstP2Tmp > 0xffff) {
+ minSignalNeeded_p3 = 0xfff00000;
+ } else {
+
+ /* DMAX uses a different ambient width from sigma, so apply
+ * correction.
+ * Perform division before multiplication to prevent overflow.
+ */
+ sigmaEstimateP2 = (sigmaEstimateP2 + cAmbEffWidthSigmaEst_ns/2)/
+ cAmbEffWidthSigmaEst_ns;
+ sigmaEstimateP2 *= cAmbEffWidthDMax_ns;
+
+ /* FixPoint1616 >> 16 = uint32 */
+ minSignalNeeded_p3 = (sigmaEstimateP2 + 0x8000) >> 16;
+
+ minSignalNeeded_p3 *= minSignalNeeded_p3;
+
+ }
+
+ /* FixPoint1814 / uint32 = FixPoint1814 */
+ sigmaLimitTmp = ((cSigmaLimit << 14) + 500) / 1000;
+
+ /* FixPoint1814 * FixPoint1814 = FixPoint3628 := FixPoint0428 */
+ sigmaLimitTmp *= sigmaLimitTmp;
+
+ /* FixPoint1616 * FixPoint1616 = FixPoint3232 */
+ sigmaEstSqTmp = cSigmaEstRef * cSigmaEstRef;
+
+ /* FixPoint3232 >> 4 = FixPoint0428 */
+ sigmaEstSqTmp = (sigmaEstSqTmp + 0x08) >> 4;
+
+ /* FixPoint0428 - FixPoint0428 = FixPoint0428 */
+ sigmaLimitTmp -= sigmaEstSqTmp;
+
+ /* uint32_t * FixPoint0428 = FixPoint0428 */
+ minSignalNeeded_p4 = 4 * 12 * sigmaLimitTmp;
+
+ /* FixPoint0428 >> 14 = FixPoint1814 */
+ minSignalNeeded_p4 = (minSignalNeeded_p4 + 0x2000) >> 14;
+
+ /* uint32 + uint32 = uint32 */
+ minSignalNeeded = (minSignalNeeded_p2 + minSignalNeeded_p3);
+
+ /* uint32 / uint32 = uint32 */
+ minSignalNeeded += (peakVcselDuration_us/2);
+ minSignalNeeded /= peakVcselDuration_us;
+
+ /* uint32 << 14 = FixPoint1814 */
+ minSignalNeeded <<= 14;
+
+ /* FixPoint1814 / FixPoint1814 = uint32 */
+ minSignalNeeded += (minSignalNeeded_p4/2);
+ minSignalNeeded /= minSignalNeeded_p4;
+
+ /* FixPoint3200 * FixPoint2804 := FixPoint2804*/
+ minSignalNeeded *= minSignalNeeded_p1;
+
+ /* Apply correction by dividing by 1000000.
+ * This assumes 10E16 on the numerator of the equation
+ * and 10E-22 on the denominator.
+ * We do this because 32bit fix point calculation can't
+ * handle the larger and smaller elements of this equation,
+ * i.e. speed of light and pulse widths.
+ */
+ minSignalNeeded = (minSignalNeeded + 500) / 1000;
+ minSignalNeeded <<= 4;
+
+ minSignalNeeded = (minSignalNeeded + 500) / 1000;
+
+ /* FixPoint1616 >> 8 = FixPoint2408 */
+ signalLimitTmp = (cSignalLimit + 0x80) >> 8;
+
+ /* FixPoint2408/FixPoint2408 = uint32 */
+ if (signalLimitTmp != 0)
+ dmaxDarkTmp = (SignalAt0mm + (signalLimitTmp / 2))
+ / signalLimitTmp;
+ else
+ dmaxDarkTmp = 0;
+
+ dmaxDark = VL_isqrt(dmaxDarkTmp);
+
+ /* FixPoint2408/FixPoint2408 = uint32 */
+ if (minSignalNeeded != 0)
+ dmaxAmbient = (SignalAt0mm + minSignalNeeded/2)
+ / minSignalNeeded;
+ else
+ dmaxAmbient = 0;
+
+ dmaxAmbient = VL_isqrt(dmaxAmbient);
+
+ *pdmax_mm = dmaxDark;
+ if (dmaxDark > dmaxAmbient)
+ *pdmax_mm = dmaxAmbient;
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+
+int8_t VL_calc_sigma_estimate(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ unsigned int *pSigmaEstimate,
+ uint32_t *pDmax_mm)
+{
+ /* Expressed in 100ths of a ns, i.e. centi-ns */
+ const uint32_t cPulseEffectiveWidth_centi_ns = 800;
+ /* Expressed in 100ths of a ns, i.e. centi-ns */
+ const uint32_t cAmbientEffectiveWidth_centi_ns = 600;
+ /* 25ms */
+ const unsigned int cDfltFinalRangeIntegrationTimeMilliSecs =
+ 0x00190000;
+ const uint32_t cVcselPulseWidth_ps = 4700; /* pico secs */
+ const unsigned int cSigmaEstMax = 0x028F87AE;
+ const unsigned int cSigmaEstRtnMax = 0xF000;
+ const unsigned int cAmbToSignalRatioMax = 0xF0000000/
+ cAmbientEffectiveWidth_centi_ns;
+ /* Time Of Flight per mm (6.6 pico secs) */
+ const unsigned int cTOF_per_mm_ps = 0x0006999A;
+ const uint32_t c16BitRoundingParam = 0x00008000;
+ const unsigned int cMaxXTalk_kcps = 0x00320000;
+ const uint32_t cPllPeriod_ps = 1655;
+
+ uint32_t vcselTotalEventsRtn;
+ uint32_t finalRangeTimeoutMicroSecs;
+ uint32_t preRangeTimeoutMicroSecs;
+ uint32_t finalRangeIntegrationTimeMilliSecs;
+ unsigned int sigmaEstimateP1;
+ unsigned int sigmaEstimateP2;
+ unsigned int sigmaEstimateP3;
+ unsigned int deltaT_ps;
+ unsigned int pwMult;
+ unsigned int sigmaEstRtn;
+ unsigned int sigmaEstimate;
+ unsigned int xTalkCorrection;
+ unsigned int ambientRate_kcps;
+ unsigned int peakSignalRate_kcps;
+ unsigned int xTalkCompRate_mcps;
+ uint32_t xTalkCompRate_kcps;
+ int8_t Status = VL_ERROR_NONE;
+ unsigned int diff1_mcps;
+ unsigned int diff2_mcps;
+ unsigned int sqr1;
+ unsigned int sqr2;
+ unsigned int sqrSum;
+ unsigned int sqrtResult_centi_ns;
+ unsigned int sqrtResult;
+ unsigned int totalSignalRate_mcps;
+ unsigned int correctedSignalRate_mcps;
+ unsigned int sigmaEstRef;
+ uint32_t vcselWidth;
+ uint32_t finalRangeMacroPCLKS;
+ uint32_t preRangeMacroPCLKS;
+ uint32_t peakVcselDuration_us;
+ uint8_t finalRangeVcselPCLKS;
+ uint8_t preRangeVcselPCLKS;
+ /*! \addtogroup calc_sigma_estimate
+ * @{
+ *
+ * Estimates the range sigma
+ */
+
+ LOG_FUNCTION_START("");
+
+ VL_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps,
+ xTalkCompRate_mcps);
+
+ /*
+ * We work in kcps rather than mcps as this helps keep within the
+ * confines of the 32 Fix1616 type.
+ */
+
+ ambientRate_kcps =
+ (pRangingMeasurementData->AmbientRateRtnMegaCps * 1000) >> 16;
+
+ correctedSignalRate_mcps =
+ pRangingMeasurementData->SignalRateRtnMegaCps;
+
+
+ Status = VL_get_total_signal_rate(
+ Dev, pRangingMeasurementData, &totalSignalRate_mcps);
+ Status = VL_get_total_xtalk_rate(
+ Dev, pRangingMeasurementData, &xTalkCompRate_mcps);
+
+
+ /* Signal rate measurement provided by device is the
+ * peak signal rate, not average.
+ */
+ peakSignalRate_kcps = (totalSignalRate_mcps * 1000);
+ peakSignalRate_kcps = (peakSignalRate_kcps + 0x8000) >> 16;
+
+ xTalkCompRate_kcps = xTalkCompRate_mcps * 1000;
+
+ if (xTalkCompRate_kcps > cMaxXTalk_kcps)
+ xTalkCompRate_kcps = cMaxXTalk_kcps;
+
+ if (Status == VL_ERROR_NONE) {
+
+ /* Calculate final range macro periods */
+ finalRangeTimeoutMicroSecs = VL_GETDEVICESPECIFICPARAMETER(
+ Dev, FinalRangeTimeoutMicroSecs);
+
+ finalRangeVcselPCLKS = VL_GETDEVICESPECIFICPARAMETER(
+ Dev, FinalRangeVcselPulsePeriod);
+
+ finalRangeMacroPCLKS = VL_calc_timeout_mclks(
+ Dev, finalRangeTimeoutMicroSecs, finalRangeVcselPCLKS);
+
+ /* Calculate pre-range macro periods */
+ preRangeTimeoutMicroSecs = VL_GETDEVICESPECIFICPARAMETER(
+ Dev, PreRangeTimeoutMicroSecs);
+
+ preRangeVcselPCLKS = VL_GETDEVICESPECIFICPARAMETER(
+ Dev, PreRangeVcselPulsePeriod);
+
+ preRangeMacroPCLKS = VL_calc_timeout_mclks(
+ Dev, preRangeTimeoutMicroSecs, preRangeVcselPCLKS);
+
+ vcselWidth = 3;
+ if (finalRangeVcselPCLKS == 8)
+ vcselWidth = 2;
+
+
+ peakVcselDuration_us = vcselWidth * 2048 *
+ (preRangeMacroPCLKS + finalRangeMacroPCLKS);
+ peakVcselDuration_us = (peakVcselDuration_us + 500)/1000;
+ peakVcselDuration_us *= cPllPeriod_ps;
+ peakVcselDuration_us = (peakVcselDuration_us + 500)/1000;
+
+ /* Fix1616 >> 8 = Fix2408 */
+ totalSignalRate_mcps = (totalSignalRate_mcps + 0x80) >> 8;
+
+ /* Fix2408 * uint32 = Fix2408 */
+ vcselTotalEventsRtn = totalSignalRate_mcps *
+ peakVcselDuration_us;
+
+ /* Fix2408 >> 8 = uint32 */
+ vcselTotalEventsRtn = (vcselTotalEventsRtn + 0x80) >> 8;
+
+ /* Fix2408 << 8 = Fix1616 = */
+ totalSignalRate_mcps <<= 8;
+ }
+
+ if (Status != VL_ERROR_NONE) {
+ LOG_FUNCTION_END(Status);
+ return Status;
+ }
+
+ if (peakSignalRate_kcps == 0) {
+ *pSigmaEstimate = cSigmaEstMax;
+ PALDevDataSet(Dev, SigmaEstimate, cSigmaEstMax);
+ *pDmax_mm = 0;
+ } else {
+ if (vcselTotalEventsRtn < 1)
+ vcselTotalEventsRtn = 1;
+
+ sigmaEstimateP1 = cPulseEffectiveWidth_centi_ns;
+
+ /* ((FixPoint1616 << 16)* uint32)/uint32 = FixPoint1616 */
+ sigmaEstimateP2 = (ambientRate_kcps << 16)/peakSignalRate_kcps;
+ if (sigmaEstimateP2 > cAmbToSignalRatioMax) {
+ /* Clip to prevent overflow. Will ensure safe */
+ /* max result. */
+ sigmaEstimateP2 = cAmbToSignalRatioMax;
+ }
+ sigmaEstimateP2 *= cAmbientEffectiveWidth_centi_ns;
+
+ sigmaEstimateP3 = 2 * VL_isqrt(vcselTotalEventsRtn * 12);
+
+ /* uint32 * FixPoint1616 = FixPoint1616 */
+ deltaT_ps = pRangingMeasurementData->RangeMilliMeter *
+ cTOF_per_mm_ps;
+
+ /* vcselRate - xtalkCompRate */
+ /* (uint32 << 16) - FixPoint1616 = FixPoint1616. */
+ /* Divide result by 1000 to convert to mcps. */
+ /* 500 is added to ensure rounding when integer division */
+ /* truncates. */
+ diff1_mcps = (((peakSignalRate_kcps << 16) -
+ 2 * xTalkCompRate_kcps) + 500)/1000;
+
+ /* vcselRate + xtalkCompRate */
+ diff2_mcps = ((peakSignalRate_kcps << 16) + 500)/1000;
+
+ /* Shift by 8 bits to increase resolution prior to the */
+ /* division */
+ diff1_mcps <<= 8;
+
+ /* FixPoint0824/FixPoint1616 = FixPoint2408 */
+ xTalkCorrection = abs(diff1_mcps/diff2_mcps);
+
+ /* FixPoint2408 << 8 = FixPoint1616 */
+ xTalkCorrection <<= 8;
+
+ if (pRangingMeasurementData->RangeStatus != 0) {
+ pwMult = 1 << 16;
+ } else {
+ /* FixPoint1616/uint32 = FixPoint1616 *i */
+ /* smaller than 1.0f */
+ pwMult = deltaT_ps/cVcselPulseWidth_ps;
+
+ /* FixPoint1616 * FixPoint1616 = FixPoint3232, however both */
+ /* values are small enough such that32 bits will not be */
+ /* exceeded. */
+ pwMult *= ((1 << 16) - xTalkCorrection);
+
+ /* (FixPoint3232 >> 16) = FixPoint1616 */
+ pwMult = (pwMult + c16BitRoundingParam) >> 16;
+
+ /* FixPoint1616 + FixPoint1616 = FixPoint1616 */
+ pwMult += (1 << 16);
+
+ /* At this point the value will be 1.xx, */
+ /* therefore if we square */
+ /* the value this will exceed 32 bits. */
+ /* To address this perform */
+ /* a single shift to the right before the multiplication. */
+ pwMult >>= 1;
+ /* FixPoint1715 * FixPoint1715 = FixPoint3430 */
+ pwMult = pwMult * pwMult;
+
+ /* (FixPoint3430 >> 14) = Fix1616 */
+ pwMult >>= 14;
+ }
+
+ /* FixPoint1616 * uint32 = FixPoint1616 */
+ sqr1 = pwMult * sigmaEstimateP1;
+
+ /* (FixPoint1616 >> 16) = FixPoint3200 */
+ sqr1 = (sqr1 + 0x8000) >> 16;
+
+ /* FixPoint3200 * FixPoint3200 = FixPoint6400 */
+ sqr1 *= sqr1;
+
+ sqr2 = sigmaEstimateP2;
+
+ /* (FixPoint1616 >> 16) = FixPoint3200 */
+ sqr2 = (sqr2 + 0x8000) >> 16;
+
+ /* FixPoint3200 * FixPoint3200 = FixPoint6400 */
+ sqr2 *= sqr2;
+
+ /* FixPoint64000 + FixPoint6400 = FixPoint6400 */
+ sqrSum = sqr1 + sqr2;
+
+ /* SQRT(FixPoin6400) = FixPoint3200 */
+ sqrtResult_centi_ns = VL_isqrt(sqrSum);
+
+ /* (FixPoint3200 << 16) = FixPoint1616 */
+ sqrtResult_centi_ns <<= 16;
+
+ /*
+ * Note that the Speed Of Light is expressed in um per 1E-10
+ * seconds (2997) Therefore to get mm/ns we have to divide by
+ * 10000
+ */
+ sigmaEstRtn = (((sqrtResult_centi_ns+50)/100) /
+ sigmaEstimateP3);
+ sigmaEstRtn *= VL_SPEED_OF_LIGHT_IN_AIR;
+
+ /* Add 5000 before dividing by 10000 to ensure rounding. */
+ sigmaEstRtn += 5000;
+ sigmaEstRtn /= 10000;
+
+ if (sigmaEstRtn > cSigmaEstRtnMax) {
+ /* Clip to prevent overflow. Will ensure safe */
+ /* max result. */
+ sigmaEstRtn = cSigmaEstRtnMax;
+ }
+ finalRangeIntegrationTimeMilliSecs =
+ (finalRangeTimeoutMicroSecs +
+ preRangeTimeoutMicroSecs + 500)/1000;
+
+ /* sigmaEstRef = 1mm * 25ms/final range integration time */
+ /* (inc pre-range) sqrt(FixPoint1616/int) = FixPoint2408) */
+ sigmaEstRef =
+ VL_isqrt((cDfltFinalRangeIntegrationTimeMilliSecs +
+ finalRangeIntegrationTimeMilliSecs/2)/
+ finalRangeIntegrationTimeMilliSecs);
+
+ /* FixPoint2408 << 8 = FixPoint1616 */
+ sigmaEstRef <<= 8;
+ sigmaEstRef = (sigmaEstRef + 500)/1000;
+
+ /* FixPoint1616 * FixPoint1616 = FixPoint3232 */
+ sqr1 = sigmaEstRtn * sigmaEstRtn;
+ /* FixPoint1616 * FixPoint1616 = FixPoint3232 */
+ sqr2 = sigmaEstRef * sigmaEstRef;
+
+ /* sqrt(FixPoint3232) = FixPoint1616 */
+ sqrtResult = VL_isqrt((sqr1 + sqr2));
+ /* Note that the Shift by 4 bits increases */
+ /*resolution prior to */
+ /* the sqrt, therefore the result must be */
+ /* shifted by 2 bits to */
+ /* the right to revert back to the FixPoint1616 format. */
+
+ sigmaEstimate = 1000 * sqrtResult;
+
+ if ((peakSignalRate_kcps < 1) || (vcselTotalEventsRtn < 1) ||
+ (sigmaEstimate > cSigmaEstMax)) {
+ sigmaEstimate = cSigmaEstMax;
+ }
+
+ *pSigmaEstimate = (uint32_t)(sigmaEstimate);
+ PALDevDataSet(Dev, SigmaEstimate, *pSigmaEstimate);
+ Status = VL_calc_dmax(
+ Dev,
+ totalSignalRate_mcps,
+ correctedSignalRate_mcps,
+ pwMult,
+ sigmaEstimateP1,
+ sigmaEstimateP2,
+ peakVcselDuration_us,
+ pDmax_mm);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_pal_range_status(struct vl_data *Dev,
+ uint8_t DeviceRangeStatus,
+ unsigned int SignalRate,
+ uint16_t EffectiveSpadRtnCount,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData,
+ uint8_t *pPalRangeStatus)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t NoneFlag;
+ uint8_t SigmaLimitflag = 0;
+ uint8_t SignalRefClipflag = 0;
+ uint8_t RangeIgnoreThresholdflag = 0;
+ uint8_t SigmaLimitCheckEnable = 0;
+ uint8_t SignalRateFinalRangeLimitCheckEnable = 0;
+ uint8_t SignalRefClipLimitCheckEnable = 0;
+ uint8_t RangeIgnoreThresholdLimitCheckEnable = 0;
+ unsigned int SigmaEstimate;
+ unsigned int SigmaLimitValue;
+ unsigned int SignalRefClipValue;
+ unsigned int RangeIgnoreThresholdValue;
+ unsigned int SignalRatePerSpad;
+ uint8_t DeviceRangeStatusInternal = 0;
+ uint16_t tmpWord = 0;
+ uint8_t Temp8;
+ uint32_t Dmax_mm = 0;
+ unsigned int LastSignalRefMcps;
+
+ LOG_FUNCTION_START("");
+
+
+ /*
+ * VL53L0X has a good ranging when the value of the
+ * DeviceRangeStatus = 11. This function will replace the value 0 with
+ * the value 11 in the DeviceRangeStatus.
+ * In addition, the SigmaEstimator is not included in the VL53L0X
+ * DeviceRangeStatus, this will be added in the PalRangeStatus.
+ */
+
+ DeviceRangeStatusInternal = ((DeviceRangeStatus & 0x78) >> 3);
+
+ if (DeviceRangeStatusInternal == 0 ||
+ DeviceRangeStatusInternal == 5 ||
+ DeviceRangeStatusInternal == 7 ||
+ DeviceRangeStatusInternal == 12 ||
+ DeviceRangeStatusInternal == 13 ||
+ DeviceRangeStatusInternal == 14 ||
+ DeviceRangeStatusInternal == 15
+ ) {
+ NoneFlag = 1;
+ } else {
+ NoneFlag = 0;
+ }
+
+ /*
+ * Check if Sigma limit is enabled, if yes then do comparison with limit
+ * value and put the result back into pPalRangeStatus.
+ */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ &SigmaLimitCheckEnable);
+
+ if ((SigmaLimitCheckEnable != 0) && (Status == VL_ERROR_NONE)) {
+ /* compute the Sigma and check with limit */
+ Status = VL_calc_sigma_estimate(
+ Dev,
+ pRangingMeasurementData,
+ &SigmaEstimate,
+ &Dmax_mm);
+ if (Status == VL_ERROR_NONE)
+ pRangingMeasurementData->RangeDMaxMilliMeter = Dmax_mm;
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_GetLimitCheckValue(Dev,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ &SigmaLimitValue);
+
+ if ((SigmaLimitValue > 0) &&
+ (SigmaEstimate > SigmaLimitValue))
+ /* Limit Fail */
+ SigmaLimitflag = 1;
+ }
+ }
+
+ /* Check if Signal ref clip limit is enabled, */
+ /* if yes then do comparison */
+ /* with limit value and put the result back into pPalRangeStatus. */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP,
+ &SignalRefClipLimitCheckEnable);
+
+ if ((SignalRefClipLimitCheckEnable != 0) &&
+ (Status == VL_ERROR_NONE)) {
+
+ Status = VL_GetLimitCheckValue(Dev,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP,
+ &SignalRefClipValue);
+
+ /* Read LastSignalRefMcps from device */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, 0xFF, 0x01);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_RdWord(Dev,
+ VL_REG_RESULT_PEAK_SIGNAL_RATE_REF,
+ &tmpWord);
+
+ if (Status == VL_ERROR_NONE)
+ Status = VL_WrByte(Dev, 0xFF, 0x00);
+
+ LastSignalRefMcps = VL_FIXPOINT97TOFIXPOINT1616(tmpWord);
+ PALDevDataSet(Dev, LastSignalRefMcps, LastSignalRefMcps);
+
+ if ((SignalRefClipValue > 0) &&
+ (LastSignalRefMcps > SignalRefClipValue)) {
+ /* Limit Fail */
+ SignalRefClipflag = 1;
+ }
+ }
+
+ /*
+ * Check if Signal ref clip limit is enabled, if yes then do comparison
+ * with limit value and put the result back into pPalRangeStatus.
+ * EffectiveSpadRtnCount has a format 8.8
+ * If (Return signal rate < (1.5 x Xtalk x number of Spads)) : FAIL
+ */
+ if (Status == VL_ERROR_NONE)
+ Status = VL_GetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD,
+ &RangeIgnoreThresholdLimitCheckEnable);
+
+ if ((RangeIgnoreThresholdLimitCheckEnable != 0) &&
+ (Status == VL_ERROR_NONE)) {
+
+ /* Compute the signal rate per spad */
+ if (EffectiveSpadRtnCount == 0) {
+ SignalRatePerSpad = 0;
+ } else {
+ SignalRatePerSpad = (unsigned int)((256 * SignalRate)
+ / EffectiveSpadRtnCount);
+ }
+
+ Status = VL_GetLimitCheckValue(Dev,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD,
+ &RangeIgnoreThresholdValue);
+
+ if ((RangeIgnoreThresholdValue > 0) &&
+ (SignalRatePerSpad < RangeIgnoreThresholdValue)) {
+ /* Limit Fail add 2^6 to range status */
+ RangeIgnoreThresholdflag = 1;
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ if (NoneFlag == 1) {
+ *pPalRangeStatus = 255; /* NONE */
+ } else if (DeviceRangeStatusInternal == 1 ||
+ DeviceRangeStatusInternal == 2 ||
+ DeviceRangeStatusInternal == 3) {
+ *pPalRangeStatus = 5; /* HW fail */
+ } else if (DeviceRangeStatusInternal == 6 ||
+ DeviceRangeStatusInternal == 9) {
+ *pPalRangeStatus = 4; /* Phase fail */
+ } else if (DeviceRangeStatusInternal == 8 ||
+ DeviceRangeStatusInternal == 10 ||
+ SignalRefClipflag == 1) {
+ *pPalRangeStatus = 3; /* Min range */
+ } else if (DeviceRangeStatusInternal == 4 ||
+ RangeIgnoreThresholdflag == 1) {
+ *pPalRangeStatus = 2; /* Signal Fail */
+ } else if (SigmaLimitflag == 1) {
+ *pPalRangeStatus = 1; /* Sigma Fail */
+ } else {
+ *pPalRangeStatus = 0; /* Range Valid */
+ }
+ }
+
+ /* DMAX only relevant during range error */
+ if (*pPalRangeStatus == 0)
+ pRangingMeasurementData->RangeDMaxMilliMeter = 0;
+
+ /* fill the Limit Check Status */
+
+ Status = VL_GetLimitCheckEnable(Dev,
+ VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
+ &SignalRateFinalRangeLimitCheckEnable);
+
+ if (Status == VL_ERROR_NONE) {
+ if ((SigmaLimitCheckEnable == 0) || (SigmaLimitflag == 1))
+ Temp8 = 1;
+ else
+ Temp8 = 0;
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8);
+
+ if ((DeviceRangeStatusInternal == 4) ||
+ (SignalRateFinalRangeLimitCheckEnable == 0))
+ Temp8 = 1;
+ else
+ Temp8 = 0;
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus,
+ VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
+ Temp8);
+
+ if ((SignalRefClipLimitCheckEnable == 0) ||
+ (SignalRefClipflag == 1))
+ Temp8 = 1;
+ else
+ Temp8 = 0;
+
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus,
+ VL_CHECKENABLE_SIGNAL_REF_CLIP, Temp8);
+
+ if ((RangeIgnoreThresholdLimitCheckEnable == 0) ||
+ (RangeIgnoreThresholdflag == 1))
+ Temp8 = 1;
+ else
+ Temp8 = 0;
+
+ VL_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus,
+ VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD,
+ Temp8);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+
+}
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_api_ranging.c b/drivers/input/misc/vl53l0x/src/vl53l0x_api_ranging.c
new file mode 100644
index 0000000..a1f4683
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_api_ranging.c
@@ -0,0 +1,32 @@
+/*
+ * vl53l0x_api_ranging.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "vl53l0x_api.h"
+#include "vl53l0x_api_core.h"
+
+
+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
+#define LOG_FUNCTION_START(fmt, ...) \
+ _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__)
+#define LOG_FUNCTION_END(status, ...) \
+ _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__)
+#define LOG_FUNCTION_END_FMT(status, fmt, ...) \
+ _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__)
+
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_api_strings.c b/drivers/input/misc/vl53l0x/src/vl53l0x_api_strings.c
new file mode 100644
index 0000000..71fec10
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_api_strings.c
@@ -0,0 +1,455 @@
+/*
+ * vl53l0x_api_strings.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "vl53l0x_api.h"
+#include "vl53l0x_api_core.h"
+#include "vl53l0x_api_strings.h"
+
+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
+
+#define LOG_FUNCTION_START(fmt, ...) \
+ _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__)
+#define LOG_FUNCTION_END(status, ...) \
+ _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__)
+#define LOG_FUNCTION_END_FMT(status, fmt, ...) \
+ _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__)
+
+
+int8_t VL_check_part_used(struct vl_data *Dev,
+ uint8_t *Revision,
+ struct VL_DeviceInfo_t *pVL_DeviceInfo)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t ModuleIdInt;
+ char *ProductId_tmp;
+
+ LOG_FUNCTION_START("");
+
+ Status = VL_get_info_from_device(Dev, 2);
+
+ if (Status == VL_ERROR_NONE) {
+ ModuleIdInt = VL_GETDEVICESPECIFICPARAMETER(Dev, ModuleId);
+
+ if (ModuleIdInt == 0) {
+ *Revision = 0;
+ VL_COPYSTRING(pVL_DeviceInfo->ProductId, "");
+ } else {
+ *Revision = VL_GETDEVICESPECIFICPARAMETER(Dev, Revision);
+ ProductId_tmp = VL_GETDEVICESPECIFICPARAMETER(Dev,
+ ProductId);
+ VL_COPYSTRING(pVL_DeviceInfo->ProductId,
+ ProductId_tmp);
+ }
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+
+int8_t VL_get_device_info(struct vl_data *Dev,
+ struct VL_DeviceInfo_t *pVL_DeviceInfo)
+{
+ int8_t Status = VL_ERROR_NONE;
+ uint8_t revision_id;
+ uint8_t Revision;
+
+ Status = VL_check_part_used(Dev, &Revision, pVL_DeviceInfo);
+
+ if (Status == VL_ERROR_NONE) {
+ if (Revision == 0) {
+ VL_COPYSTRING(pVL_DeviceInfo->Name,
+ VL_STRING_DEVICE_INFO_NAME_TS0);
+ } else if ((Revision <= 34) && (Revision != 32)) {
+ VL_COPYSTRING(pVL_DeviceInfo->Name,
+ VL_STRING_DEVICE_INFO_NAME_TS1);
+ } else if (Revision < 39) {
+ VL_COPYSTRING(pVL_DeviceInfo->Name,
+ VL_STRING_DEVICE_INFO_NAME_TS2);
+ } else {
+ VL_COPYSTRING(pVL_DeviceInfo->Name,
+ VL_STRING_DEVICE_INFO_NAME_ES1);
+ }
+
+ VL_COPYSTRING(pVL_DeviceInfo->Type,
+ VL_STRING_DEVICE_INFO_TYPE);
+
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdByte(Dev,
+ VL_REG_IDENTIFICATION_MODEL_ID,
+ &pVL_DeviceInfo->ProductType);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = VL_RdByte(Dev,
+ VL_REG_IDENTIFICATION_REVISION_ID,
+ &revision_id);
+ pVL_DeviceInfo->ProductRevisionMajor = 1;
+ pVL_DeviceInfo->ProductRevisionMinor =
+ (revision_id & 0xF0) >> 4;
+ }
+
+ return Status;
+}
+
+
+int8_t VL_get_device_error_string(uint8_t ErrorCode,
+ char *pDeviceErrorString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ switch (ErrorCode) {
+ case VL_DEVICEERROR_NONE:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_NONE);
+ break;
+ case VL_DEVICEERROR_VCSELCONTINUITYTESTFAILURE:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE);
+ break;
+ case VL_DEVICEERROR_VCSELWATCHDOGTESTFAILURE:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE);
+ break;
+ case VL_DEVICEERROR_NOVHVVALUEFOUND:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_NOVHVVALUEFOUND);
+ break;
+ case VL_DEVICEERROR_MSRCNOTARGET:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_MSRCNOTARGET);
+ break;
+ case VL_DEVICEERROR_SNRCHECK:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_SNRCHECK);
+ break;
+ case VL_DEVICEERROR_RANGEPHASECHECK:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_RANGEPHASECHECK);
+ break;
+ case VL_DEVICEERROR_SIGMATHRESHOLDCHECK:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK);
+ break;
+ case VL_DEVICEERROR_TCC:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_TCC);
+ break;
+ case VL_DEVICEERROR_PHASECONSISTENCY:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_PHASECONSISTENCY);
+ break;
+ case VL_DEVICEERROR_MINCLIP:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_MINCLIP);
+ break;
+ case VL_DEVICEERROR_RANGECOMPLETE:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_RANGECOMPLETE);
+ break;
+ case VL_DEVICEERROR_ALGOUNDERFLOW:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_ALGOUNDERFLOW);
+ break;
+ case VL_DEVICEERROR_ALGOOVERFLOW:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_ALGOOVERFLOW);
+ break;
+ case VL_DEVICEERROR_RANGEIGNORETHRESHOLD:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD);
+ break;
+
+ default:
+ VL_COPYSTRING(pDeviceErrorString,
+ VL_STRING_UNKNOWN_ERROR_CODE);
+
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_range_status_string(uint8_t RangeStatus,
+ char *pRangeStatusString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ switch (RangeStatus) {
+ case 0:
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_RANGEVALID);
+ break;
+ case 1:
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_SIGMA);
+ break;
+ case 2:
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_SIGNAL);
+ break;
+ case 3:
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_MINRANGE);
+ break;
+ case 4:
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_PHASE);
+ break;
+ case 5:
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_HW);
+ break;
+
+ default: /**/
+ VL_COPYSTRING(pRangeStatusString,
+ VL_STRING_RANGESTATUS_NONE);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_pal_error_string(int8_t PalErrorCode,
+ char *pPalErrorString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ switch (PalErrorCode) {
+ case VL_ERROR_NONE:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_NONE);
+ break;
+ case VL_ERROR_CALIBRATION_WARNING:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_CALIBRATION_WARNING);
+ break;
+ case VL_ERROR_MIN_CLIPPED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_MIN_CLIPPED);
+ break;
+ case VL_ERROR_UNDEFINED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_UNDEFINED);
+ break;
+ case VL_ERROR_INVALID_PARAMS:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_INVALID_PARAMS);
+ break;
+ case VL_ERROR_NOT_SUPPORTED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_NOT_SUPPORTED);
+ break;
+ case VL_ERROR_INTERRUPT_NOT_CLEARED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_INTERRUPT_NOT_CLEARED);
+ break;
+ case VL_ERROR_RANGE_ERROR:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_RANGE_ERROR);
+ break;
+ case VL_ERROR_TIME_OUT:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_TIME_OUT);
+ break;
+ case VL_ERROR_MODE_NOT_SUPPORTED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_MODE_NOT_SUPPORTED);
+ break;
+ case VL_ERROR_BUFFER_TOO_SMALL:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_BUFFER_TOO_SMALL);
+ break;
+ case VL_ERROR_GPIO_NOT_EXISTING:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_GPIO_NOT_EXISTING);
+ break;
+ case VL_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED);
+ break;
+ case VL_ERROR_CONTROL_INTERFACE:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_CONTROL_INTERFACE);
+ break;
+ case VL_ERROR_INVALID_COMMAND:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_INVALID_COMMAND);
+ break;
+ case VL_ERROR_DIVISION_BY_ZERO:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_DIVISION_BY_ZERO);
+ break;
+ case VL_ERROR_REF_SPAD_INIT:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_REF_SPAD_INIT);
+ break;
+ case VL_ERROR_NOT_IMPLEMENTED:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_ERROR_NOT_IMPLEMENTED);
+ break;
+
+ default:
+ VL_COPYSTRING(pPalErrorString,
+ VL_STRING_UNKNOWN_ERROR_CODE);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_pal_state_string(uint8_t PalStateCode,
+ char *pPalStateString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ switch (PalStateCode) {
+ case VL_STATE_POWERDOWN:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_POWERDOWN);
+ break;
+ case VL_STATE_WAIT_STATICINIT:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_WAIT_STATICINIT);
+ break;
+ case VL_STATE_STANDBY:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_STANDBY);
+ break;
+ case VL_STATE_IDLE:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_IDLE);
+ break;
+ case VL_STATE_RUNNING:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_RUNNING);
+ break;
+ case VL_STATE_UNKNOWN:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_UNKNOWN);
+ break;
+ case VL_STATE_ERROR:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_ERROR);
+ break;
+
+ default:
+ VL_COPYSTRING(pPalStateString,
+ VL_STRING_STATE_UNKNOWN);
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
+
+int8_t VL_get_sequence_steps_info(
+ uint8_t SequenceStepId,
+ char *pSequenceStepsString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ switch (SequenceStepId) {
+ case VL_SEQUENCESTEP_TCC:
+ VL_COPYSTRING(pSequenceStepsString,
+ VL_STRING_SEQUENCESTEP_TCC);
+ break;
+ case VL_SEQUENCESTEP_DSS:
+ VL_COPYSTRING(pSequenceStepsString,
+ VL_STRING_SEQUENCESTEP_DSS);
+ break;
+ case VL_SEQUENCESTEP_MSRC:
+ VL_COPYSTRING(pSequenceStepsString,
+ VL_STRING_SEQUENCESTEP_MSRC);
+ break;
+ case VL_SEQUENCESTEP_PRE_RANGE:
+ VL_COPYSTRING(pSequenceStepsString,
+ VL_STRING_SEQUENCESTEP_PRE_RANGE);
+ break;
+ case VL_SEQUENCESTEP_FINAL_RANGE:
+ VL_COPYSTRING(pSequenceStepsString,
+ VL_STRING_SEQUENCESTEP_FINAL_RANGE);
+ break;
+
+ default:
+ Status = VL_ERROR_INVALID_PARAMS;
+ }
+
+ LOG_FUNCTION_END(Status);
+
+ return Status;
+}
+
+
+int8_t VL_get_limit_check_info(struct vl_data *Dev,
+ uint16_t LimitCheckId, char *pLimitCheckString)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+
+ switch (LimitCheckId) {
+ case VL_CHECKENABLE_SIGMA_FINAL_RANGE:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_CHECKENABLE_SIGMA_FINAL_RANGE);
+ break;
+ case VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE);
+ break;
+ case VL_CHECKENABLE_SIGNAL_REF_CLIP:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_CHECKENABLE_SIGNAL_REF_CLIP);
+ break;
+ case VL_CHECKENABLE_RANGE_IGNORE_THRESHOLD:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD);
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_MSRC:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_CHECKENABLE_SIGNAL_RATE_MSRC);
+ break;
+
+ case VL_CHECKENABLE_SIGNAL_RATE_PRE_RANGE:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_CHECKENABLE_SIGNAL_RATE_PRE_RANGE);
+ break;
+
+ default:
+ VL_COPYSTRING(pLimitCheckString,
+ VL_STRING_UNKNOWN_ERROR_CODE);
+
+ }
+
+ LOG_FUNCTION_END(Status);
+ return Status;
+}
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_i2c_platform.c b/drivers/input/misc/vl53l0x/src/vl53l0x_i2c_platform.c
new file mode 100644
index 0000000..edc4b5c
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_i2c_platform.c
@@ -0,0 +1,384 @@
+/*
+ * vl53l0x_i2c_platform.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*!
+ * \file VL_platform.c
+ * \brief Code function definitions for EWOK Platform Layer
+ *
+ */
+
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "stmvl53l0x-i2c.h"
+#include "stmvl53l0x-cci.h"
+
+#include "vl53l0x_platform.h"
+#include "vl53l0x_i2c_platform.h"
+#include "vl53l0x_def.h"
+
+#include "vl53l0x_platform_log.h"
+
+#ifdef VL_LOG_ENABLE
+#define trace_print(level, ...) \
+ trace_print_module_function(TRACE_MODULE_PLATFORM, level,\
+ TRACE_FUNCTION_NONE, ##__VA_ARGS__)
+#define trace_i2c(...) \
+ trace_print_module_function(TRACE_MODULE_NONE, \
+ TRACE_LEVEL_NONE, TRACE_FUNCTION_I2C, ##__VA_ARGS__)
+#endif
+
+/**
+ * @def I2C_BUFFER_CONFIG
+ *
+ * @brief Configure Device register I2C access
+ *
+ * @li 0 : one GLOBAL buffer \n
+ * Use one global buffer of MAX_I2C_XFER_SIZE byte in data space \n
+ * This solution is not multi-Device compliant nor multi-thread cpu safe \n
+ * It can be the best option for small 8/16 bit MCU without stack and limited
+ * ram (STM8s, 80C51 ...)
+ *
+ * @li 1 : ON_STACK/local \n
+ * Use local variable (on stack) buffer \n
+ * This solution is multi-thread with use of i2c resource lock or mutex see
+ * VL6180x_GetI2CAccess() \n
+ *
+ * @li 2 : User defined \n
+ * Per Device potentially dynamic allocated. Requires VL6180x_GetI2cBuffer()
+ * to be implemented.
+ * @ingroup Configuration
+ */
+#define I2C_BUFFER_CONFIG 1
+
+#if I2C_BUFFER_CONFIG == 0
+ /* GLOBAL config buffer */
+ uint8_t i2c_global_buffer[VL_MAX_I2C_XFER_SIZE];
+
+ #define DECL_I2C_BUFFER
+ #define VL_GetLocalBuffer(Dev, n_byte) i2c_global_buffer
+
+#elif I2C_BUFFER_CONFIG == 1
+ /* ON STACK */
+ uint8_t LocBuffer[VL_MAX_I2C_XFER_SIZE];
+ #define VL_GetLocalBuffer(Dev, n_byte) LocBuffer
+#elif I2C_BUFFER_CONFIG == 2
+ /* user define buffer type declare DECL_I2C_BUFFER as access via */
+ /* VL_GetLocalBuffer */
+ #define DECL_I2C_BUFFER
+#else
+#error "invalid I2C_BUFFER_CONFIG "
+#endif
+
+
+#define VL_I2C_USER_VAR /* none but could be for a flag var to */
+ /* get/pass to mutex interruptible return flags and try again */
+#define VL_GetI2CAccess(Dev) /* todo mutex acquire */
+#define VL_DoneI2CAcces(Dev) /* todo mutex release */
+
+
+char debug_string[VL_MAX_STRING_LENGTH_PLT];
+
+
+#define MIN_COMMS_VERSION_MAJOR 1
+#define MIN_COMMS_VERSION_MINOR 8
+#define MIN_COMMS_VERSION_BUILD 1
+#define MIN_COMMS_VERSION_REVISION 0
+
+#define STATUS_OK 0x00
+#define STATUS_FAIL 0x01
+
+bool _check_min_version(void)
+{
+ bool min_version_comms_dll = false;
+
+ min_version_comms_dll = true;
+
+ return min_version_comms_dll;
+}
+
+int32_t VL_comms_initialise(uint8_t comms_type, uint16_t comms_speed_khz)
+{
+ int32_t status = STATUS_OK;
+
+ return status;
+}
+
+int32_t VL_comms_close(void)
+{
+ int32_t status = STATUS_OK;
+
+
+ return status;
+}
+
+int32_t VL_set_page(struct vl_data *dev, uint8_t page_data)
+{
+ int32_t status = STATUS_OK;
+ uint16_t page_index = 0xFF;
+ uint8_t *buffer;
+
+ buffer = VL_GetLocalBuffer(dev, 3);
+ buffer[0] = page_index >> 8;
+ buffer[1] = page_index & 0xff;
+ buffer[2] = page_data;
+
+ status = VL_I2CWrite(dev, buffer, (uint8_t) 3);
+ return status;
+}
+
+int32_t VL_write_multi(struct vl_data *dev, uint8_t index, uint8_t *pdata,
+ int32_t count)
+{
+ int32_t status = STATUS_OK;
+ uint8_t *buffer;
+
+#ifdef VL_LOG_ENABLE
+ int32_t i = 0;
+ char value_as_str[VL_MAX_STRING_LENGTH_PLT];
+ char *pvalue_as_str;
+
+ pvalue_as_str = value_as_str;
+
+ for (i = 0 ; i < count ; i++) {
+ snprintf(pvalue_as_str, sizeof(pvalue_as_str),
+ "%02X", *(pdata + i));
+
+ pvalue_as_str += 2;
+ }
+ trace_i2c("Write reg : 0x%04X, Val : 0x%s\n", index, value_as_str);
+#endif
+ if ((count + 1) > VL_MAX_I2C_XFER_SIZE)
+ return STATUS_FAIL;
+ buffer = VL_GetLocalBuffer(dev, (count+1));
+ buffer[0] = index;
+ memcpy(&buffer[1], pdata, count);
+ status = VL_I2CWrite(dev, buffer, (count+1));
+
+ return status;
+}
+
+int32_t VL_read_multi(struct vl_data *dev, uint8_t index, uint8_t *pdata,
+ int32_t count)
+{
+ int32_t status = STATUS_OK;
+ uint8_t *buffer;
+
+#ifdef VL_LOG_ENABLE
+ int32_t i = 0;
+ char value_as_str[VL_MAX_STRING_LENGTH_PLT];
+ char *pvalue_as_str;
+#endif
+
+ if ((count + 1) > VL_MAX_I2C_XFER_SIZE)
+ return STATUS_FAIL;
+
+ buffer = VL_GetLocalBuffer(dev, 1);
+ buffer[0] = index;
+ status = VL_I2CWrite(dev, (uint8_t *)buffer, (uint8_t)1);
+ if (!status) {
+ pdata[0] = index;
+ status = VL_I2CRead(dev, pdata, count);
+ }
+
+#ifdef VL_LOG_ENABLE
+ pvalue_as_str = value_as_str;
+
+ for (i = 0 ; i < count ; i++) {
+ snprintf(pvalue_as_str, sizeof(value_as_str),
+ "%02X", *(pdata+i));
+ pvalue_as_str += 2;
+ }
+
+ trace_i2c("Read reg : 0x%04X, Val : 0x%s\n", index, value_as_str);
+#endif
+
+ return status;
+}
+
+
+int32_t VL_write_byte(struct vl_data *dev, uint8_t index, uint8_t data)
+{
+ int32_t status = STATUS_OK;
+ const int32_t cbyte_count = 1;
+
+ status = VL_write_multi(dev, index, &data, cbyte_count);
+
+ return status;
+
+}
+
+
+int32_t VL_write_word(struct vl_data *dev, uint8_t index, uint16_t data)
+{
+ int32_t status = STATUS_OK;
+
+ uint8_t buffer[BYTES_PER_WORD];
+
+ /* Split 16-bit word into MS and LS uint8_t */
+ buffer[0] = (uint8_t)(data >> 8);
+ buffer[1] = (uint8_t)(data & 0x00FF);
+
+ status = VL_write_multi(dev, index, buffer, BYTES_PER_WORD);
+
+ return status;
+
+}
+
+
+int32_t VL_write_dword(struct vl_data *dev, uint8_t index, uint32_t data)
+{
+ int32_t status = STATUS_OK;
+ uint8_t buffer[BYTES_PER_DWORD];
+
+ /* Split 32-bit word into MS ... LS bytes */
+ buffer[0] = (uint8_t) (data >> 24);
+ buffer[1] = (uint8_t)((data & 0x00FF0000) >> 16);
+ buffer[2] = (uint8_t)((data & 0x0000FF00) >> 8);
+ buffer[3] = (uint8_t) (data & 0x000000FF);
+
+ status = VL_write_multi(dev, index, buffer, BYTES_PER_DWORD);
+
+ return status;
+
+}
+
+
+int32_t VL_read_byte(struct vl_data *dev, uint8_t index, uint8_t *pdata)
+{
+ int32_t status = STATUS_OK;
+ int32_t cbyte_count = 1;
+
+ status = VL_read_multi(dev, index, pdata, cbyte_count);
+
+ return status;
+
+}
+
+
+int32_t VL_read_word(struct vl_data *dev, uint8_t index, uint16_t *pdata)
+{
+ int32_t status = STATUS_OK;
+ uint8_t buffer[BYTES_PER_WORD];
+
+ status = VL_read_multi(dev, index, buffer, BYTES_PER_WORD);
+ *pdata = ((uint16_t)buffer[0]<<8) + (uint16_t)buffer[1];
+
+ return status;
+
+}
+
+int32_t VL_read_dword(struct vl_data *dev, uint8_t index, uint32_t *pdata)
+{
+ int32_t status = STATUS_OK;
+ uint8_t buffer[BYTES_PER_DWORD];
+
+ status = VL_read_multi(dev, index, buffer, BYTES_PER_DWORD);
+ *pdata = ((uint32_t)buffer[0]<<24) + ((uint32_t)buffer[1]<<16) +
+ ((uint32_t)buffer[2]<<8) + (uint32_t)buffer[3];
+
+ return status;
+
+}
+
+int32_t VL_platform_wait_us(int32_t wait_us)
+{
+ int32_t status = STATUS_OK;
+
+ msleep((wait_us/1000));
+
+#ifdef VL_LOG_ENABLE
+ trace_i2c("Wait us : %6d\n", wait_us);
+#endif
+
+ return status;
+
+}
+
+
+int32_t VL_wait_ms(int32_t wait_ms)
+{
+ int32_t status = STATUS_OK;
+
+ msleep(wait_ms);
+
+#ifdef VL_LOG_ENABLE
+ trace_i2c("Wait ms : %6d\n", wait_ms);
+#endif
+
+ return status;
+
+}
+
+
+int32_t VL_set_gpio(uint8_t level)
+{
+ int32_t status = STATUS_OK;
+#ifdef VL_LOG_ENABLE
+ trace_i2c("// Set GPIO = %d;\n", level);
+#endif
+
+ return status;
+
+}
+
+
+int32_t VL_get_gpio(uint8_t *plevel)
+{
+ int32_t status = STATUS_OK;
+#ifdef VL_LOG_ENABLE
+ trace_i2c("// Get GPIO = %d;\n", *plevel);
+#endif
+ return status;
+}
+
+
+int32_t VL_release_gpio(void)
+{
+ int32_t status = STATUS_OK;
+#ifdef VL_LOG_ENABLE
+ trace_i2c("// Releasing force on GPIO\n");
+#endif
+ return status;
+
+}
+
+int32_t VL_cycle_power(void)
+{
+ int32_t status = STATUS_OK;
+#ifdef VL_LOG_ENABLE
+ trace_i2c("// cycle sensor power\n");
+#endif
+
+ return status;
+}
+
+
+int32_t VL_get_timer_frequency(int32_t *ptimer_freq_hz)
+{
+ *ptimer_freq_hz = 0;
+ return STATUS_FAIL;
+}
+
+
+int32_t VL_get_timer_value(int32_t *ptimer_count)
+{
+ *ptimer_count = 0;
+ return STATUS_FAIL;
+}
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_platform.c b/drivers/input/misc/vl53l0x/src/vl53l0x_platform.c
new file mode 100644
index 0000000..79df3db
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_platform.c
@@ -0,0 +1,232 @@
+/*
+ * vl53l0x_platform.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * @file VL_i2c.c
+ *
+ * Copyright (C) 2014 ST MicroElectronics
+ *
+ * provide variable word size byte/Word/dword VL6180x register access via i2c
+ *
+ */
+#include "vl53l0x_platform.h"
+#include "vl53l0x_i2c_platform.h"
+#include "vl53l0x_api.h"
+
+#define LOG_FUNCTION_START(fmt, ...) \
+ _LOG_FUNCTION_START(TRACE_MODULE_PLATFORM, fmt, ##__VA_ARGS__)
+#define LOG_FUNCTION_END(status, ...) \
+ _LOG_FUNCTION_END(TRACE_MODULE_PLATFORM, status, ##__VA_ARGS__)
+#define LOG_FUNCTION_END_FMT(status, fmt, ...)\
+ _LOG_FUNCTION_END_FMT(TRACE_MODULE_PLATFORM, status,\
+ fmt, ##__VA_ARGS__)
+
+
+
+int8_t VL_LockSequenceAccess(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ return Status;
+}
+
+int8_t VL_UnlockSequenceAccess(struct vl_data *Dev)
+{
+ int8_t Status = VL_ERROR_NONE;
+
+ return Status;
+}
+
+/* the ranging_sensor_comms.dll will take care of the page selection */
+int8_t VL_WriteMulti(struct vl_data *Dev, uint8_t index,
+ uint8_t *pdata, uint32_t count)
+{
+
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int = 0;
+ uint8_t deviceAddress;
+
+ if (count >= VL_MAX_I2C_XFER_SIZE)
+ Status = VL_ERROR_INVALID_PARAMS;
+
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_write_multi(Dev, index, pdata, count);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+/* the ranging_sensor_comms.dll will take care of the page selection */
+int8_t VL_ReadMulti(struct vl_data *Dev, uint8_t index,
+ uint8_t *pdata, uint32_t count)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ if (count >= VL_MAX_I2C_XFER_SIZE)
+ Status = VL_ERROR_INVALID_PARAMS;
+
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_read_multi(Dev, index, pdata, count);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+
+int8_t VL_WrByte(struct vl_data *Dev, uint8_t index, uint8_t data)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_write_byte(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+int8_t VL_WrWord(struct vl_data *Dev, uint8_t index, uint16_t data)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_write_word(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+int8_t VL_WrDWord(struct vl_data *Dev, uint8_t index, uint32_t data)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_write_dword(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+int8_t VL_UpdateByte(struct vl_data *Dev, uint8_t index,
+ uint8_t AndData, uint8_t OrData)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+ uint8_t data;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_read_byte(Dev, index, &data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ if (Status == VL_ERROR_NONE) {
+ data = (data & AndData) | OrData;
+ status_int = VL_write_byte(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+ }
+
+ return Status;
+}
+
+int8_t VL_RdByte(struct vl_data *Dev, uint8_t index, uint8_t *data)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_read_byte(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+int8_t VL_RdWord(struct vl_data *Dev, uint8_t index, uint16_t *data)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_read_word(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+int8_t VL_RdDWord(struct vl_data *Dev, uint8_t index, uint32_t *data)
+{
+ int8_t Status = VL_ERROR_NONE;
+ int32_t status_int;
+ uint8_t deviceAddress;
+
+ deviceAddress = Dev->I2cDevAddr;
+
+ status_int = VL_read_dword(Dev, index, data);
+
+ if (status_int != 0)
+ Status = VL_ERROR_CONTROL_INTERFACE;
+
+ return Status;
+}
+
+#define VL_POLLINGDELAY_LOOPNB 250
+int8_t VL_PollingDelay(struct vl_data *Dev)
+{
+ int8_t status = VL_ERROR_NONE;
+
+ LOG_FUNCTION_START("");
+ usleep_range(1000, 1001);
+ LOG_FUNCTION_END(status);
+ return status;
+}
diff --git a/drivers/input/misc/vl53l0x/src/vl53l0x_port_i2c.c b/drivers/input/misc/vl53l0x/src/vl53l0x_port_i2c.c
new file mode 100644
index 0000000..9e14f79
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/src/vl53l0x_port_i2c.c
@@ -0,0 +1,168 @@
+/*
+ * vl53l0x_port_i2c.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include "stmvl53l0x-i2c.h"
+#include "stmvl53l0x-cci.h"
+#include "vl53l0x_platform.h"
+#include "vl53l0x_i2c_platform.h"
+#include "stmvl53l0x.h"
+
+#define I2C_M_WR 0x00
+#define STATUS_OK 0x00
+#define STATUS_FAIL (-1)
+/** int VL_I2CWrite(VL_Dev_t dev, void *buff, uint8_t len);
+ * @brief Write data buffer to VL53L0 device via i2c
+ * @param dev The device to write to
+ * @param buff The data buffer
+ * @param len The length of the transaction in byte
+ * @return 0 on success
+ */
+int VL_I2CWrite(struct vl_data *dev, uint8_t *buff, uint8_t len)
+{
+
+
+ int err = 0;
+
+ if (dev->bus_type == CCI_BUS) {
+#ifdef CAMERA_CCI
+ uint16_t index;
+ struct cci_data *cci_client_obj =
+ (struct cci_data *)dev->client_object;
+ struct msm_camera_i2c_client *client = cci_client_obj->client;
+
+ index = buff[0];
+ /*dbg("%s: index: %d len: %d\n", __func__, index, len); */
+
+ if (len == 2) {
+ uint8_t data;
+
+ data = buff[1];
+ /* for byte access */
+ err = client->i2c_func_tbl->i2c_write(client, index,
+ data, MSM_CAMERA_I2C_BYTE_DATA);
+ if (err < 0) {
+ dbg("%s:%d failed status=%d\n",
+ __func__, __LINE__, err);
+ return err;
+ }
+ } else if (len == 3) {
+ uint16_t data;
+
+ data = ((uint16_t)buff[1] << 8) | (uint16_t)buff[2];
+ err = client->i2c_func_tbl->i2c_write(client, index,
+ data, MSM_CAMERA_I2C_WORD_DATA);
+ if (err < 0) {
+ dbg("%s:%d failed status=%d\n",
+ __func__, __LINE__, err);
+ return err;
+ }
+ } else if (len >= 5) {
+ err = client->i2c_func_tbl->i2c_write_seq(client,
+ index, &buff[1], (len-1));
+ if (err < 0) {
+ dbg("%s:%d failed status=%d\n",
+ __func__, __LINE__, err);
+ return err;
+ }
+
+ }
+#endif
+#ifndef CAMERA_CCI
+ } else {
+ struct i2c_msg msg[1];
+ struct i2c_data *i2c_client_obj =
+ (struct i2c_data *)dev->client_object;
+ struct i2c_client *client =
+ (struct i2c_client *)i2c_client_obj->client;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = I2C_M_WR;
+ msg[0].buf = buff;
+ msg[0].len = len;
+
+ err = i2c_transfer(client->adapter, msg, 1);
+ /* return the actual messages transfer */
+ if (err != 1) {
+ dbg("%s: i2c_transfer err:%d, addr:0x%x, reg:0x%x\n",
+ __func__, err, client->addr,
+ (buff[0] << 8 | buff[1]));
+ return STATUS_FAIL;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+
+/** int VL_I2CRead(VL_Dev_t dev, void *buff, uint8_t len);
+ * @brief Read data buffer from VL53L0 device via i2c
+ * @param dev The device to read from
+ * @param buff The data buffer to fill
+ * @param len The length of the transaction in byte
+ * @return transaction status
+ */
+int VL_I2CRead(struct vl_data *dev, uint8_t *buff, uint8_t len)
+{
+
+ int err = 0;
+
+ if (dev->bus_type == CCI_BUS) {
+#ifdef CAMERA_CCI
+ uint16_t index;
+ struct cci_data *cci_client_obj =
+ (struct cci_data *)dev->client_object;
+ struct msm_camera_i2c_client *client = cci_client_obj->client;
+
+ index = buff[0];
+ /* dbg("%s: index: %d\n", __func__, index); */
+ err = client->i2c_func_tbl->i2c_read_seq(client,
+ index, buff, len);
+ if (err < 0) {
+ dbg("%s:%d failed status=%d\n",
+ __func__, __LINE__, err);
+ return err;
+ }
+#endif
+ } else {
+#ifndef CAMERA_CCI
+ struct i2c_msg msg[1];
+ struct i2c_data *i2c_client_obj =
+ (struct i2c_data *)dev->client_object;
+ struct i2c_client *client =
+ (struct i2c_client *) i2c_client_obj->client;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = I2C_M_RD|client->flags;
+ msg[0].buf = buff;
+ msg[0].len = len;
+
+ err = i2c_transfer(client->adapter, &msg[0], 1);
+ /* return the actual message transfer */
+ if (err != 1) {
+ dbg("%s: Read i2c_transfer err:%d, addr:0x%x\n",
+ __func__, err, client->addr);
+ return STATUS_FAIL;
+ }
+#endif
+ }
+
+ return 0;
+}
diff --git a/drivers/input/misc/vl53l0x/stmvl53l0x-cci.h b/drivers/input/misc/vl53l0x/stmvl53l0x-cci.h
new file mode 100644
index 0000000..5476ef9
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/stmvl53l0x-cci.h
@@ -0,0 +1,57 @@
+/*
+ * stmvl53l0x-cci.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef STMVL_CCI_H
+#define STMVL_CCI_H
+#include <linux/types.h>
+
+#ifdef CAMERA_CCI
+#include <soc/qcom/camera2.h>
+#include "msm_camera_i2c.h"
+#include "msm_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+#include "msm_cci.h"
+
+#define MSM_TOF_MAX_VREGS (10)
+
+struct msm_tof_vreg {
+ struct camera_vreg_t *cam_vreg;
+ void *data[MSM_TOF_MAX_VREGS];
+ int num_vreg;
+};
+
+struct cci_data {
+ struct msm_camera_i2c_client g_client;
+ struct msm_camera_i2c_client *client;
+ struct platform_device *pdev;
+ enum msm_camera_device_type_t device_type;
+ enum cci_i2c_master_t cci_master;
+ struct msm_tof_vreg vreg_cfg;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev sdev;
+ struct v4l2_subdev_ops *subdev_ops;
+ char subdev_initialized;
+ uint32_t subdev_id;
+ uint8_t power_up;
+};
+int stmvl53l0x_init_cci(void);
+void stmvl53l0x_exit_cci(void *cci_object);
+int stmvl53l0x_power_down_cci(void *cci_object);
+int stmvl53l0x_power_up_cci(void *cci_object, unsigned int *preset_flag);
+#endif /* CAMERA_CCI */
+#endif /* STMVL_CCI_H */
diff --git a/drivers/input/misc/vl53l0x/stmvl53l0x-i2c.h b/drivers/input/misc/vl53l0x/stmvl53l0x-i2c.h
new file mode 100644
index 0000000..68f7d37
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/stmvl53l0x-i2c.h
@@ -0,0 +1,35 @@
+/*
+ * stmvl53l0x-i2c.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef STMVL_I2C_H
+#define STMVL_I2C_H
+#include <linux/types.h>
+
+#ifndef CAMERA_CCI
+struct i2c_data {
+ struct i2c_client *client;
+ struct regulator *vana;
+ uint8_t power_up;
+};
+int stmvl53l0x_init_i2c(void);
+void stmvl53l0x_exit_i2c(void *i2c_object);
+int stmvl53l0x_power_up_i2c(void *i2c_object, unsigned int *preset_flag);
+int stmvl53l0x_power_down_i2c(void *i2c_object);
+
+#endif /* NOT CAMERA_CCI */
+#endif /* STMVL_I2C_H */
diff --git a/drivers/input/misc/vl53l0x/stmvl53l0x.h b/drivers/input/misc/vl53l0x/stmvl53l0x.h
new file mode 100644
index 0000000..1be251f
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/stmvl53l0x.h
@@ -0,0 +1,191 @@
+/*
+ * stmvl53l0x.h - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef STMVL_H
+#define STMVL_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+
+
+#define STMVL_DRV_NAME "stmvl53l0"
+#define STMVL_SLAVE_ADDR (0x52>>1)
+
+#define DRIVER_VERSION "1.0.5"
+#define I2C_M_WR 0x00
+/* #define INT_POLLING_DELAY 20 */
+
+/* if don't want to have output from dbg, comment out #DEBUG macro */
+#define DEBUG
+#ifdef DEBUG
+#define dbg(fmt, ...) \
+ printk(fmt, ##__VA_ARGS__)
+#else
+#define dbg(fmt, ...)
+#endif
+
+#define err(fmt, ...) \
+ printk(fmt, ##__VA_ARGS__)
+
+#define VL_VDD_MIN 2600000
+#define VL_VDD_MAX 3000000
+
+enum init_mode_e {
+ NORMAL_MODE = 0,
+ OFFSETCALIB_MODE = 1,
+ XTALKCALIB_MODE = 2,
+ SPADCALIB_MODE = 3,
+ REFCALIB_MODE = 4,
+};
+
+enum parameter_name_e {
+ OFFSET_PAR = 0,
+ XTALKRATE_PAR = 1,
+ XTALKENABLE_PAR = 2,
+ GPIOFUNC_PAR = 3,
+ LOWTHRESH_PAR = 4,
+ HIGHTHRESH_PAR = 5,
+ DEVICEMODE_PAR = 6,
+ INTERMEASUREMENT_PAR = 7,
+ REFERENCESPADS_PAR = 8,
+ REFCALIBRATION_PAR = 9,
+};
+
+enum {
+ CCI_BUS = 0,
+ I2C_BUS = 1,
+};
+
+/*
+ * IOCTL register data structs
+ */
+struct stmvl53l0x_register {
+ uint32_t is_read; /*1: read 0: write*/
+ uint32_t reg_index;
+ uint32_t reg_bytes;
+ uint32_t reg_data;
+ int32_t status;
+};
+
+/*
+ * IOCTL parameter structs
+ */
+struct stmvl53l0x_parameter {
+ uint32_t is_read; /*1: Get 0: Set*/
+ enum parameter_name_e name;
+ int32_t value;
+ int32_t value2;
+ int32_t status;
+};
+
+/*
+ * driver data structs
+ */
+struct vl_data {
+
+ struct VL_DevData_t Data; /* !<embed ST VL53L0 Dev data as "dev_data" */
+ uint8_t I2cDevAddr; /* !< i2c device address user specific field */
+ uint8_t comms_type; /* !< Type of comms : */
+ /* VL_COMMS_I2C or VL_COMMS_SPI */
+ uint16_t comms_speed_khz; /*!< Comms speed [kHz] : */
+ /*typically 400kHz for I2C */
+ uint8_t bus_type; /* CCI_BUS; I2C_BUS */
+
+ void *client_object; /* cci or i2c client */
+
+ struct mutex update_lock;
+ struct delayed_work dwork; /* for PS work handler */
+ struct input_dev *input_dev_ps;
+ struct kobject *range_kobj;
+
+ const char *dev_name;
+ /* function pointer */
+
+ /* misc device */
+ struct miscdevice miscdev;
+
+ int irq;
+ unsigned int reset;
+
+ /* control flag from HAL */
+ unsigned int enable_ps_sensor;
+
+ /* PS parameters */
+ unsigned int ps_data; /* to store PS data */
+
+ /* Calibration parameters */
+ unsigned int offsetCalDistance;
+ unsigned int xtalkCalDistance;
+
+
+ /* Range Data */
+ struct VL_RangingMeasurementData_t rangeData;
+
+ /* Device parameters */
+ uint8_t deviceMode;
+ uint32_t interMeasurems;
+ uint8_t gpio_function;
+ uint8_t gpio_polarity;
+ unsigned int low_threshold;
+ unsigned int high_threshold;
+
+ /* delay time in miniseconds*/
+ uint8_t delay_ms;
+
+ /* Timing Budget */
+ uint32_t timingBudget;
+ /* Use this threshold to force restart ranging */
+ uint32_t noInterruptCount;
+ /* Use this flag to use long ranging*/
+ int useLongRange;
+
+ /* Polling thread */
+ struct task_struct *poll_thread;
+ /* Wait Queue on which the poll thread blocks */
+ wait_queue_head_t poll_thread_wq;
+
+ /* Recent interrupt status */
+ uint32_t interruptStatus;
+
+ struct mutex work_mutex;
+
+ /* Debug */
+ unsigned int enableDebug;
+ uint8_t interrupt_received;
+ int32_t default_offset_calibration;
+ unsigned int default_xtalk_Compensation;
+};
+
+/*
+ * function pointer structs
+ */
+struct stmvl53l0x_module_fn_t {
+ int (*init)(void);
+ void (*deinit)(void *);
+ int (*power_up)(void *, unsigned int *);
+ int (*power_down)(void *);
+};
+
+
+
+int stmvl53l0x_setup(struct vl_data *data);
+
+#endif /* STMVL_H */
diff --git a/drivers/input/misc/vl53l0x/stmvl53l0x_module-cci.c b/drivers/input/misc/vl53l0x/stmvl53l0x_module-cci.c
new file mode 100644
index 0000000..39b802f
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/stmvl53l0x_module-cci.c
@@ -0,0 +1,411 @@
+/*
+ * stmvl53l0x_module-cci.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+/*
+ * power specific includes
+ */
+#include <linux/pwm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/clk.h>
+#include <linux/of_gpio.h>
+/*
+ * API includes
+ */
+#include "vl53l0x_api.h"
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+#include "stmvl53l0x-cci.h"
+#include "stmvl53l0x-i2c.h"
+#include "stmvl53l0x.h"
+
+#ifdef CAMERA_CCI
+/*
+ * Global data
+ */
+static struct v4l2_file_operations msm_tof_v4l2_subdev_fops;
+static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
+ .i2c_read = msm_camera_cci_i2c_read,
+ .i2c_read_seq = msm_camera_cci_i2c_read_seq,
+ .i2c_write = msm_camera_cci_i2c_write,
+ .i2c_write_seq = msm_camera_cci_i2c_write_seq,
+ .i2c_write_table = msm_camera_cci_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_cci_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_cci_i2c_util,
+ .i2c_poll = msm_camera_cci_i2c_poll,
+};
+static int stmvl53l0x_get_dt_data(struct device *dev, struct cci_data *data);
+
+/*
+ * QCOM specific functions
+ */
+static int stmvl53l0x_get_dt_data(struct device *dev, struct cci_data *data)
+{
+ int rc = 0;
+
+ dbg("Enter\n");
+
+ if (dev->of_node) {
+ struct device_node *of_node = dev->of_node;
+ struct msm_tof_vreg *vreg_cfg;
+
+ if (!of_node) {
+ err("failed %d\n", __LINE__);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(of_node,
+ "cell-index", &data->pdev->id);
+ if (rc < 0) {
+ err("failed %d\n", __LINE__);
+ return rc;
+ }
+ dbg("cell-index: %d\n", data->pdev->id);
+ rc = of_property_read_u32(of_node, "qcom,cci-master",
+ &data->cci_master);
+ if (rc < 0) {
+ err("failed %d\n", __LINE__);
+ /* Set default master 0 */
+ data->cci_master = MASTER_0;
+ rc = 0;
+ }
+ dbg("cci_master: %d\n", data->cci_master);
+ if (of_find_property(of_node, "qcom,cam-vreg-name", NULL)) {
+ vreg_cfg = &data->vreg_cfg;
+ rc = msm_camera_get_dt_vreg_data(of_node,
+ &vreg_cfg->cam_vreg, &vreg_cfg->num_vreg);
+ if (rc < 0) {
+ err("failed %d\n", __LINE__);
+ return rc;
+ }
+ }
+ dbg("vreg-name: %s min_volt: %d max_volt: %d",
+ vreg_cfg->cam_vreg->reg_name,
+ vreg_cfg->cam_vreg->min_voltage,
+ vreg_cfg->cam_vreg->max_voltage);
+ }
+ dbg("End rc =%d\n", rc);
+
+ return rc;
+}
+
+static int32_t stmvl53l0x_vreg_control(struct cci_data *data, int config)
+{
+ int rc = 0, i, cnt;
+ struct msm_tof_vreg *vreg_cfg;
+
+ dbg("Enter\n");
+
+ vreg_cfg = &data->vreg_cfg;
+ cnt = vreg_cfg->num_vreg;
+ dbg("num_vreg: %d\n", cnt);
+ if (!cnt) {
+ err("failed %d\n", __LINE__);
+ return 0;
+ }
+
+ if (cnt >= MSM_TOF_MAX_VREGS) {
+ err("failed %d cnt %d\n", __LINE__, cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ rc = msm_camera_config_single_vreg(&(data->pdev->dev),
+ &vreg_cfg->cam_vreg[i],
+ (struct regulator **)&vreg_cfg->data[i],
+ config);
+ }
+
+ dbg("EXIT rc =%d\n", rc);
+ return rc;
+}
+
+
+static int msm_tof_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return 0;
+}
+
+
+static const struct v4l2_subdev_internal_ops msm_tof_internal_ops = {
+ .close = msm_tof_close,
+};
+
+static long msm_tof_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ dbg("Subdev_ioctl not handled\n");
+ return 0;
+}
+
+static int32_t msm_tof_power(struct v4l2_subdev *sd, int on)
+{
+ dbg("TOF power called\n");
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops msm_tof_subdev_core_ops = {
+ .ioctl = msm_tof_subdev_ioctl,
+ .s_power = msm_tof_power,
+};
+
+static struct v4l2_subdev_ops msm_tof_subdev_ops = {
+ .core = &msm_tof_subdev_core_ops,
+};
+
+static int stmvl53l0x_cci_init(struct cci_data *data)
+{
+ int rc = 0;
+ struct msm_camera_cci_client *cci_client = data->client->cci_client;
+
+ if (data->subdev_initialized == FALSE) {
+ data->client->i2c_func_tbl = &msm_sensor_cci_func_tbl;
+ data->client->cci_client =
+ kzalloc(sizeof(struct msm_camera_cci_client),
+ GFP_KERNEL);
+ if (!data->client->cci_client) {
+ err("%d, failed no memory\n", __LINE__);
+ return -ENOMEM;
+ }
+ cci_client = data->client->cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = data->cci_master;
+ v4l2_subdev_init(&data->msm_sd.sd, data->subdev_ops);
+ v4l2_set_subdevdata(&data->msm_sd.sd, data);
+ data->msm_sd.sd.internal_ops = &msm_tof_internal_ops;
+ data->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(data->msm_sd.sd.name, ARRAY_SIZE(data->msm_sd.sd.name),
+ "msm_tof");
+ media_entity_init(&data->msm_sd.sd.entity, 0, NULL, 0);
+ data->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ data->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_TOF;
+ data->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;
+ msm_sd_register(&data->msm_sd);
+ msm_tof_v4l2_subdev_fops = v4l2_subdev_fops;
+ data->msm_sd.sd.devnode->fops = &msm_tof_v4l2_subdev_fops;
+ data->subdev_initialized = TRUE;
+ }
+
+ cci_client->sid = 0x29;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ cci_client->cci_i2c_master = data->cci_master;
+ rc = data->client->i2c_func_tbl->i2c_util(data->client, MSM_CCI_INIT);
+ if (rc < 0) {
+ err("%d: CCI Init failed\n", __LINE__);
+ return rc;
+ }
+ dbg("CCI Init Succeeded\n");
+
+ data->client->addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
+
+ return 0;
+}
+
+static int32_t stmvl53l0x_platform_probe(struct platform_device *pdev)
+{
+ struct vl_data *vl53l0x_data = NULL;
+ struct cci_data *cci_object = NULL;
+ int32_t rc = 0;
+
+ dbg("Enter\n");
+
+ if (!pdev->dev.of_node) {
+ err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ vl53l0x_data = kzalloc(sizeof(struct vl_data), GFP_KERNEL);
+ if (!vl53l0x_data) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ if (vl53l0x_data) {
+ vl53l0x_data->client_object =
+ kzalloc(sizeof(struct cci_data), GFP_KERNEL);
+ cci_object = (struct cci_data *)vl53l0x_data->client_object;
+ }
+ cci_object->client =
+ (struct msm_camera_i2c_client *)&cci_object->g_client;
+
+ /* setup bus type */
+ vl53l0x_data->bus_type = CCI_BUS;
+
+ /* Set platform device handle */
+ cci_object->subdev_ops = &msm_tof_subdev_ops;
+ cci_object->pdev = pdev;
+ rc = stmvl53l0x_get_dt_data(&pdev->dev, cci_object);
+ if (rc < 0) {
+ err("%d, failed rc %d\n", __LINE__, rc);
+ return rc;
+ }
+ cci_object->subdev_id = pdev->id;
+
+ /* Set device type as platform device */
+ cci_object->device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ cci_object->subdev_initialized = FALSE;
+
+ /* setup device name */
+ vl53l0x_data->dev_name = dev_name(&pdev->dev);
+
+ /* setup device data */
+ dev_set_drvdata(&pdev->dev, vl53l0x_data);
+
+ /* setup other stuff */
+ rc = stmvl53l0x_setup(vl53l0x_data);
+
+ /* init default value */
+ cci_object->power_up = 0;
+
+ dbg("End\n");
+
+ return rc;
+
+}
+
+static int32_t stmvl53l0x_platform_remove(struct platform_device *pdev)
+{
+ struct vl_data *vl53l0x_data = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(vl53l0x_data->client_object);
+ kfree(vl53l0x_data);
+
+ return 0;
+}
+
+static const struct of_device_id st_stmvl53l0x_dt_match[] = {
+ { .compatible = "st,stmvl53l0", },
+ { },
+};
+
+static struct platform_driver stmvl53l0x_platform_driver = {
+ .probe = stmvl53l0x_platform_probe,
+ .remove = stmvl53l0x_platform_remove,
+ .driver = {
+ .name = STMVL_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = st_stmvl53l0x_dt_match,
+ },
+};
+
+int stmvl53l0x_power_up_cci(void *cci_object, unsigned int *preset_flag)
+{
+ int ret = 0;
+ struct cci_data *data = (struct cci_data *)cci_object;
+
+ dbg("Enter");
+
+ /* need to init cci first */
+ ret = stmvl53l0x_cci_init(data);
+ if (ret) {
+ err("stmvl53l0x_cci_init failed %d\n", __LINE__);
+ return ret;
+ }
+ /* actual power up */
+ if (data && data->device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ ret = stmvl53l0x_vreg_control(data, 1);
+ if (ret < 0) {
+ err("stmvl53l0x_vreg_control failed %d\n",
+ __LINE__);
+ return ret;
+ }
+ }
+ data->power_up = 1;
+ *preset_flag = 1;
+ dbg("End\n");
+
+ return ret;
+}
+
+int stmvl53l0x_power_down_cci(void *cci_object)
+{
+ int ret = 0;
+ struct cci_data *data = (struct cci_data *)cci_object;
+
+ dbg("Enter\n");
+ if (data->power_up) {
+ /* need to release cci first */
+ ret = data->client->i2c_func_tbl->i2c_util(data->client,
+ MSM_CCI_RELEASE);
+ if (ret < 0)
+ err("CCI Release failed rc %d\n", ret);
+
+ /* actual power down */
+ if (data->device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ ret = stmvl53l0x_vreg_control(data, 0);
+ if (ret < 0) {
+ err(
+ "stmvl53l0x_vreg_control failed %d\n",
+ __LINE__);
+ return ret;
+ }
+ }
+ }
+ data->power_up = 0;
+ dbg("End\n");
+ return ret;
+}
+
+int stmvl53l0x_init_cci(void)
+{
+ int ret = 0;
+
+ dbg("Enter\n");
+
+ /* register as a platform device */
+ ret = platform_driver_register(&stmvl53l0x_platform_driver);
+ if (ret)
+ err("%d, error ret:%d\n", __LINE__, ret);
+
+ dbg("End\n");
+
+ return ret;
+}
+
+void stmvl53l0x_exit_cci(void *cci_object)
+{
+ struct cci_data *data = (struct cci_data *)cci_object;
+
+ dbg("Enter\n");
+
+ if (data && data->client->cci_client)
+ kfree(data->client->cci_client);
+
+ dbg("End\n");
+}
+#endif /* end of CAMERA_CCI */
diff --git a/drivers/input/misc/vl53l0x/stmvl53l0x_module-i2c.c b/drivers/input/misc/vl53l0x/stmvl53l0x_module-i2c.c
new file mode 100644
index 0000000..7e0cc15
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/stmvl53l0x_module-i2c.c
@@ -0,0 +1,246 @@
+/*
+ * stmvl53l0x_module-i2c.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+/*
+ * power specific includes
+ */
+#include <linux/pwm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/clk.h>
+#include <linux/of_gpio.h>
+/*
+ * API includes
+ */
+#include "vl53l0x_api.h"
+#include "vl53l0x_def.h"
+#include "vl53l0x_platform.h"
+#include "stmvl53l0x-i2c.h"
+#include "stmvl53l0x-cci.h"
+#include "stmvl53l0x.h"
+#ifndef CAMERA_CCI
+
+/*
+ * Global data
+ */
+static int stmvl53l0x_parse_vdd(struct device *dev, struct i2c_data *data);
+
+/*
+ * QCOM specific functions
+ */
+static int stmvl53l0x_parse_vdd(struct device *dev, struct i2c_data *data)
+{
+ int ret = 0;
+
+ dbg("Enter\n");
+
+ if (dev->of_node) {
+ data->vana = regulator_get(dev, "vdd");
+ if (IS_ERR(data->vana)) {
+ err("vdd supply is not provided\n");
+ ret = -1;
+ }
+ }
+ dbg("End\n");
+
+ return ret;
+}
+
+static int stmvl53l0x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct vl_data *vl53l0x_data = NULL;
+ struct i2c_data *i2c_object = NULL;
+
+ dbg("Enter\n");
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
+ rc = -EIO;
+ return rc;
+ }
+
+ vl53l0x_data = kzalloc(sizeof(struct vl_data), GFP_KERNEL);
+ if (!vl53l0x_data) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ if (vl53l0x_data) {
+ vl53l0x_data->client_object =
+ kzalloc(sizeof(struct i2c_data), GFP_KERNEL);
+ i2c_object = (struct i2c_data *)vl53l0x_data->client_object;
+ }
+ i2c_object->client = client;
+
+ /* setup bus type */
+ vl53l0x_data->bus_type = I2C_BUS;
+
+ /* setup regulator */
+ stmvl53l0x_parse_vdd(&i2c_object->client->dev, i2c_object);
+
+ /* setup device name */
+ vl53l0x_data->dev_name = dev_name(&client->dev);
+
+ /* setup device data */
+ dev_set_drvdata(&client->dev, vl53l0x_data);
+
+ /* setup client data */
+ i2c_set_clientdata(client, vl53l0x_data);
+
+ /* setup other stuff */
+ rc = stmvl53l0x_setup(vl53l0x_data);
+
+ /* init default value */
+ i2c_object->power_up = 0;
+
+ dbg("End\n");
+ return rc;
+}
+
+static int stmvl53l0x_remove(struct i2c_client *client)
+{
+ struct vl_data *data = i2c_get_clientdata(client);
+
+ dbg("Enter\n");
+
+ /* Power down the device */
+ stmvl53l0x_power_down_i2c(data->client_object);
+ kfree(data->client_object);
+ kfree(data);
+ dbg("End\n");
+ return 0;
+}
+
+static const struct i2c_device_id stmvl53l0x_id[] = {
+ { STMVL_DRV_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, stmvl53l0x_id);
+
+static const struct of_device_id st_stmvl53l0x_dt_match[] = {
+ { .compatible = "st,stmvl53l0", },
+ { },
+};
+
+static struct i2c_driver stmvl53l0x_driver = {
+ .driver = {
+ .name = STMVL_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = st_stmvl53l0x_dt_match,
+ },
+ .probe = stmvl53l0x_probe,
+ .remove = stmvl53l0x_remove,
+ .id_table = stmvl53l0x_id,
+
+};
+
+int stmvl53l0x_power_up_i2c(void *i2c_object, unsigned int *preset_flag)
+{
+ int ret = 0;
+#ifndef STM_TEST
+ struct i2c_data *data = (struct i2c_data *)i2c_object;
+#endif
+
+ dbg("Enter\n");
+
+ /* actual power on */
+#ifndef STM_TEST
+ ret = regulator_set_voltage(data->vana,
+ VL_VDD_MIN, VL_VDD_MAX);
+ if (ret < 0) {
+ err("set_vol(%p) fail %d\n", data->vana, ret);
+ return ret;
+ }
+ ret = regulator_enable(data->vana);
+ usleep_range(3000, 3001);
+ if (ret < 0) {
+ err("reg enable(%p) failed.rc=%d\n",
+ data->vana, ret);
+ return ret;
+ }
+ data->power_up = 1;
+ *preset_flag = 1;
+#endif
+
+ dbg("End\n");
+ return ret;
+}
+
+int stmvl53l0x_power_down_i2c(void *i2c_object)
+{
+ int ret = 0;
+#ifndef STM_TEST
+ struct i2c_data *data = (struct i2c_data *)i2c_object;
+#endif
+
+ dbg("Enter\n");
+#ifndef STM_TEST
+ msleep(30);
+ ret = regulator_disable(data->vana);
+ if (ret < 0)
+ err("reg disable(%p) failed.rc=%d\n",
+ data->vana, ret);
+
+ data->power_up = 0;
+#endif
+
+ dbg("End\n");
+ return ret;
+}
+
+int stmvl53l0x_init_i2c(void)
+{
+ int ret = 0;
+
+ dbg("Enter\n");
+
+ /* register as a i2c client device */
+ ret = i2c_add_driver(&stmvl53l0x_driver);
+ if (ret)
+ err("%d erro ret:%d\n", __LINE__, ret);
+
+ dbg("End with rc:%d\n", ret);
+
+ return ret;
+}
+
+void stmvl53l0x_exit_i2c(void *i2c_object)
+{
+ dbg("Enter\n");
+ i2c_del_driver(&stmvl53l0x_driver);
+
+ dbg("End\n");
+}
+
+#endif /* end of NOT CAMERA_CCI */
diff --git a/drivers/input/misc/vl53l0x/stmvl53l0x_module.c b/drivers/input/misc/vl53l0x/stmvl53l0x_module.c
new file mode 100644
index 0000000..99cbc11
--- /dev/null
+++ b/drivers/input/misc/vl53l0x/stmvl53l0x_module.c
@@ -0,0 +1,2023 @@
+/*
+ * stmvl53l0x_module.c - Linux kernel modules for
+ * STM VL53L0 FlightSense TOF sensor
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/kthread.h>
+/*
+ * API includes
+ */
+#include "vl53l0x_api.h"
+
+#define IRQ_NUM 59
+#ifdef DEBUG_TIME_LOG
+struct timeval start_tv, stop_tv;
+#endif
+
+/*
+ * Global data
+ */
+
+#ifdef CAMERA_CCI
+static struct stmvl53l0x_module_fn_t stmvl53l0x_module_func_tbl = {
+ .init = stmvl53l0x_init_cci,
+ .deinit = stmvl53l0x_exit_cci,
+ .power_up = stmvl53l0x_power_up_cci,
+ .power_down = stmvl53l0x_power_down_cci,
+};
+#else
+static struct stmvl53l0x_module_fn_t stmvl53l0x_module_func_tbl = {
+ .init = stmvl53l0x_init_i2c,
+ .deinit = stmvl53l0x_exit_i2c,
+ .power_up = stmvl53l0x_power_up_i2c,
+ .power_down = stmvl53l0x_power_down_i2c,
+};
+#endif
+struct stmvl53l0x_module_fn_t *pmodule_func_tbl;
+struct stmvl53l0x_api_fn_t {
+ int8_t (*GetVersion)(struct VL_Version_t *pVersion);
+ int8_t (*GetPalSpecVersion)(struct VL_Version_t *pPalSpecVersion);
+
+ int8_t (*GetProductRevision)(struct vl_data *Dev,
+ uint8_t *pProductRevisionMajor,
+ uint8_t *pProductRevisionMinor);
+ int8_t (*GetDeviceInfo)(struct vl_data *Dev,
+ struct VL_DeviceInfo_t *pVL_DeviceInfo);
+ int8_t (*GetDeviceErrorStatus)(struct vl_data *Dev,
+ uint8_t *pDeviceErrorStatus);
+ int8_t (*GetRangeStatusString)(uint8_t RangeStatus,
+ char *pRangeStatusString);
+ int8_t (*GetDeviceErrorString)(uint8_t ErrorCode,
+ char *pDeviceErrorString);
+ int8_t (*GetPalErrorString)(int8_t PalErrorCode,
+ char *pPalErrorString);
+ int8_t (*GetPalStateString)(uint8_t PalStateCode,
+ char *pPalStateString);
+ int8_t (*GetPalState)(struct vl_data *Dev, uint8_t *pPalState);
+ int8_t (*SetPowerMode)(struct vl_data *Dev,
+ uint8_t PowerMode);
+ int8_t (*GetPowerMode)(struct vl_data *Dev,
+ uint8_t *pPowerMode);
+ int8_t (*SetOffsetCalibrationDataMicroMeter)(struct vl_data *Dev,
+ int32_t OffsetCalibrationDataMicroMeter);
+ int8_t (*GetOffsetCalibrationDataMicroMeter)(struct vl_data *Dev,
+ int32_t *pOffsetCalibrationDataMicroMeter);
+ int8_t (*SetLinearityCorrectiveGain)(struct vl_data *Dev,
+ int16_t LinearityCorrectiveGain);
+ int8_t (*GetLinearityCorrectiveGain)(struct vl_data *Dev,
+ uint16_t *pLinearityCorrectiveGain);
+ int8_t (*SetGroupParamHold)(struct vl_data *Dev,
+ uint8_t GroupParamHold);
+ int8_t (*GetUpperLimitMilliMeter)(struct vl_data *Dev,
+ uint16_t *pUpperLimitMilliMeter);
+ int8_t (*SetDeviceAddress)(struct vl_data *Dev,
+ uint8_t DeviceAddress);
+ int8_t (*DataInit)(struct vl_data *Dev);
+ int8_t (*SetTuningSettingBuffer)(struct vl_data *Dev,
+ uint8_t *pTuningSettingBuffer,
+ uint8_t UseInternalTuningSettings);
+ int8_t (*GetTuningSettingBuffer)(struct vl_data *Dev,
+ uint8_t **pTuningSettingBuffer,
+ uint8_t *pUseInternalTuningSettings);
+ int8_t (*StaticInit)(struct vl_data *Dev);
+ int8_t (*WaitDeviceBooted)(struct vl_data *Dev);
+ int8_t (*ResetDevice)(struct vl_data *Dev);
+ int8_t (*SetDeviceParameters)(struct vl_data *Dev,
+ const struct VL_DeviceParameters_t *pDeviceParameters);
+ int8_t (*GetDeviceParameters)(struct vl_data *Dev,
+ struct VL_DeviceParameters_t *pDeviceParameters);
+ int8_t (*SetDeviceMode)(struct vl_data *Dev,
+ uint8_t DeviceMode);
+ int8_t (*GetDeviceMode)(struct vl_data *Dev,
+ uint8_t *pDeviceMode);
+ int8_t (*SetHistogramMode)(struct vl_data *Dev,
+ uint8_t HistogramMode);
+ int8_t (*GetHistogramMode)(struct vl_data *Dev,
+ uint8_t *pHistogramMode);
+ int8_t (*SetMeasurementTimingBudgetMicroSeconds)(struct vl_data *Dev,
+ uint32_t MeasurementTimingBudgetMicroSeconds);
+ int8_t (*GetMeasurementTimingBudgetMicroSeconds)(
+ struct vl_data *Dev,
+ uint32_t *pMeasurementTimingBudgetMicroSeconds);
+ int8_t (*GetVcselPulsePeriod)(struct vl_data *Dev,
+ uint8_t VcselPeriodType,
+ uint8_t *pVCSELPulsePeriod);
+ int8_t (*SetVcselPulsePeriod)(struct vl_data *Dev,
+ uint8_t VcselPeriodType,
+ uint8_t VCSELPulsePeriod);
+ int8_t (*SetSequenceStepEnable)(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ uint8_t SequenceStepEnabled);
+ int8_t (*GetSequenceStepEnable)(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ uint8_t *pSequenceStepEnabled);
+ int8_t (*GetSequenceStepEnables)(struct vl_data *Dev,
+ struct VL_SchedulerSequenceSteps_t *pSchedulerSequenceSteps);
+ int8_t (*SetSequenceStepTimeout)(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ unsigned int TimeOutMilliSecs);
+ int8_t (*GetSequenceStepTimeout)(struct vl_data *Dev,
+ uint8_t SequenceStepId,
+ unsigned int *pTimeOutMilliSecs);
+ int8_t (*GetNumberOfSequenceSteps)(struct vl_data *Dev,
+ uint8_t *pNumberOfSequenceSteps);
+ int8_t (*GetSequenceStepsInfo)(
+ uint8_t SequenceStepId,
+ char *pSequenceStepsString);
+ int8_t (*SetInterMeasurementPeriodMilliSeconds)(
+ struct vl_data *Dev,
+ uint32_t InterMeasurementPeriodMilliSeconds);
+ int8_t (*GetInterMeasurementPeriodMilliSeconds)(
+ struct vl_data *Dev,
+ uint32_t *pInterMeasurementPeriodMilliSeconds);
+ int8_t (*SetXTalkCompensationEnable)(struct vl_data *Dev,
+ uint8_t XTalkCompensationEnable);
+ int8_t (*GetXTalkCompensationEnable)(struct vl_data *Dev,
+ uint8_t *pXTalkCompensationEnable);
+ int8_t (*SetXTalkCompensationRateMegaCps)(
+ struct vl_data *Dev,
+ unsigned int XTalkCompensationRateMegaCps);
+ int8_t (*GetXTalkCompensationRateMegaCps)(
+ struct vl_data *Dev,
+ unsigned int *pXTalkCompensationRateMegaCps);
+ int8_t (*GetNumberOfLimitCheck)(
+ uint16_t *pNumberOfLimitCheck);
+ int8_t (*GetLimitCheckInfo)(struct vl_data *Dev,
+ uint16_t LimitCheckId, char *pLimitCheckString);
+ int8_t (*SetLimitCheckEnable)(struct vl_data *Dev,
+ uint16_t LimitCheckId,
+ uint8_t LimitCheckEnable);
+ int8_t (*GetLimitCheckEnable)(struct vl_data *Dev,
+ uint16_t LimitCheckId, uint8_t *pLimitCheckEnable);
+ int8_t (*SetLimitCheckValue)(struct vl_data *Dev,
+ uint16_t LimitCheckId,
+ unsigned int LimitCheckValue);
+ int8_t (*GetLimitCheckValue)(struct vl_data *Dev,
+ uint16_t LimitCheckId,
+ unsigned int *pLimitCheckValue);
+ int8_t (*GetLimitCheckCurrent)(struct vl_data *Dev,
+ uint16_t LimitCheckId, unsigned int *pLimitCheckCurrent);
+ int8_t (*SetWrapAroundCheckEnable)(struct vl_data *Dev,
+ uint8_t WrapAroundCheckEnable);
+ int8_t (*GetWrapAroundCheckEnable)(struct vl_data *Dev,
+ uint8_t *pWrapAroundCheckEnable);
+ int8_t (*PerformSingleMeasurement)(struct vl_data *Dev);
+ int8_t (*PerformRefCalibration)(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal);
+ int8_t (*SetRefCalibration)(struct vl_data *Dev,
+ uint8_t VhvSettings, uint8_t PhaseCal);
+ int8_t (*GetRefCalibration)(struct vl_data *Dev,
+ uint8_t *pVhvSettings, uint8_t *pPhaseCal);
+ int8_t (*PerformXTalkCalibration)(struct vl_data *Dev,
+ unsigned int XTalkCalDistance,
+ unsigned int *pXTalkCompensationRateMegaCps);
+ int8_t (*PerformOffsetCalibration)(struct vl_data *Dev,
+ unsigned int CalDistanceMilliMeter,
+ int32_t *pOffsetMicroMeter);
+ int8_t (*StartMeasurement)(struct vl_data *Dev);
+ int8_t (*StopMeasurement)(struct vl_data *Dev);
+ int8_t (*GetMeasurementDataReady)(struct vl_data *Dev,
+ uint8_t *pMeasurementDataReady);
+ int8_t (*WaitDeviceReadyForNewMeasurement)(struct vl_data *Dev,
+ uint32_t MaxLoop);
+ int8_t (*GetRangingMeasurementData)(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData);
+ int8_t (*GetHistogramMeasurementData)(struct vl_data *Dev,
+ struct VL_HistogramMeasurementData_t *pHistogramMeasurementData);
+ int8_t (*PerformSingleRangingMeasurement)(struct vl_data *Dev,
+ struct VL_RangingMeasurementData_t *pRangingMeasurementData);
+ int8_t (*PerformSingleHistogramMeasurement)(struct vl_data *Dev,
+ struct VL_HistogramMeasurementData_t *pHistogramMeasurementData);
+ int8_t (*SetNumberOfROIZones)(struct vl_data *Dev,
+ uint8_t NumberOfROIZones);
+ int8_t (*GetNumberOfROIZones)(struct vl_data *Dev,
+ uint8_t *pNumberOfROIZones);
+ int8_t (*GetMaxNumberOfROIZones)(struct vl_data *Dev,
+ uint8_t *pMaxNumberOfROIZones);
+ int8_t (*SetGpioConfig)(struct vl_data *Dev,
+ uint8_t Pin,
+ uint8_t DeviceMode,
+ uint8_t Functionality,
+ uint8_t Polarity);
+ int8_t (*GetGpioConfig)(struct vl_data *Dev,
+ uint8_t Pin,
+ uint8_t *pDeviceMode,
+ uint8_t *pFunctionality,
+ uint8_t *pPolarity);
+ int8_t (*SetInterruptThresholds)(struct vl_data *Dev,
+ uint8_t DeviceMode,
+ unsigned int ThresholdLow,
+ unsigned int ThresholdHigh);
+ int8_t (*GetInterruptThresholds)(struct vl_data *Dev,
+ uint8_t DeviceMode,
+ unsigned int *pThresholdLow,
+ unsigned int *pThresholdHigh);
+ int8_t (*ClearInterruptMask)(struct vl_data *Dev,
+ uint32_t InterruptMask);
+ int8_t (*GetInterruptMaskStatus)(struct vl_data *Dev,
+ uint32_t *pInterruptMaskStatus);
+ int8_t (*EnableInterruptMask)(struct vl_data *Dev,
+ uint32_t InterruptMask);
+ int8_t (*SetSpadAmbientDamperThreshold)(struct vl_data *Dev,
+ uint16_t SpadAmbientDamperThreshold);
+ int8_t (*GetSpadAmbientDamperThreshold)(struct vl_data *Dev,
+ uint16_t *pSpadAmbientDamperThreshold);
+ int8_t (*SetSpadAmbientDamperFactor)(struct vl_data *Dev,
+ uint16_t SpadAmbientDamperFactor);
+ int8_t (*GetSpadAmbientDamperFactor)(struct vl_data *Dev,
+ uint16_t *pSpadAmbientDamperFactor);
+ int8_t (*PerformRefSpadManagement)(struct vl_data *Dev,
+ uint32_t *refSpadCount, uint8_t *isApertureSpads);
+ int8_t (*SetReferenceSpads)(struct vl_data *Dev,
+ uint32_t count, uint8_t isApertureSpads);
+ int8_t (*GetReferenceSpads)(struct vl_data *Dev,
+ uint32_t *pSpadCount, uint8_t *pIsApertureSpads);
+};
+
+static struct stmvl53l0x_api_fn_t stmvl53l0x_api_func_tbl = {
+ .GetVersion = VL_GetVersion,
+ .GetPalSpecVersion = VL_GetPalSpecVersion,
+ .GetProductRevision = VL_GetProductRevision,
+ .GetDeviceInfo = VL_GetDeviceInfo,
+ .GetDeviceErrorStatus = VL_GetDeviceErrorStatus,
+ .GetRangeStatusString = VL_GetRangeStatusString,
+ .GetDeviceErrorString = VL_GetDeviceErrorString,
+ .GetPalErrorString = VL_GetPalErrorString,
+ .GetPalState = VL_GetPalState,
+ .SetPowerMode = VL_SetPowerMode,
+ .GetPowerMode = VL_GetPowerMode,
+ .SetOffsetCalibrationDataMicroMeter =
+ VL_SetOffsetCalibrationDataMicroMeter,
+ .SetLinearityCorrectiveGain =
+ VL_SetLinearityCorrectiveGain,
+ .GetLinearityCorrectiveGain =
+ VL_GetLinearityCorrectiveGain,
+ .GetOffsetCalibrationDataMicroMeter =
+ VL_GetOffsetCalibrationDataMicroMeter,
+ .SetGroupParamHold = VL_SetGroupParamHold,
+ .GetUpperLimitMilliMeter = VL_GetUpperLimitMilliMeter,
+ .SetDeviceAddress = VL_SetDeviceAddress,
+ .DataInit = VL_DataInit,
+ .SetTuningSettingBuffer = VL_SetTuningSettingBuffer,
+ .GetTuningSettingBuffer = VL_GetTuningSettingBuffer,
+ .StaticInit = VL_StaticInit,
+ .WaitDeviceBooted = VL_WaitDeviceBooted,
+ .ResetDevice = VL_ResetDevice,
+ .SetDeviceParameters = VL_SetDeviceParameters,
+ .SetDeviceMode = VL_SetDeviceMode,
+ .GetDeviceMode = VL_GetDeviceMode,
+ .SetHistogramMode = VL_SetHistogramMode,
+ .GetHistogramMode = VL_GetHistogramMode,
+ .SetMeasurementTimingBudgetMicroSeconds =
+ VL_SetMeasurementTimingBudgetMicroSeconds,
+ .GetMeasurementTimingBudgetMicroSeconds =
+ VL_GetMeasurementTimingBudgetMicroSeconds,
+ .GetVcselPulsePeriod = VL_GetVcselPulsePeriod,
+ .SetVcselPulsePeriod = VL_SetVcselPulsePeriod,
+ .SetSequenceStepEnable = VL_SetSequenceStepEnable,
+ .GetSequenceStepEnable = VL_GetSequenceStepEnable,
+ .GetSequenceStepEnables = VL_GetSequenceStepEnables,
+ .SetSequenceStepTimeout = VL_SetSequenceStepTimeout,
+ .GetSequenceStepTimeout = VL_GetSequenceStepTimeout,
+ .GetNumberOfSequenceSteps = VL_GetNumberOfSequenceSteps,
+ .GetSequenceStepsInfo = VL_GetSequenceStepsInfo,
+ .SetInterMeasurementPeriodMilliSeconds =
+ VL_SetInterMeasurementPeriodMilliSeconds,
+ .GetInterMeasurementPeriodMilliSeconds =
+ VL_GetInterMeasurementPeriodMilliSeconds,
+ .SetXTalkCompensationEnable = VL_SetXTalkCompensationEnable,
+ .GetXTalkCompensationEnable = VL_GetXTalkCompensationEnable,
+ .SetXTalkCompensationRateMegaCps =
+ VL_SetXTalkCompensationRateMegaCps,
+ .GetXTalkCompensationRateMegaCps =
+ VL_GetXTalkCompensationRateMegaCps,
+ .GetNumberOfLimitCheck = VL_GetNumberOfLimitCheck,
+ .GetLimitCheckInfo = VL_GetLimitCheckInfo,
+ .SetLimitCheckEnable = VL_SetLimitCheckEnable,
+ .GetLimitCheckEnable = VL_GetLimitCheckEnable,
+ .SetLimitCheckValue = VL_SetLimitCheckValue,
+ .GetLimitCheckValue = VL_GetLimitCheckValue,
+ .GetLimitCheckCurrent = VL_GetLimitCheckCurrent,
+ .SetWrapAroundCheckEnable = VL_SetWrapAroundCheckEnable,
+ .GetWrapAroundCheckEnable = VL_GetWrapAroundCheckEnable,
+ .PerformSingleMeasurement = VL_PerformSingleMeasurement,
+ .PerformRefCalibration = VL_PerformRefCalibration,
+ .SetRefCalibration = VL_SetRefCalibration,
+ .GetRefCalibration = VL_GetRefCalibration,
+ .PerformXTalkCalibration = VL_PerformXTalkCalibration,
+ .PerformOffsetCalibration = VL_PerformOffsetCalibration,
+ .StartMeasurement = VL_StartMeasurement,
+ .StopMeasurement = VL_StopMeasurement,
+ .GetMeasurementDataReady = VL_GetMeasurementDataReady,
+ .WaitDeviceReadyForNewMeasurement =
+ VL_WaitDeviceReadyForNewMeasurement,
+ .GetRangingMeasurementData = VL_GetRangingMeasurementData,
+ .GetHistogramMeasurementData = VL_GetHistogramMeasurementData,
+ .PerformSingleRangingMeasurement =
+ VL_PerformSingleRangingMeasurement,
+ .PerformSingleHistogramMeasurement =
+ VL_PerformSingleHistogramMeasurement,
+ .SetNumberOfROIZones = VL_SetNumberOfROIZones,
+ .GetNumberOfROIZones = VL_GetNumberOfROIZones,
+ .GetMaxNumberOfROIZones = VL_GetMaxNumberOfROIZones,
+ .SetGpioConfig = VL_SetGpioConfig,
+ .GetGpioConfig = VL_GetGpioConfig,
+ .SetInterruptThresholds = VL_SetInterruptThresholds,
+ .GetInterruptThresholds = VL_GetInterruptThresholds,
+ .ClearInterruptMask = VL_ClearInterruptMask,
+ .GetInterruptMaskStatus = VL_GetInterruptMaskStatus,
+ .EnableInterruptMask = VL_EnableInterruptMask,
+ .SetSpadAmbientDamperThreshold = VL_SetSpadAmbientDamperThreshold,
+ .GetSpadAmbientDamperThreshold = VL_GetSpadAmbientDamperThreshold,
+ .SetSpadAmbientDamperFactor = VL_SetSpadAmbientDamperFactor,
+ .GetSpadAmbientDamperFactor = VL_GetSpadAmbientDamperFactor,
+ .PerformRefSpadManagement = VL_PerformRefSpadManagement,
+ .SetReferenceSpads = VL_SetReferenceSpads,
+ .GetReferenceSpads = VL_GetReferenceSpads,
+
+};
+struct stmvl53l0x_api_fn_t *papi_func_tbl;
+
+/*
+ * IOCTL definitions
+ */
+#define VL_IOCTL_INIT _IO('p', 0x01)
+#define VL_IOCTL_XTALKCALB _IOW('p', 0x02, unsigned int)
+#define VL_IOCTL_OFFCALB _IOW('p', 0x03, unsigned int)
+#define VL_IOCTL_STOP _IO('p', 0x05)
+#define VL_IOCTL_SETXTALK _IOW('p', 0x06, unsigned int)
+#define VL_IOCTL_SETOFFSET _IOW('p', 0x07, int8_t)
+#define VL_IOCTL_GETDATAS \
+ _IOR('p', 0x0b, struct VL_RangingMeasurementData_t)
+#define VL_IOCTL_REGISTER \
+ _IOWR('p', 0x0c, struct stmvl53l0x_register)
+#define VL_IOCTL_PARAMETER \
+ _IOWR('p', 0x0d, struct stmvl53l0x_parameter)
+
+static long stmvl53l0x_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg);
+/*static int stmvl53l0x_flush(struct file *file, fl_owner_t id);*/
+static int stmvl53l0x_open(struct inode *inode, struct file *file);
+static int stmvl53l0x_init_client(struct vl_data *data);
+static int stmvl53l0x_start(struct vl_data *data, uint8_t scaling,
+ enum init_mode_e mode);
+static int stmvl53l0x_stop(struct vl_data *data);
+
+#ifdef DEBUG_TIME_LOG
+static void stmvl53l0x_DebugTimeGet(struct timeval *ptv)
+{
+ do_gettimeofday(ptv);
+}
+
+static void stmvl53l0x_DebugTimeDuration(struct timeval *pstart_tv,
+ struct timeval *pstop_tv)
+{
+ long total_sec, total_msec;
+
+ total_sec = pstop_tv->tv_sec - pstart_tv->tv_sec;
+ total_msec = (pstop_tv->tv_usec - pstart_tv->tv_usec)/1000;
+ total_msec += total_sec * 1000;
+ dbg("elapsedTime:%ld\n", total_msec);
+}
+#endif
+
+static void stmvl53l0x_setupAPIFunctions(void)
+{
+
+ /*cut 1.1*/
+ err("to setup API cut 1.1\n");
+ papi_func_tbl->GetVersion = VL_GetVersion;
+ papi_func_tbl->GetPalSpecVersion = VL_GetPalSpecVersion;
+ papi_func_tbl->GetProductRevision = VL_GetProductRevision;
+ papi_func_tbl->GetDeviceInfo = VL_GetDeviceInfo;
+ papi_func_tbl->GetDeviceErrorStatus = VL_GetDeviceErrorStatus;
+ papi_func_tbl->GetRangeStatusString = VL_GetRangeStatusString;
+ papi_func_tbl->GetDeviceErrorString = VL_GetDeviceErrorString;
+ papi_func_tbl->GetPalErrorString = VL_GetPalErrorString;
+ papi_func_tbl->GetPalState = VL_GetPalState;
+ papi_func_tbl->SetPowerMode = VL_SetPowerMode;
+ papi_func_tbl->GetPowerMode = VL_GetPowerMode;
+ papi_func_tbl->SetOffsetCalibrationDataMicroMeter =
+ VL_SetOffsetCalibrationDataMicroMeter;
+ papi_func_tbl->GetOffsetCalibrationDataMicroMeter =
+ VL_GetOffsetCalibrationDataMicroMeter;
+ papi_func_tbl->SetLinearityCorrectiveGain =
+ VL_SetLinearityCorrectiveGain;
+ papi_func_tbl->GetLinearityCorrectiveGain =
+ VL_GetLinearityCorrectiveGain;
+ papi_func_tbl->SetGroupParamHold = VL_SetGroupParamHold;
+ papi_func_tbl->GetUpperLimitMilliMeter =
+ VL_GetUpperLimitMilliMeter;
+ papi_func_tbl->SetDeviceAddress = VL_SetDeviceAddress;
+ papi_func_tbl->DataInit = VL_DataInit;
+ papi_func_tbl->SetTuningSettingBuffer = VL_SetTuningSettingBuffer;
+ papi_func_tbl->GetTuningSettingBuffer = VL_GetTuningSettingBuffer;
+ papi_func_tbl->StaticInit = VL_StaticInit;
+ papi_func_tbl->WaitDeviceBooted = VL_WaitDeviceBooted;
+ papi_func_tbl->ResetDevice = VL_ResetDevice;
+ papi_func_tbl->SetDeviceParameters = VL_SetDeviceParameters;
+ papi_func_tbl->SetDeviceMode = VL_SetDeviceMode;
+ papi_func_tbl->GetDeviceMode = VL_GetDeviceMode;
+ papi_func_tbl->SetHistogramMode = VL_SetHistogramMode;
+ papi_func_tbl->GetHistogramMode = VL_GetHistogramMode;
+ papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds =
+ VL_SetMeasurementTimingBudgetMicroSeconds;
+ papi_func_tbl->GetMeasurementTimingBudgetMicroSeconds =
+ VL_GetMeasurementTimingBudgetMicroSeconds;
+ papi_func_tbl->GetVcselPulsePeriod = VL_GetVcselPulsePeriod;
+ papi_func_tbl->SetVcselPulsePeriod = VL_SetVcselPulsePeriod;
+ papi_func_tbl->SetSequenceStepEnable = VL_SetSequenceStepEnable;
+ papi_func_tbl->GetSequenceStepEnable = VL_GetSequenceStepEnable;
+ papi_func_tbl->GetSequenceStepEnables = VL_GetSequenceStepEnables;
+ papi_func_tbl->SetSequenceStepTimeout = VL_SetSequenceStepTimeout;
+ papi_func_tbl->GetSequenceStepTimeout = VL_GetSequenceStepTimeout;
+ papi_func_tbl->GetNumberOfSequenceSteps =
+ VL_GetNumberOfSequenceSteps;
+ papi_func_tbl->GetSequenceStepsInfo = VL_GetSequenceStepsInfo;
+ papi_func_tbl->SetInterMeasurementPeriodMilliSeconds =
+ VL_SetInterMeasurementPeriodMilliSeconds;
+ papi_func_tbl->GetInterMeasurementPeriodMilliSeconds =
+ VL_GetInterMeasurementPeriodMilliSeconds;
+ papi_func_tbl->SetXTalkCompensationEnable =
+ VL_SetXTalkCompensationEnable;
+ papi_func_tbl->GetXTalkCompensationEnable =
+ VL_GetXTalkCompensationEnable;
+ papi_func_tbl->SetXTalkCompensationRateMegaCps =
+ VL_SetXTalkCompensationRateMegaCps;
+ papi_func_tbl->GetXTalkCompensationRateMegaCps =
+ VL_GetXTalkCompensationRateMegaCps;
+ papi_func_tbl->GetNumberOfLimitCheck = VL_GetNumberOfLimitCheck;
+ papi_func_tbl->GetLimitCheckInfo = VL_GetLimitCheckInfo;
+ papi_func_tbl->SetLimitCheckEnable = VL_SetLimitCheckEnable;
+ papi_func_tbl->GetLimitCheckEnable = VL_GetLimitCheckEnable;
+ papi_func_tbl->SetLimitCheckValue = VL_SetLimitCheckValue;
+ papi_func_tbl->GetLimitCheckValue = VL_GetLimitCheckValue;
+ papi_func_tbl->GetLimitCheckCurrent = VL_GetLimitCheckCurrent;
+ papi_func_tbl->SetWrapAroundCheckEnable =
+ VL_SetWrapAroundCheckEnable;
+ papi_func_tbl->GetWrapAroundCheckEnable =
+ VL_GetWrapAroundCheckEnable;
+ papi_func_tbl->PerformSingleMeasurement =
+ VL_PerformSingleMeasurement;
+ papi_func_tbl->PerformRefCalibration = VL_PerformRefCalibration;
+ papi_func_tbl->SetRefCalibration = VL_SetRefCalibration;
+ papi_func_tbl->GetRefCalibration = VL_GetRefCalibration;
+ papi_func_tbl->PerformXTalkCalibration =
+ VL_PerformXTalkCalibration;
+ papi_func_tbl->PerformOffsetCalibration =
+ VL_PerformOffsetCalibration;
+ papi_func_tbl->StartMeasurement = VL_StartMeasurement;
+ papi_func_tbl->StopMeasurement = VL_StopMeasurement;
+ papi_func_tbl->GetMeasurementDataReady =
+ VL_GetMeasurementDataReady;
+ papi_func_tbl->WaitDeviceReadyForNewMeasurement =
+ VL_WaitDeviceReadyForNewMeasurement;
+ papi_func_tbl->GetRangingMeasurementData =
+ VL_GetRangingMeasurementData;
+ papi_func_tbl->GetHistogramMeasurementData =
+ VL_GetHistogramMeasurementData;
+ papi_func_tbl->PerformSingleRangingMeasurement =
+ VL_PerformSingleRangingMeasurement;
+ papi_func_tbl->PerformSingleHistogramMeasurement =
+ VL_PerformSingleHistogramMeasurement;
+ papi_func_tbl->SetNumberOfROIZones = VL_SetNumberOfROIZones;
+ papi_func_tbl->GetNumberOfROIZones = VL_GetNumberOfROIZones;
+ papi_func_tbl->GetMaxNumberOfROIZones = VL_GetMaxNumberOfROIZones;
+ papi_func_tbl->SetGpioConfig = VL_SetGpioConfig;
+ papi_func_tbl->GetGpioConfig = VL_GetGpioConfig;
+ papi_func_tbl->SetInterruptThresholds = VL_SetInterruptThresholds;
+ papi_func_tbl->GetInterruptThresholds = VL_GetInterruptThresholds;
+ papi_func_tbl->ClearInterruptMask = VL_ClearInterruptMask;
+ papi_func_tbl->GetInterruptMaskStatus = VL_GetInterruptMaskStatus;
+ papi_func_tbl->EnableInterruptMask = VL_EnableInterruptMask;
+ papi_func_tbl->SetSpadAmbientDamperThreshold =
+ VL_SetSpadAmbientDamperThreshold;
+ papi_func_tbl->GetSpadAmbientDamperThreshold =
+ VL_GetSpadAmbientDamperThreshold;
+ papi_func_tbl->SetSpadAmbientDamperFactor =
+ VL_SetSpadAmbientDamperFactor;
+ papi_func_tbl->GetSpadAmbientDamperFactor =
+ VL_GetSpadAmbientDamperFactor;
+ papi_func_tbl->PerformRefSpadManagement =
+ VL_PerformRefSpadManagement;
+ papi_func_tbl->SetReferenceSpads = VL_SetReferenceSpads;
+ papi_func_tbl->GetReferenceSpads = VL_GetReferenceSpads;
+
+}
+
+static void stmvl53l0x_ps_read_measurement(struct vl_data *data)
+{
+ struct timeval tv;
+ struct vl_data *vl53l0x_dev = data;
+ int8_t Status = VL_ERROR_NONE;
+ unsigned int LimitCheckCurrent;
+
+ do_gettimeofday(&tv);
+
+ data->ps_data = data->rangeData.RangeMilliMeter;
+ input_report_abs(data->input_dev_ps, ABS_DISTANCE,
+ (int)(data->ps_data + 5) / 10);
+ input_report_abs(data->input_dev_ps, ABS_HAT0X, tv.tv_sec);
+ input_report_abs(data->input_dev_ps, ABS_HAT0Y, tv.tv_usec);
+ input_report_abs(data->input_dev_ps, ABS_HAT1X,
+ data->rangeData.RangeMilliMeter);
+ input_report_abs(data->input_dev_ps, ABS_HAT1Y,
+ data->rangeData.RangeStatus);
+ input_report_abs(data->input_dev_ps, ABS_HAT2X,
+ data->rangeData.SignalRateRtnMegaCps);
+ input_report_abs(data->input_dev_ps, ABS_HAT2Y,
+ data->rangeData.AmbientRateRtnMegaCps);
+ input_report_abs(data->input_dev_ps, ABS_HAT3X,
+ data->rangeData.MeasurementTimeUsec);
+ input_report_abs(data->input_dev_ps, ABS_HAT3Y,
+ data->rangeData.RangeDMaxMilliMeter);
+ Status = papi_func_tbl->GetLimitCheckCurrent(vl53l0x_dev,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ &LimitCheckCurrent);
+ if (Status == VL_ERROR_NONE) {
+ input_report_abs(data->input_dev_ps, ABS_WHEEL,
+ LimitCheckCurrent);
+ }
+ input_report_abs(data->input_dev_ps, ABS_PRESSURE,
+ data->rangeData.EffectiveSpadRtnCount);
+ input_sync(data->input_dev_ps);
+
+ if (data->enableDebug)
+ err("range:%d, RtnRateMcps:%d,err:0x%x\n",
+ data->rangeData.RangeMilliMeter,
+ data->rangeData.SignalRateRtnMegaCps,
+ data->rangeData.RangeStatus);
+ err("Dmax:%d,rtnambr:%d,time:%d,Spad:%d,SigmaLimit:%d\n",
+ data->rangeData.RangeDMaxMilliMeter,
+ data->rangeData.AmbientRateRtnMegaCps,
+ data->rangeData.MeasurementTimeUsec,
+ data->rangeData.EffectiveSpadRtnCount,
+ LimitCheckCurrent);
+
+
+}
+
+static void stmvl53l0x_cancel_handler(struct vl_data *data)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&data->update_lock.wait_lock, flags);
+ /*
+ * If work is already scheduled then subsequent schedules will not
+ * change the scheduled time that's why we have to cancel it first.
+ */
+ ret = cancel_delayed_work(&data->dwork);
+ if (ret == 0)
+ err("cancel_delayed_work return FALSE\n");
+
+ spin_unlock_irqrestore(&data->update_lock.wait_lock, flags);
+
+}
+
+void stmvl53l0x_schedule_handler(struct vl_data *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->update_lock.wait_lock, flags);
+ /*
+ * If work is already scheduled then subsequent schedules will not
+ * change the scheduled time that's why we have to cancel it first.
+ */
+ cancel_delayed_work(&data->dwork);
+ schedule_delayed_work(&data->dwork, 0);
+ spin_unlock_irqrestore(&data->update_lock.wait_lock, flags);
+
+}
+
+/* Flag used to exit the thread when kthread_stop() is invoked */
+static int poll_thread_exit;
+int stmvl53l0x_poll_thread(void *data)
+{
+ struct vl_data *vl53l0x_dev = data;
+ int8_t Status = VL_ERROR_NONE;
+ uint32_t sleep_time = 0;
+ uint32_t interruptStatus = 0;
+
+ dbg("Starting Polling thread\n");
+
+ while (!kthread_should_stop()) {
+ /* Check if enable_ps_sensor is true or */
+ /* exit request is made. If not block */
+ wait_event(vl53l0x_dev->poll_thread_wq,
+ (vl53l0x_dev->enable_ps_sensor || poll_thread_exit));
+ if (poll_thread_exit) {
+ dbg("Exiting the poll thread\n");
+ break;
+ }
+
+ mutex_lock(&vl53l0x_dev->work_mutex);
+
+ sleep_time = vl53l0x_dev->delay_ms;
+ Status = VL_GetInterruptMaskStatus(vl53l0x_dev,
+ &interruptStatus);
+ if (Status == VL_ERROR_NONE &&
+ interruptStatus &&
+ interruptStatus != vl53l0x_dev->interruptStatus) {
+ vl53l0x_dev->interruptStatus = interruptStatus;
+ vl53l0x_dev->noInterruptCount = 0;
+ stmvl53l0x_schedule_handler(vl53l0x_dev);
+
+ } else {
+ vl53l0x_dev->noInterruptCount++;
+ }
+
+ /* Force Clear interrupt mask and restart if no interrupt */
+ /* after twice the timingBudget */
+ if ((vl53l0x_dev->noInterruptCount * vl53l0x_dev->delay_ms) >
+ (vl53l0x_dev->timingBudget * 2)) {
+ dbg("No interrupt after (%u) msec(TimingBudget = %u)\n",
+ (vl53l0x_dev->noInterruptCount * vl53l0x_dev->delay_ms),
+ vl53l0x_dev->timingBudget);
+ Status = papi_func_tbl->ClearInterruptMask(vl53l0x_dev, 0);
+ if (vl53l0x_dev->deviceMode ==
+ VL_DEVICEMODE_SINGLE_RANGING) {
+ Status = papi_func_tbl->StartMeasurement(vl53l0x_dev);
+ if (Status != VL_ERROR_NONE)
+ dbg("Status = %d\n", Status);
+ }
+ }
+
+ mutex_unlock(&vl53l0x_dev->work_mutex);
+
+ msleep(sleep_time);
+ }
+
+ return 0;
+}
+
+/* work handler */
+static void stmvl53l0x_work_handler(struct work_struct *work)
+{
+ struct vl_data *data = container_of(work,
+ struct vl_data, dwork.work);
+
+ struct vl_data *vl53l0x_dev = data;
+
+ int8_t Status = VL_ERROR_NONE;
+
+ mutex_lock(&data->work_mutex);
+ /* dbg("Enter\n"); */
+
+
+ if (vl53l0x_dev->enable_ps_sensor == 1) {
+#ifdef DEBUG_TIME_LOG
+ stmvl53l0x_DebugTimeGet(&stop_tv);
+ stmvl53l0x_DebugTimeDuration(&start_tv, &stop_tv);
+#endif
+ /* ISR has scheduled this function */
+ if (vl53l0x_dev->interrupt_received == 1) {
+ Status = papi_func_tbl->GetInterruptMaskStatus(
+ vl53l0x_dev, &vl53l0x_dev->interruptStatus);
+ if (Status != VL_ERROR_NONE) {
+ dbg("%s(%d) : Status = %d\n",
+ __func__, __LINE__, Status);
+ }
+ vl53l0x_dev->interrupt_received = 0;
+ }
+ if (data->enableDebug)
+ dbg("interruptStatus:0x%x, interrupt_received:%d\n",
+ vl53l0x_dev->interruptStatus,
+ vl53l0x_dev->interrupt_received);
+
+ if (vl53l0x_dev->interruptStatus ==
+ vl53l0x_dev->gpio_function) {
+ Status = papi_func_tbl->ClearInterruptMask(vl53l0x_dev,
+ 0);
+ if (Status != VL_ERROR_NONE) {
+ dbg("%s(%d) : Status = %d\n",
+ __func__, __LINE__, Status);
+ } else {
+ Status =
+ papi_func_tbl->GetRangingMeasurementData(
+ vl53l0x_dev, &(data->rangeData));
+ /* to push the measurement */
+ if (Status == VL_ERROR_NONE)
+ stmvl53l0x_ps_read_measurement(data);
+ else
+ dbg("%s(%d) : Status = %d\n",
+ __func__, __LINE__, Status);
+
+ dbg("Measured range:%d\n",
+ data->rangeData.RangeMilliMeter);
+
+ if (data->enableDebug)
+ dbg("Measured range:%d\n",
+ data->rangeData.RangeMilliMeter);
+
+ if (data->deviceMode ==
+ VL_DEVICEMODE_SINGLE_RANGING)
+ Status =
+ papi_func_tbl->StartMeasurement(
+ vl53l0x_dev);
+ }
+ }
+#ifdef DEBUG_TIME_LOG
+ stmvl53l0x_DebugTimeGet(&start_tv);
+#endif
+
+ }
+
+
+ vl53l0x_dev->interruptStatus = 0;
+
+ mutex_unlock(&data->work_mutex);
+
+}
+
+
+/*
+ * SysFS support
+ */
+static ssize_t stmvl53l0x_show_enable_ps_sensor(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, 5, "%d\n", data->enable_ps_sensor);
+}
+
+static ssize_t stmvl53l0x_store_enable_ps_sensor(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int ret;
+ unsigned int val;
+
+ ret = kstrtoint(buf, 10, &val);
+ if ((val != 0) && (val != 1)) {
+ err("store unvalid value=%d\n", val);
+ return count;
+ }
+ mutex_lock(&data->work_mutex);
+ dbg("Enter, enable_ps_sensor flag:%d\n",
+ data->enable_ps_sensor);
+ dbg("enable ps senosr ( %d)\n", val);
+
+ if (val == 1) {
+ /* turn on tof sensor */
+ if (data->enable_ps_sensor == 0) {
+ /* to start */
+ stmvl53l0x_start(data, 3, NORMAL_MODE);
+ } else {
+ err("Already enabled. Skip !");
+ }
+ } else {
+ /* turn off tof sensor */
+ if (data->enable_ps_sensor == 1) {
+ data->enable_ps_sensor = 0;
+ /* to stop */
+ stmvl53l0x_stop(data);
+ }
+ }
+ dbg("End\n");
+ mutex_unlock(&data->work_mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(enable_ps_sensor, 0664/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_enable_ps_sensor,
+ stmvl53l0x_store_enable_ps_sensor);
+
+static ssize_t stmvl53l0x_show_enable_debug(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, 5, "%d\n", data->enableDebug);
+}
+
+/* for debug */
+static ssize_t stmvl53l0x_store_enable_debug(struct device *dev,
+ struct device_attribute *attr, const
+ char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int on;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &on);
+ if ((on != 0) && (on != 1)) {
+ err("set debug=%d\n", on);
+ return count;
+ }
+ data->enableDebug = on;
+
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(enable_debug, 0660/*S_IWUSR | S_IRUGO*/,
+ stmvl53l0x_show_enable_debug,
+ stmvl53l0x_store_enable_debug);
+
+static ssize_t stmvl53l0x_show_set_delay_ms(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, 5, "%d\n", data->delay_ms);
+}
+
+/* for work handler scheduler time */
+static ssize_t stmvl53l0x_store_set_delay_ms(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int delay_ms;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &delay_ms);
+ if (delay_ms == 0) {
+ err("set delay_ms=%d\n", delay_ms);
+ return count;
+ }
+ mutex_lock(&data->work_mutex);
+ data->delay_ms = delay_ms;
+ mutex_unlock(&data->work_mutex);
+
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(set_delay_ms, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_set_delay_ms,
+ stmvl53l0x_store_set_delay_ms);
+
+/* Timing Budget */
+static ssize_t stmvl53l0x_show_timing_budget(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, 10, "%d\n", data->timingBudget);
+}
+
+static ssize_t stmvl53l0x_store_set_timing_budget(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int timingBudget;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &timingBudget);
+ if (timingBudget == 0) {
+ err("set timingBudget=%d\n", timingBudget);
+ return count;
+ }
+ mutex_lock(&data->work_mutex);
+ data->timingBudget = timingBudget;
+ mutex_unlock(&data->work_mutex);
+
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(set_timing_budget, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_timing_budget,
+ stmvl53l0x_store_set_timing_budget);
+
+
+/* Long Range */
+static ssize_t stmvl53l0x_show_long_range(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, 5, "%d\n", data->useLongRange);
+}
+
+static ssize_t stmvl53l0x_store_set_long_range(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int useLongRange;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &useLongRange);
+ if ((useLongRange != 0) && (useLongRange != 1)) {
+ err("set useLongRange=%d\n", useLongRange);
+ return count;
+ }
+
+ mutex_lock(&data->work_mutex);
+ data->useLongRange = useLongRange;
+ if (useLongRange)
+ data->timingBudget = 26000;
+ else
+ data->timingBudget = 200000;
+
+ mutex_unlock(&data->work_mutex);
+
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(set_long_range, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_long_range,
+ stmvl53l0x_store_set_long_range);
+
+static ssize_t stmvl53l0x_show_meter(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ struct VL_RangingMeasurementData_t Measure;
+
+ papi_func_tbl->PerformSingleRangingMeasurement(data, &Measure);
+ return snprintf(buf, 4, "%d\n", Measure.RangeMilliMeter);
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(show_meter, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_meter,
+ NULL);
+
+static ssize_t stmvl53l0x_show_xtalk(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t stmvl53l0x_set_xtalk(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ unsigned int targetDistance;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &targetDistance);
+ data->xtalkCalDistance = targetDistance;
+ stmvl53l0x_start(data, 3, XTALKCALIB_MODE);
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(xtalk_cal, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_xtalk,
+ stmvl53l0x_set_xtalk);
+
+static ssize_t stmvl53l0x_show_offset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ struct VL_RangingMeasurementData_t Measure;
+
+ papi_func_tbl->PerformSingleRangingMeasurement(data, &Measure);
+ return snprintf(buf, 4, "%d\n", Measure.RangeMilliMeter);
+}
+
+static ssize_t stmvl53l0x_set_offset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ unsigned int targetDistance;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &targetDistance);
+ data->offsetCalDistance = targetDistance;
+ stmvl53l0x_start(data, 3, OFFSETCALIB_MODE);
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(offset_cal, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_offset,
+ stmvl53l0x_set_offset);
+
+static ssize_t stmvl53l0x_set_spad(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ stmvl53l0x_start(data, 3, SPADCALIB_MODE);
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(spad_cal, 0660/*S_IWUGO | S_IRUGO*/,
+ NULL,
+ stmvl53l0x_set_spad);
+
+static ssize_t stmvl53l0x_set_ref(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+
+ stmvl53l0x_start(data, 3, REFCALIB_MODE);
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(ref_cal, 0660/*S_IWUGO | S_IRUGO*/,
+ NULL,
+ stmvl53l0x_set_ref);
+
+
+static ssize_t stmvl53l0x_show_OffsetCalibrationData(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int32_t offset_calibration_data;
+
+ papi_func_tbl->GetOffsetCalibrationDataMicroMeter(data,
+ &offset_calibration_data);
+ dbg("GetOffsetCalibrationDataMicroMeter = %d\n",
+ offset_calibration_data);
+ return snprintf(buf, 2, "%d\n",
+ offset_calibration_data);
+}
+
+static ssize_t stmvl53l0x_set_OffsetCalibrationData(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int32_t offset_calibration_data;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &offset_calibration_data);
+ papi_func_tbl->SetOffsetCalibrationDataMicroMeter(data,
+ offset_calibration_data);
+ return count;
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(set_offsetdata, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_OffsetCalibrationData,
+ stmvl53l0x_set_OffsetCalibrationData);
+
+static ssize_t stmvl53l0x_show_XTalkCompensationRateMegaCps(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ int32_t xtalk_compensation_rate;
+
+ papi_func_tbl->GetXTalkCompensationRateMegaCps(data,
+ &xtalk_compensation_rate);
+ dbg("xtalk_compensation_rate = %d\n",
+ xtalk_compensation_rate);
+ return snprintf(buf, 2, "%d\n", xtalk_compensation_rate);
+
+}
+
+static ssize_t stmvl53l0x_set_XTalkCompensationRateMegaCps(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vl_data *data = dev_get_drvdata(dev);
+ unsigned int xtalk_compensation_rate;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &xtalk_compensation_rate);
+ papi_func_tbl->SetXTalkCompensationRateMegaCps(data,
+ xtalk_compensation_rate);
+ return count;
+
+}
+
+/* DEVICE_ATTR(name,mode,show,store) */
+static DEVICE_ATTR(set_xtalkdata, 0660/*S_IWUGO | S_IRUGO*/,
+ stmvl53l0x_show_XTalkCompensationRateMegaCps,
+ stmvl53l0x_set_XTalkCompensationRateMegaCps);
+
+
+
+static struct attribute *stmvl53l0x_attributes[] = {
+ &dev_attr_enable_ps_sensor.attr,
+ &dev_attr_enable_debug.attr,
+ &dev_attr_set_delay_ms.attr,
+ &dev_attr_set_timing_budget.attr,
+ &dev_attr_set_long_range.attr,
+ &dev_attr_show_meter.attr,
+ &dev_attr_xtalk_cal.attr,
+ &dev_attr_offset_cal.attr,
+ &dev_attr_spad_cal.attr,
+ &dev_attr_ref_cal.attr,
+ &dev_attr_set_offsetdata.attr,
+ &dev_attr_set_xtalkdata.attr,
+ NULL,
+};
+
+
+static const struct attribute_group stmvl53l0x_attr_group = {
+ .attrs = stmvl53l0x_attributes,
+};
+
+/*
+ * misc device file operation functions
+ */
+static int stmvl53l0x_ioctl_handler(struct file *file,
+ unsigned int cmd, unsigned long arg,
+ void __user *p)
+{
+ int rc = 0;
+ unsigned int xtalkint = 0;
+ unsigned int targetDistance = 0;
+ int8_t offsetint = 0;
+ struct vl_data *data =
+ container_of(file->private_data,
+ struct vl_data, miscdev);
+ struct stmvl53l0x_register reg;
+ struct stmvl53l0x_parameter parameter;
+ struct vl_data *vl53l0x_dev = data;
+ uint8_t deviceMode;
+ uint8_t page_num = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ dbg("Enter enable_ps_sensor:%d\n", data->enable_ps_sensor);
+ switch (cmd) {
+ /* enable */
+ case VL_IOCTL_INIT:
+ dbg("VL_IOCTL_INIT\n");
+ /* turn on tof sensor only if it's not enabled by other */
+ /* client */
+ if (data->enable_ps_sensor == 0) {
+ /* to start */
+ stmvl53l0x_start(data, 3, NORMAL_MODE);
+ } else
+ rc = -EINVAL;
+ break;
+ /* crosstalk calibration */
+ case VL_IOCTL_XTALKCALB:
+ dbg("VL_IOCTL_XTALKCALB\n");
+ data->xtalkCalDistance = 100;
+ if (copy_from_user(&targetDistance, (unsigned int *)p,
+ sizeof(unsigned int))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ data->xtalkCalDistance = targetDistance;
+
+ /* turn on tof sensor only if it's not enabled by other */
+ /* client */
+ if (data->enable_ps_sensor == 0) {
+ /* to start */
+ stmvl53l0x_start(data, 3, XTALKCALIB_MODE);
+ } else
+ rc = -EINVAL;
+ break;
+ /* set up Xtalk value */
+ case VL_IOCTL_SETXTALK:
+ dbg("VL_IOCTL_SETXTALK\n");
+ if (copy_from_user(&xtalkint, (unsigned int *)p,
+ sizeof(unsigned int))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ dbg("SETXTALK as 0x%x\n", xtalkint);
+/* later
+ * SetXTalkCompensationRate(vl53l0x_dev, xtalkint);
+ */
+ break;
+ /* offset calibration */
+ case VL_IOCTL_OFFCALB:
+ dbg("VL_IOCTL_OFFCALB\n");
+ data->offsetCalDistance = 50;
+ if (copy_from_user(&targetDistance, (unsigned int *)p,
+ sizeof(unsigned int))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ data->offsetCalDistance = targetDistance;
+ if (data->enable_ps_sensor == 0) {
+ /* to start */
+ stmvl53l0x_start(data, 3, OFFSETCALIB_MODE);
+ } else
+ rc = -EINVAL;
+ break;
+ /* set up offset value */
+ case VL_IOCTL_SETOFFSET:
+ dbg("VL_IOCTL_SETOFFSET\n");
+ if (copy_from_user(&offsetint, (int8_t *)p, sizeof(int8_t))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ dbg("SETOFFSET as %d\n", offsetint);
+ /* later SetOffsetCalibrationData(vl53l0x_dev, offsetint); */
+ break;
+ /* disable */
+ case VL_IOCTL_STOP:
+ dbg("VL_IOCTL_STOP\n");
+ /* turn off tof sensor only if it's enabled by other client */
+ if (data->enable_ps_sensor == 1) {
+ data->enable_ps_sensor = 0;
+ /* to stop */
+ stmvl53l0x_stop(data);
+ }
+ break;
+ /* Get all range data */
+ case VL_IOCTL_GETDATAS:
+ dbg("VL_IOCTL_GETDATAS\n");
+ if (copy_to_user((struct VL_RangingMeasurementData_t *)p,
+ &(data->rangeData),
+ sizeof(struct VL_RangingMeasurementData_t))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ break;
+ /* Register tool */
+ case VL_IOCTL_REGISTER:
+ dbg("VL_IOCTL_REGISTER\n");
+ if (copy_from_user(®, (struct stmvl53l0x_register *)p,
+ sizeof(struct stmvl53l0x_register))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ reg.status = 0;
+ page_num = (uint8_t)((reg.reg_index & 0x0000ff00) >> 8);
+ dbg(
+"VL_IOCTL_REGISTER, page number:%d\n", page_num);
+ if (page_num != 0)
+ reg.status = VL_WrByte(vl53l0x_dev,
+ 0xFF, page_num);
+
+ switch (reg.reg_bytes) {
+ case(4):
+ if (reg.is_read)
+ reg.status = VL_RdDWord(vl53l0x_dev,
+ (uint8_t)reg.reg_index,
+ ®.reg_data);
+ else
+ reg.status = VL_WrDWord(vl53l0x_dev,
+ (uint8_t)reg.reg_index,
+ reg.reg_data);
+ break;
+ case(2):
+ if (reg.is_read)
+ reg.status = VL_RdWord(vl53l0x_dev,
+ (uint8_t)reg.reg_index,
+ (uint16_t *)®.reg_data);
+ else
+ reg.status = VL_WrWord(vl53l0x_dev,
+ (uint8_t)reg.reg_index,
+ (uint16_t)reg.reg_data);
+ break;
+ case(1):
+ if (reg.is_read)
+ reg.status = VL_RdByte(vl53l0x_dev,
+ (uint8_t)reg.reg_index,
+ (uint8_t *)®.reg_data);
+ else
+ reg.status = VL_WrByte(vl53l0x_dev,
+ (uint8_t)reg.reg_index,
+ (uint8_t)reg.reg_data);
+ break;
+ default:
+ reg.status = -1;
+
+ }
+ if (page_num != 0)
+ reg.status = VL_WrByte(vl53l0x_dev, 0xFF, 0);
+
+
+ if (copy_to_user((struct stmvl53l0x_register *)p, ®,
+ sizeof(struct stmvl53l0x_register))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ break;
+ /* parameter access */
+ case VL_IOCTL_PARAMETER:
+ dbg("VL_IOCTL_PARAMETER\n");
+ if (copy_from_user(¶meter, (struct stmvl53l0x_parameter *)p,
+ sizeof(struct stmvl53l0x_parameter))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ parameter.status = 0;
+ if (data->enableDebug)
+ dbg("VL_IOCTL_PARAMETER Name = %d\n",
+ parameter.name);
+ switch (parameter.name) {
+ case (OFFSET_PAR):
+ if (parameter.is_read)
+ parameter.status =
+ papi_func_tbl->GetOffsetCalibrationDataMicroMeter(
+ vl53l0x_dev, ¶meter.value);
+ else
+ parameter.status =
+ papi_func_tbl->SetOffsetCalibrationDataMicroMeter(
+ vl53l0x_dev, parameter.value);
+ dbg("get parameter value as %d\n",
+ parameter.value);
+ break;
+
+ case (REFERENCESPADS_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetReferenceSpads(vl53l0x_dev,
+ (uint32_t *)&(parameter.value),
+ (uint8_t *)&(parameter.value2));
+ if (data->enableDebug)
+ dbg("Get RefSpad: Count:%u, Type:%u\n",
+ parameter.value, (uint8_t)parameter.value2);
+ } else {
+ if (data->enableDebug)
+ dbg("Set RefSpad: Count:%u, Type:%u\n",
+ parameter.value, (uint8_t)parameter.value2);
+
+ parameter.status =
+ papi_func_tbl->SetReferenceSpads(
+ vl53l0x_dev,
+ (uint32_t)(parameter.value),
+ (uint8_t)(parameter.value2));
+ }
+ break;
+
+ case (REFCALIBRATION_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetRefCalibration(vl53l0x_dev,
+ (uint8_t *)&(parameter.value),
+ (uint8_t *)&(parameter.value2));
+ if (data->enableDebug)
+ dbg("Get Ref: Vhv:%u, PhaseCal:%u\n",
+ (uint8_t)parameter.value,
+ (uint8_t)parameter.value2);
+ } else {
+ if (data->enableDebug)
+ dbg("Set Ref: Vhv:%u, PhaseCal:%u\n",
+ (uint8_t)parameter.value,
+ (uint8_t)parameter.value2);
+ parameter.status =
+ papi_func_tbl->SetRefCalibration(
+ vl53l0x_dev, (uint8_t)(parameter.value),
+ (uint8_t)(parameter.value2));
+ }
+ break;
+ case (XTALKRATE_PAR):
+ if (parameter.is_read)
+ parameter.status =
+ papi_func_tbl->GetXTalkCompensationRateMegaCps(
+ vl53l0x_dev, (unsigned int *)
+ ¶meter.value);
+ else
+ parameter.status =
+ papi_func_tbl->SetXTalkCompensationRateMegaCps(
+ vl53l0x_dev,
+ (unsigned int)
+ parameter.value);
+
+ break;
+ case (XTALKENABLE_PAR):
+ if (parameter.is_read)
+ parameter.status =
+ papi_func_tbl->GetXTalkCompensationEnable(
+ vl53l0x_dev,
+ (uint8_t *) ¶meter.value);
+ else
+ parameter.status =
+ papi_func_tbl->SetXTalkCompensationEnable(
+ vl53l0x_dev,
+ (uint8_t) parameter.value);
+ break;
+ case (GPIOFUNC_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetGpioConfig(vl53l0x_dev, 0,
+ &deviceMode,
+ &data->gpio_function,
+ &data->gpio_polarity);
+ parameter.value = data->gpio_function;
+ } else {
+ data->gpio_function = parameter.value;
+ parameter.status =
+ papi_func_tbl->SetGpioConfig(vl53l0x_dev, 0, 0,
+ data->gpio_function,
+ data->gpio_polarity);
+ }
+ break;
+ case (LOWTHRESH_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetInterruptThresholds(
+ vl53l0x_dev, 0, &(data->low_threshold),
+ &(data->high_threshold));
+ parameter.value = data->low_threshold >> 16;
+ } else {
+ data->low_threshold = parameter.value << 16;
+ parameter.status =
+ papi_func_tbl->SetInterruptThresholds(
+ vl53l0x_dev, 0, data->low_threshold,
+ data->high_threshold);
+ }
+ break;
+ case (HIGHTHRESH_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetInterruptThresholds(
+ vl53l0x_dev, 0, &(data->low_threshold),
+ &(data->high_threshold));
+ parameter.value = data->high_threshold >> 16;
+ } else {
+ data->high_threshold = parameter.value << 16;
+ parameter.status =
+ papi_func_tbl->SetInterruptThresholds(
+ vl53l0x_dev, 0, data->low_threshold,
+ data->high_threshold);
+ }
+ break;
+ case (DEVICEMODE_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetDeviceMode(
+ vl53l0x_dev,
+ (uint8_t *)&
+ (parameter.value));
+ } else {
+ parameter.status =
+ papi_func_tbl->SetDeviceMode(
+ vl53l0x_dev,
+ (uint8_t)(parameter.value));
+ data->deviceMode =
+ (uint8_t)(parameter.value);
+ }
+ break;
+
+
+
+ case (INTERMEASUREMENT_PAR):
+ if (parameter.is_read) {
+ parameter.status =
+ papi_func_tbl->GetInterMeasurementPeriodMilliSeconds(
+ vl53l0x_dev, (uint32_t *)&(parameter.value));
+ } else {
+ parameter.status =
+ papi_func_tbl->SetInterMeasurementPeriodMilliSeconds(
+ vl53l0x_dev, (uint32_t)(parameter.value));
+ data->interMeasurems = parameter.value;
+ }
+ break;
+
+ }
+
+ if (copy_to_user((struct stmvl53l0x_parameter *)p, ¶meter,
+ sizeof(struct stmvl53l0x_parameter))) {
+ err("%d, fail\n", __LINE__);
+ return -EFAULT;
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int stmvl53l0x_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int stmvl53l0x_flush(struct file *file, fl_owner_t id)
+{
+ struct vl_data *data = container_of(file->private_data,
+ struct vl_data, miscdev);
+ (void) file;
+ (void) id;
+
+ if (data) {
+ if (data->enable_ps_sensor == 1) {
+ /* turn off tof sensor if it's enabled */
+ data->enable_ps_sensor = 0;
+ /* to stop */
+ stmvl53l0x_stop(data);
+ }
+ }
+ return 0;
+}
+
+static long stmvl53l0x_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+ struct vl_data *data =
+ container_of(file->private_data,
+ struct vl_data, miscdev);
+ mutex_lock(&data->work_mutex);
+ ret = stmvl53l0x_ioctl_handler(file, cmd, arg, (void __user *)arg);
+ mutex_unlock(&data->work_mutex);
+
+ return ret;
+}
+
+/*
+ * Initialization function
+ */
+static int stmvl53l0x_init_client(struct vl_data *data)
+{
+
+ int8_t Status = VL_ERROR_NONE;
+ struct VL_DeviceInfo_t DeviceInfo;
+ struct vl_data *vl53l0x_dev = data;
+
+ uint32_t refSpadCount;
+ uint8_t isApertureSpads;
+ uint8_t VhvSettings;
+ uint8_t PhaseCal;
+
+ dbg("Enter\n");
+
+ vl53l0x_dev->I2cDevAddr = 0x52;
+ vl53l0x_dev->comms_type = 1;
+ vl53l0x_dev->comms_speed_khz = 400;
+
+ /* Setup API functions based on revision */
+ stmvl53l0x_setupAPIFunctions();
+
+ if (Status == VL_ERROR_NONE && data->reset) {
+ dbg("Call of VL_DataInit\n");
+ /* Data initialization */
+ Status = papi_func_tbl->DataInit(vl53l0x_dev);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ dbg("VL_GetDeviceInfo:\n");
+ Status = papi_func_tbl->GetDeviceInfo(vl53l0x_dev, &DeviceInfo);
+ if (Status == VL_ERROR_NONE) {
+ dbg("Device Name : %s\n", DeviceInfo.Name);
+ dbg("Device Type : %s\n", DeviceInfo.Type);
+ dbg("Device ID : %s\n", DeviceInfo.ProductId);
+ dbg("Product type: %d\n", DeviceInfo.ProductType);
+ dbg("ProductRevisionMajor : %d\n",
+ DeviceInfo.ProductRevisionMajor);
+ dbg("ProductRevisionMinor : %d\n",
+ DeviceInfo.ProductRevisionMinor);
+ }
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ dbg("Call of VL_StaticInit\n");
+ Status = papi_func_tbl->StaticInit(vl53l0x_dev);
+ /* Device Initialization */
+ }
+
+ if (Status == VL_ERROR_NONE && data->reset) {
+ if (papi_func_tbl->PerformRefCalibration != NULL) {
+ dbg("Call of VL_PerformRefCalibration\n");
+ Status = papi_func_tbl->PerformRefCalibration(
+ vl53l0x_dev, &VhvSettings, &PhaseCal);
+ /* Ref calibration */
+ }
+ }
+
+ if (Status == VL_ERROR_NONE && data->reset) {
+ if (papi_func_tbl->PerformRefSpadManagement != NULL) {
+ dbg("Call of VL_PerformRefSpadManagement\n");
+ Status = papi_func_tbl->PerformRefSpadManagement(
+ vl53l0x_dev, &refSpadCount, &isApertureSpads);
+ /* Ref Spad Management */
+ }
+ data->reset = 0;
+ /* needed, even the function is NULL */
+ }
+
+ if (Status == VL_ERROR_NONE) {
+
+ dbg("Call of VL_SetDeviceMode\n");
+ Status = papi_func_tbl->SetDeviceMode(vl53l0x_dev,
+ VL_DEVICEMODE_SINGLE_RANGING);
+ /* Setup in single ranging mode */
+ }
+ if (Status == VL_ERROR_NONE)
+ Status = papi_func_tbl->SetWrapAroundCheckEnable(
+ vl53l0x_dev, 1);
+
+ dbg("End\n");
+
+ return 0;
+}
+
+static int stmvl53l0x_start(struct vl_data *data, uint8_t scaling,
+ enum init_mode_e mode)
+{
+ int rc = 0;
+ struct vl_data *vl53l0x_dev = data;
+ int8_t Status = VL_ERROR_NONE;
+
+ dbg("Enter\n");
+
+ /* Power up */
+ rc = pmodule_func_tbl->power_up(data->client_object, &data->reset);
+ if (rc) {
+ err("%d,error rc %d\n", __LINE__, rc);
+ return rc;
+ }
+ /* init */
+ rc = stmvl53l0x_init_client(data);
+ if (rc) {
+ err("%d, error rc %d\n", __LINE__, rc);
+ pmodule_func_tbl->power_down(data->client_object);
+ return -EINVAL;
+ }
+
+ /* check mode */
+ if (mode != NORMAL_MODE)
+ papi_func_tbl->SetXTalkCompensationEnable(vl53l0x_dev, 1);
+
+ if (mode == OFFSETCALIB_MODE) {
+ /*VL_SetOffsetCalibrationDataMicroMeter(vl53l0x_dev, 0);*/
+ unsigned int OffsetMicroMeter;
+
+ papi_func_tbl->PerformOffsetCalibration(vl53l0x_dev,
+ (data->offsetCalDistance<<16),
+ &OffsetMicroMeter);
+ dbg("Offset calibration:%u\n", OffsetMicroMeter);
+ return rc;
+ } else if (mode == XTALKCALIB_MODE) {
+ unsigned int xtalk_compensation_rate_mega_cps;
+ /*caltarget distance : 100mm and convert to */
+ /* fixed point 16 16 format */
+ papi_func_tbl->PerformXTalkCalibration(vl53l0x_dev,
+ (data->xtalkCalDistance<<16),
+ &xtalk_compensation_rate_mega_cps);
+ dbg("Xtalk calibration:%u\n", xtalk_compensation_rate_mega_cps);
+ return rc;
+ } else if (mode == SPADCALIB_MODE) {
+ uint32_t ref_spad_count;
+ uint8_t is_aperture_spads;
+
+ papi_func_tbl->PerformRefSpadManagement(
+ vl53l0x_dev,
+ &ref_spad_count,
+ &is_aperture_spads);
+ dbg("SPAD calibration:%d,%d\n", ref_spad_count,
+ (unsigned int)is_aperture_spads);
+ return rc;
+ } else if (mode == REFCALIB_MODE) {
+ uint8_t vhv_settings;
+ uint8_t phase_cal;
+
+ papi_func_tbl->PerformRefCalibration(
+ vl53l0x_dev,
+ &vhv_settings,
+ &phase_cal);
+ dbg("Ref calibration:%u,%u\n",
+ (unsigned int)vhv_settings,
+ (unsigned int)phase_cal);
+ return rc;
+ }
+ /* set up device parameters */
+ data->gpio_polarity = VL_INTERRUPTPOLARITY_LOW;
+
+ /* Following two calls are made from IOCTL as well */
+ papi_func_tbl->SetGpioConfig(vl53l0x_dev, 0, 0,
+ data->gpio_function,
+ VL_INTERRUPTPOLARITY_LOW);
+
+ papi_func_tbl->SetInterruptThresholds(vl53l0x_dev, 0,
+ data->low_threshold, data->high_threshold);
+
+ if (data->deviceMode == VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING) {
+ papi_func_tbl->SetInterMeasurementPeriodMilliSeconds(
+ vl53l0x_dev, data->interMeasurems);
+ }
+
+ dbg("DeviceMode:0x%x, interMeasurems:%d==\n", data->deviceMode,
+ data->interMeasurems);
+ papi_func_tbl->SetDeviceMode(vl53l0x_dev,
+ data->deviceMode);
+ papi_func_tbl->ClearInterruptMask(vl53l0x_dev,
+ 0);
+
+ if (vl53l0x_dev->useLongRange) {
+ dbg("Configure Long Ranging\n");
+ Status = papi_func_tbl->SetLimitCheckValue(vl53l0x_dev,
+ VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
+ (unsigned int)(65536/10)); /* 0.1 * 65536 */
+ if (Status == VL_ERROR_NONE) {
+ Status = papi_func_tbl->SetLimitCheckValue(vl53l0x_dev,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ (unsigned int)(60*65536));
+ } else {
+ dbg("SIGNAL_RATE_FINAL_RANGE failed err = %d\n",
+ Status);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ dbg("Set Timing Budget = %u\n",
+ vl53l0x_dev->timingBudget);
+ Status =
+ papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds(
+ vl53l0x_dev, vl53l0x_dev->timingBudget);
+ } else {
+ dbg("SetLimitCheckValue failed err =%d\n",
+ Status);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0x_dev,
+ VL_VCSEL_PERIOD_PRE_RANGE, 18);
+ } else {
+ dbg("SetMeasurementTimingBudget failed err = %d\n",
+ Status);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0x_dev,
+ VL_VCSEL_PERIOD_FINAL_RANGE, 14);
+ } else {
+ dbg("SetVcselPulsePeriod(PRE, 18) failed err = %d\n",
+ Status);
+ }
+
+ if (Status != VL_ERROR_NONE) {
+ dbg("SetVcselPulsePeriod(FINAL, 14) failed err = %d\n",
+ Status);
+ }
+ } else {
+ dbg("Configure High Accuracy\n");
+ Status = papi_func_tbl->SetLimitCheckValue(vl53l0x_dev,
+ VL_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
+ (unsigned int)(25 * 65536 / 100)); /* 0.25 * 65536 */
+ if (Status == VL_ERROR_NONE) {
+ Status = papi_func_tbl->SetLimitCheckValue(vl53l0x_dev,
+ VL_CHECKENABLE_SIGMA_FINAL_RANGE,
+ (unsigned int)(18*65536));
+ } else {
+ dbg("SIGNAL_RATE_FINAL_RANGE failed err = %d\n",
+ Status);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ dbg("Set Timing Budget = %u\n",
+ vl53l0x_dev->timingBudget);
+ Status =
+ papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds(
+ vl53l0x_dev, vl53l0x_dev->timingBudget);
+ } else {
+ dbg("SetLimitCheckValue failed err = %d\n",
+ Status);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0x_dev,
+ VL_VCSEL_PERIOD_PRE_RANGE, 14);
+ } else {
+ dbg("SetMeasurementTimingBudget failed err = %d\n",
+ Status);
+ }
+
+ if (Status == VL_ERROR_NONE) {
+ Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0x_dev,
+ VL_VCSEL_PERIOD_FINAL_RANGE, 10);
+ } else {
+ dbg("SetVcselPulsePeriod failed err = %d\n",
+ Status);
+ }
+
+ if (Status != VL_ERROR_NONE) {
+ dbg("SetVcselPulsePeriod failed err = %d\n",
+ Status);
+ }
+
+ }
+
+ /* start the ranging */
+ papi_func_tbl->StartMeasurement(vl53l0x_dev);
+ data->enable_ps_sensor = 1;
+
+
+ dbg("End\n");
+
+ return rc;
+}
+
+static int stmvl53l0x_stop(struct vl_data *data)
+{
+ int rc = 0;
+ struct vl_data *vl53l0x_dev = data;
+
+ dbg("Enter\n");
+
+ /* stop - if continuous mode */
+ if (data->deviceMode == VL_DEVICEMODE_CONTINUOUS_RANGING ||
+ data->deviceMode == VL_DEVICEMODE_CONTINUOUS_TIMED_RANGING)
+ papi_func_tbl->StopMeasurement(vl53l0x_dev);
+
+ /* clean interrupt */
+ papi_func_tbl->ClearInterruptMask(vl53l0x_dev, 0);
+
+ /* cancel work handler */
+ stmvl53l0x_cancel_handler(data);
+ /* power down */
+ rc = pmodule_func_tbl->power_down(data->client_object);
+ if (rc) {
+ err("%d, error rc %d\n", __LINE__, rc);
+ return rc;
+ }
+ dbg("End\n");
+
+ return rc;
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static const struct file_operations stmvl53l0x_ranging_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = stmvl53l0x_ioctl,
+ .open = stmvl53l0x_open,
+ .flush = stmvl53l0x_flush,
+};
+
+int stmvl53l0x_setup(struct vl_data *data)
+{
+ int rc = 0;
+
+ dbg("Enter\n");
+
+ /* init mutex */
+ mutex_init(&data->update_lock);
+ mutex_init(&data->work_mutex);
+
+ init_waitqueue_head(&data->poll_thread_wq);
+
+ data->poll_thread = kthread_run(&stmvl53l0x_poll_thread,
+ (void *)data, "STM-VL53L0");
+ if (data->poll_thread == NULL) {
+ dbg("%s(%d) - Failed to create Polling thread\n",
+ __func__, __LINE__);
+ goto exit_free_irq;
+ }
+
+ /* init work handler */
+ INIT_DELAYED_WORK(&data->dwork, stmvl53l0x_work_handler);
+
+ /* Register to Input Device */
+ data->input_dev_ps = input_allocate_device();
+ if (!data->input_dev_ps) {
+ rc = -ENOMEM;
+ err("%d error:%d\n", __LINE__, rc);
+
+ goto exit_free_irq;
+ }
+ set_bit(EV_ABS, data->input_dev_ps->evbit);
+ /* range in cm*/
+ input_set_abs_params(data->input_dev_ps, ABS_DISTANCE, 0, 76, 0, 0);
+ /* tv_sec */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT0X, 0, 0xffffffff,
+ 0, 0);
+ /* tv_usec */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT0Y, 0, 0xffffffff,
+ 0, 0);
+ /* range in_mm */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT1X, 0, 765, 0, 0);
+ /* error code change maximum to 0xff for more flexibility */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT1Y, 0, 0xff, 0, 0);
+ /* rtnRate */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT2X, 0, 0xffffffff,
+ 0, 0);
+ /* rtn_amb_rate */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT2Y, 0, 0xffffffff,
+ 0, 0);
+ /* rtn_conv_time */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT3X, 0, 0xffffffff,
+ 0, 0);
+ /* dmax */
+ input_set_abs_params(data->input_dev_ps, ABS_HAT3Y, 0, 0xffffffff,
+ 0, 0);
+
+ input_set_abs_params(data->input_dev_ps, ABS_PRESSURE, 0, 0xffffffff,
+ 0, 0);
+
+ input_set_abs_params(data->input_dev_ps, ABS_WHEEL, 0, 0xffffffff,
+ 0, 0);
+
+ data->input_dev_ps->name = "STM VL53L0 proximity sensor";
+
+ rc = input_register_device(data->input_dev_ps);
+ if (rc) {
+ rc = -ENOMEM;
+ err("%d error:%d\n", __LINE__, rc);
+ goto exit_free_dev_ps;
+ }
+ /* setup drv data */
+ input_set_drvdata(data->input_dev_ps, data);
+
+ /* Register sysfs hooks */
+ data->range_kobj = kobject_create_and_add("range", kernel_kobj);
+ if (!data->range_kobj) {
+ rc = -ENOMEM;
+ err("%d error:%d\n", __LINE__, rc);
+ goto exit_unregister_dev_ps;
+ }
+ rc = sysfs_create_group(&data->input_dev_ps->dev.kobj,
+ &stmvl53l0x_attr_group);
+ if (rc) {
+ rc = -ENOMEM;
+ err("%d error:%d\n", __LINE__, rc);
+ goto exit_unregister_dev_ps_1;
+ }
+ /* init default device parameter value */
+ data->enable_ps_sensor = 0;
+ data->reset = 1;
+ data->delay_ms = 30; /* delay time to 30ms */
+ data->enableDebug = 0;
+ data->gpio_polarity = VL_INTERRUPTPOLARITY_LOW;
+ data->gpio_function = VL_GPIOFUNCTIONALITY_NEW_MEASURE_READY;
+ data->low_threshold = 60;
+ data->high_threshold = 200;
+ data->deviceMode = VL_DEVICEMODE_SINGLE_RANGING;
+ data->interMeasurems = 30;
+ data->timingBudget = 26000;
+ data->useLongRange = 1;
+
+ dbg("support ver. %s enabled\n", DRIVER_VERSION);
+ dbg("End");
+
+ return 0;
+exit_unregister_dev_ps_1:
+ kobject_put(data->range_kobj);
+exit_unregister_dev_ps:
+ input_unregister_device(data->input_dev_ps);
+exit_free_dev_ps:
+ input_free_device(data->input_dev_ps);
+exit_free_irq:
+ kfree(data);
+ return rc;
+}
+
+static int __init stmvl53l0x_init(void)
+{
+ int ret = -1;
+
+ dbg("Enter\n");
+
+ /* assign function table */
+ pmodule_func_tbl = &stmvl53l0x_module_func_tbl;
+ papi_func_tbl = &stmvl53l0x_api_func_tbl;
+
+ /* client specific init function */
+ ret = pmodule_func_tbl->init();
+
+ if (ret)
+ err("%d failed with %d\n", __LINE__, ret);
+
+ dbg("End\n");
+
+ return ret;
+}
+
+static void __exit stmvl53l0x_exit(void)
+{
+ dbg("Enter\n");
+
+ dbg("End\n");
+}
+
+MODULE_DESCRIPTION("ST FlightSense Time-of-Flight sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(stmvl53l0x_init);
+module_exit(stmvl53l0x_exit);
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index af83d2e..a8a96def 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -2538,13 +2538,31 @@
}
static int alps_update_dual_info_ss4_v2(unsigned char otp[][4],
- struct alps_data *priv)
+ struct alps_data *priv,
+ struct psmouse *psmouse)
{
bool is_dual = false;
+ int reg_val = 0;
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
- if (IS_SS4PLUS_DEV(priv->dev_id))
+ if (IS_SS4PLUS_DEV(priv->dev_id)) {
is_dual = (otp[0][0] >> 4) & 0x01;
+ if (!is_dual) {
+ /* For support TrackStick of Thinkpad L/E series */
+ if (alps_exit_command_mode(psmouse) == 0 &&
+ alps_enter_command_mode(psmouse) == 0) {
+ reg_val = alps_command_mode_read_reg(psmouse,
+ 0xD7);
+ }
+ alps_exit_command_mode(psmouse);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+ if (reg_val == 0x0C || reg_val == 0x1D)
+ is_dual = true;
+ }
+ }
+
if (is_dual)
priv->flags |= ALPS_DUALPOINT |
ALPS_DUALPOINT_WITH_PRESSURE;
@@ -2567,7 +2585,7 @@
alps_update_btn_info_ss4_v2(otp, priv);
- alps_update_dual_info_ss4_v2(otp, priv);
+ alps_update_dual_info_ss4_v2(otp, priv, psmouse);
return 0;
}
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index c9d491b..3851d57 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1082,6 +1082,13 @@
return error;
}
+ /* Make sure there is something at this address */
+ error = i2c_smbus_read_byte(client);
+ if (error < 0) {
+ dev_dbg(&client->dev, "nothing at this address: %d\n", error);
+ return -ENXIO;
+ }
+
/* Initialize the touchpad. */
error = elan_initialize(data);
if (error)
diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c
index a679e56..765879d 100644
--- a/drivers/input/mouse/elan_i2c_i2c.c
+++ b/drivers/input/mouse/elan_i2c_i2c.c
@@ -557,7 +557,14 @@
long ret;
int error;
int len;
- u8 buffer[ETP_I2C_INF_LENGTH];
+ u8 buffer[ETP_I2C_REPORT_LEN];
+
+ len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_LEN);
+ if (len != ETP_I2C_REPORT_LEN) {
+ error = len < 0 ? len : -EIO;
+ dev_warn(dev, "failed to read I2C data after FW WDT reset: %d (%d)\n",
+ error, len);
+ }
reinit_completion(completion);
enable_irq(client->irq);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 59603a5..c519c0b 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1711,6 +1711,17 @@
etd->samples[0], etd->samples[1], etd->samples[2]);
}
+ if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) {
+ /*
+ * This module has a bug which makes absolute mode
+ * unusable, so let's abort so we'll be using standard
+ * PS/2 protocol.
+ */
+ psmouse_info(psmouse,
+ "absolute mode broken, forcing standard PS/2 protocol\n");
+ goto init_fail;
+ }
+
if (elantech_set_absolute_mode(psmouse)) {
psmouse_err(psmouse,
"failed to put touchpad into absolute mode.\n");
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index b604564..30328e5 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -15,6 +15,7 @@
#define MOUSEDEV_MINORS 31
#define MOUSEDEV_MIX 63
+#include <linux/bitops.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
@@ -103,7 +104,7 @@
spinlock_t packet_lock;
int pos_x, pos_y;
- signed char ps2[6];
+ u8 ps2[6];
unsigned char ready, buffer, bufsiz;
unsigned char imexseq, impsseq;
enum mousedev_emul mode;
@@ -291,11 +292,10 @@
}
client->pos_x += packet->dx;
- client->pos_x = client->pos_x < 0 ?
- 0 : (client->pos_x >= xres ? xres : client->pos_x);
+ client->pos_x = clamp_val(client->pos_x, 0, xres);
+
client->pos_y += packet->dy;
- client->pos_y = client->pos_y < 0 ?
- 0 : (client->pos_y >= yres ? yres : client->pos_y);
+ client->pos_y = clamp_val(client->pos_y, 0, yres);
p->dx += packet->dx;
p->dy += packet->dy;
@@ -571,44 +571,50 @@
return error;
}
-static inline int mousedev_limit_delta(int delta, int limit)
-{
- return delta > limit ? limit : (delta < -limit ? -limit : delta);
-}
-
-static void mousedev_packet(struct mousedev_client *client,
- signed char *ps2_data)
+static void mousedev_packet(struct mousedev_client *client, u8 *ps2_data)
{
struct mousedev_motion *p = &client->packets[client->tail];
+ s8 dx, dy, dz;
- ps2_data[0] = 0x08 |
- ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
- ps2_data[1] = mousedev_limit_delta(p->dx, 127);
- ps2_data[2] = mousedev_limit_delta(p->dy, 127);
- p->dx -= ps2_data[1];
- p->dy -= ps2_data[2];
+ dx = clamp_val(p->dx, -127, 127);
+ p->dx -= dx;
+
+ dy = clamp_val(p->dy, -127, 127);
+ p->dy -= dy;
+
+ ps2_data[0] = BIT(3);
+ ps2_data[0] |= ((dx & BIT(7)) >> 3) | ((dy & BIT(7)) >> 2);
+ ps2_data[0] |= p->buttons & 0x07;
+ ps2_data[1] = dx;
+ ps2_data[2] = dy;
switch (client->mode) {
case MOUSEDEV_EMUL_EXPS:
- ps2_data[3] = mousedev_limit_delta(p->dz, 7);
- p->dz -= ps2_data[3];
- ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
+ dz = clamp_val(p->dz, -7, 7);
+ p->dz -= dz;
+
+ ps2_data[3] = (dz & 0x0f) | ((p->buttons & 0x18) << 1);
client->bufsiz = 4;
break;
case MOUSEDEV_EMUL_IMPS:
- ps2_data[0] |=
- ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
- ps2_data[3] = mousedev_limit_delta(p->dz, 127);
- p->dz -= ps2_data[3];
+ dz = clamp_val(p->dz, -127, 127);
+ p->dz -= dz;
+
+ ps2_data[0] |= ((p->buttons & 0x10) >> 3) |
+ ((p->buttons & 0x08) >> 1);
+ ps2_data[3] = dz;
+
client->bufsiz = 4;
break;
case MOUSEDEV_EMUL_PS2:
default:
- ps2_data[0] |=
- ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
p->dz = 0;
+
+ ps2_data[0] |= ((p->buttons & 0x10) >> 3) |
+ ((p->buttons & 0x08) >> 1);
+
client->bufsiz = 3;
break;
}
@@ -714,7 +720,7 @@
{
struct mousedev_client *client = file->private_data;
struct mousedev *mousedev = client->mousedev;
- signed char data[sizeof(client->ps2)];
+ u8 data[sizeof(client->ps2)];
int retval = 0;
if (!client->ready && !client->buffer && mousedev->exist &&
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index d1051e3..e484ea2 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -530,6 +530,20 @@
{ }
};
+static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = {
+ {
+ /*
+ * Sony Vaio VGN-CS series require MUX or the touch sensor
+ * buttons will disturb touchpad operation
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"),
+ },
+ },
+ { }
+};
+
/*
* On some Asus laptops, just running self tests cause problems.
*/
@@ -693,6 +707,13 @@
},
},
{
+ /* Lenovo ThinkPad L460 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"),
+ },
+ },
+ {
/* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
@@ -1223,6 +1244,9 @@
if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = true;
+ if (dmi_check_system(i8042_dmi_forcemux_table))
+ i8042_nomux = false;
+
if (dmi_check_system(i8042_dmi_notimeout_table))
i8042_notimeout = true;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b705f4a..6c4156a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1258,6 +1258,17 @@
To compile this driver as a module, choose M here: the
module will be called ft5x06_ts.
+config TOUCHSCREEN_GEN_VKEYS
+ tristate "Touchscreen Virtual Keys Driver"
+ help
+ Say Y here if you want to generate a sysfs entry for virtual
+ keys on Android.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gen_vkeys.
+
config FT_SECURE_TOUCH
bool "Secure Touch support for Focaltech Touchscreen"
depends on TOUCHSCREEN_FT5X06
@@ -1268,4 +1279,16 @@
If unsure, say N.
+config TOUCHSCREEN_HIMAX_CHIPSET
+ bool "Himax touchpanel CHIPSET"
+ depends on I2C
+ help
+ Say Y here if you have a Himax CHIPSET touchscreen.
+ HIMAX controllers are multi touch controllers which can
+ report 10 touches at a time.
+
+ If unsure, say N.
+
+source "drivers/input/touchscreen/hxchipset/Kconfig"
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index b2f6911..a5952ca 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -40,6 +40,7 @@
obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
+obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
@@ -103,3 +104,4 @@
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/
+obj-$(CONFIG_TOUCHSCREEN_HIMAX_CHIPSET) += hxchipset/
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
index 71b5a63..e7bb155 100644
--- a/drivers/input/touchscreen/ar1021_i2c.c
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -152,7 +152,7 @@
static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
static const struct i2c_device_id ar1021_i2c_id[] = {
- { "MICROCHIP_AR1021_I2C", 0 },
+ { "ar1021", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index e5d185f..2613240 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -3028,6 +3028,15 @@
.driver_data = samus_platform_data,
},
{
+ /* Samsung Chromebook Pro */
+ .ident = "Samsung Chromebook Pro",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"),
+ },
+ .driver_data = samus_platform_data,
+ },
+ {
/* Other Google Chromebooks */
.ident = "Chromebook",
.matches = {
diff --git a/drivers/input/touchscreen/gen_vkeys.c b/drivers/input/touchscreen/gen_vkeys.c
new file mode 100644
index 0000000..67b32ea
--- /dev/null
+++ b/drivers/input/touchscreen/gen_vkeys.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2013, 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/input/gen_vkeys.h>
+
+#define MAX_BUF_SIZE 256
+#define VKEY_VER_CODE "0x01"
+
+#define HEIGHT_SCALE_NUM 8
+#define HEIGHT_SCALE_DENOM 10
+
+#define VKEY_Y_OFFSET_DEFAULT 0
+
+/* numerator and denomenator for border equations */
+#define BORDER_ADJUST_NUM 3
+#define BORDER_ADJUST_DENOM 4
+
+static struct kobject *vkey_obj;
+static char *vkey_buf;
+
+static ssize_t vkey_show(struct kobject *obj,
+ struct kobj_attribute *attr, char *buf)
+{
+ strlcpy(buf, vkey_buf, MAX_BUF_SIZE);
+ return strnlen(buf, MAX_BUF_SIZE);
+}
+
+static struct kobj_attribute vkey_obj_attr = {
+ .attr = {
+ .mode = 0444,
+ },
+ .show = vkey_show,
+};
+
+static struct attribute *vkey_attr[] = {
+ &vkey_obj_attr.attr,
+ NULL,
+};
+
+static struct attribute_group vkey_grp = {
+ .attrs = vkey_attr,
+};
+
+static int vkey_parse_dt(struct device *dev,
+ struct vkeys_platform_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ struct property *prop;
+ int rc, val;
+
+ rc = of_property_read_string(np, "label", &pdata->name);
+ if (rc) {
+ dev_err(dev, "Failed to read label\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,disp-maxx", &pdata->disp_maxx);
+ if (rc) {
+ dev_err(dev, "Failed to read display max x\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,disp-maxy", &pdata->disp_maxy);
+ if (rc) {
+ dev_err(dev, "Failed to read display max y\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,panel-maxx", &pdata->panel_maxx);
+ if (rc) {
+ dev_err(dev, "Failed to read panel max x\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(np, "qcom,panel-maxy", &pdata->panel_maxy);
+ if (rc) {
+ dev_err(dev, "Failed to read panel max y\n");
+ return -EINVAL;
+ }
+
+ prop = of_find_property(np, "qcom,key-codes", NULL);
+ if (prop) {
+ pdata->num_keys = prop->length / sizeof(u32);
+ pdata->keycodes = devm_kzalloc(dev,
+ sizeof(u32) * pdata->num_keys, GFP_KERNEL);
+ if (!pdata->keycodes)
+ return -ENOMEM;
+ rc = of_property_read_u32_array(np, "qcom,key-codes",
+ pdata->keycodes, pdata->num_keys);
+ if (rc) {
+ dev_err(dev, "Failed to read key codes\n");
+ return -EINVAL;
+ }
+ }
+
+ pdata->y_offset = VKEY_Y_OFFSET_DEFAULT;
+ rc = of_property_read_u32(np, "qcom,y-offset", &val);
+ if (!rc)
+ pdata->y_offset = val;
+ else if (rc != -EINVAL) {
+ dev_err(dev, "Failed to read y position offset\n");
+ return rc;
+ }
+ return 0;
+}
+
+static int vkeys_probe(struct platform_device *pdev)
+{
+ struct vkeys_platform_data *pdata;
+ int width, height, center_x, center_y;
+ int x1 = 0, x2 = 0, i, c = 0, ret, border;
+ char *name;
+
+ vkey_buf = devm_kzalloc(&pdev->dev, MAX_BUF_SIZE, GFP_KERNEL);
+ if (!vkey_buf)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct vkeys_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = vkey_parse_dt(&pdev->dev, pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "Parsing DT failed(%d)", ret);
+ return ret;
+ }
+ } else
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata || !pdata->name || !pdata->keycodes || !pdata->num_keys ||
+ !pdata->disp_maxx || !pdata->disp_maxy || !pdata->panel_maxy) {
+ dev_err(&pdev->dev, "pdata is invalid\n");
+ return -EINVAL;
+ }
+
+ border = (pdata->panel_maxx - pdata->disp_maxx) * 2;
+ width = ((pdata->disp_maxx - (border * (pdata->num_keys - 1)))
+ / pdata->num_keys);
+ height = (pdata->panel_maxy - pdata->disp_maxy);
+ center_y = pdata->disp_maxy + (height / 2) + pdata->y_offset;
+ height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM;
+
+ x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM;
+
+ for (i = 0; i < pdata->num_keys; i++) {
+ x1 = x2 + border;
+ x2 = x2 + border + width;
+ center_x = x1 + (x2 - x1) / 2;
+ c += snprintf(vkey_buf + c, MAX_BUF_SIZE - c,
+ "%s:%d:%d:%d:%d:%d\n",
+ VKEY_VER_CODE, pdata->keycodes[i],
+ center_x, center_y, width, height);
+ }
+
+ vkey_buf[c] = '\0';
+
+ name = devm_kzalloc(&pdev->dev, sizeof(*name) * MAX_BUF_SIZE,
+ GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ snprintf(name, MAX_BUF_SIZE,
+ "virtualkeys.%s", pdata->name);
+ vkey_obj_attr.attr.name = name;
+
+ vkey_obj = kobject_create_and_add("board_properties", NULL);
+ if (!vkey_obj) {
+ dev_err(&pdev->dev, "unable to create kobject\n");
+ return -ENOMEM;
+ }
+
+ ret = sysfs_create_group(vkey_obj, &vkey_grp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create attributes\n");
+ goto destroy_kobj;
+ }
+ return 0;
+
+destroy_kobj:
+ kobject_put(vkey_obj);
+
+ return ret;
+}
+
+static int vkeys_remove(struct platform_device *pdev)
+{
+ sysfs_remove_group(vkey_obj, &vkey_grp);
+ kobject_put(vkey_obj);
+
+ return 0;
+}
+
+static const struct of_device_id vkey_match_table[] = {
+ { .compatible = "qcom,gen-vkeys",},
+ { },
+};
+
+static struct platform_driver vkeys_driver = {
+ .probe = vkeys_probe,
+ .remove = vkeys_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "gen_vkeys",
+ .of_match_table = vkey_match_table,
+ },
+};
+
+module_platform_driver(vkeys_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 240b16f..5907fdd 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -778,8 +778,10 @@
int error;
/* We need gpio pins to suspend/resume */
- if (!ts->gpiod_int || !ts->gpiod_rst)
+ if (!ts->gpiod_int || !ts->gpiod_rst) {
+ disable_irq(client->irq);
return 0;
+ }
wait_for_completion(&ts->firmware_loading_complete);
@@ -819,8 +821,10 @@
struct goodix_ts_data *ts = i2c_get_clientdata(client);
int error;
- if (!ts->gpiod_int || !ts->gpiod_rst)
+ if (!ts->gpiod_int || !ts->gpiod_rst) {
+ enable_irq(client->irq);
return 0;
+ }
/*
* Exit sleep mode by outputting HIGH level to INT pin
diff --git a/drivers/input/touchscreen/hxchipset/HX83100_Amber_0901_030B.i b/drivers/input/touchscreen/hxchipset/HX83100_Amber_0901_030B.i
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/HX83100_Amber_0901_030B.i
diff --git a/drivers/input/touchscreen/hxchipset/HX_CRC_124.i b/drivers/input/touchscreen/hxchipset/HX_CRC_124.i
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/HX_CRC_124.i
diff --git a/drivers/input/touchscreen/hxchipset/HX_CRC_128.i b/drivers/input/touchscreen/hxchipset/HX_CRC_128.i
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/HX_CRC_128.i
diff --git a/drivers/input/touchscreen/hxchipset/HX_CRC_60.i b/drivers/input/touchscreen/hxchipset/HX_CRC_60.i
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/HX_CRC_60.i
diff --git a/drivers/input/touchscreen/hxchipset/HX_CRC_64.i b/drivers/input/touchscreen/hxchipset/HX_CRC_64.i
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/HX_CRC_64.i
diff --git a/drivers/input/touchscreen/hxchipset/Kconfig b/drivers/input/touchscreen/hxchipset/Kconfig
new file mode 100644
index 0000000..ebf3aa4
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/Kconfig
@@ -0,0 +1,21 @@
+#
+# Himax Touchscreen driver configuration
+#
+
+config TOUCHSCREEN_HIMAX_I2C
+ tristate "HIMAX chipset i2c touchscreen"
+ depends on TOUCHSCREEN_HIMAX_CHIPSET
+ help
+ This enables support for HIMAX CHIPSET over I2C based touchscreens.
+
+config TOUCHSCREEN_HIMAX_DEBUG
+ tristate "HIMAX debug function"
+ depends on TOUCHSCREEN_HIMAX_I2C
+ help
+ This enables support for HIMAX debug function.
+
+config HMX_DB
+ tristate "HIMAX driver test over Dragon Board"
+ depends on TOUCHSCREEN_HIMAX_I2C
+ help
+ This enables support for HIMAX driver test over Dragon Board.
diff --git a/drivers/input/touchscreen/hxchipset/Makefile b/drivers/input/touchscreen/hxchipset/Makefile
new file mode 100644
index 0000000..509d491
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/Makefile
@@ -0,0 +1,3 @@
+# Makefile for the Himax touchscreen drivers.
+
+obj-$(CONFIG_TOUCHSCREEN_HIMAX_I2C) += himax_platform.o himax_ic.o himax_common.o himax_debug.o
diff --git a/drivers/input/touchscreen/hxchipset/himax_common.c b/drivers/input/touchscreen/hxchipset/himax_common.c
new file mode 100644
index 0000000..417b0c0
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_common.c
@@ -0,0 +1,1936 @@
+/* Himax Android Driver Sample Code for Himax chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include "himax_common.h"
+#include "himax_ic.h"
+
+#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F
+#define TS_WAKE_LOCK_TIMEOUT (2 * HZ)
+#define FRAME_COUNT 5
+
+#if defined(HX_AUTO_UPDATE_FW)
+ static unsigned char i_CTPM_FW[]=
+ {
+ #include "HX83100_Amber_0901_030B.i"
+ };
+#endif
+
+#ifdef HX_ESD_WORKAROUND
+ extern void HX_report_ESD_event(void);
+ unsigned char ESD_00_counter = 0;
+ unsigned char ESD_00_Flag = 0;
+#endif
+
+//static int tpd_keys_local[HX_KEY_MAX_COUNT] = HX_KEY_ARRAY; // for Virtual key array
+
+struct himax_ts_data *private_ts;
+struct himax_ic_data* ic_data;
+
+static int HX_TOUCH_INFO_POINT_CNT;
+
+#ifdef HX_AUTO_UPDATE_FW
+extern unsigned long FW_VER_MAJ_FLASH_ADDR;
+extern unsigned long FW_VER_MIN_FLASH_ADDR;
+extern unsigned long CFG_VER_MAJ_FLASH_ADDR;
+extern unsigned long CFG_VER_MIN_FLASH_ADDR;
+#endif
+extern unsigned long FW_VER_MAJ_FLASH_LENG;
+extern unsigned long FW_VER_MIN_FLASH_LENG;
+extern unsigned long CFG_VER_MAJ_FLASH_LENG;
+extern unsigned long CFG_VER_MIN_FLASH_LENG;
+extern unsigned char IC_TYPE;
+extern unsigned char IC_CHECKSUM;
+
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+extern int himax_touch_proc_init(void);
+extern void himax_touch_proc_deinit(void);
+//PROC-START
+#ifdef HX_TP_PROC_FLASH_DUMP
+extern void himax_ts_flash_func(void);
+extern void setFlashBuffer(void);
+extern bool getFlashDumpGoing(void);
+extern uint8_t getSysOperation(void);
+extern void setSysOperation(uint8_t operation);
+#endif
+
+#ifdef HX_TP_PROC_HITOUCH
+extern bool hitouch_is_connect;
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+ extern int touch_monitor_stop_flag;
+ extern int touch_monitor_stop_limit;
+ extern void himax_ts_diag_func(void);
+ extern int16_t *getMutualBuffer(void);
+ extern int16_t *getMutualNewBuffer(void);
+ extern int16_t *getMutualOldBuffer(void);
+ extern int16_t *getSelfBuffer(void);
+ extern uint8_t getXChannel(void);
+ extern uint8_t getYChannel(void);
+ extern uint8_t getDiagCommand(void);
+ extern void setXChannel(uint8_t x);
+ extern void setYChannel(uint8_t y);
+ extern void setMutualBuffer(void);
+ extern void setMutualNewBuffer(void);
+ extern void setMutualOldBuffer(void);
+ extern uint8_t coordinate_dump_enable;
+ extern struct file *coordinate_fn;
+ extern uint8_t diag_coor[128];
+#ifdef HX_TP_PROC_2T2R
+ extern int16_t *getMutualBuffer_2(void);
+ extern uint8_t getXChannel_2(void);
+ extern uint8_t getYChannel_2(void);
+ extern void setXChannel_2(uint8_t x);
+ extern void setYChannel_2(uint8_t y);
+ extern void setMutualBuffer_2(void);
+#endif
+#endif
+//PROC-END
+#endif
+
+extern int himax_parse_dt(struct himax_ts_data *ts,
+ struct himax_i2c_platform_data *pdata);
+extern int himax_ts_pinctrl_init(struct himax_ts_data *ts);
+
+static uint8_t vk_press;
+static uint8_t AA_press;
+static uint8_t EN_NoiseFilter;
+static uint8_t Last_EN_NoiseFilter;
+static int hx_point_num; // for himax_ts_work_func use
+static int p_point_num = 0xFFFF;
+static int tpd_key;
+static int tpd_key_old;
+static int probe_fail_flag;
+static bool config_load;
+static struct himax_config *config_selected;
+
+//static int iref_number = 11;
+//static bool iref_found = false;
+
+#ifdef HX_USB_DETECT2
+extern bool USB_Flag;
+#endif
+
+#if defined(CONFIG_FB)
+int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void himax_ts_early_suspend(struct early_suspend *h);
+static void himax_ts_late_resume(struct early_suspend *h);
+#endif
+
+int himax_input_register(struct himax_ts_data *ts)
+{
+ int ret;
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ E("%s: Failed to allocate input device\n", __func__);
+ return ret;
+ }
+ ts->input_dev->name = "himax-touchscreen";
+
+ set_bit(EV_SYN, ts->input_dev->evbit);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+ set_bit(EV_KEY, ts->input_dev->evbit);
+
+ set_bit(KEY_BACK, ts->input_dev->keybit);
+ set_bit(KEY_HOME, ts->input_dev->keybit);
+ set_bit(KEY_MENU, ts->input_dev->keybit);
+ set_bit(KEY_SEARCH, ts->input_dev->keybit);
+#if defined(HX_SMART_WAKEUP)
+ set_bit(KEY_POWER, ts->input_dev->keybit);
+ set_bit(KEY_CUST_01, ts->input_dev->keybit);
+ set_bit(KEY_CUST_02, ts->input_dev->keybit);
+ set_bit(KEY_CUST_03, ts->input_dev->keybit);
+ set_bit(KEY_CUST_04, ts->input_dev->keybit);
+ set_bit(KEY_CUST_05, ts->input_dev->keybit);
+ set_bit(KEY_CUST_06, ts->input_dev->keybit);
+ set_bit(KEY_CUST_07, ts->input_dev->keybit);
+ set_bit(KEY_CUST_08, ts->input_dev->keybit);
+ set_bit(KEY_CUST_09, ts->input_dev->keybit);
+ set_bit(KEY_CUST_10, ts->input_dev->keybit);
+ set_bit(KEY_CUST_11, ts->input_dev->keybit);
+ set_bit(KEY_CUST_12, ts->input_dev->keybit);
+ set_bit(KEY_CUST_13, ts->input_dev->keybit);
+ set_bit(KEY_CUST_14, ts->input_dev->keybit);
+ set_bit(KEY_CUST_15, ts->input_dev->keybit);
+#endif
+ set_bit(BTN_TOUCH, ts->input_dev->keybit);
+
+ set_bit(KEY_F10, ts->input_dev->keybit);
+
+ set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+
+ if (ts->protocol_type == PROTOCOL_TYPE_A) {
+ //ts->input_dev->mtsize = ts->nFinger_support;
+ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID,
+ 0, 3, 0, 0);
+ } else {/* PROTOCOL_TYPE_B */
+ set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
+ input_mt_init_slots(ts->input_dev, ts->nFinger_support,0);
+ }
+
+ I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n",
+ ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max);
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE,ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,ts->pdata->abs_width_min, ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0);
+
+// input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->pdata->abs_pressure_max << 16) | ts->pdata->abs_width_max), 0, 0);
+// input_set_abs_params(ts->input_dev, ABS_MT_POSITION, 0, (BIT(31) | (ts->pdata->abs_x_max << 16) | ts->pdata->abs_y_max), 0, 0);
+
+ return input_register_device(ts->input_dev);
+}
+
+static void calcDataSize(uint8_t finger_num)
+{
+ struct himax_ts_data *ts_data = private_ts;
+ ts_data->coord_data_size = 4 * finger_num;
+ ts_data->area_data_size = ((finger_num / 4) + (finger_num % 4 ? 1 : 0)) * 4;
+ ts_data->raw_data_frame_size = 128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1;
+ ts_data->raw_data_nframes = ((uint32_t)ts_data->x_channel * ts_data->y_channel +
+ ts_data->x_channel + ts_data->y_channel) / ts_data->raw_data_frame_size +
+ (((uint32_t)ts_data->x_channel * ts_data->y_channel +
+ ts_data->x_channel + ts_data->y_channel) % ts_data->raw_data_frame_size)? 1 : 0;
+ I("%s: coord_data_size: %d, area_data_size:%d, raw_data_frame_size:%d, raw_data_nframes:%d", __func__, ts_data->coord_data_size, ts_data->area_data_size, ts_data->raw_data_frame_size, ts_data->raw_data_nframes);
+}
+
+static void calculate_point_number(void)
+{
+ HX_TOUCH_INFO_POINT_CNT = ic_data->HX_MAX_PT * 4 ;
+
+ if ( (ic_data->HX_MAX_PT % 4) == 0)
+ HX_TOUCH_INFO_POINT_CNT += (ic_data->HX_MAX_PT / 4) * 4 ;
+ else
+ HX_TOUCH_INFO_POINT_CNT += ((ic_data->HX_MAX_PT / 4) +1) * 4 ;
+}
+
+#if 0
+static int himax_read_Sensor_ID(struct i2c_client *client)
+{
+ uint8_t val_high[1], val_low[1], ID0=0, ID1=0;
+ char data[3];
+ const int normalRetry = 10;
+ int sensor_id;
+
+ data[0] = 0x56; data[1] = 0x02; data[2] = 0x02;/*ID pin PULL High*/
+ i2c_himax_master_write(client, &data[0],3,normalRetry);
+ usleep_range(1000, 2000);
+
+ //read id pin high
+ i2c_himax_read(client, 0x57, val_high, 1, normalRetry);
+
+ data[0] = 0x56; data[1] = 0x01; data[2] = 0x01;/*ID pin PULL Low*/
+ i2c_himax_master_write(client, &data[0],3,normalRetry);
+ usleep_range(1000, 2000);
+
+ //read id pin low
+ i2c_himax_read(client, 0x57, val_low, 1, normalRetry);
+
+ if((val_high[0] & 0x01) ==0)
+ ID0=0x02;/*GND*/
+ else if((val_low[0] & 0x01) ==0)
+ ID0=0x01;/*Floating*/
+ else
+ ID0=0x04;/*VCC*/
+
+ if((val_high[0] & 0x02) ==0)
+ ID1=0x02;/*GND*/
+ else if((val_low[0] & 0x02) ==0)
+ ID1=0x01;/*Floating*/
+ else
+ ID1=0x04;/*VCC*/
+ if((ID0==0x04)&&(ID1!=0x04))
+ {
+ data[0] = 0x56; data[1] = 0x02; data[2] = 0x01;/*ID pin PULL High,Low*/
+ i2c_himax_master_write(client, &data[0],3,normalRetry);
+ usleep_range(1000, 2000);
+
+ }
+ else if((ID0!=0x04)&&(ID1==0x04))
+ {
+ data[0] = 0x56; data[1] = 0x01; data[2] = 0x02;/*ID pin PULL Low,High*/
+ i2c_himax_master_write(client, &data[0],3,normalRetry);
+ usleep_range(1000, 2000);
+
+ }
+ else if((ID0==0x04)&&(ID1==0x04))
+ {
+ data[0] = 0x56; data[1] = 0x02; data[2] = 0x02;/*ID pin PULL High,High*/
+ i2c_himax_master_write(client, &data[0],3,normalRetry);
+ usleep_range(1000, 2000);
+
+ }
+ sensor_id=(ID1<<4)|ID0;
+
+ data[0] = 0xE4; data[1] = sensor_id;
+ i2c_himax_master_write(client, &data[0],2,normalRetry);/*Write to MCU*/
+ usleep_range(1000, 2000);
+
+ return sensor_id;
+
+}
+#endif
+static void himax_power_on_initCMD(struct i2c_client *client)
+{
+ I("%s:\n", __func__);
+
+ himax_touch_information(client);
+
+ //himax_sense_on(private_ts->client, 0x01);//1=Flash, 0=SRAM
+}
+
+#ifdef HX_AUTO_UPDATE_FW
+static int i_update_FW(void)
+{
+ int upgrade_times = 0;
+ unsigned char* ImageBuffer = i_CTPM_FW;
+ int fullFileLength = sizeof(i_CTPM_FW);
+ int i_FW_VER = 0, i_CFG_VER = 0;
+ uint8_t ret = -1, result = 0;
+// uint8_t tmp_addr[4];
+// uint8_t tmp_data[4];
+ int CRC_from_FW = 0;
+ int CRC_Check_result = 0;
+
+ i_FW_VER = i_CTPM_FW[FW_VER_MAJ_FLASH_ADDR]<<8 |i_CTPM_FW[FW_VER_MIN_FLASH_ADDR];
+ i_CFG_VER = i_CTPM_FW[CFG_VER_MAJ_FLASH_ADDR]<<8 |i_CTPM_FW[CFG_VER_MIN_FLASH_ADDR];
+
+ I("%s: i_fullFileLength = %d\n", __func__,fullFileLength);
+
+ himax_sense_off(private_ts->client);
+ msleep(500);
+
+ CRC_from_FW = himax_check_CRC(private_ts->client,fw_image_64k);
+ CRC_Check_result = Calculate_CRC_with_AP(ImageBuffer, CRC_from_FW,fw_image_64k);
+ I("%s: Check sum result = %d\n", __func__,CRC_Check_result);
+ //I("%s: ic_data->vendor_fw_ver = %X, i_FW_VER = %X,\n", __func__,ic_data->vendor_fw_ver, i_FW_VER);
+ //I("%s: ic_data->vendor_config_ver = %X, i_CFG_VER = %X,\n", __func__,ic_data->vendor_config_ver, i_CFG_VER);
+
+ if ((CRC_Check_result == 0)|| ( ic_data->vendor_fw_ver < i_FW_VER ) || ( ic_data->vendor_config_ver < i_CFG_VER ))
+ {
+ himax_int_enable(private_ts->client->irq,0);
+update_retry:
+ if(fullFileLength == FW_SIZE_60k){
+ ret = fts_ctpm_fw_upgrade_with_sys_fs_60k(private_ts->client,ImageBuffer,fullFileLength,false);
+ }else if (fullFileLength == FW_SIZE_64k){
+ ret = fts_ctpm_fw_upgrade_with_sys_fs_64k(private_ts->client,ImageBuffer,fullFileLength,false);
+ }else if (fullFileLength == FW_SIZE_124k){
+ ret = fts_ctpm_fw_upgrade_with_sys_fs_124k(private_ts->client,ImageBuffer,fullFileLength,false);
+ }else if (fullFileLength == FW_SIZE_128k){
+ ret = fts_ctpm_fw_upgrade_with_sys_fs_128k(private_ts->client,ImageBuffer,fullFileLength,false);
+ }
+ if(ret == 0){
+ upgrade_times++;
+ E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times);
+ if(upgrade_times < 3)
+ goto update_retry;
+ else
+ {
+ himax_sense_on(private_ts->client, 0x01);
+ msleep(120);
+#ifdef HX_ESD_WORKAROUND
+ HX_ESD_RESET_ACTIVATE = 1;
+#endif
+ result = -1;//upgrade fail
+ }
+ }
+ else if(ret == 1){
+/*
+ // 1. Set DDREG_Req = 1 (0x9000_0020 = 0x0000_0001) (Lock register R/W from driver)
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01;
+ himax_register_write(private_ts->client, tmp_addr, 1, tmp_data);
+
+ // 2. Write driver initial code condition
+ // write value from AHB I2C : 0x8001_C603 = 0x000000FF
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x01; tmp_addr[1] = 0xC6; tmp_addr[0] = 0x03;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0xFF;
+ himax_register_write(private_ts->client, tmp_addr, 1, tmp_data);
+
+ // 1. Set DDREG_Req = 0 (0x9000_0020 = 0x0000_0001) (Lock register R/W from driver)
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_register_write(private_ts->client, tmp_addr, 1, tmp_data);
+*/
+ himax_sense_on(private_ts->client, 0x01);
+ msleep(120);
+#ifdef HX_ESD_WORKAROUND
+ HX_ESD_RESET_ACTIVATE = 1;
+#endif
+
+ ic_data->vendor_fw_ver = i_FW_VER;
+ ic_data->vendor_config_ver = i_CFG_VER;
+ result = 1;//upgrade success
+ I("%s: TP upgrade OK\n", __func__);
+ }
+
+ himax_int_enable(private_ts->client->irq,1);
+ return result;
+ }
+ else
+ {
+ himax_sense_on(private_ts->client, 0x01);
+ return 0;//NO upgrade
+ }
+}
+#endif
+
+#ifdef HX_RST_PIN_FUNC
+void himax_HW_reset(uint8_t loadconfig,uint8_t int_off)
+{
+ struct himax_ts_data *ts = private_ts;
+ int ret = 0;
+
+ return;
+ if (ts->rst_gpio) {
+ if(int_off)
+ {
+ if (ts->use_irq)
+ himax_int_enable(private_ts->client->irq,0);
+ else {
+ hrtimer_cancel(&ts->timer);
+ ret = cancel_work_sync(&ts->work);
+ }
+ }
+
+ I("%s: Now reset the Touch chip.\n", __func__);
+
+ himax_rst_gpio_set(ts->rst_gpio, 0);
+ msleep(20);
+ himax_rst_gpio_set(ts->rst_gpio, 1);
+ msleep(20);
+
+ if(loadconfig)
+ himax_loadSensorConfig(private_ts->client,private_ts->pdata);
+
+ if(int_off)
+ {
+ if (ts->use_irq)
+ himax_int_enable(private_ts->client->irq,1);
+ else
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+ }
+}
+#endif
+
+int himax_loadSensorConfig(struct i2c_client *client, struct himax_i2c_platform_data *pdata)
+{
+
+ if (!client) {
+ E("%s: Necessary parameters client are null!\n", __func__);
+ return -EINVAL;
+ }
+
+ if(config_load == false)
+ {
+ config_selected = kzalloc(sizeof(*config_selected), GFP_KERNEL);
+ if (config_selected == NULL) {
+ E("%s: alloc config_selected fail!\n", __func__);
+ return -ENOMEM;
+ }
+ }
+
+ himax_power_on_initCMD(client);
+
+ himax_int_enable(client->irq,0);
+ himax_read_FW_ver(client);
+#ifdef HX_RST_PIN_FUNC
+ himax_HW_reset(true,false);
+#endif
+ himax_int_enable(client->irq,1);
+ I("FW_VER : %X \n",ic_data->vendor_fw_ver);
+
+ ic_data->vendor_sensor_id=0x2602;
+ I("sensor_id=%x.\n",ic_data->vendor_sensor_id);
+
+ himax_sense_on(private_ts->client, 0x01);//1=Flash, 0=SRAM
+ msleep(120);
+#ifdef HX_ESD_WORKAROUND
+ HX_ESD_RESET_ACTIVATE = 1;
+#endif
+ I("%s: initialization complete\n", __func__);
+
+ return 1;
+}
+
+#ifdef HX_ESD_WORKAROUND
+void ESD_HW_REST(void)
+{
+ I("START_Himax TP: ESD - Reset\n");
+
+ HX_report_ESD_event();
+ ESD_00_counter = 0;
+ ESD_00_Flag = 0;
+ /*************************************/
+ if (private_ts->protocol_type == PROTOCOL_TYPE_A)
+ input_mt_sync(private_ts->input_dev);
+ input_report_key(private_ts->input_dev, BTN_TOUCH, 0);
+ input_sync(private_ts->input_dev);
+ /*************************************/
+
+ I("END_Himax TP: ESD - Reset\n");
+}
+#endif
+#ifdef HX_HIGH_SENSE
+void himax_set_HSEN_func(struct i2c_client *client,uint8_t HSEN_enable)
+{
+ uint8_t tmp_data[4];
+
+ if(HSEN_enable)
+ {
+ I(" %s in", __func__);
+ HSEN_bit_retry:
+ himax_set_HSEN_enable(client,HSEN_enable);
+ msleep(20);
+ himax_get_HSEN_enable(client,tmp_data);
+ I("%s: Read HSEN bit data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", __func__
+ ,tmp_data[0],tmp_data[1],tmp_data[2],tmp_data[3]);
+ if(tmp_data[0]!= 0x01)
+ {
+ I("%s: retry HSEN bit write data[0]=%x \n",__func__,tmp_data[0]);
+ goto HSEN_bit_retry;
+ }
+ }
+}
+
+static void himax_HSEN_func(struct work_struct *work)
+{
+ struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
+ hsen_work.work);
+
+ himax_set_HSEN_func(ts->client, ts->HSEN_enable);
+}
+
+#endif
+
+#ifdef HX_SMART_WAKEUP
+#ifdef HX_GESTURE_TRACK
+static void gest_pt_log_coordinate(int rx, int tx)
+{
+ //driver report x y with range 0 - 255 , we scale it up to x/y pixel
+ gest_pt_x[gest_pt_cnt] = rx*(ic_data->HX_X_RES)/255;
+ gest_pt_y[gest_pt_cnt] = tx*(ic_data->HX_Y_RES)/255;
+}
+#endif
+static int himax_parse_wake_event(struct himax_ts_data *ts)
+{
+ uint8_t buf[64];
+ unsigned char check_sum_cal = 0;
+#ifdef HX_GESTURE_TRACK
+ int tmp_max_x=0x00,tmp_min_x=0xFFFF,tmp_max_y=0x00,tmp_min_y=0xFFFF;
+ int gest_len;
+#endif
+ int i=0, check_FC = 0, gesture_flag = 0;
+
+ himax_burst_enable(ts->client, 0);
+ himax_read_event_stack(ts->client,buf,56);
+
+ for(i=0;i<GEST_PTLG_ID_LEN;i++)
+ {
+ if (check_FC==0)
+ {
+ if((buf[0]!=0x00)&&((buf[0]<=0x0F)||(buf[0]==0x80)))
+ {
+ check_FC = 1;
+ gesture_flag = buf[i];
+ }
+ else
+ {
+ check_FC = 0;
+ I("ID START at %x , value = %x skip the event\n", i, buf[i]);
+ break;
+ }
+ }
+ else
+ {
+ if(buf[i]!=gesture_flag)
+ {
+ check_FC = 0;
+ I("ID NOT the same %x != %x So STOP parse event\n", buf[i], gesture_flag);
+ break;
+ }
+ }
+
+ I("0x%2.2X ", buf[i]);
+ if (i % 8 == 7)
+ I("\n");
+ }
+ I("Himax gesture_flag= %x\n",gesture_flag );
+ I("Himax check_FC is %d\n", check_FC);
+
+ if (check_FC == 0)
+ return 0;
+ if(buf[GEST_PTLG_ID_LEN] != GEST_PTLG_HDR_ID1 ||
+ buf[GEST_PTLG_ID_LEN+1] != GEST_PTLG_HDR_ID2)
+ return 0;
+ for(i=0;i<(GEST_PTLG_ID_LEN+GEST_PTLG_HDR_LEN);i++)
+ {
+ I("P[%x]=0x%2.2X \n", i, buf[i]);
+ I("checksum=0x%2.2X \n", check_sum_cal);
+ check_sum_cal += buf[i];
+ }
+ if ((check_sum_cal != 0x00) )
+ {
+ I(" %s : check_sum_cal: 0x%02X\n",__func__ ,check_sum_cal);
+ return 0;
+ }
+#ifdef HX_GESTURE_TRACK
+ if(buf[GEST_PTLG_ID_LEN] == GEST_PTLG_HDR_ID1 &&
+ buf[GEST_PTLG_ID_LEN+1] == GEST_PTLG_HDR_ID2)
+ {
+ gest_len = buf[GEST_PTLG_ID_LEN+2];
+
+ I("gest_len = %d ",gest_len);
+
+ i = 0;
+ gest_pt_cnt = 0;
+ I("gest doornidate start \n %s",__func__);
+ while(i<(gest_len+1)/2)
+ {
+ gest_pt_log_coordinate(buf[GEST_PTLG_ID_LEN+4+i*2],buf[GEST_PTLG_ID_LEN+4+i*2+1]);
+ i++;
+
+ I("gest_pt_x[%d]=%d \n",gest_pt_cnt,gest_pt_x[gest_pt_cnt]);
+ I("gest_pt_y[%d]=%d \n",gest_pt_cnt,gest_pt_y[gest_pt_cnt]);
+
+ gest_pt_cnt +=1;
+ }
+ if(gest_pt_cnt)
+ {
+ for(i=0; i<gest_pt_cnt; i++)
+ {
+ if(tmp_max_x<gest_pt_x[i])
+ tmp_max_x=gest_pt_x[i];
+ if(tmp_min_x>gest_pt_x[i])
+ tmp_min_x=gest_pt_x[i];
+ if(tmp_max_y<gest_pt_y[i])
+ tmp_max_y=gest_pt_y[i];
+ if(tmp_min_y>gest_pt_y[i])
+ tmp_min_y=gest_pt_y[i];
+ }
+ I("gest_point x_min= %d, x_max= %d, y_min= %d, y_max= %d\n",tmp_min_x,tmp_max_x,tmp_min_y,tmp_max_y);
+ gest_start_x=gest_pt_x[0];
+ gn_gesture_coor[0] = gest_start_x;
+ gest_start_y=gest_pt_y[0];
+ gn_gesture_coor[1] = gest_start_y;
+ gest_end_x=gest_pt_x[gest_pt_cnt-1];
+ gn_gesture_coor[2] = gest_end_x;
+ gest_end_y=gest_pt_y[gest_pt_cnt-1];
+ gn_gesture_coor[3] = gest_end_y;
+ gest_width = tmp_max_x - tmp_min_x;
+ gn_gesture_coor[4] = gest_width;
+ gest_height = tmp_max_y - tmp_min_y;
+ gn_gesture_coor[5] = gest_height;
+ gest_mid_x = (tmp_max_x + tmp_min_x)/2;
+ gn_gesture_coor[6] = gest_mid_x;
+ gest_mid_y = (tmp_max_y + tmp_min_y)/2;
+ gn_gesture_coor[7] = gest_mid_y;
+ gn_gesture_coor[8] = gest_mid_x;//gest_up_x
+ gn_gesture_coor[9] = gest_mid_y-gest_height/2;//gest_up_y
+ gn_gesture_coor[10] = gest_mid_x;//gest_down_x
+ gn_gesture_coor[11] = gest_mid_y+gest_height/2; //gest_down_y
+ gn_gesture_coor[12] = gest_mid_x-gest_width/2; //gest_left_x
+ gn_gesture_coor[13] = gest_mid_y; //gest_left_y
+ gn_gesture_coor[14] = gest_mid_x+gest_width/2; //gest_right_x
+ gn_gesture_coor[15] = gest_mid_y; //gest_right_y
+
+ }
+
+ }
+#endif
+ if(gesture_flag != 0x80)
+ {
+ if(!ts->gesture_cust_en[gesture_flag])
+ {
+ I("%s NOT report customer key \n ",__func__);
+ return 0;//NOT report customer key
+ }
+ }
+ else
+ {
+ if(!ts->gesture_cust_en[0])
+ {
+ I("%s NOT report report double click \n",__func__);
+ return 0;//NOT report power key
+ }
+ }
+
+ if(gesture_flag == 0x80)
+ return EV_GESTURE_PWR;
+ else
+ return gesture_flag;
+}
+
+void himax_wake_check_func(void)
+{
+ int ret_event = 0, KEY_EVENT = 0;
+
+ ret_event = himax_parse_wake_event(private_ts);
+ switch (ret_event) {
+ case EV_GESTURE_PWR:
+ KEY_EVENT = KEY_POWER;
+ break;
+ case EV_GESTURE_01:
+ KEY_EVENT = KEY_CUST_01;
+ break;
+ case EV_GESTURE_02:
+ KEY_EVENT = KEY_CUST_02;
+ break;
+ case EV_GESTURE_03:
+ KEY_EVENT = KEY_CUST_03;
+ break;
+ case EV_GESTURE_04:
+ KEY_EVENT = KEY_CUST_04;
+ break;
+ case EV_GESTURE_05:
+ KEY_EVENT = KEY_CUST_05;
+ break;
+ case EV_GESTURE_06:
+ KEY_EVENT = KEY_CUST_06;
+ break;
+ case EV_GESTURE_07:
+ KEY_EVENT = KEY_CUST_07;
+ break;
+ case EV_GESTURE_08:
+ KEY_EVENT = KEY_CUST_08;
+ break;
+ case EV_GESTURE_09:
+ KEY_EVENT = KEY_CUST_09;
+ break;
+ case EV_GESTURE_10:
+ KEY_EVENT = KEY_CUST_10;
+ break;
+ case EV_GESTURE_11:
+ KEY_EVENT = KEY_CUST_11;
+ break;
+ case EV_GESTURE_12:
+ KEY_EVENT = KEY_CUST_12;
+ break;
+ case EV_GESTURE_13:
+ KEY_EVENT = KEY_CUST_13;
+ break;
+ case EV_GESTURE_14:
+ KEY_EVENT = KEY_CUST_14;
+ break;
+ case EV_GESTURE_15:
+ KEY_EVENT = KEY_CUST_15;
+ break;
+ }
+ if(ret_event)
+ {
+ I(" %s SMART WAKEUP KEY event %x press\n",__func__,KEY_EVENT);
+ input_report_key(private_ts->input_dev, KEY_EVENT, 1);
+ input_sync(private_ts->input_dev);
+ //msleep(100);
+ I(" %s SMART WAKEUP KEY event %x release\n",__func__,KEY_EVENT);
+ input_report_key(private_ts->input_dev, KEY_EVENT, 0);
+ input_sync(private_ts->input_dev);
+ FAKE_POWER_KEY_SEND=true;
+#ifdef HX_GESTURE_TRACK
+ I("gest_start_x= %d, gest_start_y= %d, gest_end_x= %d, gest_end_y= %d\n",gest_start_x,gest_start_y,
+ gest_end_x,gest_end_y);
+ I("gest_width= %d, gest_height= %d, gest_mid_x= %d, gest_mid_y= %d\n",gest_width,gest_height,
+ gest_mid_x,gest_mid_y);
+ I("gest_up_x= %d, gest_up_y= %d, gest_down_x= %d, gest_down_y= %d\n",gn_gesture_coor[8],gn_gesture_coor[9],
+ gn_gesture_coor[10],gn_gesture_coor[11]);
+ I("gest_left_x= %d, gest_left_y= %d, gest_right_x= %d, gest_right_y= %d\n",gn_gesture_coor[12],gn_gesture_coor[13],
+ gn_gesture_coor[14],gn_gesture_coor[15]);
+#endif
+ }
+}
+
+#endif
+static void himax_ts_button_func(int tp_key_index,struct himax_ts_data *ts)
+{
+ uint16_t x_position = 0, y_position = 0;
+if ( tp_key_index != 0x00)
+ {
+ I("virtual key index =%x\n",tp_key_index);
+ if ( tp_key_index == 0x01) {
+ vk_press = 1;
+ I("back key pressed\n");
+ if (ts->pdata->virtual_key)
+ {
+ if (ts->button[0].index) {
+ x_position = (ts->button[0].x_range_min + ts->button[0].x_range_max) / 2;
+ y_position = (ts->button[0].y_range_min + ts->button[0].y_range_max) / 2;
+ }
+ if (ts->protocol_type == PROTOCOL_TYPE_A) {
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ x_position);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ y_position);
+ input_mt_sync(ts->input_dev);
+ } else if (ts->protocol_type == PROTOCOL_TYPE_B) {
+ input_mt_slot(ts->input_dev, 0);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
+ 1);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ x_position);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ y_position);
+ }
+ }
+ else
+ input_report_key(ts->input_dev, KEY_BACK, 1);
+ }
+ else if ( tp_key_index == 0x02) {
+ vk_press = 1;
+ I("home key pressed\n");
+ if (ts->pdata->virtual_key)
+ {
+ if (ts->button[1].index) {
+ x_position = (ts->button[1].x_range_min + ts->button[1].x_range_max) / 2;
+ y_position = (ts->button[1].y_range_min + ts->button[1].y_range_max) / 2;
+ }
+ if (ts->protocol_type == PROTOCOL_TYPE_A) {
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ x_position);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ y_position);
+ input_mt_sync(ts->input_dev);
+ } else if (ts->protocol_type == PROTOCOL_TYPE_B) {
+ input_mt_slot(ts->input_dev, 0);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
+ 1);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ x_position);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ y_position);
+ }
+ }
+ else
+ input_report_key(ts->input_dev, KEY_HOME, 1);
+ }
+ else if ( tp_key_index == 0x04) {
+ vk_press = 1;
+ I("APP_switch key pressed\n");
+ if (ts->pdata->virtual_key)
+ {
+ if (ts->button[2].index) {
+ x_position = (ts->button[2].x_range_min + ts->button[2].x_range_max) / 2;
+ y_position = (ts->button[2].y_range_min + ts->button[2].y_range_max) / 2;
+ }
+ if (ts->protocol_type == PROTOCOL_TYPE_A) {
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ x_position);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ y_position);
+ input_mt_sync(ts->input_dev);
+ } else if (ts->protocol_type == PROTOCOL_TYPE_B) {
+ input_mt_slot(ts->input_dev, 0);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
+ 1);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+ 100);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+ x_position);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+ y_position);
+ }
+ }
+ else
+ input_report_key(ts->input_dev, KEY_F10, 1);
+ }
+ input_sync(ts->input_dev);
+ }
+else/*tp_key_index =0x00*/
+ {
+ I("virtual key released\n");
+ vk_press = 0;
+ if (ts->protocol_type == PROTOCOL_TYPE_A) {
+ input_mt_sync(ts->input_dev);
+ }
+ else if (ts->protocol_type == PROTOCOL_TYPE_B) {
+ input_mt_slot(ts->input_dev, 0);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
+ }
+ input_report_key(ts->input_dev, KEY_BACK, 0);
+ input_report_key(ts->input_dev, KEY_HOME, 0);
+ input_report_key(ts->input_dev, KEY_F10, 0);
+ input_sync(ts->input_dev);
+ }
+}
+
+void himax_ts_work(struct himax_ts_data *ts)
+{
+ int ret = 0;
+ uint8_t finger_num, hw_reset_check[2];
+ uint8_t buf[128];
+ uint8_t finger_on = 0;
+ int32_t loop_i;
+ uint16_t check_sum_cal = 0;
+ int raw_cnt_max ;
+ int raw_cnt_rmd ;
+ int hx_touch_info_size;
+ uint8_t coordInfoSize = ts->coord_data_size + ts->area_data_size + 4;
+
+#ifdef HX_TP_PROC_DIAG
+ int16_t *mutual_data;
+ int16_t *self_data;
+ uint8_t diag_cmd;
+ int i;
+ int mul_num;
+ int self_num;
+ int RawDataLen = 0;
+ //coordinate dump start
+ char coordinate_char[15+(ic_data->HX_MAX_PT+5)*2*5+2];
+ //coordinate dump end
+#endif
+
+ memset(buf, 0x00, sizeof(buf));
+ memset(hw_reset_check, 0x00, sizeof(hw_reset_check));
+
+ raw_cnt_max = ic_data->HX_MAX_PT/4;
+ raw_cnt_rmd = ic_data->HX_MAX_PT%4;
+
+#if defined(HX_USB_DETECT2)
+ himax_cable_detect_func();
+#endif
+
+ if (raw_cnt_rmd != 0x00) //more than 4 fingers
+ {
+ RawDataLen = cal_data_len(raw_cnt_rmd, ic_data->HX_MAX_PT, raw_cnt_max);
+ hx_touch_info_size = (ic_data->HX_MAX_PT+raw_cnt_max+2)*4;
+ }
+ else //less than 4 fingers
+ {
+ RawDataLen = cal_data_len(raw_cnt_rmd, ic_data->HX_MAX_PT, raw_cnt_max);
+ hx_touch_info_size = (ic_data->HX_MAX_PT+raw_cnt_max+1)*4;
+ }
+
+#ifdef HX_TP_PROC_DIAG
+ diag_cmd = getDiagCommand();
+ if( diag_cmd ){
+ ret = read_event_stack(ts->client, buf, 128);
+ }
+ else{
+ if(touch_monitor_stop_flag != 0){
+ ret = read_event_stack(ts->client, buf, 128);
+ touch_monitor_stop_flag-- ;
+ }
+ else{
+ ret = read_event_stack(ts->client, buf, hx_touch_info_size);
+ }
+ }
+
+ if (!ret)
+#else
+ if(!read_event_stack(ts->client, buf, hx_touch_info_size))
+#endif
+ {
+ E("%s: can't read data from chip!\n", __func__);
+ goto err_workqueue_out;
+ }
+ post_read_event_stack(ts->client);
+#ifdef HX_ESD_WORKAROUND
+ for(i = 0; i < hx_touch_info_size; i++)
+ {
+ if(buf[i] == 0xED)/*case 1 ESD recovery flow*/
+ {
+ check_sum_cal = 1;
+
+ }else if(buf[i] == 0x00)
+ {
+ ESD_00_Flag = 1;
+ }
+ else
+ {
+ check_sum_cal = 0;
+ ESD_00_counter = 0;
+ ESD_00_Flag = 0;
+ i = hx_touch_info_size;
+ break;
+ }
+ }
+ if (ESD_00_Flag == 1){
+ ESD_00_counter ++;
+ }
+ if (ESD_00_counter > 1){
+ check_sum_cal = 2;
+ }
+
+ if (check_sum_cal == 2 && HX_ESD_RESET_ACTIVATE == 0)
+ {
+ I("[HIMAX TP MSG]: ESD event checked - ALL Zero.\n");
+ ESD_HW_REST();
+ return;
+ }
+
+ if (check_sum_cal == 1 && HX_ESD_RESET_ACTIVATE == 0)
+ {
+ I("[HIMAX TP MSG]: ESD event checked - ALL 0xED.\n");
+ ESD_HW_REST();
+ return;
+ }
+ else if (HX_ESD_RESET_ACTIVATE)
+ {
+#ifdef HX_SMART_WAKEUP
+ queue_delayed_work(ts->himax_smwp_wq, &ts->smwp_work, msecs_to_jiffies(50));
+#endif
+#ifdef HX_HIGH_SENSE
+ queue_delayed_work(ts->himax_hsen_wq, &ts->hsen_work, msecs_to_jiffies(50));
+#endif
+ HX_ESD_RESET_ACTIVATE = 0;/*drop 1st interrupts after chip reset*/
+ I("[HIMAX TP MSG]:%s: Back from reset, ready to serve.\n", __func__);
+ }
+#endif
+ for (loop_i = 0, check_sum_cal = 0; loop_i < hx_touch_info_size; loop_i++)
+ check_sum_cal += buf[loop_i];
+
+ if ((check_sum_cal % 0x100 != 0) )
+ {
+ I("[HIMAX TP MSG] checksum fail : check_sum_cal: 0x%02X\n", check_sum_cal);
+ return;
+ }
+
+ if (ts->debug_log_level & BIT(0)) {
+ I("%s: raw data:\n", __func__);
+ for (loop_i = 0; loop_i < hx_touch_info_size; loop_i++) {
+ I("P %d = 0x%2.2X ", loop_i, buf[loop_i]);
+ if (loop_i % 8 == 7)
+ I("\n");
+ }
+ }
+
+ //touch monitor raw data fetch
+#ifdef HX_TP_PROC_DIAG
+ diag_cmd = getDiagCommand();
+ if (diag_cmd >= 1 && diag_cmd <= 6)
+ {
+ //Check 124th byte CRC
+ if(!diag_check_sum(hx_touch_info_size, buf))
+ {
+ goto bypass_checksum_failed_packet;
+ }
+#ifdef HX_TP_PROC_2T2R
+ if(Is_2T2R && diag_cmd == 4)
+ {
+ mutual_data = getMutualBuffer_2();
+ self_data = getSelfBuffer();
+
+ // initiallize the block number of mutual and self
+ mul_num = getXChannel_2() * getYChannel_2();
+
+#ifdef HX_EN_SEL_BUTTON
+ self_num = getXChannel_2() + getYChannel_2() + ic_data->HX_BT_NUM;
+#else
+ self_num = getXChannel_2() + getYChannel_2();
+#endif
+ }
+ else
+#endif
+ {
+ mutual_data = getMutualBuffer();
+ self_data = getSelfBuffer();
+
+ // initiallize the block number of mutual and self
+ mul_num = getXChannel() * getYChannel();
+
+#ifdef HX_EN_SEL_BUTTON
+ self_num = getXChannel() + getYChannel() + ic_data->HX_BT_NUM;
+#else
+ self_num = getXChannel() + getYChannel();
+#endif
+ }
+
+ diag_parse_raw_data(hx_touch_info_size, RawDataLen, mul_num, self_num, buf, diag_cmd, mutual_data, self_data);
+
+ }
+ else if (diag_cmd == 7)
+ {
+ memcpy(&(diag_coor[0]), &buf[0], 128);
+ }
+ //coordinate dump start
+ if (coordinate_dump_enable == 1)
+ {
+ for(i=0; i<(15 + (ic_data->HX_MAX_PT+5)*2*5); i++)
+ {
+ coordinate_char[i] = 0x20;
+ }
+ coordinate_char[15 + (ic_data->HX_MAX_PT+5)*2*5] = 0xD;
+ coordinate_char[15 + (ic_data->HX_MAX_PT+5)*2*5 + 1] = 0xA;
+ }
+ //coordinate dump end
+bypass_checksum_failed_packet:
+#endif
+ EN_NoiseFilter = (buf[HX_TOUCH_INFO_POINT_CNT+2]>>3);
+ //I("EN_NoiseFilter=%d\n",EN_NoiseFilter);
+ EN_NoiseFilter = EN_NoiseFilter & 0x01;
+ //I("EN_NoiseFilter2=%d\n",EN_NoiseFilter);
+
+#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON)
+ tpd_key = (buf[HX_TOUCH_INFO_POINT_CNT+2]>>4);
+ if (tpd_key == 0x0F)/*All (VK+AA)leave*/
+ {
+ tpd_key = 0x00;
+ }
+ //I("[DEBUG] tpd_key: %x\r\n", tpd_key);
+#else
+ tpd_key = 0x00;
+#endif
+
+ p_point_num = hx_point_num;
+
+ if (buf[HX_TOUCH_INFO_POINT_CNT] == 0xff)
+ hx_point_num = 0;
+ else
+ hx_point_num= buf[HX_TOUCH_INFO_POINT_CNT] & 0x0f;
+
+ // Touch Point information
+ if (hx_point_num != 0 ) {
+ if(vk_press == 0x00)
+ {
+ uint16_t old_finger = ts->pre_finger_mask;
+ ts->pre_finger_mask = 0;
+ finger_num = buf[coordInfoSize - 4] & 0x0F;
+ finger_on = 1;
+ AA_press = 1;
+ for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) {
+ int base = loop_i * 4;
+ int x = buf[base] << 8 | buf[base + 1];
+ int y = (buf[base + 2] << 8 | buf[base + 3]);
+ int w = buf[(ts->nFinger_support * 4) + loop_i];
+ if(x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max){
+ finger_num--;
+
+ if ((ts->debug_log_level & BIT(3)) > 0)
+ {
+ if (old_finger >> loop_i == 0)
+ {
+ if (ts->useScreenRes)
+ {
+ I("status: Screen:F:%02d Down, X:%d, Y:%d, W:%d, N:%d\n",
+ loop_i+1, x * ts->widthFactor >> SHIFTBITS,
+ y * ts->heightFactor >> SHIFTBITS, w, EN_NoiseFilter);
+ }
+ else
+ {
+ I("status: Raw:F:%02d Down, X:%d, Y:%d, W:%d, N:%d\n",
+ loop_i+1, x, y, w, EN_NoiseFilter);
+ }
+ }
+ }
+
+ if (ts->protocol_type == PROTOCOL_TYPE_B)
+ {
+ input_mt_slot(ts->input_dev, loop_i);
+ }
+
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, w);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+
+ if (ts->protocol_type == PROTOCOL_TYPE_A)
+ {
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, loop_i);
+ input_mt_sync(ts->input_dev);
+ }
+ else
+ {
+ ts->last_slot = loop_i;
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1);
+ }
+
+ if (!ts->first_pressed)
+ {
+ ts->first_pressed = 1;
+ I("S1@%d, %d\n", x, y);
+ }
+
+ ts->pre_finger_data[loop_i][0] = x;
+ ts->pre_finger_data[loop_i][1] = y;
+
+
+ if (ts->debug_log_level & BIT(1))
+ I("Finger %d=> X:%d, Y:%d W:%d, Z:%d, F:%d, N:%d\n",
+ loop_i + 1, x, y, w, w, loop_i + 1, EN_NoiseFilter);
+
+ ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i);
+
+ } else {
+ if (ts->protocol_type == PROTOCOL_TYPE_B)
+ {
+ input_mt_slot(ts->input_dev, loop_i);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ if (loop_i == 0 && ts->first_pressed == 1)
+ {
+ ts->first_pressed = 2;
+ I("E1@%d, %d\n",
+ ts->pre_finger_data[0][0] , ts->pre_finger_data[0][1]);
+ }
+ if ((ts->debug_log_level & BIT(3)) > 0)
+ {
+ if (old_finger >> loop_i == 1)
+ {
+ if (ts->useScreenRes)
+ {
+ I("status: Screen:F:%02d Up, X:%d, Y:%d, N:%d\n",
+ loop_i+1, ts->pre_finger_data[loop_i][0] * ts->widthFactor >> SHIFTBITS,
+ ts->pre_finger_data[loop_i][1] * ts->heightFactor >> SHIFTBITS, Last_EN_NoiseFilter);
+ }
+ else
+ {
+ I("status: Raw:F:%02d Up, X:%d, Y:%d, N:%d\n",
+ loop_i+1, ts->pre_finger_data[loop_i][0],
+ ts->pre_finger_data[loop_i][1], Last_EN_NoiseFilter);
+ }
+ }
+ }
+ }
+ }
+
+ }else if ((tpd_key_old != 0x00)&&(tpd_key == 0x00)) {
+ //temp_x[0] = 0xFFFF;
+ //temp_y[0] = 0xFFFF;
+ //temp_x[1] = 0xFFFF;
+ //temp_y[1] = 0xFFFF;
+ himax_ts_button_func(tpd_key,ts);
+ finger_on = 0;
+ }
+ input_report_key(ts->input_dev, BTN_TOUCH, finger_on);
+ input_sync(ts->input_dev);
+ } else if (hx_point_num == 0){
+ if(AA_press)
+ {
+ // leave event
+ finger_on = 0;
+ AA_press = 0;
+ if (ts->protocol_type == PROTOCOL_TYPE_A)
+ input_mt_sync(ts->input_dev);
+
+ for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) {
+ if (((ts->pre_finger_mask >> loop_i) & 1) == 1) {
+ if (ts->protocol_type == PROTOCOL_TYPE_B) {
+ input_mt_slot(ts->input_dev, loop_i);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
+ }
+ }
+ }
+ if (ts->pre_finger_mask > 0) {
+ for (loop_i = 0; loop_i < ts->nFinger_support && (ts->debug_log_level & BIT(3)) > 0; loop_i++) {
+ if (((ts->pre_finger_mask >> loop_i) & 1) == 1) {
+ if (ts->useScreenRes) {
+ I("status:%X, Screen:F:%02d Up, X:%d, Y:%d, N:%d\n", 0, loop_i+1, ts->pre_finger_data[loop_i][0] * ts->widthFactor >> SHIFTBITS,
+ ts->pre_finger_data[loop_i][1] * ts->heightFactor >> SHIFTBITS, Last_EN_NoiseFilter);
+ } else {
+ I("status:%X, Raw:F:%02d Up, X:%d, Y:%d, N:%d\n",0, loop_i+1, ts->pre_finger_data[loop_i][0],ts->pre_finger_data[loop_i][1], Last_EN_NoiseFilter);
+ }
+ }
+ }
+ ts->pre_finger_mask = 0;
+ }
+
+ if (ts->first_pressed == 1) {
+ ts->first_pressed = 2;
+ I("E1@%d, %d\n",ts->pre_finger_data[0][0] , ts->pre_finger_data[0][1]);
+ }
+
+ if (ts->debug_log_level & BIT(1))
+ I("All Finger leave\n");
+
+ }
+ else if (tpd_key != 0x00) {
+ himax_ts_button_func(tpd_key,ts);
+ finger_on = 1;
+ }
+ else if ((tpd_key_old != 0x00)&&(tpd_key == 0x00)) {
+ himax_ts_button_func(tpd_key,ts);
+ finger_on = 0;
+ }
+ input_report_key(ts->input_dev, BTN_TOUCH, finger_on);
+ input_sync(ts->input_dev);
+ }
+ tpd_key_old = tpd_key;
+ Last_EN_NoiseFilter = EN_NoiseFilter;
+
+workqueue_out:
+ return;
+
+err_workqueue_out:
+ I("%s: Now reset the Touch chip.\n", __func__);
+
+#ifdef HX_RST_PIN_FUNC
+ himax_HW_reset(true,false);
+#endif
+
+ goto workqueue_out;
+}
+enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer)
+{
+ struct himax_ts_data *ts;
+
+ ts = container_of(timer, struct himax_ts_data, timer);
+ queue_work(ts->himax_wq, &ts->work);
+ hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+#if defined(HX_USB_DETECT)
+static void himax_cable_tp_status_handler_func(int connect_status)
+{
+ struct himax_ts_data *ts;
+ I("Touch: cable change to %d\n", connect_status);
+ ts = private_ts;
+ if (ts->cable_config) {
+ if (!atomic_read(&ts->suspend_mode)) {
+ if ((!!connect_status) != ts->usb_connected) {
+ if (!!connect_status) {
+ ts->cable_config[1] = 0x01;
+ ts->usb_connected = 0x01;
+ } else {
+ ts->cable_config[1] = 0x00;
+ ts->usb_connected = 0x00;
+ }
+
+ i2c_himax_master_write(ts->client, ts->cable_config,
+ sizeof(ts->cable_config), HIMAX_I2C_RETRY_TIMES);
+
+ I("%s: Cable status change: 0x%2.2X\n", __func__, ts->cable_config[1]);
+ } else
+ I("%s: Cable status is the same as previous one, ignore.\n", __func__);
+ } else {
+ if (connect_status)
+ ts->usb_connected = 0x01;
+ else
+ ts->usb_connected = 0x00;
+ I("%s: Cable status remembered: 0x%2.2X\n", __func__, ts->usb_connected);
+ }
+ }
+}
+
+static struct t_cable_status_notifier himax_cable_status_handler = {
+ .name = "usb_tp_connected",
+ .func = himax_cable_tp_status_handler_func,
+};
+
+#endif
+
+#if defined(HX_USB_DETECT2)
+void himax_cable_detect_func(void)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[128];
+ struct himax_ts_data *ts;
+ u32 connect_status = 0;
+
+ connect_status = USB_Flag;//upmu_is_chr_det();
+ ts = private_ts;
+ //I("Touch: cable status=%d, cable_config=%p, usb_connected=%d \n", connect_status,ts->cable_config, ts->usb_connected);
+ if (ts->cable_config) {
+ if ((!!connect_status) != ts->usb_connected) {
+ //notify USB plug/unplug
+ // 0x9008_8060 ==> 0x0000_0000/0001
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x60;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00;
+
+ if (!!connect_status) {
+ tmp_data[0] = 0x01;
+ ts->usb_connected = 0x01;
+ } else {
+ tmp_data[0] = 0x00;
+ ts->usb_connected = 0x00;
+ }
+
+ himax_flash_write_burst(ts->client, tmp_addr, tmp_data);
+
+ I("%s: Cable status change: 0x%2.2X\n", __func__, ts->usb_connected);
+ }
+ //else
+ //I("%s: Cable status is the same as previous one, ignore.\n", __func__);
+ }
+}
+#endif
+
+#ifdef CONFIG_FB
+int himax_fb_register(struct himax_ts_data *ts)
+{
+ int ret = 0;
+
+ I(" %s in", __func__);
+ ts->fb_notif.notifier_call = fb_notifier_callback;
+ ret = fb_register_client(&ts->fb_notif);
+ if (ret)
+ E(" Unable to register fb_notifier: %d\n", ret);
+
+ return ret;
+}
+#endif
+
+#ifdef HX_SMART_WAKEUP
+void himax_set_SMWP_func(struct i2c_client *client,uint8_t SMWP_enable)
+{
+ uint8_t tmp_data[4];
+
+ if(SMWP_enable)
+ {
+ SMWP_bit_retry:
+ himax_set_SMWP_enable(client, SMWP_enable);
+ msleep(20);
+ himax_get_SMWP_enable(client,tmp_data);
+ I("%s: Read SMWP bit data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", __func__
+ ,tmp_data[0],tmp_data[1],tmp_data[2],tmp_data[3]);
+ if(tmp_data[0]!= 0x01)
+ {
+ I("%s: retry SMWP bit write data[0]=%x \n",__func__,tmp_data[0]);
+ goto SMWP_bit_retry;
+ }
+ }
+}
+
+static void himax_SMWP_work(struct work_struct *work)
+{
+ struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
+ smwp_work.work);
+ I(" %s in", __func__);
+
+ himax_set_SMWP_func(ts->client,ts->SMWP_enable);
+
+}
+#endif
+
+#ifdef HX_TP_PROC_FLASH_DUMP
+static void himax_ts_flash_work_func(struct work_struct *work)
+{
+ himax_ts_flash_func();
+}
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+static void himax_ts_diag_work_func(struct work_struct *work)
+{
+ himax_ts_diag_func();
+}
+#endif
+
+void himax_ts_init(struct himax_ts_data *ts)
+{
+ int ret = 0, err = 0;
+ struct himax_i2c_platform_data *pdata;
+ struct i2c_client *client;
+
+ client = ts->client;
+ pdata = ts->pdata;
+
+ I("%s: Start.\n", __func__);
+
+ /* Set pinctrl in active state */
+ if (ts->ts_pinctrl) {
+ ret = pinctrl_select_state(ts->ts_pinctrl,
+ ts->pinctrl_state_active);
+ if (ret < 0) {
+ E("Failed to set pin in active state %d",ret);
+ }
+ }
+
+ himax_burst_enable(client, 0);
+
+ //Get Himax IC Type / FW information / Calculate the point number
+ if (himax_check_chip_version(ts->client) == false) {
+ E("Himax chip doesn NOT EXIST");
+ goto err_ic_package_failed;
+ }
+ if (himax_ic_package_check(ts->client) == false) {
+ E("Himax chip doesn NOT EXIST");
+ goto err_ic_package_failed;
+ }
+
+ if (pdata->virtual_key)
+ ts->button = pdata->virtual_key;
+#ifdef HX_TP_PROC_FLASH_DUMP
+ ts->flash_wq = create_singlethread_workqueue("himax_flash_wq");
+ if (!ts->flash_wq)
+ {
+ E("%s: create flash workqueue failed\n", __func__);
+ err = -ENOMEM;
+ goto err_create_wq_failed;
+ }
+
+ INIT_WORK(&ts->flash_work, himax_ts_flash_work_func);
+
+ setSysOperation(0);
+ setFlashBuffer();
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+ ts->himax_diag_wq = create_singlethread_workqueue("himax_diag");
+ if (!ts->himax_diag_wq)
+ {
+ E("%s: create diag workqueue failed\n", __func__);
+ err = -ENOMEM;
+ goto err_create_wq_failed;
+ }
+ INIT_DELAYED_WORK(&ts->himax_diag_delay_wrok, himax_ts_diag_work_func);
+#endif
+
+himax_read_FW_ver(client);
+
+#ifdef HX_AUTO_UPDATE_FW
+ I(" %s in", __func__);
+ if(i_update_FW() == false)
+ I("NOT Have new FW=NOT UPDATE=\n");
+ else
+ I("Have new FW=UPDATE=\n");
+#endif
+
+ //Himax Power On and Load Config
+ if (himax_loadSensorConfig(client, pdata) < 0) {
+ E("%s: Load Sesnsor configuration failed, unload driver.\n", __func__);
+ goto err_detect_failed;
+ }
+
+ calculate_point_number();
+#ifdef HX_TP_PROC_DIAG
+ setXChannel(ic_data->HX_RX_NUM); // X channel
+ setYChannel(ic_data->HX_TX_NUM); // Y channel
+
+ setMutualBuffer();
+ setMutualNewBuffer();
+ setMutualOldBuffer();
+ if (getMutualBuffer() == NULL) {
+ E("%s: mutual buffer allocate fail failed\n", __func__);
+ return;
+ }
+#ifdef HX_TP_PROC_2T2R
+ if(Is_2T2R){
+ setXChannel_2(ic_data->HX_RX_NUM_2); // X channel
+ setYChannel_2(ic_data->HX_TX_NUM_2); // Y channel
+
+ setMutualBuffer_2();
+
+ if (getMutualBuffer_2() == NULL) {
+ E("%s: mutual buffer 2 allocate fail failed\n", __func__);
+ return;
+ }
+ }
+#endif
+#endif
+#ifdef CONFIG_OF
+ ts->power = pdata->power;
+#endif
+ ts->pdata = pdata;
+
+ ts->x_channel = ic_data->HX_RX_NUM;
+ ts->y_channel = ic_data->HX_TX_NUM;
+ ts->nFinger_support = ic_data->HX_MAX_PT;
+ //calculate the i2c data size
+ calcDataSize(ts->nFinger_support);
+ I("%s: calcDataSize complete\n", __func__);
+#ifdef CONFIG_OF
+ ts->pdata->abs_pressure_min = 0;
+ ts->pdata->abs_pressure_max = 200;
+ ts->pdata->abs_width_min = 0;
+ ts->pdata->abs_width_max = 200;
+ pdata->cable_config[0] = 0x90;
+ pdata->cable_config[1] = 0x00;
+#endif
+ ts->suspended = false;
+#if defined(HX_USB_DETECT)||defined(HX_USB_DETECT2)
+ ts->usb_connected = 0x00;
+ ts->cable_config = pdata->cable_config;
+#endif
+ ts->protocol_type = pdata->protocol_type;
+ I("%s: Use Protocol Type %c\n", __func__,
+ ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B');
+
+ ret = himax_input_register(ts);
+ if (ret) {
+ E("%s: Unable to register %s input device\n",
+ __func__, ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+#ifdef HX_SMART_WAKEUP
+ ts->SMWP_enable=0;
+ wake_lock_init(&ts->ts_SMWP_wake_lock, WAKE_LOCK_SUSPEND, HIMAX_common_NAME);
+
+ ts->himax_smwp_wq = create_singlethread_workqueue("HMX_SMWP_WORK");
+ if (!ts->himax_smwp_wq) {
+ E(" allocate himax_smwp_wq failed\n");
+ err = -ENOMEM;
+ goto err_smwp_wq_failed;
+ }
+ INIT_DELAYED_WORK(&ts->smwp_work, himax_SMWP_work);
+#endif
+#ifdef HX_HIGH_SENSE
+ ts->HSEN_enable=0;
+ ts->himax_hsen_wq = create_singlethread_workqueue("HMX_HSEN_WORK");
+ if (!ts->himax_hsen_wq) {
+ E(" allocate himax_hsen_wq failed\n");
+ err = -ENOMEM;
+ goto err_hsen_wq_failed;
+ }
+ INIT_DELAYED_WORK(&ts->hsen_work, himax_HSEN_func);
+#endif
+
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+ himax_touch_proc_init();
+#endif
+
+#if defined(HX_USB_DETECT)
+ if (ts->cable_config)
+ cable_detect_register_notifier(&himax_cable_status_handler);
+#endif
+
+ err = himax_ts_register_interrupt(ts->client);
+ if (err)
+ goto err_register_interrupt_failed;
+ return;
+
+err_register_interrupt_failed:
+#ifdef HX_HIGH_SENSE
+err_hsen_wq_failed:
+#endif
+#ifdef HX_SMART_WAKEUP
+err_smwp_wq_failed:
+ wake_lock_destroy(&ts->ts_SMWP_wake_lock);
+#endif
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+err_detect_failed:
+#ifdef HX_TP_PROC_FLASH_DUMP
+err_create_wq_failed:
+#endif
+err_ic_package_failed:
+
+return;
+}
+
+int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct himax_ts_data *ts;
+ struct himax_i2c_platform_data *pdata;
+
+ //Check I2C functionality
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ E("%s: i2c check functionality error\n", __func__);
+ err = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL);
+ if (ts == NULL) {
+ E("%s: allocate himax_ts_data failed\n", __func__);
+ err = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+
+ i2c_set_clientdata(client, ts);
+ ts->client = client;
+ ts->dev = &client->dev;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) { /*Allocate Platform data space*/
+ err = -ENOMEM;
+ goto err_dt_platform_data_fail;
+ }
+
+ ic_data = kzalloc(sizeof(*ic_data), GFP_KERNEL);
+ if (ic_data == NULL) { /*Allocate IC data space*/
+ err = -ENOMEM;
+ goto err_dt_ic_data_fail;
+ }
+
+#ifdef CONFIG_OF
+ if (client->dev.of_node) { /*DeviceTree Init Platform_data*/
+ err = himax_parse_dt(ts, pdata);
+ if (err < 0) {
+ I(" pdata is NULL for DT\n");
+ goto err_alloc_dt_pdata_failed;
+ }
+ }
+#endif
+
+#ifdef HX_RST_PIN_FUNC
+ ts->rst_gpio = pdata->gpio_reset;
+#endif
+
+himax_gpio_power_config(ts->client, pdata);
+
+ err = himax_ts_pinctrl_init(ts);
+ if (err || ts->ts_pinctrl == NULL) {
+ E(" Pinctrl init failed\n");
+ }
+
+#ifndef CONFIG_OF
+ if (pdata->power) {
+ err = pdata->power(1);
+ if (err < 0) {
+ E("%s: power on failed\n", __func__);
+ goto err_power_failed;
+ }
+ }
+#endif
+ ts->pdata = pdata;
+ private_ts = ts;
+
+ mutex_init(&ts->fb_mutex);
+ /* ts initialization is deferred till FB_UNBLACK event;
+ * probe is considered pending till then.*/
+ ts->probe_done = false;
+#ifdef CONFIG_FB
+ err = himax_fb_register(ts);
+ if (err) {
+ E("Falied to register fb notifier\n");
+ err = -ENOMEM;
+ goto err_fb_notif_wq_create;
+ }
+#endif
+
+ return 0;
+
+#ifdef CONFIG_FB
+err_fb_notif_wq_create:
+#endif
+#ifdef CONFIG_OF
+err_alloc_dt_pdata_failed:
+#else
+err_power_failed:
+err_get_platform_data_fail:
+#endif
+ if (ts->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(ts->pinctrl_state_release)) {
+ devm_pinctrl_put(ts->ts_pinctrl);
+ ts->ts_pinctrl = NULL;
+ } else {
+ err = pinctrl_select_state(ts->ts_pinctrl,
+ ts->pinctrl_state_release);
+ if (err)
+ E("failed to select relase pinctrl state %d\n",
+ err);
+ }
+ }
+ kfree(ic_data);
+
+err_dt_ic_data_fail:
+ kfree(pdata);
+
+err_dt_platform_data_fail:
+ kfree(ts);
+
+err_alloc_data_failed:
+
+err_check_functionality_failed:
+ probe_fail_flag = 1;
+ return err;
+
+}
+
+int himax_chip_common_remove(struct i2c_client *client)
+{
+ struct himax_ts_data *ts = i2c_get_clientdata(client);
+ int ret;
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+ himax_touch_proc_deinit();
+#endif
+#ifdef CONFIG_FB
+ if (fb_unregister_client(&ts->fb_notif))
+ dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
+#endif
+
+ if (!ts->use_irq)
+ hrtimer_cancel(&ts->timer);
+
+ destroy_workqueue(ts->himax_wq);
+
+ if (ts->protocol_type == PROTOCOL_TYPE_B)
+ input_mt_destroy_slots(ts->input_dev);
+
+ input_unregister_device(ts->input_dev);
+
+ if (ts->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(ts->pinctrl_state_release)) {
+ devm_pinctrl_put(ts->ts_pinctrl);
+ ts->ts_pinctrl = NULL;
+ } else {
+ ret = pinctrl_select_state(ts->ts_pinctrl,
+ ts->pinctrl_state_release);
+ if (ret)
+ E("failed to select relase pinctrl state %d\n",
+ ret);
+ }
+ }
+#ifdef HX_SMART_WAKEUP
+ wake_lock_destroy(&ts->ts_SMWP_wake_lock);
+#endif
+ kfree(ts);
+
+ return 0;
+
+}
+
+int himax_chip_common_suspend(struct himax_ts_data *ts)
+{
+ int ret;
+
+ if(ts->suspended)
+ {
+ I("%s: Already suspended. Skipped. \n", __func__);
+ return 0;
+ }
+ else
+ {
+ ts->suspended = true;
+ I("%s: enter \n", __func__);
+ }
+
+#ifdef HX_TP_PROC_FLASH_DUMP
+ if (getFlashDumpGoing())
+ {
+ I("[himax] %s: Flash dump is going, reject suspend\n",__func__);
+ return 0;
+ }
+#endif
+#ifdef HX_TP_PROC_HITOUCH
+ if(hitouch_is_connect)
+ {
+ I("[himax] %s: Hitouch connect, reject suspend\n",__func__);
+ return 0;
+ }
+#endif
+#ifdef HX_SMART_WAKEUP
+ if(ts->SMWP_enable)
+ {
+ atomic_set(&ts->suspend_mode, 1);
+ ts->pre_finger_mask = 0;
+ FAKE_POWER_KEY_SEND=false;
+ I("[himax] %s: SMART_WAKEUP enable, reject suspend\n",__func__);
+ return 0;
+ }
+#endif
+#ifdef HX_ESD_WORKAROUND
+ ESD_00_counter = 0;
+ ESD_00_Flag = 0;
+#endif
+ if (!ts->use_irq) {
+ ret = cancel_work_sync(&ts->work);
+ if (ret)
+ himax_int_enable(ts->client->irq,1);
+ }
+
+ //ts->first_pressed = 0;
+ atomic_set(&ts->suspend_mode, 1);
+ ts->pre_finger_mask = 0;
+
+ if (ts->ts_pinctrl) {
+ ret = pinctrl_select_state(ts->ts_pinctrl,
+ ts->pinctrl_state_suspend);
+ if (ret < 0) {
+ E("Failed to get idle pinctrl state %d\n", ret);
+ }
+ }
+
+ if (ts->pdata->powerOff3V3 && ts->pdata->power)
+ ts->pdata->power(0);
+
+ return 0;
+}
+
+int himax_chip_common_resume(struct himax_ts_data *ts)
+{
+ int retval;
+
+ I("%s: enter \n", __func__);
+
+ if (ts->pdata->powerOff3V3 && ts->pdata->power)
+ ts->pdata->power(1);
+
+
+ /*************************************/
+ if (ts->protocol_type == PROTOCOL_TYPE_A)
+ input_mt_sync(ts->input_dev);
+ input_report_key(ts->input_dev, BTN_TOUCH, 0);
+ input_sync(ts->input_dev);
+ /*************************************/
+
+
+ if (ts->ts_pinctrl) {
+ retval = pinctrl_select_state(ts->ts_pinctrl,
+ ts->pinctrl_state_active);
+ if (retval < 0) {
+ E("Cannot get default pinctrl state %d\n", retval);
+ goto err_pinctrl_select_resume;
+ }
+ }
+
+ atomic_set(&ts->suspend_mode, 0);
+
+ himax_int_enable(ts->client->irq,1);
+
+ ts->suspended = false;
+#if defined(HX_USB_DETECT2)
+ ts->usb_connected = 0x00;
+ himax_cable_detect_func();
+#endif
+#ifdef HX_SMART_WAKEUP
+ queue_delayed_work(ts->himax_smwp_wq, &ts->smwp_work, msecs_to_jiffies(1000));
+#endif
+#ifdef HX_HIGH_SENSE
+ queue_delayed_work(ts->himax_hsen_wq, &ts->hsen_work, msecs_to_jiffies(1000));
+#endif
+ return 0;
+err_pinctrl_select_resume:
+ if (ts->pdata->powerOff3V3 && ts->pdata->power)
+ ts->pdata->power(0);
+ return retval;
+}
+
diff --git a/drivers/input/touchscreen/hxchipset/himax_common.h b/drivers/input/touchscreen/hxchipset/himax_common.h
new file mode 100644
index 0000000..27ce9aa
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_common.h
@@ -0,0 +1,395 @@
+/* Himax Android Driver Sample Code for Himax chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#ifndef HIMAX_COMMON_H
+#define HIMAX_COMMON_H
+
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/async.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/input/mt.h>
+#include <linux/firmware.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include "himax_platform.h"
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+#define HIMAX_DRIVER_VER "0.2.4.0"
+
+#define FLASH_DUMP_FILE "/data/user/Flash_Dump.bin"
+#define DIAG_COORDINATE_FILE "/sdcard/Coordinate_Dump.csv"
+
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+
+#define HX_TP_PROC_DIAG
+#define HX_TP_PROC_REGISTER
+#define HX_TP_PROC_DEBUG
+#define HX_TP_PROC_FLASH_DUMP
+#define HX_TP_PROC_SELF_TEST
+#define HX_TP_PROC_RESET
+#define HX_TP_PROC_SENSE_ON_OFF
+//#define HX_TP_PROC_2T2R
+
+int himax_touch_proc_init(void);
+void himax_touch_proc_deinit(void);
+#endif
+
+//===========Himax Option function=============
+//#define HX_RST_PIN_FUNC
+//#define HX_AUTO_UPDATE_FW
+//#define HX_HIGH_SENSE
+//#define HX_SMART_WAKEUP
+//#define HX_USB_DETECT
+//#define HX_ESD_WORKAROUND
+//#define HX_USB_DETECT2
+
+//#define HX_EN_SEL_BUTTON // Support Self Virtual key ,default is close
+#define HX_EN_MUT_BUTTON // Support Mutual Virtual Key ,default is close
+
+#define HX_KEY_MAX_COUNT 4
+#define DEFAULT_RETRY_CNT 3
+
+#define HX_VKEY_0 KEY_BACK
+#define HX_VKEY_1 KEY_HOME
+#define HX_VKEY_2 KEY_RESERVED
+#define HX_VKEY_3 KEY_RESERVED
+#define HX_KEY_ARRAY {HX_VKEY_0, HX_VKEY_1, HX_VKEY_2, HX_VKEY_3}
+
+#define SHIFTBITS 5
+//#define FLASH_SIZE 131072
+#define FW_SIZE_60k 61440
+#define FW_SIZE_64k 65536
+#define FW_SIZE_124k 126976
+#define FW_SIZE_128k 131072
+
+struct himax_ic_data {
+ int vendor_fw_ver;
+ int vendor_config_ver;
+ int vendor_sensor_id;
+ int HX_RX_NUM;
+ int HX_TX_NUM;
+ int HX_BT_NUM;
+ int HX_X_RES;
+ int HX_Y_RES;
+ int HX_MAX_PT;
+ bool HX_XY_REVERSE;
+ bool HX_INT_IS_EDGE;
+#ifdef HX_TP_PROC_2T2R
+ int HX_RX_NUM_2;
+ int HX_TX_NUM_2;
+#endif
+};
+
+struct himax_virtual_key {
+ int index;
+ int keycode;
+ int x_range_min;
+ int x_range_max;
+ int y_range_min;
+ int y_range_max;
+};
+
+struct himax_config {
+ uint8_t default_cfg;
+ uint8_t sensor_id;
+ uint8_t fw_ver;
+ uint16_t length;
+ uint32_t tw_x_min;
+ uint32_t tw_x_max;
+ uint32_t tw_y_min;
+ uint32_t tw_y_max;
+ uint32_t pl_x_min;
+ uint32_t pl_x_max;
+ uint32_t pl_y_min;
+ uint32_t pl_y_max;
+ uint8_t c1[11];
+ uint8_t c2[11];
+ uint8_t c3[11];
+ uint8_t c4[11];
+ uint8_t c5[11];
+ uint8_t c6[11];
+ uint8_t c7[11];
+ uint8_t c8[11];
+ uint8_t c9[11];
+ uint8_t c10[11];
+ uint8_t c11[11];
+ uint8_t c12[11];
+ uint8_t c13[11];
+ uint8_t c14[11];
+ uint8_t c15[11];
+ uint8_t c16[11];
+ uint8_t c17[11];
+ uint8_t c18[17];
+ uint8_t c19[15];
+ uint8_t c20[5];
+ uint8_t c21[11];
+ uint8_t c22[4];
+ uint8_t c23[3];
+ uint8_t c24[3];
+ uint8_t c25[4];
+ uint8_t c26[2];
+ uint8_t c27[2];
+ uint8_t c28[2];
+ uint8_t c29[2];
+ uint8_t c30[2];
+ uint8_t c31[2];
+ uint8_t c32[2];
+ uint8_t c33[2];
+ uint8_t c34[2];
+ uint8_t c35[3];
+ uint8_t c36[5];
+ uint8_t c37[5];
+ uint8_t c38[9];
+ uint8_t c39[14];
+ uint8_t c40[159];
+ uint8_t c41[99];
+};
+
+struct himax_ts_data {
+ bool suspended;
+ bool probe_done;
+ struct mutex fb_mutex;
+ atomic_t suspend_mode;
+ uint8_t x_channel;
+ uint8_t y_channel;
+ uint8_t useScreenRes;
+ uint8_t diag_command;
+
+ uint8_t protocol_type;
+ uint8_t first_pressed;
+ uint8_t coord_data_size;
+ uint8_t area_data_size;
+ uint8_t raw_data_frame_size;
+ uint8_t raw_data_nframes;
+ uint8_t nFinger_support;
+ uint8_t irq_enabled;
+ uint8_t diag_self[50];
+
+ uint16_t finger_pressed;
+ uint16_t last_slot;
+ uint16_t pre_finger_mask;
+
+ uint32_t debug_log_level;
+ uint32_t widthFactor;
+ uint32_t heightFactor;
+ uint32_t tw_x_min;
+ uint32_t tw_x_max;
+ uint32_t tw_y_min;
+ uint32_t tw_y_max;
+ uint32_t pl_x_min;
+ uint32_t pl_x_max;
+ uint32_t pl_y_min;
+ uint32_t pl_y_max;
+
+ int use_irq;
+ int (*power)(int on);
+ int pre_finger_data[10][2];
+
+ struct device *dev;
+ struct workqueue_struct *himax_wq;
+ struct work_struct work;
+ struct input_dev *input_dev;
+ struct hrtimer timer;
+ struct i2c_client *client;
+ struct himax_i2c_platform_data *pdata;
+ struct himax_virtual_key *button;
+
+#if defined(CONFIG_FB)
+ struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+
+#ifdef HX_TP_PROC_FLASH_DUMP
+ struct workqueue_struct *flash_wq;
+ struct work_struct flash_work;
+#endif
+
+#ifdef HX_RST_PIN_FUNC
+ int rst_gpio;
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+ struct workqueue_struct *himax_diag_wq;
+ struct delayed_work himax_diag_delay_wrok;
+#endif
+#ifdef HX_SMART_WAKEUP
+ uint8_t SMWP_enable;
+ uint8_t gesture_cust_en[16];
+ struct wake_lock ts_SMWP_wake_lock;
+ struct workqueue_struct *himax_smwp_wq;
+ struct delayed_work smwp_work;
+#endif
+
+#ifdef HX_HIGH_SENSE
+ uint8_t HSEN_enable;
+ struct workqueue_struct *himax_hsen_wq;
+ struct delayed_work hsen_work;
+#endif
+
+#if defined(HX_USB_DETECT)||defined(HX_USB_DETECT2)
+ uint8_t usb_connected;
+ uint8_t *cable_config;
+#endif
+
+ /* pinctrl data */
+ struct pinctrl *ts_pinctrl;
+ struct pinctrl_state *pinctrl_state_active;
+ struct pinctrl_state *pinctrl_state_suspend;
+ struct pinctrl_state *pinctrl_state_release;
+};
+
+#define HX_CMD_NOP 0x00
+#define HX_CMD_SETMICROOFF 0x35
+#define HX_CMD_SETROMRDY 0x36
+#define HX_CMD_TSSLPIN 0x80
+#define HX_CMD_TSSLPOUT 0x81
+#define HX_CMD_TSSOFF 0x82
+#define HX_CMD_TSSON 0x83
+#define HX_CMD_ROE 0x85
+#define HX_CMD_RAE 0x86
+#define HX_CMD_RLE 0x87
+#define HX_CMD_CLRES 0x88
+#define HX_CMD_TSSWRESET 0x9E
+#define HX_CMD_SETDEEPSTB 0xD7
+#define HX_CMD_SET_CACHE_FUN 0xDD
+#define HX_CMD_SETIDLE 0xF2
+#define HX_CMD_SETIDLEDELAY 0xF3
+#define HX_CMD_SELFTEST_BUFFER 0x8D
+#define HX_CMD_MANUALMODE 0x42
+#define HX_CMD_FLASH_ENABLE 0x43
+#define HX_CMD_FLASH_SET_ADDRESS 0x44
+#define HX_CMD_FLASH_WRITE_REGISTER 0x45
+#define HX_CMD_FLASH_SET_COMMAND 0x47
+#define HX_CMD_FLASH_WRITE_BUFFER 0x48
+#define HX_CMD_FLASH_PAGE_ERASE 0x4D
+#define HX_CMD_FLASH_SECTOR_ERASE 0x4E
+#define HX_CMD_CB 0xCB
+#define HX_CMD_EA 0xEA
+#define HX_CMD_4A 0x4A
+#define HX_CMD_4F 0x4F
+#define HX_CMD_B9 0xB9
+#define HX_CMD_76 0x76
+
+enum input_protocol_type {
+ PROTOCOL_TYPE_A = 0x00,
+ PROTOCOL_TYPE_B = 0x01,
+};
+
+#ifdef HX_HIGH_SENSE
+void himax_set_HSEN_func(struct i2c_client *client,uint8_t HSEN_enable);
+#endif
+
+#ifdef HX_SMART_WAKEUP
+#define GEST_PTLG_ID_LEN (4)
+#define GEST_PTLG_HDR_LEN (4)
+#define GEST_PTLG_HDR_ID1 (0xCC)
+#define GEST_PTLG_HDR_ID2 (0x44)
+#define GEST_PT_MAX_NUM (128)
+
+#ifdef HX_GESTURE_TRACK
+static int gest_pt_cnt;
+static int gest_pt_x[GEST_PT_MAX_NUM];
+static int gest_pt_y[GEST_PT_MAX_NUM];
+static int gest_start_x,gest_start_y,gest_end_x,gest_end_y;
+static int gest_width,gest_height,gest_mid_x,gest_mid_y;
+static int gn_gesture_coor[16];
+#endif
+
+void himax_set_SMWP_func(struct i2c_client *client,uint8_t SMWP_enable);
+extern bool FAKE_POWER_KEY_SEND;
+
+ enum gesture_event_type {
+ EV_GESTURE_01 = 0x01,
+ EV_GESTURE_02,
+ EV_GESTURE_03,
+ EV_GESTURE_04,
+ EV_GESTURE_05,
+ EV_GESTURE_06,
+ EV_GESTURE_07,
+ EV_GESTURE_08,
+ EV_GESTURE_09,
+ EV_GESTURE_10,
+ EV_GESTURE_11,
+ EV_GESTURE_12,
+ EV_GESTURE_13,
+ EV_GESTURE_14,
+ EV_GESTURE_15,
+ EV_GESTURE_PWR = 0x80,
+ };
+
+#define KEY_CUST_01 251
+#define KEY_CUST_02 252
+#define KEY_CUST_03 253
+#define KEY_CUST_04 254
+#define KEY_CUST_05 255
+#define KEY_CUST_06 256
+#define KEY_CUST_07 257
+#define KEY_CUST_08 258
+#define KEY_CUST_09 259
+#define KEY_CUST_10 260
+#define KEY_CUST_11 261
+#define KEY_CUST_12 262
+#define KEY_CUST_13 263
+#define KEY_CUST_14 264
+#define KEY_CUST_15 265
+#endif
+
+#ifdef HX_ESD_WORKAROUND
+ extern u8 HX_ESD_RESET_ACTIVATE;
+#endif
+
+extern int irq_enable_count;
+
+#ifdef QCT
+irqreturn_t himax_ts_thread(int irq, void *ptr);
+int himax_input_register(struct himax_ts_data *ts);
+#endif
+
+extern int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id);
+extern int himax_chip_common_remove(struct i2c_client *client);
+extern int himax_chip_common_suspend(struct himax_ts_data *ts);
+extern int himax_chip_common_resume(struct himax_ts_data *ts);
+int himax_loadSensorConfig(struct i2c_client *client, struct himax_i2c_platform_data *pdata);
+
+#ifdef HX_USB_DETECT2
+//extern kal_bool upmu_is_chr_det(void);
+void himax_cable_detect_func(void);
+#endif
+
+#endif
+
diff --git a/drivers/input/touchscreen/hxchipset/himax_debug.c b/drivers/input/touchscreen/hxchipset/himax_debug.c
new file mode 100644
index 0000000..f8bee11
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_debug.c
@@ -0,0 +1,2329 @@
+/* Himax Android Driver Sample Code for Himax chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include "himax_debug.h"
+#include "himax_ic.h"
+
+//struct himax_debug_data* debug_data;
+
+extern struct himax_ic_data* ic_data;
+extern struct himax_ts_data *private_ts;
+extern unsigned char IC_TYPE;
+extern unsigned char IC_CHECKSUM;
+extern int himax_input_register(struct himax_ts_data *ts);
+#ifdef QCT
+extern irqreturn_t himax_ts_thread(int irq, void *ptr);
+#endif
+#ifdef MTK
+#ifdef CONFIG_OF_TOUCH
+extern irqreturn_t tpd_eint_interrupt_handler(int irq, void *desc);
+#else
+extern void tpd_eint_interrupt_handler(void);
+#endif
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+#ifdef HX_TP_PROC_2T2R
+int HX_RX_NUM_2 = 0;
+int HX_TX_NUM_2 = 0;
+#endif
+int touch_monitor_stop_flag = 0;
+int touch_monitor_stop_limit = 5;
+uint8_t g_diag_arr_num = 0;
+#endif
+
+#ifdef HX_ESD_WORKAROUND
+u8 HX_ESD_RESET_ACTIVATE;
+#endif
+
+#ifdef HX_SMART_WAKEUP
+bool FAKE_POWER_KEY_SEND;
+#endif
+
+//=============================================================================================================
+//
+// Segment : Himax PROC Debug Function
+//
+//=============================================================================================================
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+
+static ssize_t himax_vendor_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ ssize_t ret = 0;
+ char *temp_buf;
+
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+
+ ret += snprintf(temp_buf, len, "%s_FW:%#x_CFG:%#x_SensorId:%#x\n", HIMAX_common_NAME,
+ ic_data->vendor_fw_ver, ic_data->vendor_config_ver, ic_data->vendor_sensor_id);
+ HX_PROC_SEND_FLAG=1;
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+
+ return ret;
+}
+
+static const struct file_operations himax_proc_vendor_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_vendor_read,
+};
+
+static ssize_t himax_attn_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ ssize_t ret = 0;
+ struct himax_ts_data *ts_data;
+ char *temp_buf;
+
+ ts_data = private_ts;
+
+ if (!HX_PROC_SEND_FLAG) {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ ret += snprintf(temp_buf, len, "attn = %x\n", himax_int_gpio_read(ts_data->pdata->gpio_irq));
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+
+ return ret;
+}
+
+
+static const struct file_operations himax_proc_attn_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_attn_read,
+};
+
+static ssize_t himax_int_en_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ size_t ret = 0;
+ char *temp_buf;
+
+ if (!HX_PROC_SEND_FLAG) {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ ret += snprintf(temp_buf, len, "%d ", ts->irq_enabled);
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+}
+
+static ssize_t himax_int_en_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ char buf_tmp[12]= {0};
+ int value, ret=0;
+
+ if (len >= 12)
+ {
+ I("%s: no command exceeds 12 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf_tmp, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ if (buf_tmp[0] == '0')
+ value = false;
+ else if (buf_tmp[0] == '1')
+ value = true;
+ else
+ return -EINVAL;
+
+ if (value) {
+ if(ic_data->HX_INT_IS_EDGE)
+ {
+#ifdef MTK
+#ifdef CONFIG_OF_TOUCH
+ himax_int_enable(ts->client->irq,1);
+#else
+ //mt_eint_set_sens(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE);
+ //mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+ mt_eint_registration(ts->client->irq, EINTF_TRIGGER_FALLING, tpd_eint_interrupt_handler, 1);
+#endif
+#endif
+#ifdef QCT
+ ret = request_threaded_irq(ts->client->irq, NULL, himax_ts_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ts->client->name, ts);
+#endif
+ }
+ else
+ {
+#ifdef MTK
+#ifdef CONFIG_OF_TOUCH
+ himax_int_enable(ts->client->irq,1);
+#else
+ //mt_eint_set_sens(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE);
+ //mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+ mt_eint_registration(ts->client->irq, EINTF_TRIGGER_LOW, tpd_eint_interrupt_handler, 1);
+#endif
+#endif
+#ifdef QCT
+ ret = request_threaded_irq(ts->client->irq, NULL, himax_ts_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, ts->client->name, ts);
+#endif
+ }
+ if (ret == 0) {
+ ts->irq_enabled = 1;
+ irq_enable_count = 1;
+ }
+ } else {
+ himax_int_enable(ts->client->irq,0);
+ free_irq(ts->client->irq, ts);
+ ts->irq_enabled = 0;
+ }
+
+ return len;
+}
+
+static const struct file_operations himax_proc_int_en_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_int_en_read,
+ .write = himax_int_en_write,
+};
+
+static ssize_t himax_layout_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ size_t ret = 0;
+ char *temp_buf;
+
+ if (!HX_PROC_SEND_FLAG) {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ ret += snprintf(temp_buf, len, "%d ", ts->pdata->abs_x_min);
+ ret += snprintf(temp_buf+ret, len-ret, "%d ", ts->pdata->abs_x_max);
+ ret += snprintf(temp_buf+ret, len-ret, "%d ", ts->pdata->abs_y_min);
+ ret += snprintf(temp_buf+ret, len-ret, "%d ", ts->pdata->abs_y_max);
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+
+ return ret;
+}
+
+static ssize_t himax_layout_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ char buf_tmp[5];
+ int i = 0, j = 0, k = 0, ret;
+ unsigned long value;
+ int layout[4] = {0};
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ for (i = 0; i < 20; i++) {
+ if (buf[i] == ',' || buf[i] == '\n') {
+ memset(buf_tmp, 0x0, sizeof(buf_tmp));
+ if (i - j <= 5)
+ memcpy(buf_tmp, buf + j, i - j);
+ else {
+ I("buffer size is over 5 char\n");
+ return len;
+ }
+ j = i + 1;
+ if (k < 4) {
+ ret = kstrtoul(buf_tmp, 10, &value);
+ layout[k++] = value;
+ }
+ }
+ }
+ if (k == 4) {
+ ts->pdata->abs_x_min=layout[0];
+ ts->pdata->abs_x_max=layout[1];
+ ts->pdata->abs_y_min=layout[2];
+ ts->pdata->abs_y_max=layout[3];
+ I("%d, %d, %d, %d\n",ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max);
+ input_unregister_device(ts->input_dev);
+ himax_input_register(ts);
+ } else
+ I("ERR@%d, %d, %d, %d\n",ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max);
+ return len;
+}
+
+static const struct file_operations himax_proc_layout_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_layout_read,
+ .write = himax_layout_write,
+};
+
+static ssize_t himax_debug_level_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts_data;
+ size_t ret = 0;
+ char *temp_buf;
+ ts_data = private_ts;
+
+ if (!HX_PROC_SEND_FLAG) {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ ret += snprintf(temp_buf, len, "%d\n", ts_data->debug_log_level);
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+
+ return ret;
+}
+
+static ssize_t himax_debug_level_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts;
+ char buf_tmp[11];
+ int i;
+ ts = private_ts;
+
+ if (len >= 12)
+ {
+ I("%s: no command exceeds 12 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf_tmp, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ ts->debug_log_level = 0;
+ for(i=0; i<len-1; i++)
+ {
+ if( buf_tmp[i]>='0' && buf_tmp[i]<='9' )
+ ts->debug_log_level |= (buf_tmp[i]-'0');
+ else if( buf_tmp[i]>='A' && buf_tmp[i]<='F' )
+ ts->debug_log_level |= (buf_tmp[i]-'A'+10);
+ else if( buf_tmp[i]>='a' && buf_tmp[i]<='f' )
+ ts->debug_log_level |= (buf_tmp[i]-'a'+10);
+
+ if(i!=len-2)
+ ts->debug_log_level <<= 4;
+ }
+
+ if (ts->debug_log_level & BIT(3)) {
+ if (ts->pdata->screenWidth > 0 && ts->pdata->screenHeight > 0 &&
+ (ts->pdata->abs_x_max - ts->pdata->abs_x_min) > 0 &&
+ (ts->pdata->abs_y_max - ts->pdata->abs_y_min) > 0) {
+ ts->widthFactor = (ts->pdata->screenWidth << SHIFTBITS)/(ts->pdata->abs_x_max - ts->pdata->abs_x_min);
+ ts->heightFactor = (ts->pdata->screenHeight << SHIFTBITS)/(ts->pdata->abs_y_max - ts->pdata->abs_y_min);
+ if (ts->widthFactor > 0 && ts->heightFactor > 0)
+ ts->useScreenRes = 1;
+ else {
+ ts->heightFactor = 0;
+ ts->widthFactor = 0;
+ ts->useScreenRes = 0;
+ }
+ } else
+ I("Enable finger debug with raw position mode!\n");
+ } else {
+ ts->useScreenRes = 0;
+ ts->widthFactor = 0;
+ ts->heightFactor = 0;
+ }
+
+ return len;
+}
+
+static const struct file_operations himax_proc_debug_level_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_debug_level_read,
+ .write = himax_debug_level_write,
+};
+
+#ifdef HX_TP_PROC_REGISTER
+static ssize_t himax_proc_register_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ int ret = 0;
+ uint16_t loop_i;
+ uint8_t data[128];
+ char *temp_buf;
+
+ memset(data, 0x00, sizeof(data));
+
+ I("himax_register_show: %x,%x,%x,%x\n", register_command[0],register_command[1],register_command[2],register_command[3]);
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ himax_register_read(private_ts->client, register_command, 1, data);
+
+ ret += snprintf(temp_buf, len, "command: %x,%x,%x,%x\n", register_command[0],register_command[1],register_command[2],register_command[3]);
+
+ for (loop_i = 0; loop_i < 128; loop_i++) {
+ ret += snprintf(temp_buf+ret, len-ret, "0x%2.2X ", data[loop_i]);
+ if ((loop_i % 16) == 15)
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+ }
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+ HX_PROC_SEND_FLAG=1;
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+}
+
+static ssize_t himax_proc_register_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ char buf_tmp[16], length = 0;
+ unsigned long result = 0;
+ uint8_t loop_i = 0;
+ uint16_t base = 5;
+ uint8_t write_da[128];
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ memset(buf_tmp, 0x0, sizeof(buf_tmp));
+ memset(write_da, 0x0, sizeof(write_da));
+
+ I("himax %s \n",buf);
+
+ if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':') {
+
+ if (buf[2] == 'x') {
+ memcpy(buf_tmp, buf + 3, 8);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ register_command[0] = (uint8_t)result;
+ register_command[1] = (uint8_t)(result >> 8);
+ register_command[2] = (uint8_t)(result >> 16);
+ register_command[3] = (uint8_t)(result >> 24);
+ }
+ base = 11;
+ I("CMD: %x,%x,%x,%x\n", register_command[0],register_command[1],register_command[2],register_command[3]);
+
+ for (loop_i = 0; loop_i < 128 && (base+10)<80; loop_i++) {
+ if (buf[base] == '\n') {
+ if (buf[0] == 'w') {
+ himax_register_write(private_ts->client, register_command, 1, write_da);
+ I("CMD: %x, %x, %x, %x, len=%d\n", write_da[0], write_da[1],write_da[2],write_da[3],length);
+ }
+ I("\n");
+ return len;
+ }
+ if (buf[base + 1] == 'x') {
+ buf_tmp[10] = '\n';
+ buf_tmp[11] = '\0';
+ memcpy(buf_tmp, buf + base + 2, 8);
+ if (!kstrtoul(buf_tmp, 16, &result)) {
+ write_da[loop_i] = (uint8_t)result;
+ write_da[loop_i+1] = (uint8_t)(result >> 8);
+ write_da[loop_i+2] = (uint8_t)(result >> 16);
+ write_da[loop_i+3] = (uint8_t)(result >> 24);
+ }
+ length+=4;
+ }
+ base += 10;
+ }
+ }
+ }
+ return len;
+}
+
+static const struct file_operations himax_proc_register_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_proc_register_read,
+ .write = himax_proc_register_write,
+};
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+int16_t *getMutualBuffer(void)
+{
+ return diag_mutual;
+}
+int16_t *getMutualNewBuffer(void)
+{
+ return diag_mutual_new;
+}
+int16_t *getMutualOldBuffer(void)
+{
+ return diag_mutual_old;
+}
+int16_t *getSelfBuffer(void)
+{
+ return &diag_self[0];
+}
+uint8_t getXChannel(void)
+{
+ return x_channel;
+}
+uint8_t getYChannel(void)
+{
+ return y_channel;
+}
+uint8_t getDiagCommand(void)
+{
+ return diag_command;
+}
+void setXChannel(uint8_t x)
+{
+ x_channel = x;
+}
+void setYChannel(uint8_t y)
+{
+ y_channel = y;
+}
+void setMutualBuffer(void)
+{
+ diag_mutual = kzalloc(x_channel * y_channel * sizeof(int16_t), GFP_KERNEL);
+}
+void setMutualNewBuffer(void)
+{
+ diag_mutual_new = kzalloc(x_channel * y_channel * sizeof(int16_t), GFP_KERNEL);
+}
+void setMutualOldBuffer(void)
+{
+ diag_mutual_old = kzalloc(x_channel * y_channel * sizeof(int16_t), GFP_KERNEL);
+}
+
+#ifdef HX_TP_PROC_2T2R
+int16_t *getMutualBuffer_2(void)
+{
+ return diag_mutual_2;
+}
+uint8_t getXChannel_2(void)
+{
+ return x_channel_2;
+}
+uint8_t getYChannel_2(void)
+{
+ return y_channel_2;
+}
+void setXChannel_2(uint8_t x)
+{
+ x_channel_2 = x;
+}
+void setYChannel_2(uint8_t y)
+{
+ y_channel_2 = y;
+}
+void setMutualBuffer_2(void)
+{
+ diag_mutual_2 = kzalloc(x_channel_2 * y_channel_2 * sizeof(int16_t), GFP_KERNEL);
+}
+#endif
+
+static ssize_t himax_diag_arrange_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ //struct himax_ts_data *ts = private_ts;
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ g_diag_arr_num = buf[0] - '0';
+ I("%s: g_diag_arr_num = %d \n", __func__,g_diag_arr_num);
+
+ return len;
+}
+
+static const struct file_operations himax_proc_diag_arrange_ops =
+{
+ .owner = THIS_MODULE,
+ .write = himax_diag_arrange_write,
+};
+
+static void himax_diag_arrange_print(struct seq_file *s, int i, int j, int transpose)
+{
+ if(transpose)
+ seq_printf(s, "%6d", diag_mutual[ j + i*x_channel]);
+ else
+ seq_printf(s, "%6d", diag_mutual[ i + j*x_channel]);
+}
+
+static void himax_diag_arrange_inloop(struct seq_file *s, int in_init,bool transpose, int j)
+{
+ int i;
+ int in_max = 0;
+
+ if(transpose)
+ in_max = y_channel;
+ else
+ in_max = x_channel;
+
+ if (in_init > 0)
+ {
+ for(i = in_init-1;i >= 0;i--)
+ {
+ himax_diag_arrange_print(s, i, j, transpose);
+ }
+ }
+ else
+ {
+ for (i = 0; i < in_max; i++)
+ {
+ himax_diag_arrange_print(s, i, j, transpose);
+ }
+ }
+}
+
+static void himax_diag_arrange_outloop(struct seq_file *s, int transpose, int out_init, int in_init)
+{
+ int j;
+ int out_max = 0;
+
+ if(transpose)
+ out_max = x_channel;
+ else
+ out_max = y_channel;
+
+ if(out_init > 0)
+ {
+ for(j = out_init-1;j >= 0;j--)
+ {
+ himax_diag_arrange_inloop(s, in_init, transpose, j);
+ seq_printf(s, " %5d\n", diag_self[j]);
+ }
+ }
+ else
+ {
+ for(j = 0;j < out_max;j++)
+ {
+ himax_diag_arrange_inloop(s, in_init, transpose, j);
+ seq_printf(s, " %5d\n", diag_self[j]);
+ }
+ }
+}
+
+static void himax_diag_arrange(struct seq_file *s)
+{
+ int bit2,bit1,bit0;
+ int i;
+
+ bit2 = g_diag_arr_num >> 2;
+ bit1 = g_diag_arr_num >> 1 & 0x1;
+ bit0 = g_diag_arr_num & 0x1;
+
+ if (g_diag_arr_num < 4)
+ {
+ himax_diag_arrange_outloop(s, bit2, bit1 * y_channel, bit0 * x_channel);
+ for (i = y_channel; i < x_channel + y_channel; i++) {
+ seq_printf(s, "%6d", diag_self[i]);
+ }
+ }
+ else
+ {
+ himax_diag_arrange_outloop(s, bit2, bit1 * x_channel, bit0 * y_channel);
+ for (i = x_channel; i < x_channel + y_channel; i++) {
+ seq_printf(s, "%6d", diag_self[i]);
+ }
+ }
+}
+
+static void *himax_diag_seq_start(struct seq_file *s, loff_t *pos)
+{
+ if (*pos>=1) return NULL;
+ return (void *)((unsigned long) *pos+1);
+}
+
+static void *himax_diag_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return NULL;
+}
+static void himax_diag_seq_stop(struct seq_file *s, void *v)
+{
+}
+static int himax_diag_seq_read(struct seq_file *s, void *v)
+{
+ size_t count = 0;
+ int32_t loop_i;//,loop_j
+ uint16_t mutual_num, self_num, width;
+
+#ifdef HX_TP_PROC_2T2R
+ if(Is_2T2R && diag_command == 4)
+ {
+ mutual_num = x_channel_2 * y_channel_2;
+ self_num = x_channel_2 + y_channel_2; //don't add KEY_COUNT
+ width = x_channel_2;
+ seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_channel_2, y_channel_2);
+ }
+ else
+#endif
+ {
+ mutual_num = x_channel * y_channel;
+ self_num = x_channel + y_channel; //don't add KEY_COUNT
+ width = x_channel;
+ seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_channel, y_channel);
+ }
+
+ // start to show out the raw data in adb shell
+ if (diag_command >= 1 && diag_command <= 6) {
+ if (diag_command <= 3) {
+ himax_diag_arrange(s);
+ seq_printf(s, "\n\n");
+#ifdef HX_EN_SEL_BUTTON
+ seq_printf(s, "\n");
+ for (loop_i = 0; loop_i < HX_BT_NUM; loop_i++)
+ seq_printf(s, "%6d", diag_self[HX_RX_NUM + HX_TX_NUM + loop_i]);
+#endif
+#ifdef HX_TP_PROC_2T2R
+ }else if(Is_2T2R && diag_command == 4 ) {
+ for (loop_i = 0; loop_i < mutual_num; loop_i++) {
+ seq_printf(s, "%4d", diag_mutual_2[loop_i]);
+ if ((loop_i % width) == (width - 1))
+ seq_printf(s, " %6d\n", diag_self[width + loop_i/width]);
+ }
+ seq_printf(s, "\n");
+ for (loop_i = 0; loop_i < width; loop_i++) {
+ seq_printf(s, "%6d", diag_self[loop_i]);
+ if (((loop_i) % width) == (width - 1))
+ seq_printf(s, "\n");
+ }
+#ifdef HX_EN_SEL_BUTTON
+ seq_printf(s, "\n");
+ for (loop_i = 0; loop_i < HX_BT_NUM; loop_i++)
+ seq_printf(s, "%4d", diag_self[HX_RX_NUM_2 + HX_TX_NUM_2 + loop_i]);
+#endif
+#endif
+ } else if (diag_command > 4) {
+ for (loop_i = 0; loop_i < self_num; loop_i++) {
+ seq_printf(s, "%4d", diag_self[loop_i]);
+ if (((loop_i - mutual_num) % width) == (width - 1))
+ seq_printf(s, "\n");
+ }
+ } else {
+ for (loop_i = 0; loop_i < mutual_num; loop_i++) {
+ seq_printf(s, "%4d", diag_mutual[loop_i]);
+ if ((loop_i % width) == (width - 1))
+ seq_printf(s, "\n");
+ }
+ }
+ seq_printf(s, "ChannelEnd");
+ seq_printf(s, "\n");
+ } else if (diag_command == 7) {
+ for (loop_i = 0; loop_i < 128 ;loop_i++) {
+ if ((loop_i % 16) == 0)
+ seq_printf(s, "LineStart:");
+ seq_printf(s, "%4d", diag_coor[loop_i]);
+ if ((loop_i % 16) == 15)
+ seq_printf(s, "\n");
+ }
+ } else if (diag_command == 9 || diag_command == 91 || diag_command == 92){
+ himax_diag_arrange(s);
+ seq_printf(s, "\n");
+ }
+
+ return count;
+}
+static const struct seq_operations himax_diag_seq_ops =
+{
+ .start = himax_diag_seq_start,
+ .next = himax_diag_seq_next,
+ .stop = himax_diag_seq_stop,
+ .show = himax_diag_seq_read,
+};
+static int himax_diag_proc_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &himax_diag_seq_ops);
+};
+bool DSRAM_Flag;
+
+//DSRAM thread
+void himax_ts_diag_func(void)
+{
+ int i=0, j=0;
+ unsigned int index = 0;
+ int total_size = ic_data->HX_TX_NUM * ic_data->HX_RX_NUM * 2;
+ uint8_t info_data[total_size];
+ int16_t *mutual_data = NULL;
+ int16_t *mutual_data_new = NULL;
+ int16_t *mutual_data_old = NULL;
+ int16_t new_data;
+
+ himax_burst_enable(private_ts->client, 1);
+ if(diag_command == 9 || diag_command == 91)
+ {
+ mutual_data = getMutualBuffer();
+ }else if(diag_command == 92){
+ mutual_data = getMutualBuffer();
+ mutual_data_new = getMutualNewBuffer();
+ mutual_data_old = getMutualOldBuffer();
+ }
+ himax_get_DSRAM_data(private_ts->client, info_data);
+
+ index = 0;
+ for (i = 0; i < ic_data->HX_TX_NUM; i++)
+ {
+ for (j = 0; j < ic_data->HX_RX_NUM; j++)
+ {
+ new_data = (short)(info_data[index + 1] << 8 | info_data[index]);
+ if(diag_command == 9){
+ mutual_data[i*ic_data->HX_RX_NUM+j] = new_data;
+ }else if(diag_command == 91){ //Keep max data for 100 frame
+ if(mutual_data[i * ic_data->HX_RX_NUM + j] < new_data)
+ mutual_data[i * ic_data->HX_RX_NUM + j] = new_data;
+ }else if(diag_command == 92){ //Cal data for [N]-[N-1] frame
+ mutual_data_new[i * ic_data->HX_RX_NUM + j] = new_data;
+ mutual_data[i * ic_data->HX_RX_NUM + j] = mutual_data_new[i * ic_data->HX_RX_NUM + j] - mutual_data_old[i * ic_data->HX_RX_NUM + j];
+ }
+ index += 2;
+ }
+ }
+ if(diag_command == 92){
+ memcpy(mutual_data_old,mutual_data_new,x_channel * y_channel * sizeof(int16_t)); //copy N data to N-1 array
+ }
+ diag_max_cnt++;
+ if(diag_command == 9 || diag_command == 92){
+ queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 1/10*HZ);
+ }else if(diag_command == 91){
+ if(diag_max_cnt > 100) //count for 100 frame
+ {
+ //Clear DSRAM flag
+ DSRAM_Flag = false;
+
+ //Enable ISR
+ himax_int_enable(private_ts->client->irq,1);
+
+ //=====================================
+ // test result command : 0x8002_0324 ==> 0x00
+ //=====================================
+ himax_diag_register_set(private_ts->client, 0x00);
+ }else{
+ queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 1/10*HZ);
+ }
+ }
+}
+
+static ssize_t himax_diag_write(struct file *filp, const char __user *buff, size_t len, loff_t *data)
+{
+ char messages[80] = {0};
+
+ uint8_t command[2] = {0x00, 0x00};
+ uint8_t receive[1];
+
+ memset(receive, 0x00, sizeof(receive));
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(messages, buff, len))
+ {
+ return -EFAULT;
+ }
+ if (messages[1] == 0x0A){
+ diag_command =messages[0] - '0';
+ }else{
+ diag_command =(messages[0] - '0')*10 + (messages[1] - '0');
+ }
+
+ I("[Himax]diag_command=0x%x\n",diag_command);
+ if (diag_command < 0x04){
+ if(DSRAM_Flag)
+ {
+ //1. Clear DSRAM flag
+ DSRAM_Flag = false;
+
+ //2. Stop DSRAM thread
+ cancel_delayed_work_sync(&private_ts->himax_diag_delay_wrok);
+
+ //3. Enable ISR
+ himax_int_enable(private_ts->client->irq,1);
+ }
+ command[0] = diag_command;
+ himax_diag_register_set(private_ts->client, command[0]);
+ }
+ //coordinate dump start
+ else if (diag_command == 0x08) {
+ E("%s: coordinate_dump_file_create error\n", __func__);
+ }
+ else if (diag_command == 0x09 || diag_command == 91 || diag_command == 92){
+ diag_max_cnt = 0;
+ memset(diag_mutual, 0x00, x_channel * y_channel * sizeof(int16_t)); //Set data 0 everytime
+
+ //1. Disable ISR
+ himax_int_enable(private_ts->client->irq,0);
+
+ //2. Start DSRAM thread
+ //himax_diag_register_set(private_ts->client, 0x0A);
+
+ queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 2*HZ/100);
+
+ I("%s: Start get raw data in DSRAM\n", __func__);
+
+ //3. Set DSRAM flag
+ DSRAM_Flag = true;
+ }else{
+ command[0] = 0x00;
+ himax_diag_register_set(private_ts->client, command[0]);
+ E("[Himax]Diag command error!diag_command=0x%x\n",diag_command);
+ }
+ return len;
+}
+
+static const struct file_operations himax_proc_diag_ops =
+{
+ .owner = THIS_MODULE,
+ .open = himax_diag_proc_open,
+ .read = seq_read,
+ .write = himax_diag_write,
+};
+#endif
+
+#ifdef HX_TP_PROC_RESET
+static ssize_t himax_reset_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ char buf_tmp[12];
+
+ if (len >= 12)
+ {
+ I("%s: no command exceeds 12 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf_tmp, buff, len))
+ {
+ return -EFAULT;
+ }
+ //if (buf_tmp[0] == '1')
+ // ESD_HW_REST();
+
+ return len;
+}
+
+static const struct file_operations himax_proc_reset_ops =
+{
+ .owner = THIS_MODULE,
+ .write = himax_reset_write,
+};
+#endif
+
+#ifdef HX_TP_PROC_DEBUG
+static ssize_t himax_debug_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ size_t count = 0;
+ char *temp_buf;
+
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf){
+ HX_PROC_SEND_FLAG=0;
+ return count;
+ }
+
+ if (debug_level_cmd == 't')
+ {
+ if (fw_update_complete)
+ count += snprintf(temp_buf+count, len-count, "FW Update Complete ");
+ else
+ {
+ count += snprintf(temp_buf+count, len-count, "FW Update Fail ");
+ }
+ }
+ else if (debug_level_cmd == 'h')
+ {
+ if (handshaking_result == 0)
+ {
+ count += snprintf(temp_buf+count, len-count, "Handshaking Result = %d (MCU Running)\n", handshaking_result);
+ }
+ else if (handshaking_result == 1)
+ {
+ count += snprintf(temp_buf+count, len-count, "Handshaking Result = %d (MCU Stop)\n", handshaking_result);
+ }
+ else if (handshaking_result == 2)
+ {
+ count += snprintf(temp_buf+count, len-count, "Handshaking Result = %d (I2C Error)\n", handshaking_result);
+ }
+ else
+ {
+ count += snprintf(temp_buf+count, len-count, "Handshaking Result = error\n");
+ }
+ }
+ else if (debug_level_cmd == 'v')
+ {
+ count += snprintf(temp_buf+count, len-count, "FW_VER = ");
+ count += snprintf(temp_buf+count, len-count, "0x%2.2X\n", ic_data->vendor_fw_ver);
+ count += snprintf(temp_buf+count, len-count, "CONFIG_VER = ");
+ count += snprintf(temp_buf+count, len-count, "0x%2.2X\n", ic_data->vendor_config_ver);
+ count += snprintf(temp_buf+count, len-count, "\n");
+ }
+ else if (debug_level_cmd == 'd')
+ {
+ count += snprintf(temp_buf+count, len-count, "Himax Touch IC Information :\n");
+ if (IC_TYPE == HX_85XX_D_SERIES_PWON)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Type : D\n");
+ }
+ else if (IC_TYPE == HX_85XX_E_SERIES_PWON)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Type : E\n");
+ }
+ else if (IC_TYPE == HX_85XX_ES_SERIES_PWON)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Type : ES\n");
+ }
+ else if (IC_TYPE == HX_85XX_F_SERIES_PWON)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Type : F\n");
+ }
+ else
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Type error.\n");
+ }
+
+ if (IC_CHECKSUM == HX_TP_BIN_CHECKSUM_SW)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Checksum : SW\n");
+ }
+ else if (IC_CHECKSUM == HX_TP_BIN_CHECKSUM_HW)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Checksum : HW\n");
+ }
+ else if (IC_CHECKSUM == HX_TP_BIN_CHECKSUM_CRC)
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Checksum : CRC\n");
+ }
+ else
+ {
+ count += snprintf(temp_buf+count, len-count, "IC Checksum error.\n");
+ }
+
+ if (ic_data->HX_INT_IS_EDGE)
+ {
+ count += snprintf(temp_buf+count, len-count, "Interrupt : EDGE TIRGGER\n");
+ }
+ else
+ {
+ count += snprintf(temp_buf+count, len-count, "Interrupt : LEVEL TRIGGER\n");
+ }
+
+ count += snprintf(temp_buf+count, len-count, "RX Num : %d\n", ic_data->HX_RX_NUM);
+ count += snprintf(temp_buf+count, len-count, "TX Num : %d\n", ic_data->HX_TX_NUM);
+ count += snprintf(temp_buf+count, len-count, "BT Num : %d\n", ic_data->HX_BT_NUM);
+ count += snprintf(temp_buf+count, len-count, "X Resolution : %d\n", ic_data->HX_X_RES);
+ count += snprintf(temp_buf+count, len-count, "Y Resolution : %d\n", ic_data->HX_Y_RES);
+ count += snprintf(temp_buf+count, len-count, "Max Point : %d\n", ic_data->HX_MAX_PT);
+ count += snprintf(temp_buf+count, len-count, "XY reverse : %d\n", ic_data->HX_XY_REVERSE);
+ #ifdef HX_TP_PROC_2T2R
+ if(Is_2T2R)
+ {
+ count += snprintf(temp_buf+count, len-count, "2T2R panel\n");
+ count += snprintf(temp_buf+count, len-count, "RX Num_2 : %d\n", HX_RX_NUM_2);
+ count += snprintf(temp_buf+count, len-count, "TX Num_2 : %d\n", HX_TX_NUM_2);
+ }
+ #endif
+ }
+ else if (debug_level_cmd == 'i')
+ {
+ count += snprintf(temp_buf+count, len-count, "Himax Touch Driver Version:\n");
+ count += snprintf(temp_buf+count, len-count, "%s\n", HIMAX_DRIVER_VER);
+ }
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG=1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+ return count;
+}
+
+static ssize_t himax_debug_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ const struct firmware *fw = NULL;
+ unsigned char *fw_data = NULL;
+ char fileName[128];
+ char buf[80] = {0};
+ int result;
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ if ( buf[0] == 'h') //handshaking
+ {
+ debug_level_cmd = buf[0];
+
+ himax_int_enable(private_ts->client->irq,0);
+
+ handshaking_result = himax_hand_shaking(private_ts->client); //0:Running, 1:Stop, 2:I2C Fail
+
+ himax_int_enable(private_ts->client->irq,1);
+
+ return len;
+ }
+
+ else if ( buf[0] == 'v') //firmware version
+ {
+ debug_level_cmd = buf[0];
+ himax_int_enable(private_ts->client->irq,0);
+#ifdef HX_RST_PIN_FUNC
+ himax_HW_reset(false,false);
+#endif
+ himax_read_FW_ver(private_ts->client);
+ //himax_check_chip_version();
+#ifdef HX_RST_PIN_FUNC
+ himax_HW_reset(true,false);
+#endif
+ himax_int_enable(private_ts->client->irq,1);
+ return len;
+ }
+
+ else if ( buf[0] == 'd') //ic information
+ {
+ debug_level_cmd = buf[0];
+ return len;
+ }
+
+ else if ( buf[0] == 'i') //driver version
+ {
+ debug_level_cmd = buf[0];
+ return len;
+ }
+
+ else if (buf[0] == 't')
+ {
+
+ himax_int_enable(private_ts->client->irq,0);
+
+ debug_level_cmd = buf[0];
+ fw_update_complete = false;
+
+ memset(fileName, 0, 128);
+ // parse the file name
+ snprintf(fileName, len-4, "%s", &buf[4]);
+ I("%s: upgrade from file(%s) start!\n", __func__, fileName);
+ // open file
+ result = request_firmware(&fw, fileName, private_ts->dev);
+ if (result) {
+ E("%s: open firmware file failed\n", __func__);
+ goto firmware_upgrade_done;
+ //return len;
+ }
+
+ I("%s: FW len %d\n", __func__, fw->size);
+ fw_data = (unsigned char *)fw->data;
+
+ I("%s: FW image,len %d: %02X, %02X, %02X, %02X\n", __func__, result, upgrade_fw[0], upgrade_fw[1], upgrade_fw[2], upgrade_fw[3]);
+
+ if (fw_data != NULL)
+ {
+ // start to upgrade
+ himax_int_enable(private_ts->client->irq,0);
+
+ if ((buf[1] == '6') && (buf[2] == '0'))
+ {
+ if (fts_ctpm_fw_upgrade_with_sys_fs_60k(private_ts->client,upgrade_fw, result, false) == 0)
+ {
+ E("%s: TP upgrade error, line: %d\n", __func__, __LINE__);
+ fw_update_complete = false;
+ }
+ else
+ {
+ I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__);
+ fw_update_complete = true;
+ }
+ }
+ else if ((buf[1] == '6') && (buf[2] == '4'))
+ {
+ if (fts_ctpm_fw_upgrade_with_sys_fs_64k(private_ts->client,upgrade_fw, result, false) == 0)
+ {
+ E("%s: TP upgrade error, line: %d\n", __func__, __LINE__);
+ fw_update_complete = false;
+ }
+ else
+ {
+ I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__);
+ fw_update_complete = true;
+ }
+ }
+ else if ((buf[1] == '2') && (buf[2] == '4'))
+ {
+ if (fts_ctpm_fw_upgrade_with_sys_fs_124k(private_ts->client,upgrade_fw, result, false) == 0)
+ {
+ E("%s: TP upgrade error, line: %d\n", __func__, __LINE__);
+ fw_update_complete = false;
+ }
+ else
+ {
+ I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__);
+ fw_update_complete = true;
+ }
+ }
+ else if ((buf[1] == '2') && (buf[2] == '8'))
+ {
+ if (fts_ctpm_fw_upgrade_with_sys_fs_128k(private_ts->client,upgrade_fw, result, false) == 0)
+ {
+ E("%s: TP upgrade error, line: %d\n", __func__, __LINE__);
+ fw_update_complete = false;
+ }
+ else
+ {
+ I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__);
+ fw_update_complete = true;
+ }
+ }
+ else
+ {
+ E("%s: Flash command fail: %d\n", __func__, __LINE__);
+ fw_update_complete = false;
+ }
+ release_firmware(fw);
+ goto firmware_upgrade_done;
+ //return count;
+ }
+ }
+
+ firmware_upgrade_done:
+
+#ifdef HX_RST_PIN_FUNC
+ himax_HW_reset(true,false);
+#endif
+
+ himax_sense_on(private_ts->client, 0x01);
+ msleep(120);
+#ifdef HX_ESD_WORKAROUND
+ HX_ESD_RESET_ACTIVATE = 1;
+#endif
+ himax_int_enable(private_ts->client->irq,1);
+
+ //todo himax_chip->tp_firmware_upgrade_proceed = 0;
+ //todo himax_chip->suspend_state = 0;
+ //todo enable_irq(himax_chip->irq);
+ return len;
+}
+
+static const struct file_operations himax_proc_debug_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_debug_read,
+ .write = himax_debug_write,
+};
+
+#endif
+
+#ifdef HX_TP_PROC_FLASH_DUMP
+
+static uint8_t getFlashCommand(void)
+{
+ return flash_command;
+}
+
+static uint8_t getFlashDumpProgress(void)
+{
+ return flash_progress;
+}
+
+static uint8_t getFlashDumpComplete(void)
+{
+ return flash_dump_complete;
+}
+
+static uint8_t getFlashDumpFail(void)
+{
+ return flash_dump_fail;
+}
+
+uint8_t getSysOperation(void)
+{
+ return sys_operation;
+}
+
+static uint8_t getFlashReadStep(void)
+{
+ return flash_read_step;
+}
+/*
+static uint8_t getFlashDumpSector(void)
+{
+ return flash_dump_sector;
+}
+
+static uint8_t getFlashDumpPage(void)
+{
+ return flash_dump_page;
+}
+*/
+bool getFlashDumpGoing(void)
+{
+ return flash_dump_going;
+}
+
+void setFlashBuffer(void)
+{
+ flash_buffer = kzalloc(Flash_Size * sizeof(uint8_t), GFP_KERNEL);
+ if (flash_buffer)
+ memset(flash_buffer,0x00,Flash_Size);
+}
+
+void setSysOperation(uint8_t operation)
+{
+ sys_operation = operation;
+}
+
+static void setFlashDumpProgress(uint8_t progress)
+{
+ flash_progress = progress;
+ //I("setFlashDumpProgress : progress = %d ,flash_progress = %d \n",progress,flash_progress);
+}
+
+static void setFlashDumpComplete(uint8_t status)
+{
+ flash_dump_complete = status;
+}
+
+static void setFlashDumpFail(uint8_t fail)
+{
+ flash_dump_fail = fail;
+}
+
+static void setFlashCommand(uint8_t command)
+{
+ flash_command = command;
+}
+
+static void setFlashReadStep(uint8_t step)
+{
+ flash_read_step = step;
+}
+
+static void setFlashDumpSector(uint8_t sector)
+{
+ flash_dump_sector = sector;
+}
+
+static void setFlashDumpPage(uint8_t page)
+{
+ flash_dump_page = page;
+}
+
+static void setFlashDumpGoing(bool going)
+{
+ flash_dump_going = going;
+}
+
+static ssize_t himax_proc_flash_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ int ret = 0;
+ int loop_i;
+ uint8_t local_flash_read_step=0;
+ uint8_t local_flash_complete = 0;
+ uint8_t local_flash_progress = 0;
+ uint8_t local_flash_command = 0;
+ uint8_t local_flash_fail = 0;
+ char *temp_buf;
+ local_flash_complete = getFlashDumpComplete();
+ local_flash_progress = getFlashDumpProgress();
+ local_flash_command = getFlashCommand();
+ local_flash_fail = getFlashDumpFail();
+
+ I("flash_progress = %d \n",local_flash_progress);
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+
+ if (local_flash_fail)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "FlashStart:Fail \n");
+ ret += snprintf(temp_buf+ret, len-ret, "FlashEnd");
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ return ret;
+ }
+
+ if (!local_flash_complete)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "FlashStart:Ongoing:0x%2.2x \n",flash_progress);
+ ret += snprintf(temp_buf+ret, len-ret, "FlashEnd");
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ return ret;
+ }
+
+ if (local_flash_command == 1 && local_flash_complete)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "FlashStart:Complete \n");
+ ret += snprintf(temp_buf+ret, len-ret, "FlashEnd");
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ return ret;
+ }
+
+ if (local_flash_command == 3 && local_flash_complete)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "FlashStart: \n");
+ for(loop_i = 0; loop_i < 128; loop_i++)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "x%2.2x", flash_buffer[loop_i]);
+ if ((loop_i % 16) == 15)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+ }
+ }
+ ret += snprintf(temp_buf+ret, len-ret, "FlashEnd");
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ return ret;
+ }
+
+ //flash command == 0 , report the data
+ local_flash_read_step = getFlashReadStep();
+
+ ret += snprintf(temp_buf+ret, len-ret, "FlashStart:%2.2x \n",local_flash_read_step);
+
+ for (loop_i = 0; loop_i < 1024; loop_i++)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "x%2.2X", flash_buffer[local_flash_read_step*1024 + loop_i]);
+
+ if ((loop_i % 16) == 15)
+ {
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+ }
+ }
+
+ ret += snprintf(temp_buf+ret, len-ret, "FlashEnd");
+ ret += snprintf(temp_buf+ret, len-ret, "\n");
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+}
+
+static ssize_t himax_proc_flash_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ char buf_tmp[6];
+ unsigned long result = 0;
+ uint8_t loop_i = 0;
+ int base = 0;
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+ memset(buf_tmp, 0x0, sizeof(buf_tmp));
+
+ I("%s: buf[0] = %s\n", __func__, buf);
+
+ if (getSysOperation() == 1)
+ {
+ E("%s: PROC is busy , return!\n", __func__);
+ return len;
+ }
+
+ if (buf[0] == '0')
+ {
+ setFlashCommand(0);
+ if (buf[1] == ':' && buf[2] == 'x')
+ {
+ memcpy(buf_tmp, buf + 3, 2);
+ I("%s: read_Step = %s\n", __func__, buf_tmp);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ I("%s: read_Step = %lu \n", __func__, result);
+ setFlashReadStep(result);
+ }
+ }
+ }
+ else if (buf[0] == '1')// 1_60,1_64,1_24,1_28 for flash size 60k,64k,124k,128k
+ {
+ setSysOperation(1);
+ setFlashCommand(1);
+ setFlashDumpProgress(0);
+ setFlashDumpComplete(0);
+ setFlashDumpFail(0);
+ if ((buf[1] == '_' ) && (buf[2] == '6' )){
+ if (buf[3] == '0'){
+ Flash_Size = FW_SIZE_60k;
+ }else if (buf[3] == '4'){
+ Flash_Size = FW_SIZE_64k;
+ }
+ }else if ((buf[1] == '_' ) && (buf[2] == '2' )){
+ if (buf[3] == '4'){
+ Flash_Size = FW_SIZE_124k;
+ }else if (buf[3] == '8'){
+ Flash_Size = FW_SIZE_128k;
+ }
+ }
+ queue_work(private_ts->flash_wq, &private_ts->flash_work);
+ }
+ else if (buf[0] == '2') // 2_60,2_64,2_24,2_28 for flash size 60k,64k,124k,128k
+ {
+ setSysOperation(1);
+ setFlashCommand(2);
+ setFlashDumpProgress(0);
+ setFlashDumpComplete(0);
+ setFlashDumpFail(0);
+ if ((buf[1] == '_' ) && (buf[2] == '6' )){
+ if (buf[3] == '0'){
+ Flash_Size = FW_SIZE_60k;
+ }else if (buf[3] == '4'){
+ Flash_Size = FW_SIZE_64k;
+ }
+ }else if ((buf[1] == '_' ) && (buf[2] == '2' )){
+ if (buf[3] == '4'){
+ Flash_Size = FW_SIZE_124k;
+ }else if (buf[3] == '8'){
+ Flash_Size = FW_SIZE_128k;
+ }
+ }
+ queue_work(private_ts->flash_wq, &private_ts->flash_work);
+ }
+ else if (buf[0] == '3')
+ {
+ setSysOperation(1);
+ setFlashCommand(3);
+ setFlashDumpProgress(0);
+ setFlashDumpComplete(0);
+ setFlashDumpFail(0);
+
+ memcpy(buf_tmp, buf + 3, 2);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ setFlashDumpSector(result);
+ }
+
+ memcpy(buf_tmp, buf + 7, 2);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ setFlashDumpPage(result);
+ }
+
+ queue_work(private_ts->flash_wq, &private_ts->flash_work);
+ }
+ else if (buf[0] == '4')
+ {
+ I("%s: command 4 enter.\n", __func__);
+ setSysOperation(1);
+ setFlashCommand(4);
+ setFlashDumpProgress(0);
+ setFlashDumpComplete(0);
+ setFlashDumpFail(0);
+
+ memcpy(buf_tmp, buf + 3, 2);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ setFlashDumpSector(result);
+ }
+ else
+ {
+ E("%s: command 4 , sector error.\n", __func__);
+ return len;
+ }
+
+ memcpy(buf_tmp, buf + 7, 2);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ setFlashDumpPage(result);
+ }
+ else
+ {
+ E("%s: command 4 , page error.\n", __func__);
+ return len;
+ }
+
+ base = 11;
+
+ I("=========Himax flash page buffer start=========\n");
+ for(loop_i=0;loop_i<128 && base<80;loop_i++)
+ {
+ memcpy(buf_tmp, buf + base, 2);
+ if (!kstrtoul(buf_tmp, 16, &result))
+ {
+ flash_buffer[loop_i] = result;
+ I("%d ",flash_buffer[loop_i]);
+ if (loop_i % 16 == 15)
+ {
+ I("\n");
+ }
+ }
+ base += 3;
+ }
+ I("=========Himax flash page buffer end=========\n");
+
+ queue_work(private_ts->flash_wq, &private_ts->flash_work);
+ }
+ return len;
+}
+
+static const struct file_operations himax_proc_flash_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_proc_flash_read,
+ .write = himax_proc_flash_write,
+};
+
+void himax_ts_flash_func(void)
+{
+ uint8_t local_flash_command = 0;
+
+ himax_int_enable(private_ts->client->irq,0);
+ setFlashDumpGoing(true);
+
+ //sector = getFlashDumpSector();
+ //page = getFlashDumpPage();
+
+ local_flash_command = getFlashCommand();
+
+ msleep(100);
+
+ I("%s: local_flash_command = %d enter.\n", __func__,local_flash_command);
+
+ if ((local_flash_command == 1 || local_flash_command == 2)|| (local_flash_command==0x0F))
+ {
+ himax_flash_dump_func(private_ts->client, local_flash_command,Flash_Size, flash_buffer);
+ }
+
+ I("Complete~~~~~~~~~~~~~~~~~~~~~~~\n");
+
+ if (local_flash_command == 2)
+ {
+ E("Flash dump failed\n");
+ }
+
+ himax_int_enable(private_ts->client->irq,1);
+ setFlashDumpGoing(false);
+
+ setFlashDumpComplete(1);
+ setSysOperation(0);
+ return;
+
+/* Flash_Dump_i2c_transfer_error:
+
+ himax_int_enable(private_ts->client->irq,1);
+ setFlashDumpGoing(false);
+ setFlashDumpComplete(0);
+ setFlashDumpFail(1);
+ setSysOperation(0);
+ return;
+*/
+}
+
+#endif
+
+#ifdef HX_TP_PROC_SELF_TEST
+static ssize_t himax_self_test_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ int val=0x00;
+ int ret = 0;
+ char *temp_buf;
+
+ I("%s: enter, %d \n", __func__, __LINE__);
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ himax_int_enable(private_ts->client->irq,0);//disable irq
+ val = himax_chip_self_test(private_ts->client);
+#ifdef HX_ESD_WORKAROUND
+ HX_ESD_RESET_ACTIVATE = 1;
+#endif
+ himax_int_enable(private_ts->client->irq,1);//enable irq
+
+ if (val == 0x01) {
+ ret += snprintf(temp_buf+ret, len-ret, "Self_Test Pass\n");
+ } else {
+ ret += snprintf(temp_buf+ret, len-ret, "Self_Test Fail\n");
+ }
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+}
+
+/*
+static ssize_t himax_chip_self_test_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
+{
+ char buf_tmp[2];
+ unsigned long result = 0;
+
+ memset(buf_tmp, 0x0, sizeof(buf_tmp));
+ memcpy(buf_tmp, buf, 2);
+ if(!kstrtoul(buf_tmp, 16, &result))
+ {
+ sel_type = (uint8_t)result;
+ }
+ I("sel_type = %x \r\n", sel_type);
+ return count;
+}
+*/
+
+static const struct file_operations himax_proc_self_test_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_self_test_read,
+};
+#endif
+
+#ifdef HX_TP_PROC_SENSE_ON_OFF
+static ssize_t himax_sense_on_off_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ if(buf[0] == '0')
+ {
+ himax_sense_off(private_ts->client);
+ I("Sense off \n");
+ }
+ else if(buf[0] == '1')
+ {
+ if(buf[1] == '1'){
+ himax_sense_on(private_ts->client, 0x01);
+ I("Sense on re-map off, run flash \n");
+ }else if(buf[1] == '0'){
+ himax_sense_on(private_ts->client, 0x00);
+ I("Sense on re-map on, run sram \n");
+ }else{
+ I("Do nothing \n");
+ }
+ }
+ else
+ {
+ I("Do nothing \n");
+ }
+ return len;
+}
+
+static const struct file_operations himax_proc_sense_on_off_ops =
+{
+ .owner = THIS_MODULE,
+ .write = himax_sense_on_off_write,
+};
+#endif
+
+#ifdef HX_HIGH_SENSE
+static ssize_t himax_HSEN_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ size_t count = 0;
+ char *temp_buf;
+
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return count;
+ }
+ count = snprintf(temp_buf, len, "%d\n", ts->HSEN_enable);
+ HX_PROC_SEND_FLAG=1;
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+ return count;
+}
+
+static ssize_t himax_HSEN_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ char buf[80] = {0};
+
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ if (buf[0] == '0'){
+ ts->HSEN_enable = 0;
+ }
+ else if (buf[0] == '1'){
+ ts->HSEN_enable = 1;
+ }
+ else
+ return -EINVAL;
+
+ himax_set_HSEN_func(ts->client, ts->HSEN_enable);
+
+ I("%s: HSEN_enable = %d.\n", __func__, ts->HSEN_enable);
+
+ return len;
+}
+
+static const struct file_operations himax_proc_HSEN_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_HSEN_read,
+ .write = himax_HSEN_write,
+};
+#endif
+
+#ifdef HX_SMART_WAKEUP
+static ssize_t himax_SMWP_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ size_t count = 0;
+ struct himax_ts_data *ts = private_ts;
+ char *temp_buf;
+
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return count;
+ }
+ count = snprintf(temp_buf, len, "%d\n", ts->SMWP_enable);
+
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG=1;
+ }
+ else
+ HX_PROC_SEND_FLAG=0;
+
+ return count;
+}
+
+static ssize_t himax_SMWP_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+
+ if (buf[0] == '0')
+ {
+ ts->SMWP_enable = 0;
+ }
+ else if (buf[0] == '1')
+ {
+ ts->SMWP_enable = 1;
+ }
+ else
+ return -EINVAL;
+
+ himax_set_SMWP_func(ts->client, ts->SMWP_enable);
+ HX_SMWP_EN = ts->SMWP_enable;
+ I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN);
+
+ return len;
+}
+
+static const struct file_operations himax_proc_SMWP_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_SMWP_read,
+ .write = himax_SMWP_write,
+};
+
+static ssize_t himax_GESTURE_read(struct file *file, char *buf,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ int i =0;
+ int ret = 0;
+ char *temp_buf;
+
+ if(!HX_PROC_SEND_FLAG)
+ {
+ temp_buf = kzalloc(len, GFP_KERNEL);
+ if (!temp_buf) {
+ HX_PROC_SEND_FLAG=0;
+ return ret;
+ }
+ for(i=0;i<16;i++)
+ ret += snprintf(temp_buf+ret, len-ret, "ges_en[%d]=%d\n", i, ts->gesture_cust_en[i]);
+ HX_PROC_SEND_FLAG = 1;
+ if (copy_to_user(buf, temp_buf, len))
+ {
+ I("%s,here:%d\n", __func__, __LINE__);
+ }
+
+ kfree(temp_buf);
+ HX_PROC_SEND_FLAG = 1;
+ }
+ else
+ {
+ HX_PROC_SEND_FLAG = 0;
+ ret = 0;
+ }
+ return ret;
+}
+
+static ssize_t himax_GESTURE_write(struct file *file, const char *buff,
+ size_t len, loff_t *pos)
+{
+ struct himax_ts_data *ts = private_ts;
+ int i =0;
+ char buf[80] = {0};
+
+ if (len >= 80)
+ {
+ I("%s: no command exceeds 80 chars.\n", __func__);
+ return -EFAULT;
+ }
+ if (copy_from_user(buf, buff, len))
+ {
+ return -EFAULT;
+ }
+
+ I("himax_GESTURE_store= %s \n",buf);
+ for (i=0;i<16;i++)
+ {
+ if (buf[i] == '0')
+ ts->gesture_cust_en[i]= 0;
+ else if (buf[i] == '1')
+ ts->gesture_cust_en[i]= 1;
+ else
+ ts->gesture_cust_en[i]= 0;
+ I("gesture en[%d]=%d \n", i, ts->gesture_cust_en[i]);
+ }
+ return len;
+}
+
+static const struct file_operations himax_proc_Gesture_ops =
+{
+ .owner = THIS_MODULE,
+ .read = himax_GESTURE_read,
+ .write = himax_GESTURE_write,
+};
+#endif
+
+int himax_touch_proc_init(void)
+{
+ himax_touch_proc_dir = proc_mkdir( HIMAX_PROC_TOUCH_FOLDER, NULL);
+ if (himax_touch_proc_dir == NULL)
+ {
+ E(" %s: himax_touch_proc_dir file create failed!\n", __func__);
+ return -ENOMEM;
+ }
+
+ himax_proc_debug_level_file = proc_create(HIMAX_PROC_DEBUG_LEVEL_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_debug_level_ops);
+ if (himax_proc_debug_level_file == NULL)
+ {
+ E(" %s: proc debug_level file create failed!\n", __func__);
+ goto fail_1;
+ }
+
+ himax_proc_vendor_file = proc_create(HIMAX_PROC_VENDOR_FILE, (S_IRUGO),himax_touch_proc_dir, &himax_proc_vendor_ops);
+ if(himax_proc_vendor_file == NULL)
+ {
+ E(" %s: proc vendor file create failed!\n", __func__);
+ goto fail_2;
+ }
+
+ himax_proc_attn_file = proc_create(HIMAX_PROC_ATTN_FILE, (S_IRUGO),himax_touch_proc_dir, &himax_proc_attn_ops);
+ if(himax_proc_attn_file == NULL)
+ {
+ E(" %s: proc attn file create failed!\n", __func__);
+ goto fail_3;
+ }
+
+ himax_proc_int_en_file = proc_create(HIMAX_PROC_INT_EN_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_int_en_ops);
+ if(himax_proc_int_en_file == NULL)
+ {
+ E(" %s: proc int en file create failed!\n", __func__);
+ goto fail_4;
+ }
+
+ himax_proc_layout_file = proc_create(HIMAX_PROC_LAYOUT_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_layout_ops);
+ if(himax_proc_layout_file == NULL)
+ {
+ E(" %s: proc layout file create failed!\n", __func__);
+ goto fail_5;
+ }
+
+#ifdef HX_TP_PROC_RESET
+ himax_proc_reset_file = proc_create(HIMAX_PROC_RESET_FILE, (S_IWUSR), himax_touch_proc_dir, &himax_proc_reset_ops);
+ if(himax_proc_reset_file == NULL)
+ {
+ E(" %s: proc reset file create failed!\n", __func__);
+ goto fail_6;
+ }
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+ himax_proc_diag_file = proc_create(HIMAX_PROC_DIAG_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_diag_ops);
+ if(himax_proc_diag_file == NULL)
+ {
+ E(" %s: proc diag file create failed!\n", __func__);
+ goto fail_7;
+ }
+ himax_proc_diag_arrange_file = proc_create(HIMAX_PROC_DIAG_ARR_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_diag_arrange_ops);
+ if(himax_proc_diag_arrange_file == NULL)
+ {
+ E(" %s: proc diag file create failed!\n", __func__);
+ goto fail_7_1;
+ }
+#endif
+
+#ifdef HX_TP_PROC_REGISTER
+ himax_proc_register_file = proc_create(HIMAX_PROC_REGISTER_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_register_ops);
+ if(himax_proc_register_file == NULL)
+ {
+ E(" %s: proc register file create failed!\n", __func__);
+ goto fail_8;
+ }
+#endif
+
+#ifdef HX_TP_PROC_DEBUG
+ himax_proc_debug_file = proc_create(HIMAX_PROC_DEBUG_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_debug_ops);
+ if(himax_proc_debug_file == NULL)
+ {
+ E(" %s: proc debug file create failed!\n", __func__);
+ goto fail_9;
+ }
+#endif
+
+#ifdef HX_TP_PROC_FLASH_DUMP
+ himax_proc_flash_dump_file = proc_create(HIMAX_PROC_FLASH_DUMP_FILE, (S_IWUSR|S_IRUGO), himax_touch_proc_dir, &himax_proc_flash_ops);
+ if(himax_proc_flash_dump_file == NULL)
+ {
+ E(" %s: proc flash dump file create failed!\n", __func__);
+ goto fail_10;
+ }
+#endif
+
+#ifdef HX_TP_PROC_SELF_TEST
+ himax_proc_self_test_file = proc_create(HIMAX_PROC_SELF_TEST_FILE, (S_IRUGO), himax_touch_proc_dir, &himax_proc_self_test_ops);
+ if(himax_proc_self_test_file == NULL)
+ {
+ E(" %s: proc self_test file create failed!\n", __func__);
+ goto fail_11;
+ }
+#endif
+
+#ifdef HX_HIGH_SENSE
+ himax_proc_HSEN_file = proc_create(HIMAX_PROC_HSEN_FILE, (S_IWUSR|S_IRUGO|S_IWUGO), himax_touch_proc_dir, &himax_proc_HSEN_ops);
+ if(himax_proc_HSEN_file == NULL)
+ {
+ E(" %s: proc HSEN file create failed!\n", __func__);
+ goto fail_12;
+ }
+#endif
+
+#ifdef HX_SMART_WAKEUP
+ himax_proc_SMWP_file = proc_create(HIMAX_PROC_SMWP_FILE, (S_IWUSR|S_IRUGO|S_IWUGO), himax_touch_proc_dir, &himax_proc_SMWP_ops);
+ if(himax_proc_SMWP_file == NULL)
+ {
+ E(" %s: proc SMWP file create failed!\n", __func__);
+ goto fail_13;
+ }
+ himax_proc_GESTURE_file = proc_create(HIMAX_PROC_GESTURE_FILE, (S_IWUSR|S_IRUGO|S_IWUGO), himax_touch_proc_dir, &himax_proc_Gesture_ops);
+ if(himax_proc_GESTURE_file == NULL)
+ {
+ E(" %s: proc GESTURE file create failed!\n", __func__);
+ goto fail_14;
+ }
+#endif
+
+#ifdef HX_TP_PROC_SENSE_ON_OFF
+ himax_proc_SENSE_ON_OFF_file = proc_create(HIMAX_PROC_SENSE_ON_OFF_FILE, (S_IWUSR|S_IRUGO|S_IWUGO), himax_touch_proc_dir, &himax_proc_sense_on_off_ops);
+ if(himax_proc_SENSE_ON_OFF_file == NULL)
+ {
+ E(" %s: proc SENSE_ON_OFF file create failed!\n", __func__);
+ goto fail_15;
+ }
+#endif
+
+ return 0 ;
+
+#ifdef HX_TP_PROC_SENSE_ON_OFF
+ fail_15:
+#endif
+#ifdef HX_SMART_WAKEUP
+ remove_proc_entry( HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir );
+ fail_14:
+ remove_proc_entry( HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir );
+ fail_13:
+#endif
+#ifdef HX_HIGH_SENSE
+ remove_proc_entry( HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir );
+ fail_12:
+#endif
+#ifdef HX_TP_PROC_SELF_TEST
+ remove_proc_entry( HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir );
+ fail_11:
+#endif
+#ifdef HX_TP_PROC_FLASH_DUMP
+ remove_proc_entry( HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir );
+ fail_10:
+#endif
+#ifdef HX_TP_PROC_DEBUG
+ remove_proc_entry( HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir );
+ fail_9:
+#endif
+#ifdef HX_TP_PROC_REGISTER
+ remove_proc_entry( HIMAX_PROC_REGISTER_FILE, himax_touch_proc_dir );
+ fail_8:
+#endif
+#ifdef HX_TP_PROC_DIAG
+ remove_proc_entry( HIMAX_PROC_DIAG_FILE, himax_touch_proc_dir );
+ fail_7:
+ remove_proc_entry( HIMAX_PROC_DIAG_ARR_FILE, himax_touch_proc_dir );
+ fail_7_1:
+#endif
+#ifdef HX_TP_PROC_RESET
+ remove_proc_entry( HIMAX_PROC_RESET_FILE, himax_touch_proc_dir );
+ fail_6:
+#endif
+ remove_proc_entry( HIMAX_PROC_LAYOUT_FILE, himax_touch_proc_dir );
+ fail_5: remove_proc_entry( HIMAX_PROC_INT_EN_FILE, himax_touch_proc_dir );
+ fail_4: remove_proc_entry( HIMAX_PROC_ATTN_FILE, himax_touch_proc_dir );
+ fail_3: remove_proc_entry( HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir );
+ fail_2: remove_proc_entry( HIMAX_PROC_DEBUG_LEVEL_FILE, himax_touch_proc_dir );
+ fail_1: remove_proc_entry( HIMAX_PROC_TOUCH_FOLDER, NULL );
+ return -ENOMEM;
+}
+
+void himax_touch_proc_deinit(void)
+{
+#ifdef HX_TP_PROC_SENSE_ON_OFF
+ remove_proc_entry( HIMAX_PROC_SENSE_ON_OFF_FILE, himax_touch_proc_dir );
+#endif
+#ifdef HX_SMART_WAKEUP
+ remove_proc_entry( HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir );
+ remove_proc_entry( HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir );
+#endif
+#ifdef HX_DOT_VIEW
+ remove_proc_entry( HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir );
+#endif
+#ifdef HX_TP_PROC_SELF_TEST
+ remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir);
+#endif
+#ifdef HX_TP_PROC_FLASH_DUMP
+ remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir);
+#endif
+#ifdef HX_TP_PROC_DEBUG
+ remove_proc_entry( HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir );
+#endif
+#ifdef HX_TP_PROC_REGISTER
+ remove_proc_entry(HIMAX_PROC_REGISTER_FILE, himax_touch_proc_dir);
+#endif
+#ifdef HX_TP_PROC_DIAG
+ remove_proc_entry(HIMAX_PROC_DIAG_FILE, himax_touch_proc_dir);
+#endif
+#ifdef HX_TP_PROC_RESET
+ remove_proc_entry( HIMAX_PROC_RESET_FILE, himax_touch_proc_dir );
+#endif
+ remove_proc_entry( HIMAX_PROC_LAYOUT_FILE, himax_touch_proc_dir );
+ remove_proc_entry( HIMAX_PROC_INT_EN_FILE, himax_touch_proc_dir );
+ remove_proc_entry( HIMAX_PROC_ATTN_FILE, himax_touch_proc_dir );
+ remove_proc_entry( HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir );
+ remove_proc_entry( HIMAX_PROC_DEBUG_LEVEL_FILE, himax_touch_proc_dir );
+ remove_proc_entry( HIMAX_PROC_TOUCH_FOLDER, NULL );
+}
+#endif
diff --git a/drivers/input/touchscreen/hxchipset/himax_debug.h b/drivers/input/touchscreen/hxchipset/himax_debug.h
new file mode 100644
index 0000000..91a7ae2
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_debug.h
@@ -0,0 +1,181 @@
+/* Himax Android Driver Sample Code for Himax chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include "himax_platform.h"
+#include "himax_common.h"
+
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+ #define HIMAX_PROC_TOUCH_FOLDER "android_touch"
+ #define HIMAX_PROC_DEBUG_LEVEL_FILE "debug_level"
+ #define HIMAX_PROC_VENDOR_FILE "vendor"
+ #define HIMAX_PROC_ATTN_FILE "attn"
+ #define HIMAX_PROC_INT_EN_FILE "int_en"
+ #define HIMAX_PROC_LAYOUT_FILE "layout"
+
+ static struct proc_dir_entry *himax_touch_proc_dir;
+ static struct proc_dir_entry *himax_proc_debug_level_file;
+ static struct proc_dir_entry *himax_proc_vendor_file;
+ static struct proc_dir_entry *himax_proc_attn_file;
+ static struct proc_dir_entry *himax_proc_int_en_file;
+ static struct proc_dir_entry *himax_proc_layout_file;
+
+ uint8_t HX_PROC_SEND_FLAG;
+
+extern int himax_touch_proc_init(void);
+extern void himax_touch_proc_deinit(void);
+bool getFlashDumpGoing(void);
+
+#ifdef HX_TP_PROC_REGISTER
+ #define HIMAX_PROC_REGISTER_FILE "register"
+ struct proc_dir_entry *himax_proc_register_file;
+ uint8_t register_command[4];
+#endif
+
+#ifdef HX_TP_PROC_DIAG
+ #define HIMAX_PROC_DIAG_FILE "diag"
+ struct proc_dir_entry *himax_proc_diag_file;
+ #define HIMAX_PROC_DIAG_ARR_FILE "diag_arr"
+ struct proc_dir_entry *himax_proc_diag_arrange_file;
+
+#ifdef HX_TP_PROC_2T2R
+ static bool Is_2T2R;
+ static uint8_t x_channel_2;
+ static uint8_t y_channel_2;
+ static uint8_t *diag_mutual_2;
+
+ int16_t *getMutualBuffer_2(void);
+ uint8_t getXChannel_2(void);
+ uint8_t getYChannel_2(void);
+
+ void setMutualBuffer_2(void);
+ void setXChannel_2(uint8_t x);
+ void setYChannel_2(uint8_t y);
+#endif
+ uint8_t x_channel;
+ uint8_t y_channel;
+ int16_t *diag_mutual;
+ int16_t *diag_mutual_new;
+ int16_t *diag_mutual_old;
+ uint8_t diag_max_cnt;
+
+ int diag_command;
+ uint8_t diag_coor[128];// = {0xFF};
+ int16_t diag_self[100] = {0};
+
+ int16_t *getMutualBuffer(void);
+ int16_t *getMutualNewBuffer(void);
+ int16_t *getMutualOldBuffer(void);
+ int16_t *getSelfBuffer(void);
+ uint8_t getDiagCommand(void);
+ uint8_t getXChannel(void);
+ uint8_t getYChannel(void);
+
+ void setMutualBuffer(void);
+ void setMutualNewBuffer(void);
+ void setMutualOldBuffer(void);
+ void setXChannel(uint8_t x);
+ void setYChannel(uint8_t y);
+ uint8_t coordinate_dump_enable = 0;
+ struct file *coordinate_fn;
+#endif
+
+#ifdef HX_TP_PROC_DEBUG
+ #define HIMAX_PROC_DEBUG_FILE "debug"
+ struct proc_dir_entry *himax_proc_debug_file = NULL;
+
+ bool fw_update_complete = false;
+ int handshaking_result = 0;
+ unsigned char debug_level_cmd = 0;
+ unsigned char upgrade_fw[128*1024];
+#endif
+
+#ifdef HX_TP_PROC_FLASH_DUMP
+ #define HIMAX_PROC_FLASH_DUMP_FILE "flash_dump"
+ struct proc_dir_entry *himax_proc_flash_dump_file = NULL;
+
+ static int Flash_Size = 131072;
+ static uint8_t *flash_buffer = NULL;
+ static uint8_t flash_command = 0;
+ static uint8_t flash_read_step = 0;
+ static uint8_t flash_progress = 0;
+ static uint8_t flash_dump_complete = 0;
+ static uint8_t flash_dump_fail = 0;
+ static uint8_t sys_operation = 0;
+ static uint8_t flash_dump_sector = 0;
+ static uint8_t flash_dump_page = 0;
+ static bool flash_dump_going = false;
+
+ static uint8_t getFlashCommand(void);
+ static uint8_t getFlashDumpComplete(void);
+ static uint8_t getFlashDumpFail(void);
+ static uint8_t getFlashDumpProgress(void);
+ static uint8_t getFlashReadStep(void);
+ //static uint8_t getFlashDumpSector(void);
+ //static uint8_t getFlashDumpPage(void);
+
+ void setFlashBuffer(void);
+ uint8_t getSysOperation(void);
+
+ static void setFlashCommand(uint8_t command);
+ static void setFlashReadStep(uint8_t step);
+ static void setFlashDumpComplete(uint8_t complete);
+ static void setFlashDumpFail(uint8_t fail);
+ static void setFlashDumpProgress(uint8_t progress);
+ void setSysOperation(uint8_t operation);
+ static void setFlashDumpSector(uint8_t sector);
+ static void setFlashDumpPage(uint8_t page);
+ static void setFlashDumpGoing(bool going);
+
+#endif
+
+#ifdef HX_TP_PROC_SELF_TEST
+ #define HIMAX_PROC_SELF_TEST_FILE "self_test"
+ struct proc_dir_entry *himax_proc_self_test_file = NULL;
+ uint32_t **raw_data_array;
+ uint8_t X_NUM = 0, Y_NUM = 0;
+ uint8_t sel_type = 0x0D;
+#endif
+
+#ifdef HX_TP_PROC_RESET
+#define HIMAX_PROC_RESET_FILE "reset"
+extern void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);
+struct proc_dir_entry *himax_proc_reset_file = NULL;
+#endif
+
+#ifdef HX_HIGH_SENSE
+ #define HIMAX_PROC_HSEN_FILE "HSEN"
+ struct proc_dir_entry *himax_proc_HSEN_file = NULL;
+#endif
+
+#ifdef HX_TP_PROC_SENSE_ON_OFF
+ #define HIMAX_PROC_SENSE_ON_OFF_FILE "SenseOnOff"
+ struct proc_dir_entry *himax_proc_SENSE_ON_OFF_file = NULL;
+#endif
+
+#ifdef HX_RST_PIN_FUNC
+ void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);
+#endif
+
+#ifdef HX_SMART_WAKEUP
+#define HIMAX_PROC_SMWP_FILE "SMWP"
+struct proc_dir_entry *himax_proc_SMWP_file = NULL;
+#define HIMAX_PROC_GESTURE_FILE "GESTURE"
+struct proc_dir_entry *himax_proc_GESTURE_file = NULL;
+uint8_t HX_SMWP_EN = 0;
+//extern bool FAKE_POWER_KEY_SEND;
+#endif
+
+#endif
+
diff --git a/drivers/input/touchscreen/hxchipset/himax_ic.c b/drivers/input/touchscreen/hxchipset/himax_ic.c
new file mode 100644
index 0000000..6ad8dc0
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_ic.c
@@ -0,0 +1,2118 @@
+/* Himax Android Driver Sample Code for HMX83100 chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include "himax_ic.h"
+
+static unsigned char i_TP_CRC_FW_128K[]=
+{
+ #include "HX_CRC_128.i"
+};
+static unsigned char i_TP_CRC_FW_64K[]=
+{
+ #include "HX_CRC_64.i"
+};
+static unsigned char i_TP_CRC_FW_124K[]=
+{
+ #include "HX_CRC_124.i"
+};
+static unsigned char i_TP_CRC_FW_60K[]=
+{
+ #include "HX_CRC_60.i"
+};
+
+
+unsigned long FW_VER_MAJ_FLASH_ADDR;
+unsigned long FW_VER_MAJ_FLASH_LENG;
+unsigned long FW_VER_MIN_FLASH_ADDR;
+unsigned long FW_VER_MIN_FLASH_LENG;
+unsigned long CFG_VER_MAJ_FLASH_ADDR;
+unsigned long CFG_VER_MAJ_FLASH_LENG;
+unsigned long CFG_VER_MIN_FLASH_ADDR;
+unsigned long CFG_VER_MIN_FLASH_LENG;
+
+unsigned char IC_TYPE = 0;
+unsigned char IC_CHECKSUM = 0;
+
+extern struct himax_ic_data* ic_data;
+
+int himax_hand_shaking(struct i2c_client *client) //0:Running, 1:Stop, 2:I2C Fail
+{
+ int ret, result;
+ uint8_t hw_reset_check[1];
+ uint8_t hw_reset_check_2[1];
+ uint8_t buf0[2];
+ uint8_t IC_STATUS_CHECK = 0xAA;
+
+ memset(hw_reset_check, 0x00, sizeof(hw_reset_check));
+ memset(hw_reset_check_2, 0x00, sizeof(hw_reset_check_2));
+
+ buf0[0] = 0xF2;
+ if (IC_STATUS_CHECK == 0xAA) {
+ buf0[1] = 0xAA;
+ IC_STATUS_CHECK = 0x55;
+ } else {
+ buf0[1] = 0x55;
+ IC_STATUS_CHECK = 0xAA;
+ }
+
+ ret = i2c_himax_master_write(client, buf0, 2, HIMAX_I2C_RETRY_TIMES);
+ if (ret < 0) {
+ E("[Himax]:write 0xF2 failed line: %d \n",__LINE__);
+ goto work_func_send_i2c_msg_fail;
+ }
+ msleep(50);
+
+ buf0[0] = 0xF2;
+ buf0[1] = 0x00;
+ ret = i2c_himax_master_write(client, buf0, 2, HIMAX_I2C_RETRY_TIMES);
+ if (ret < 0) {
+ E("[Himax]:write 0x92 failed line: %d \n",__LINE__);
+ goto work_func_send_i2c_msg_fail;
+ }
+ usleep_range(2000, 4000);
+
+ ret = i2c_himax_read(client, 0xD1, hw_reset_check, 1, HIMAX_I2C_RETRY_TIMES);
+ if (ret < 0) {
+ E("[Himax]:i2c_himax_read 0xD1 failed line: %d \n",__LINE__);
+ goto work_func_send_i2c_msg_fail;
+ }
+
+ if ((IC_STATUS_CHECK != hw_reset_check[0])) {
+ usleep_range(2000, 4000);
+ ret = i2c_himax_read(client, 0xD1, hw_reset_check_2, 1, HIMAX_I2C_RETRY_TIMES);
+ if (ret < 0) {
+ E("[Himax]:i2c_himax_read 0xD1 failed line: %d \n",__LINE__);
+ goto work_func_send_i2c_msg_fail;
+ }
+
+ if (hw_reset_check[0] == hw_reset_check_2[0]) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ } else {
+ result = 0;
+ }
+
+ return result;
+
+work_func_send_i2c_msg_fail:
+ return 2;
+}
+
+void himax_diag_register_set(struct i2c_client *client, uint8_t diag_command)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+
+ if(diag_command != 0)
+ diag_command = diag_command + 5;
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x01; tmp_addr[0] = 0x80;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = diag_command;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+}
+
+void himax_flash_dump_func(struct i2c_client *client, uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer)
+{
+ //struct himax_ts_data *ts = container_of(work, struct himax_ts_data, flash_work);
+
+// uint8_t sector = 0;
+// uint8_t page = 0;
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ uint8_t out_buffer[20];
+ uint8_t in_buffer[260] = {0};
+ int page_prog_start = 0;
+ int i = 0;
+
+ himax_sense_off(client);
+ himax_burst_enable(client, 0);
+ /*=============Dump Flash Start=============*/
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ for (page_prog_start = 0; page_prog_start < Flash_Size; page_prog_start = page_prog_start + 256)
+ {
+ //=================================
+ // SPI Transfer Control
+ // Set 256 bytes page read : 0x8000_0020 ==> 0x6940_02FF
+ // Set read start address : 0x8000_0028 ==> 0x0000_0000
+ // Set command : 0x8000_0024 ==> 0x0000_003B
+ //=================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x69; tmp_data[2] = 0x40; tmp_data[1] = 0x02; tmp_data[0] = 0xFF;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x28;
+ if (page_prog_start < 0x100)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = 0x00;
+ tmp_data[1] = 0x00;
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x100 && page_prog_start < 0x10000)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = 0x00;
+ tmp_data[1] = (uint8_t)(page_prog_start >> 8);
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = (uint8_t)(page_prog_start >> 16);
+ tmp_data[1] = (uint8_t)(page_prog_start >> 8);
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x3B;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //==================================
+ // AHB_I2C Burst Read
+ // Set SPI data register : 0x8000_002C ==> 0x00
+ //==================================
+ out_buffer[0] = 0x2C;
+ out_buffer[1] = 0x00;
+ out_buffer[2] = 0x00;
+ out_buffer[3] = 0x80;
+ i2c_himax_write(client, 0x00 ,out_buffer, 4, 3);
+
+ //==================================
+ // Read access : 0x0C ==> 0x00
+ //==================================
+ out_buffer[0] = 0x00;
+ i2c_himax_write(client, 0x0C ,out_buffer, 1, 3);
+
+ //==================================
+ // Read 128 bytes two times
+ //==================================
+ i2c_himax_read(client, 0x08 ,in_buffer, 128, 3);
+ for (i = 0; i < 128; i++)
+ flash_buffer[i + page_prog_start] = in_buffer[i];
+
+ i2c_himax_read(client, 0x08 ,in_buffer, 128, 3);
+ for (i = 0; i < 128; i++)
+ flash_buffer[(i + 128) + page_prog_start] = in_buffer[i];
+
+ I("%s:Verify Progress: %x\n", __func__, page_prog_start);
+ }
+
+/*=============Dump Flash End=============*/
+ //msleep(100);
+ /*
+ for( i=0 ; i<8 ;i++)
+ {
+ for(j=0 ; j<64 ; j++)
+ {
+ setFlashDumpProgress(i*32 + j);
+ }
+ }
+ */
+ himax_sense_on(client, 0x01);
+
+ return;
+
+}
+
+int himax_chip_self_test(struct i2c_client *client)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[128];
+ int pf_value=0x00;
+ uint8_t test_result_id = 0;
+ int j;
+
+ memset(tmp_addr, 0x00, sizeof(tmp_addr));
+ memset(tmp_data, 0x00, sizeof(tmp_data));
+
+ himax_interface_on(client);
+ himax_sense_off(client);
+
+ //Set criteria
+ himax_burst_enable(client, 1);
+
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x94;
+ tmp_data[3] = 0x14; tmp_data[2] = 0xC8; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ tmp_data[7] = 0x13; tmp_data[6] = 0x60; tmp_data[5] = 0x0A; tmp_data[4] = 0x99;
+
+ himax_flash_write_burst_lenth(client, tmp_addr, tmp_data, 8);
+
+ //start selftest
+ // 0x9008_805C ==> 0x0000_0001
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x5C;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ himax_sense_on(client, 1);
+
+ msleep(2000);
+
+ himax_sense_off(client);
+ msleep(20);
+
+ //=====================================
+ // Read test result ID : 0x9008_8078 ==> 0xA/0xB/0xC/0xF
+ //=====================================
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x78;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+
+ test_result_id = tmp_data[0];
+
+ I("%s: check test result, test_result_id=%x, test_result=%x\n", __func__
+ ,test_result_id,tmp_data[0]);
+
+ if (test_result_id==0xF) {
+ I("[Himax]: self-test pass\n");
+ pf_value = 0x1;
+ } else {
+ E("[Himax]: self-test fail\n");
+ pf_value = 0x0;
+ }
+ himax_burst_enable(client, 1);
+
+ for (j = 0;j < 10; j++){
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x06; tmp_addr[1] = 0x00; tmp_addr[0] = 0x0C;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+ I("[Himax]: 9006000C = %d\n", tmp_data[0]);
+ if (tmp_data[0] != 0){
+ tmp_data[3] = 0x90; tmp_data[2] = 0x06; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ if ( i2c_himax_write(client, 0x00 ,tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ }
+ tmp_data[0] = 0x00;
+ if ( i2c_himax_write(client, 0x0C ,tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ }
+ i2c_himax_read(client, 0x08, tmp_data, 124,HIMAX_I2C_RETRY_TIMES);
+ }else{
+ break;
+ }
+ }
+
+ himax_sense_on(client, 1);
+ msleep(120);
+
+ return pf_value;
+}
+
+void himax_set_HSEN_enable(struct i2c_client *client, uint8_t HSEN_enable)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+
+ himax_burst_enable(client, 0);
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x50;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = HSEN_enable;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+}
+void himax_get_HSEN_enable(struct i2c_client *client,uint8_t *tmp_data)
+{
+ uint8_t tmp_addr[4];
+
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x50;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+}
+
+void himax_set_SMWP_enable(struct i2c_client *client, uint8_t SMWP_enable)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x54;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = SMWP_enable;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+}
+
+void himax_get_SMWP_enable(struct i2c_client *client,uint8_t *tmp_data)
+{
+ uint8_t tmp_addr[4];
+
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x54;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+}
+
+int himax_burst_enable(struct i2c_client *client, uint8_t auto_add_4_byte)
+{
+ uint8_t tmp_data[4];
+
+ tmp_data[0] = 0x31;
+ if ( i2c_himax_write(client, 0x13 ,tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return -EBUSY;
+ }
+
+ tmp_data[0] = (0x10 | auto_add_4_byte);
+ if ( i2c_himax_write(client, 0x0D ,tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return -EBUSY;
+ }
+ return 0;
+
+}
+
+void himax_register_read(struct i2c_client *client, uint8_t *read_addr, int read_length, uint8_t *read_data)
+{
+ uint8_t tmp_data[4];
+ int i = 0;
+ int address = 0;
+
+ if(read_length>256)
+ {
+ E("%s: read len over 256!\n", __func__);
+ return;
+ }
+ if (read_length > 1)
+ himax_burst_enable(client, 1);
+ else
+ himax_burst_enable(client, 0);
+ address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0];
+ i = address;
+ tmp_data[0] = (uint8_t)i;
+ tmp_data[1] = (uint8_t)(i >> 8);
+ tmp_data[2] = (uint8_t)(i >> 16);
+ tmp_data[3] = (uint8_t)(i >> 24);
+ if ( i2c_himax_write(client, 0x00 ,tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+ tmp_data[0] = 0x00;
+ if ( i2c_himax_write(client, 0x0C ,tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_read(client, 0x08 ,read_data, read_length * 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+ if (read_length > 1)
+ himax_burst_enable(client, 0);
+}
+
+void himax_flash_read(struct i2c_client *client, uint8_t *reg_byte, uint8_t *read_data)
+{
+ uint8_t tmpbyte[2];
+
+ if ( i2c_himax_write(client, 0x00 ,®_byte[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(client, 0x01 ,®_byte[1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(client, 0x02 ,®_byte[2], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(client, 0x03 ,®_byte[3], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ tmpbyte[0] = 0x00;
+ if ( i2c_himax_write(client, 0x0C ,&tmpbyte[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_read(client, 0x08 ,&read_data[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_read(client, 0x09 ,&read_data[1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_read(client, 0x0A ,&read_data[2], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_read(client, 0x0B ,&read_data[3], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_read(client, 0x18 ,&tmpbyte[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }// No bus request
+
+ if ( i2c_himax_read(client, 0x0F ,&tmpbyte[1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }// idle state
+
+}
+
+void himax_flash_write_burst(struct i2c_client *client, uint8_t * reg_byte, uint8_t * write_data)
+{
+ uint8_t data_byte[8];
+ int i = 0, j = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ data_byte[i] = reg_byte[i];
+ }
+ for (j = 4; j < 8; j++)
+ {
+ data_byte[j] = write_data[j-4];
+ }
+
+ if ( i2c_himax_write(client, 0x00 ,data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+}
+
+int himax_flash_write_burst_lenth(struct i2c_client *client, uint8_t *reg_byte, uint8_t *write_data, int length)
+{
+ uint8_t data_byte[256];
+ int i = 0, j = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ data_byte[i] = reg_byte[i];
+ }
+ for (j = 4; j < length + 4; j++)
+ {
+ data_byte[j] = write_data[j - 4];
+ }
+
+ if ( i2c_himax_write(client, 0x00 ,data_byte, length + 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int himax_register_write(struct i2c_client *client, uint8_t *write_addr, int write_length, uint8_t *write_data)
+{
+ int i =0, address = 0;
+ int ret = 0;
+
+ address = (write_addr[3] << 24) + (write_addr[2] << 16) + (write_addr[1] << 8) + write_addr[0];
+
+ for (i = address; i < address + write_length * 4; i = i + 4)
+ {
+ if (write_length > 1)
+ {
+ ret = himax_burst_enable(client, 1);
+ if(ret)
+ return ret;
+ }
+ else
+ {
+ ret = himax_burst_enable(client, 0);
+ if(ret)
+ return ret;
+ }
+ ret = himax_flash_write_burst_lenth(client, write_addr, write_data, write_length * 4);
+ if(ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+void himax_sense_off(struct i2c_client *client)
+{
+ uint8_t wdt_off = 0x00;
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[5];
+
+ himax_burst_enable(client, 0);
+
+ while(wdt_off == 0x00)
+ {
+ // 0x9000_800C ==> 0x0000_AC53
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x80; tmp_addr[0] = 0x0C;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0xAC; tmp_data[0] = 0x53;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Read Watch Dog disable password : 0x9000_800C ==> 0x0000_AC53
+ //=====================================
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x80; tmp_addr[0] = 0x0C;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+
+ //Check WDT
+ if (tmp_data[0] == 0x53 && tmp_data[1] == 0xAC && tmp_data[2] == 0x00 && tmp_data[3] == 0x00)
+ wdt_off = 0x01;
+ else
+ wdt_off = 0x00;
+ }
+
+ // VCOM //0x9008_806C ==> 0x0000_0001
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x08; tmp_addr[1] = 0x80; tmp_addr[0] = 0x6C;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ msleep(20);
+
+ // 0x9000_0010 ==> 0x0000_00DA
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0xDA;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Read CPU clock off password : 0x9000_0010 ==> 0x0000_00DA
+ //=====================================
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+ I("%s: CPU clock off password data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", __func__
+ ,tmp_data[0],tmp_data[1],tmp_data[2],tmp_data[3]);
+
+}
+
+void himax_interface_on(struct i2c_client *client)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[5];
+
+ //===========================================
+ // Any Cmd for ineterface on : 0x9000_0000 ==> 0x0000_0000
+ //===========================================
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00;
+ himax_flash_read(client, tmp_addr, tmp_data); //avoid RD/WR fail
+}
+
+bool wait_wip(struct i2c_client *client, int Timing)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ uint8_t in_buffer[10];
+ //uint8_t out_buffer[20];
+ int retry_cnt = 0;
+
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ in_buffer[0] = 0x01;
+
+ do
+ {
+ //=====================================
+ // SPI Transfer Control : 0x8000_0020 ==> 0x4200_0003
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x42; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x03;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // SPI Command : 0x8000_0024 ==> 0x0000_0005
+ // read 0x8000_002C for 0x01, means wait success
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x05;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ in_buffer[0] = in_buffer[1] = in_buffer[2] = in_buffer[3] = 0xFF;
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x2C;
+ himax_register_read(client, tmp_addr, 1, in_buffer);
+
+ if ((in_buffer[0] & 0x01) == 0x00)
+ return true;
+
+ retry_cnt++;
+
+ if (in_buffer[0] != 0x00 || in_buffer[1] != 0x00 || in_buffer[2] != 0x00 || in_buffer[3] != 0x00)
+ I("%s:Wait wip retry_cnt:%d, buffer[0]=%d, buffer[1]=%d, buffer[2]=%d, buffer[3]=%d \n", __func__,
+ retry_cnt,in_buffer[0],in_buffer[1],in_buffer[2],in_buffer[3]);
+
+ if (retry_cnt > 100)
+ {
+ E("%s: Wait wip error!\n", __func__);
+ return false;
+ }
+ msleep(Timing);
+ }while ((in_buffer[0] & 0x01) == 0x01);
+ return true;
+}
+
+void himax_sense_on(struct i2c_client *client, uint8_t FlashMode)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[128];
+
+ himax_interface_on(client);
+ himax_burst_enable(client, 0);
+ //CPU reset
+ // 0x9000_0014 ==> 0x0000_00CA
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x14;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0xCA;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Read pull low CPU reset signal : 0x9000_0014 ==> 0x0000_00CA
+ //=====================================
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x14;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+
+ I("%s: check pull low CPU reset signal data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", __func__
+ ,tmp_data[0],tmp_data[1],tmp_data[2],tmp_data[3]);
+
+ // 0x9000_0014 ==> 0x0000_0000
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x14;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Read revert pull low CPU reset signal : 0x9000_0014 ==> 0x0000_0000
+ //=====================================
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x14;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+
+ I("%s: revert pull low CPU reset signal data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", __func__
+ ,tmp_data[0],tmp_data[1],tmp_data[2],tmp_data[3]);
+
+ //=====================================
+ // Reset TCON
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x01; tmp_addr[0] = 0xE0;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+ usleep_range(10000, 20000);
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x01; tmp_addr[0] = 0xE0;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ if (FlashMode == 0x00) //SRAM
+ {
+ //=====================================
+ // Re-map
+ //=====================================
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0xF1;
+ himax_flash_write_burst_lenth(client, tmp_addr, tmp_data, 4);
+ I("%s:83100_Chip_Re-map ON\n", __func__);
+ }
+ else
+ {
+ //=====================================
+ // Re-map off
+ //=====================================
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst_lenth(client, tmp_addr, tmp_data, 4);
+ I("%s:83100_Chip_Re-map OFF\n", __func__);
+ }
+ //=====================================
+ // CPU clock on
+ //=====================================
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst_lenth(client, tmp_addr, tmp_data, 4);
+
+}
+
+void himax_chip_erase(struct i2c_client *client)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+
+ himax_burst_enable(client, 0);
+
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Chip Erase
+ // Write Enable : 1. 0x8000_0020 ==> 0x4700_0000
+ // 2. 0x8000_0024 ==> 0x0000_0006
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x47; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x06;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Chip Erase
+ // Erase Command : 0x8000_0024 ==> 0x0000_00C7
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0xC7;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ msleep(2000);
+
+ if (!wait_wip(client, 100))
+ E("%s:83100_Chip_Erase Fail\n", __func__);
+
+}
+
+bool himax_block_erase(struct i2c_client *client)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+
+ himax_burst_enable(client, 0);
+
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Chip Erase
+ // Write Enable : 1. 0x8000_0020 ==> 0x4700_0000
+ // 2. 0x8000_0024 ==> 0x0000_0006
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x47; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x06;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Block Erase
+ // Erase Command : 0x8000_0028 ==> 0x0000_0000 //SPI addr
+ // 0x8000_0020 ==> 0x6700_0000 //control
+ // 0x8000_0024 ==> 0x0000_0052 //BE
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x28;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x67; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x52;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ msleep(1000);
+
+ if (!wait_wip(client, 100))
+ {
+ E("%s:83100_Erase Fail\n", __func__);
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+
+}
+
+bool himax_sector_erase(struct i2c_client *client, int start_addr)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ int page_prog_start = 0;
+
+ himax_burst_enable(client, 0);
+
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+ for (page_prog_start = start_addr; page_prog_start < start_addr + 0x0F000; page_prog_start = page_prog_start + 0x1000)
+ {
+ //=====================================
+ // Chip Erase
+ // Write Enable : 1. 0x8000_0020 ==> 0x4700_0000
+ // 2. 0x8000_0024 ==> 0x0000_0006
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x47; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x06;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=====================================
+ // Sector Erase
+ // Erase Command : 0x8000_0028 ==> 0x0000_0000 //SPI addr
+ // 0x8000_0020 ==> 0x6700_0000 //control
+ // 0x8000_0024 ==> 0x0000_0020 //SE
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x28;
+ if (page_prog_start < 0x100)
+ {
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x100 && page_prog_start < 0x10000)
+ {
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = (uint8_t)(page_prog_start >> 8); tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000)
+ {
+ tmp_data[3] = 0x00; tmp_data[2] = (uint8_t)(page_prog_start >> 16); tmp_data[1] = (uint8_t)(page_prog_start >> 8); tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x67; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x20;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ msleep(200);
+
+ if (!wait_wip(client, 100))
+ {
+ E("%s:83100_Erase Fail\n", __func__);
+ return false;
+ }
+ }
+ return true;
+}
+
+void himax_sram_write(struct i2c_client *client, uint8_t *FW_content)
+{
+ int i = 0;
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[128];
+ int FW_length = 0x4000; // 0x4000 = 16K bin file
+
+ //himax_sense_off(client);
+
+ for (i = 0; i < FW_length; i = i + 128)
+ {
+ himax_burst_enable(client, 1);
+
+ if (i < 0x100)
+ {
+ tmp_addr[3] = 0x08;
+ tmp_addr[2] = 0x00;
+ tmp_addr[1] = 0x00;
+ tmp_addr[0] = i;
+ }
+ else if (i >= 0x100 && i < 0x10000)
+ {
+ tmp_addr[3] = 0x08;
+ tmp_addr[2] = 0x00;
+ tmp_addr[1] = (i >> 8);
+ tmp_addr[0] = i;
+ }
+
+ memcpy(&tmp_data[0], &FW_content[i], 128);
+ himax_flash_write_burst_lenth(client, tmp_addr, tmp_data, 128);
+
+ }
+
+ if (!wait_wip(client, 100))
+ E("%s:83100_Sram_Write Fail\n", __func__);
+}
+
+bool himax_sram_verify(struct i2c_client *client, uint8_t *FW_File, int FW_Size)
+{
+ int i = 0;
+ uint8_t out_buffer[20];
+ uint8_t in_buffer[128];
+ uint8_t *get_fw_content;
+
+ get_fw_content = kzalloc(0x4000*sizeof(uint8_t), GFP_KERNEL);
+ if (!get_fw_content)
+ return false;
+
+ for (i = 0; i < 0x4000; i = i + 128)
+ {
+ himax_burst_enable(client, 1);
+
+ //==================================
+ // AHB_I2C Burst Read
+ //==================================
+ if (i < 0x100)
+ {
+ out_buffer[3] = 0x08;
+ out_buffer[2] = 0x00;
+ out_buffer[1] = 0x00;
+ out_buffer[0] = i;
+ }
+ else if (i >= 0x100 && i < 0x10000)
+ {
+ out_buffer[3] = 0x08;
+ out_buffer[2] = 0x00;
+ out_buffer[1] = (i >> 8);
+ out_buffer[0] = i;
+ }
+
+ if ( i2c_himax_write(client, 0x00 ,out_buffer, 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return false;
+ }
+
+ out_buffer[0] = 0x00;
+ if ( i2c_himax_write(client, 0x0C ,out_buffer, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return false;
+ }
+
+ if ( i2c_himax_read(client, 0x08 ,in_buffer, 128, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return false;
+ }
+ memcpy(&get_fw_content[i], &in_buffer[0], 128);
+ }
+
+ for (i = 0; i < FW_Size; i++)
+ {
+ if (FW_File[i] != get_fw_content[i])
+ {
+ E("%s: fail! SRAM[%x]=%x NOT CRC_ifile=%x\n", __func__,i,get_fw_content[i],FW_File[i]);
+ return false;
+ }
+ }
+
+ kfree(get_fw_content);
+
+ return true;
+}
+
+void himax_flash_programming(struct i2c_client *client, uint8_t *FW_content, int FW_Size)
+{
+ int page_prog_start = 0;
+ int program_length = 48;
+ int i = 0, j = 0, k = 0;
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ uint8_t buring_data[256]; // Read for flash data, 128K
+ // 4 bytes for 0x80002C padding
+
+ //himax_interface_on(client);
+ himax_burst_enable(client, 0);
+
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ for (page_prog_start = 0; page_prog_start < FW_Size; page_prog_start = page_prog_start + 256)
+ {
+ //msleep(5);
+ //=====================================
+ // Write Enable : 1. 0x8000_0020 ==> 0x4700_0000
+ // 2. 0x8000_0024 ==> 0x0000_0006
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x47; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x06;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=================================
+ // SPI Transfer Control
+ // Set 256 bytes page write : 0x8000_0020 ==> 0x610F_F000
+ // Set read start address : 0x8000_0028 ==> 0x0000_0000
+ //=================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x61; tmp_data[2] = 0x0F; tmp_data[1] = 0xF0; tmp_data[0] = 0x00;
+ // data bytes should be 0x6100_0000 + ((word_number)*4-1)*4096 = 0x6100_0000 + 0xFF000 = 0x610F_F000
+ // Programmable size = 1 page = 256 bytes, word_number = 256 byte / 4 = 64
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x28;
+ //tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; // Flash start address 1st : 0x0000_0000
+
+ if (page_prog_start < 0x100)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = 0x00;
+ tmp_data[1] = 0x00;
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x100 && page_prog_start < 0x10000)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = 0x00;
+ tmp_data[1] = (uint8_t)(page_prog_start >> 8);
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = (uint8_t)(page_prog_start >> 16);
+ tmp_data[1] = (uint8_t)(page_prog_start >> 8);
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+
+ //=================================
+ // Send 16 bytes data : 0x8000_002C ==> 16 bytes data
+ //=================================
+ buring_data[0] = 0x2C;
+ buring_data[1] = 0x00;
+ buring_data[2] = 0x00;
+ buring_data[3] = 0x80;
+
+ for (i = /*0*/page_prog_start, j = 0; i < 16 + page_prog_start/**/; i++, j++) /// <------ bin file
+ {
+ buring_data[j + 4] = FW_content[i];
+ }
+
+
+ if ( i2c_himax_write(client, 0x00 ,buring_data, 20, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+ //=================================
+ // Write command : 0x8000_0024 ==> 0x0000_0002
+ //=================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x02;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+
+ //=================================
+ // Send 240 bytes data : 0x8000_002C ==> 240 bytes data
+ //=================================
+
+ for (j = 0; j < 5; j++)
+ {
+ for (i = (page_prog_start + 16 + (j * 48)), k = 0; i < (page_prog_start + 16 + (j * 48)) + program_length; i++, k++) /// <------ bin file
+ {
+ buring_data[k+4] = FW_content[i];//(byte)i;
+ }
+
+ if ( i2c_himax_write(client, 0x00 ,buring_data, program_length+4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ }
+
+ if (!wait_wip(client, 1))
+ E("%s:83100_Flash_Programming Fail\n", __func__);
+ }
+}
+
+bool himax_check_chip_version(struct i2c_client *client)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ uint8_t ret_data = 0x00;
+ int i = 0;
+ int ret = 0;
+ himax_sense_off(client);
+ for (i = 0; i < 5; i++)
+ {
+ // 1. Set DDREG_Req = 1 (0x9000_0020 = 0x0000_0001) (Lock register R/W from driver)
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01;
+ ret = himax_register_write(client, tmp_addr, 1, tmp_data);
+ if(ret)
+ return false;
+
+ // 2. Set bank as 0 (0x8001_BD01 = 0x0000_0000)
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x01; tmp_addr[1] = 0xBD; tmp_addr[0] = 0x01;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ ret = himax_register_write(client, tmp_addr, 1, tmp_data);
+ if(ret)
+ return false;
+
+ // 3. Read driver ID register RF4H 1 byte (0x8001_F401)
+ // Driver register RF4H 1 byte value = 0x84H, read back value will become 0x84848484
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x01; tmp_addr[1] = 0xF4; tmp_addr[0] = 0x01;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+ ret_data = tmp_data[0];
+
+ I("%s:Read driver IC ID = %X\n", __func__, ret_data);
+ if (ret_data == 0x84)
+ {
+ IC_TYPE = HX_83100_SERIES_PWON;
+ //himax_sense_on(client, 0x01);
+ ret_data = true;
+ break;
+ }
+ else
+ {
+ ret_data = false;
+ E("%s:Read driver ID register Fail:\n", __func__);
+ }
+ }
+ // 4. After read finish, set DDREG_Req = 0 (0x9000_0020 = 0x0000_0000) (Unlock register R/W from driver)
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_register_write(client, tmp_addr, 1, tmp_data);
+ //himax_sense_on(client, 0x01);
+ return ret_data;
+}
+
+#if 1
+int himax_check_CRC(struct i2c_client *client, int mode)
+{
+ bool burnFW_success = false;
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ int tmp_value;
+ int CRC_value = 0;
+
+ memset(tmp_data, 0x00, sizeof(tmp_data));
+
+ if (1)
+ {
+ if(mode == fw_image_60k)
+ {
+ himax_sram_write(client, (i_TP_CRC_FW_60K));
+ burnFW_success = himax_sram_verify(client, i_TP_CRC_FW_60K, 0x4000);
+ }
+ else if(mode == fw_image_64k)
+ {
+ himax_sram_write(client, (i_TP_CRC_FW_64K));
+ burnFW_success = himax_sram_verify(client, i_TP_CRC_FW_64K, 0x4000);
+ }
+ else if(mode == fw_image_124k)
+ {
+ himax_sram_write(client, (i_TP_CRC_FW_124K));
+ burnFW_success = himax_sram_verify(client, i_TP_CRC_FW_124K, 0x4000);
+ }
+ else if(mode == fw_image_128k)
+ {
+ himax_sram_write(client, (i_TP_CRC_FW_128K));
+ burnFW_success = himax_sram_verify(client, i_TP_CRC_FW_128K, 0x4000);
+ }
+ if (burnFW_success)
+ {
+ I("%s: Start to do CRC FW mode=%d \n", __func__,mode);
+ himax_sense_on(client, 0x00); // run CRC firmware
+
+ while(true)
+ {
+ msleep(100);
+
+ tmp_addr[3] = 0x90;
+ tmp_addr[2] = 0x08;
+ tmp_addr[1] = 0x80;
+ tmp_addr[0] = 0x94;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+
+ I("%s: CRC from firmware is %x, %x, %x, %x \n", __func__,tmp_data[3],
+ tmp_data[2],tmp_data[1],tmp_data[0]);
+
+ if (tmp_data[3] == 0xFF && tmp_data[2] == 0xFF && tmp_data[1] == 0xFF && tmp_data[0] == 0xFF)
+ {
+ }
+ else
+ break;
+ }
+
+ CRC_value = tmp_data[3];
+
+ tmp_value = tmp_data[2] << 8;
+ CRC_value += tmp_value;
+
+ tmp_value = tmp_data[1] << 16;
+ CRC_value += tmp_value;
+
+ tmp_value = tmp_data[0] << 24;
+ CRC_value += tmp_value;
+
+ I("%s: CRC Value is %x \n", __func__, CRC_value);
+
+ //Close Remapping
+ //=====================================
+ // Re-map close
+ //=====================================
+ tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00;
+ himax_flash_write_burst_lenth(client, tmp_addr, tmp_data, 4);
+ return CRC_value;
+ }
+ else
+ {
+ E("%s: SRAM write fail\n", __func__);
+ return 0;
+ }
+ }
+ else
+ I("%s: NO CRC Check File \n", __func__);
+
+ return 0;
+}
+
+bool Calculate_CRC_with_AP(unsigned char *FW_content , int CRC_from_FW, int mode)
+{
+ uint8_t tmp_data[4];
+ int i, j;
+ int fw_data;
+ int fw_data_2;
+ int CRC = 0xFFFFFFFF;
+ int PolyNomial = 0x82F63B78;
+ int length = 0;
+
+ if (mode == fw_image_128k)
+ length = 0x8000;
+ else if (mode == fw_image_124k)
+ length = 0x7C00;
+ else if (mode == fw_image_64k)
+ length = 0x4000;
+ else //if (mode == fw_image_60k)
+ length = 0x3C00;
+
+ for (i = 0; i < length; i++)
+ {
+ fw_data = FW_content[i * 4 ];
+
+ for (j = 1; j < 4; j++)
+ {
+ fw_data_2 = FW_content[i * 4 + j];
+ fw_data += (fw_data_2) << (8 * j);
+ }
+
+ CRC = fw_data ^ CRC;
+
+ for (j = 0; j < 32; j++)
+ {
+ if ((CRC % 2) != 0)
+ {
+ CRC = ((CRC >> 1) & 0x7FFFFFFF) ^ PolyNomial;
+ }
+ else
+ {
+ CRC = (((CRC >> 1) ^ 0x7FFFFFFF)& 0x7FFFFFFF);
+ }
+ }
+ }
+
+ I("%s: CRC calculate from bin file is %x \n", __func__, CRC);
+
+ tmp_data[0] = (uint8_t)(CRC >> 24);
+ tmp_data[1] = (uint8_t)(CRC >> 16);
+ tmp_data[2] = (uint8_t)(CRC >> 8);
+ tmp_data[3] = (uint8_t) CRC;
+
+ CRC = tmp_data[0];
+ CRC += tmp_data[1] << 8;
+ CRC += tmp_data[2] << 16;
+ CRC += tmp_data[3] << 24;
+
+ I("%s: CRC calculate from bin file REVERSE %x \n", __func__, CRC);
+ I("%s: CRC calculate from FWis %x \n", __func__, CRC_from_FW);
+ if (CRC_from_FW == CRC)
+ return true;
+ else
+ return false;
+}
+#endif
+
+int fts_ctpm_fw_upgrade_with_sys_fs_60k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref)
+{
+ int CRC_from_FW = 0;
+ int burnFW_success = 0;
+
+ if (len != 0x10000) //64k
+ {
+ E("%s: The file size is not 64K bytes\n", __func__);
+ return false;
+ }
+ himax_sense_off(client);
+ msleep(500);
+ himax_interface_on(client);
+ if (!himax_sector_erase(client, 0x00000))
+ {
+ E("%s:Sector erase fail!Please restart the IC.\n", __func__);
+ return false;
+ }
+ himax_flash_programming(client, fw, 0x0F000);
+
+ //burnFW_success = himax_83100_Verify(fw, len);
+ //if(burnFW_success==false)
+ // return burnFW_success;
+
+ CRC_from_FW = himax_check_CRC(client,fw_image_60k);
+ burnFW_success = Calculate_CRC_with_AP(fw, CRC_from_FW,fw_image_60k);
+ //himax_sense_on(client, 0x01);
+ return burnFW_success;
+}
+
+int fts_ctpm_fw_upgrade_with_sys_fs_64k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref)
+{
+ int CRC_from_FW = 0;
+ int burnFW_success = 0;
+
+ if (len != 0x10000) //64k
+ {
+ E("%s: The file size is not 64K bytes\n", __func__);
+ return false;
+ }
+ himax_sense_off(client);
+ msleep(500);
+ himax_interface_on(client);
+ himax_chip_erase(client);
+ himax_flash_programming(client, fw, len);
+
+ //burnFW_success = himax_83100_Verify(fw, len);
+ //if(burnFW_success==false)
+ // return burnFW_success;
+
+ CRC_from_FW = himax_check_CRC(client,fw_image_64k);
+ burnFW_success = Calculate_CRC_with_AP(fw, CRC_from_FW,fw_image_64k);
+ //himax_sense_on(client, 0x01);
+ return burnFW_success;
+}
+
+int fts_ctpm_fw_upgrade_with_sys_fs_124k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref)
+{
+ int CRC_from_FW = 0;
+ int burnFW_success = 0;
+
+ if (len != 0x20000) //128k
+ {
+ E("%s: The file size is not 128K bytes\n", __func__);
+ return false;
+ }
+ himax_sense_off(client);
+ msleep(500);
+ himax_interface_on(client);
+ if (!himax_block_erase(client))
+ {
+ E("%s:Block erase fail!Please restart the IC.\n", __func__);
+ return false;
+ }
+
+ if (!himax_sector_erase(client, 0x10000))
+ {
+ E("%s:Sector erase fail!Please restart the IC.\n", __func__);
+ return false;
+ }
+ himax_flash_programming(client, fw, 0x1F000);
+
+
+ //burnFW_success = himax_83100_Verify(fw, len);
+ //if(burnFW_success==false)
+ // return burnFW_success;
+
+ CRC_from_FW = himax_check_CRC(client,fw_image_124k);
+ burnFW_success = Calculate_CRC_with_AP(fw, CRC_from_FW,fw_image_124k);
+ //himax_sense_on(client, 0x01);
+ return burnFW_success;
+}
+
+int fts_ctpm_fw_upgrade_with_sys_fs_128k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref)
+{
+ int CRC_from_FW = 0;
+ int burnFW_success = 0;
+
+ if (len != 0x20000) //128k
+ {
+ E("%s: The file size is not 128K bytes\n", __func__);
+ return false;
+ }
+ himax_sense_off(client);
+ msleep(500);
+ himax_interface_on(client);
+ himax_chip_erase(client);
+
+ himax_flash_programming(client, fw, len);
+
+ //burnFW_success = himax_83100_Verify(fw, len);
+ //if(burnFW_success==false)
+ // return burnFW_success;
+
+ CRC_from_FW = himax_check_CRC(client,fw_image_128k);
+ burnFW_success = Calculate_CRC_with_AP(fw, CRC_from_FW,fw_image_128k);
+ //himax_sense_on(client, 0x01);
+ return burnFW_success;
+}
+
+void himax_touch_information(struct i2c_client *client)
+{
+ uint8_t cmd[4];
+ char data[12] = {0};
+
+ I("%s:IC_TYPE =%d\n", __func__,IC_TYPE);
+
+ if(IC_TYPE == HX_83100_SERIES_PWON)
+ {
+ cmd[3] = 0x08; cmd[2] = 0x00; cmd[1] = 0x00; cmd[0] = 0xF8;
+ himax_register_read(client, cmd, 1, data);
+
+ ic_data->HX_RX_NUM = data[1];
+ ic_data->HX_TX_NUM = data[2];
+ ic_data->HX_MAX_PT = data[3];
+
+ cmd[3] = 0x08; cmd[2] = 0x00; cmd[1] = 0x00; cmd[0] = 0xFC;
+ himax_register_read(client, cmd, 1, data);
+
+ if((data[1] & 0x04) == 0x04) {
+ ic_data->HX_XY_REVERSE = true;
+ } else {
+ ic_data->HX_XY_REVERSE = false;
+ }
+ ic_data->HX_Y_RES = data[3]*256;
+ cmd[3] = 0x08; cmd[2] = 0x00; cmd[1] = 0x01; cmd[0] = 0x00;
+ himax_register_read(client, cmd, 1, data);
+ ic_data->HX_Y_RES = ic_data->HX_Y_RES + data[0];
+ ic_data->HX_X_RES = data[1]*256 + data[2];
+ cmd[3] = 0x08; cmd[2] = 0x00; cmd[1] = 0x00; cmd[0] = 0x8C;
+ himax_register_read(client, cmd, 1, data);
+ if((data[0] & 0x01) == 1) {
+ ic_data->HX_INT_IS_EDGE = true;
+ } else {
+ ic_data->HX_INT_IS_EDGE = false;
+ }
+ if (ic_data->HX_RX_NUM > 40)
+ ic_data->HX_RX_NUM = 29;
+ if (ic_data->HX_TX_NUM > 20)
+ ic_data->HX_TX_NUM = 16;
+ if (ic_data->HX_MAX_PT > 10)
+ ic_data->HX_MAX_PT = 10;
+ if (ic_data->HX_Y_RES > 2000)
+ ic_data->HX_Y_RES = 1280;
+ if (ic_data->HX_X_RES > 2000)
+ ic_data->HX_X_RES = 720;
+#ifdef HX_EN_MUT_BUTTON
+ cmd[3] = 0x08; cmd[2] = 0x00; cmd[1] = 0x00; cmd[0] = 0xE8;
+ himax_register_read(client, cmd, 1, data);
+ ic_data->HX_BT_NUM = data[3];
+#endif
+ I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d,HX_MAX_PT=%d \n", __func__,ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_MAX_PT);
+ I("%s:HX_XY_REVERSE =%d,HX_Y_RES =%d,HX_X_RES=%d \n", __func__,ic_data->HX_XY_REVERSE,ic_data->HX_Y_RES,ic_data->HX_X_RES);
+ I("%s:HX_INT_IS_EDGE =%d \n", __func__,ic_data->HX_INT_IS_EDGE);
+ }
+ else
+ {
+ ic_data->HX_RX_NUM = 0;
+ ic_data->HX_TX_NUM = 0;
+ ic_data->HX_BT_NUM = 0;
+ ic_data->HX_X_RES = 0;
+ ic_data->HX_Y_RES = 0;
+ ic_data->HX_MAX_PT = 0;
+ ic_data->HX_XY_REVERSE = false;
+ ic_data->HX_INT_IS_EDGE = false;
+ }
+}
+
+void himax_read_FW_ver(struct i2c_client *client)
+{
+ uint8_t cmd[4];
+ uint8_t data[64] = {0};
+
+ //=====================================
+ // Read FW version : 0x0000_E303
+ //=====================================
+ cmd[3] = 0x00; cmd[2] = 0x00; cmd[1] = 0xE3; cmd[0] = 0x00;
+ himax_register_read(client, cmd, 1, data);
+
+ ic_data->vendor_config_ver = data[3]<<8;
+
+ cmd[3] = 0x00; cmd[2] = 0x00; cmd[1] = 0xE3; cmd[0] = 0x04;
+ himax_register_read(client, cmd, 1, data);
+
+ ic_data->vendor_config_ver = data[0] | ic_data->vendor_config_ver;
+ I("CFG_VER : %X \n",ic_data->vendor_config_ver);
+
+ cmd[3] = 0x08; cmd[2] = 0x00; cmd[1] = 0x00; cmd[0] = 0x28;
+ himax_register_read(client, cmd, 1, data);
+
+ ic_data->vendor_fw_ver = data[0]<<8 | data[1];
+ I("FW_VER : %X \n",ic_data->vendor_fw_ver);
+
+
+ return;
+}
+
+bool himax_ic_package_check(struct i2c_client *client)
+{
+#if 0
+ uint8_t cmd[3];
+ uint8_t data[3];
+
+ memset(cmd, 0x00, sizeof(cmd));
+ memset(data, 0x00, sizeof(data));
+
+ if (i2c_himax_read(client, 0xD1, cmd, 3, HIMAX_I2C_RETRY_TIMES) < 0)
+ return false ;
+
+ if (i2c_himax_read(client, 0x31, data, 3, HIMAX_I2C_RETRY_TIMES) < 0)
+ return false;
+
+ if((data[0] == 0x85 && data[1] == 0x29))
+ {
+ IC_TYPE = HX_85XX_F_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 64901; //0xFD85
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 64902; //0xFD86
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 64928; //0xFDA0
+ CFG_VER_MAJ_FLASH_LENG = 12;
+ CFG_VER_MIN_FLASH_ADDR = 64940; //0xFDAC
+ CFG_VER_MIN_FLASH_LENG = 12;
+ I("Himax IC package 852x F\n");
+ }
+ if((data[0] == 0x85 && data[1] == 0x30) || (cmd[0] == 0x05 && cmd[1] == 0x85 && cmd[2] == 0x29))
+ {
+ IC_TYPE = HX_85XX_E_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 133; //0x0085
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 134; //0x0086
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 160; //0x00A0
+ CFG_VER_MAJ_FLASH_LENG = 12;
+ CFG_VER_MIN_FLASH_ADDR = 172; //0x00AC
+ CFG_VER_MIN_FLASH_LENG = 12;
+ I("Himax IC package 852x E\n");
+ }
+ else if((data[0] == 0x85 && data[1] == 0x31))
+ {
+ IC_TYPE = HX_85XX_ES_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 133; //0x0085
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 134; //0x0086
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 160; //0x00A0
+ CFG_VER_MAJ_FLASH_LENG = 12;
+ CFG_VER_MIN_FLASH_ADDR = 172; //0x00AC
+ CFG_VER_MIN_FLASH_LENG = 12;
+ I("Himax IC package 852x ES\n");
+ }
+ else if ((data[0] == 0x85 && data[1] == 0x28) || (cmd[0] == 0x04 && cmd[1] == 0x85 &&
+ (cmd[2] == 0x26 || cmd[2] == 0x27 || cmd[2] == 0x28))) {
+ IC_TYPE = HX_85XX_D_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 133; // 0x0085
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 134; // 0x0086
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 160; // 0x00A0
+ CFG_VER_MAJ_FLASH_LENG = 12;
+ CFG_VER_MIN_FLASH_ADDR = 172; // 0x00AC
+ CFG_VER_MIN_FLASH_LENG = 12;
+ I("Himax IC package 852x D\n");
+ } else if ((data[0] == 0x85 && data[1] == 0x23) || (cmd[0] == 0x03 && cmd[1] == 0x85 &&
+ (cmd[2] == 0x26 || cmd[2] == 0x27 || cmd[2] == 0x28 || cmd[2] == 0x29))) {
+ IC_TYPE = HX_85XX_C_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_SW;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 133; // 0x0085
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 134; // 0x0086
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 135; // 0x0087
+ CFG_VER_MAJ_FLASH_LENG = 12;
+ CFG_VER_MIN_FLASH_ADDR = 147; // 0x0093
+ CFG_VER_MIN_FLASH_LENG = 12;
+ I("Himax IC package 852x C\n");
+ } else if ((data[0] == 0x85 && data[1] == 0x26) ||
+ (cmd[0] == 0x02 && cmd[1] == 0x85 &&
+ (cmd[2] == 0x19 || cmd[2] == 0x25 || cmd[2] == 0x26))) {
+ IC_TYPE = HX_85XX_B_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_SW;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 133; // 0x0085
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 728; // 0x02D8
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 692; // 0x02B4
+ CFG_VER_MAJ_FLASH_LENG = 3;
+ CFG_VER_MIN_FLASH_ADDR = 704; // 0x02C0
+ CFG_VER_MIN_FLASH_LENG = 3;
+ I("Himax IC package 852x B\n");
+ } else if ((data[0] == 0x85 && data[1] == 0x20) || (cmd[0] == 0x01 &&
+ cmd[1] == 0x85 && cmd[2] == 0x19)) {
+ IC_TYPE = HX_85XX_A_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_SW;
+ I("Himax IC package 852x A\n");
+ } else {
+ E("Himax IC package incorrect!!\n");
+ }*/
+#else
+ IC_TYPE = HX_83100_SERIES_PWON;
+ IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC;
+ //Himax: Set FW and CFG Flash Address
+ FW_VER_MAJ_FLASH_ADDR = 57384; //0xE028
+ FW_VER_MAJ_FLASH_LENG = 1;
+ FW_VER_MIN_FLASH_ADDR = 57385; //0xE029
+ FW_VER_MIN_FLASH_LENG = 1;
+ CFG_VER_MAJ_FLASH_ADDR = 58115; //0xE303
+ CFG_VER_MAJ_FLASH_LENG = 1;
+ CFG_VER_MIN_FLASH_ADDR = 58116; //0xE304
+ CFG_VER_MIN_FLASH_LENG = 1;
+ I("Himax IC package 83100_in\n");
+
+#endif
+ return true;
+}
+
+void himax_read_event_stack(struct i2c_client *client, uint8_t *buf, uint8_t length)
+{
+ uint8_t cmd[4];
+
+ cmd[3] = 0x90; cmd[2] = 0x06; cmd[1] = 0x00; cmd[0] = 0x00;
+ if ( i2c_himax_write(client, 0x00 ,cmd, 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ }
+
+ cmd[0] = 0x00;
+ if ( i2c_himax_write(client, 0x0C ,cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ }
+
+ i2c_himax_read(client, 0x08, buf, length, HIMAX_I2C_RETRY_TIMES);
+}
+
+#if 0
+static void himax_83100_Flash_Write(uint8_t * reg_byte, uint8_t * write_data)
+{
+ uint8_t tmpbyte[2];
+
+ if ( i2c_himax_write(private_ts->client, 0x00 ,®_byte[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x01 ,®_byte[1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x02 ,®_byte[2], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x03 ,®_byte[3], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x04 ,&write_data[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x05 ,&write_data[1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x06 ,&write_data[2], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x07 ,&write_data[3], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if (isBusrtOn == false)
+ {
+ tmpbyte[0] = 0x01;
+ if ( i2c_himax_write(private_ts->client, 0x0C ,&tmpbyte[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+ }
+}
+#endif
+#if 0
+static void himax_83100_Flash_Burst_Write(uint8_t * reg_byte, uint8_t * write_data)
+{
+ //uint8_t tmpbyte[2];
+ int i = 0;
+
+ if ( i2c_himax_write(private_ts->client, 0x00 ,®_byte[0], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x01 ,®_byte[1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x02 ,®_byte[2], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x03 ,®_byte[3], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ // Write 256 bytes with continue burst mode
+ for (i = 0; i < 256; i = i + 4)
+ {
+ if ( i2c_himax_write(private_ts->client, 0x04 ,&write_data[i], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x05 ,&write_data[i+1], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x06 ,&write_data[i+2], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+
+ if ( i2c_himax_write(private_ts->client, 0x07 ,&write_data[i+3], 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ return;
+ }
+ }
+
+ //if (isBusrtOn == false)
+ //{
+ // tmpbyte[0] = 0x01;
+ // if ( i2c_himax_write(private_ts->client, 0x0C ,&tmpbyte[0], 1, 3) < 0) {
+ // E("%s: i2c access fail!\n", __func__);
+ // return;
+ // }
+ //}
+
+}
+#endif
+
+#if 0
+static bool himax_83100_Verify(uint8_t *FW_File, int FW_Size)
+{
+ uint8_t tmp_addr[4];
+ uint8_t tmp_data[4];
+ uint8_t out_buffer[20];
+ uint8_t in_buffer[260];
+
+ int fail_addr=0, fail_cnt=0;
+ int page_prog_start = 0;
+ int i = 0;
+
+ himax_interface_on(private_ts->client);
+ himax_burst_enable(private_ts->client, 0);
+
+ //=====================================
+ // SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
+ //=====================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x10;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x02; tmp_data[1] = 0x07; tmp_data[0] = 0x80;
+ himax_83100_Flash_Write(tmp_addr, tmp_data);
+
+ for (page_prog_start = 0; page_prog_start < FW_Size; page_prog_start = page_prog_start + 256)
+ {
+ //=================================
+ // SPI Transfer Control
+ // Set 256 bytes page read : 0x8000_0020 ==> 0x6940_02FF
+ // Set read start address : 0x8000_0028 ==> 0x0000_0000
+ // Set command : 0x8000_0024 ==> 0x0000_003B
+ //=================================
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20;
+ tmp_data[3] = 0x69; tmp_data[2] = 0x40; tmp_data[1] = 0x02; tmp_data[0] = 0xFF;
+ himax_83100_Flash_Write(tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x28;
+ if (page_prog_start < 0x100)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = 0x00;
+ tmp_data[1] = 0x00;
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x100 && page_prog_start < 0x10000)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = 0x00;
+ tmp_data[1] = (uint8_t)(page_prog_start >> 8);
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000)
+ {
+ tmp_data[3] = 0x00;
+ tmp_data[2] = (uint8_t)(page_prog_start >> 16);
+ tmp_data[1] = (uint8_t)(page_prog_start >> 8);
+ tmp_data[0] = (uint8_t)page_prog_start;
+ }
+ himax_83100_Flash_Write(tmp_addr, tmp_data);
+
+ tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x24;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x3B;
+ himax_83100_Flash_Write(tmp_addr, tmp_data);
+
+ //==================================
+ // AHB_I2C Burst Read
+ // Set SPI data register : 0x8000_002C ==> 0x00
+ //==================================
+ out_buffer[0] = 0x2C;
+ out_buffer[1] = 0x00;
+ out_buffer[2] = 0x00;
+ out_buffer[3] = 0x80;
+ i2c_himax_write(private_ts->client, 0x00 ,out_buffer, 4, HIMAX_I2C_RETRY_TIMES);
+
+ //==================================
+ // Read access : 0x0C ==> 0x00
+ //==================================
+ out_buffer[0] = 0x00;
+ i2c_himax_write(private_ts->client, 0x0C ,out_buffer, 1, HIMAX_I2C_RETRY_TIMES);
+
+ //==================================
+ // Read 128 bytes two times
+ //==================================
+ i2c_himax_read(private_ts->client, 0x08 ,in_buffer, 128, HIMAX_I2C_RETRY_TIMES);
+ for (i = 0; i < 128; i++)
+ flash_buffer[i + page_prog_start] = in_buffer[i];
+
+ i2c_himax_read(private_ts->client, 0x08 ,in_buffer, 128, HIMAX_I2C_RETRY_TIMES);
+ for (i = 0; i < 128; i++)
+ flash_buffer[(i + 128) + page_prog_start] = in_buffer[i];
+
+ //tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x2C;
+ //himax_register_read(tmp_addr, 32, out in_buffer);
+ //for (int i = 0; i < 128; i++)
+ // flash_buffer[i + page_prog_start] = in_buffer[i];
+ //tmp_addr[3] = 0x80; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x2C;
+ //himax_register_read(tmp_addr, 32, out in_buffer);
+ //for (int i = 0; i < 128; i++)
+ // flash_buffer[i + page_prog_start] = in_buffer[i];
+
+ I("%s:Verify Progress: %x\n", __func__, page_prog_start);
+ }
+
+ fail_cnt = 0;
+ for (i = 0; i < FW_Size; i++)
+ {
+ if (FW_File[i] != flash_buffer[i])
+ {
+ if (fail_cnt == 0)
+ fail_addr = i;
+
+ fail_cnt++;
+ //E("%s Fail Block:%x\n", __func__, i);
+ //return false;
+ }
+ }
+ if (fail_cnt > 0)
+ {
+ E("%s:Start Fail Block:%x and fail block count=%x\n" , __func__,fail_addr,fail_cnt);
+ return false;
+ }
+
+ I("%s:Byte read verify pass.\n", __func__);
+ return true;
+
+}
+#endif
+
+void himax_get_DSRAM_data(struct i2c_client *client, uint8_t *info_data)
+{
+ int i;
+ int cnt = 0;
+ unsigned char tmp_addr[4];
+ unsigned char tmp_data[4];
+ uint8_t max_i2c_size = 32;
+ int total_size = ic_data->HX_TX_NUM * ic_data->HX_RX_NUM * 2;
+ int total_size_4bytes = total_size / 4;
+ int total_read_times = 0;
+ unsigned long address = 0x08000468;
+ tmp_addr[3] = 0x08; tmp_addr[2] = 0x00; tmp_addr[1] = 0x04; tmp_addr[0] = 0x64;
+ tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x5A; tmp_data[0] = 0xA5;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+ do
+ {
+ cnt++;
+ himax_register_read(client, tmp_addr, 1, tmp_data);
+ usleep_range(10000, 20000);
+ } while ((tmp_data[1] != 0xA5 || tmp_data[0] != 0x5A) && cnt < 100);
+ tmp_addr[3] = 0x08; tmp_addr[2] = 0x00; tmp_addr[1] = 0x04; tmp_addr[0] = 0x68;
+ if (total_size_4bytes % max_i2c_size == 0)
+ {
+ total_read_times = total_size_4bytes / max_i2c_size;
+ }
+ else
+ {
+ total_read_times = total_size_4bytes / max_i2c_size + 1;
+ }
+ for (i = 0; i < (total_read_times); i++)
+ {
+ if ( total_size_4bytes >= max_i2c_size)
+ {
+ himax_register_read(client, tmp_addr, max_i2c_size, &info_data[i*max_i2c_size*4]);
+ total_size_4bytes = total_size_4bytes - max_i2c_size;
+ }
+ else
+ {
+ himax_register_read(client, tmp_addr, total_size_4bytes % max_i2c_size, &info_data[i*max_i2c_size*4]);
+ }
+ address += max_i2c_size*4;
+ tmp_addr[1] = (uint8_t)((address>>8)&0x00FF);
+ tmp_addr[0] = (uint8_t)((address)&0x00FF);
+ }
+ tmp_addr[3] = 0x08; tmp_addr[2] = 0x00; tmp_addr[1] = 0x04; tmp_addr[0] = 0x64;
+ tmp_data[3] = 0x11; tmp_data[2] = 0x22; tmp_data[1] = 0x33; tmp_data[0] = 0x44;
+ himax_flash_write_burst(client, tmp_addr, tmp_data);
+}
+//ts_work
+int cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max){
+ int RawDataLen;
+ if (raw_cnt_rmd != 0x00) {
+ RawDataLen = 124 - ((HX_MAX_PT+raw_cnt_max+3)*4) - 1;
+ }else{
+ RawDataLen = 124 - ((HX_MAX_PT+raw_cnt_max+2)*4) - 1;
+ }
+ return RawDataLen;
+}
+
+bool read_event_stack(struct i2c_client *client, uint8_t *buf, int length)
+{
+ uint8_t cmd[4];
+
+ if(length > 56)
+ length = 124;
+ //=====================
+ //AHB I2C Burst Read
+ //=====================
+ cmd[0] = 0x31;
+ if ( i2c_himax_write(client, 0x13 ,cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ goto err_workqueue_out;
+ }
+
+ cmd[0] = 0x10;
+ if ( i2c_himax_write(client, 0x0D ,cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ goto err_workqueue_out;
+ }
+ //=====================
+ //Read event stack
+ //=====================
+ cmd[3] = 0x90; cmd[2] = 0x06; cmd[1] = 0x00; cmd[0] = 0x00;
+ if ( i2c_himax_write(client, 0x00 ,cmd, 4, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ goto err_workqueue_out;
+ }
+
+ cmd[0] = 0x00;
+ if ( i2c_himax_write(client, 0x0C ,cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) {
+ E("%s: i2c access fail!\n", __func__);
+ goto err_workqueue_out;
+ }
+ i2c_himax_read(client, 0x08, buf, length,HIMAX_I2C_RETRY_TIMES);
+ return 1;
+
+ err_workqueue_out:
+ return 0;
+}
+
+bool post_read_event_stack(struct i2c_client *client)
+{
+ return 1;
+}
+bool diag_check_sum( uint8_t hx_touch_info_size, uint8_t *buf) //return checksum value
+{
+ uint16_t check_sum_cal = 0;
+ int i;
+
+ //Check 124th byte CRC
+ for (i = hx_touch_info_size, check_sum_cal = 0; i < 124; i=i+2)
+ {
+ check_sum_cal += (buf[i+1]*256 + buf[i]);
+ }
+ if (check_sum_cal % 0x10000 != 0)
+ {
+ I("%s: diag check sum fail! check_sum_cal=%X, hx_touch_info_size=%d, \n",__func__,check_sum_cal, hx_touch_info_size);
+ return 0;
+ }
+ return 1;
+}
+
+
+void diag_parse_raw_data(int hx_touch_info_size, int RawDataLen, int mul_num, int self_num, uint8_t *buf, uint8_t diag_cmd, int16_t *mutual_data, int16_t *self_data)
+{
+ int RawDataLen_word;
+ int index = 0;
+ int temp1, temp2,i;
+
+ if (buf[hx_touch_info_size] == 0x3A && buf[hx_touch_info_size+1] == 0xA3 && buf[hx_touch_info_size+2] > 0 && buf[hx_touch_info_size+3] == diag_cmd+5 )
+ {
+ RawDataLen_word = RawDataLen/2;
+ index = (buf[hx_touch_info_size+2] - 1) * RawDataLen_word;
+ //I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n", index, buf[56], buf[57], buf[58], buf[59], mul_num, self_num);
+ for (i = 0; i < RawDataLen_word; i++)
+ {
+ temp1 = index + i;
+
+ if (temp1 < mul_num)
+ { //mutual
+ mutual_data[index + i] = buf[i*2 + hx_touch_info_size+4+1]*256 + buf[i*2 + hx_touch_info_size+4]; //4: RawData Header, 1:HSB
+ }
+ else
+ {//self
+ temp1 = i + index;
+ temp2 = self_num + mul_num;
+
+ if (temp1 >= temp2)
+ {
+ break;
+ }
+
+ self_data[i+index-mul_num] = buf[i*2 + hx_touch_info_size+4]; //4: RawData Header
+ self_data[i+index-mul_num+1] = buf[i*2 + hx_touch_info_size+4+1];
+ }
+ }
+ }
+ else
+ {
+ I("[HIMAX TP MSG]%s: header format is wrong!\n", __func__);
+ I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n", index, buf[56], buf[57], buf[58], buf[59], mul_num, self_num);
+ }
+}
diff --git a/drivers/input/touchscreen/hxchipset/himax_ic.h b/drivers/input/touchscreen/hxchipset/himax_ic.h
new file mode 100644
index 0000000..18cd12b
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_ic.h
@@ -0,0 +1,82 @@
+/* Himax Android Driver Sample Code for HMX83100 chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include "himax_platform.h"
+#include "himax_common.h"
+
+#include <linux/slab.h>
+
+
+#define HX_85XX_A_SERIES_PWON 1
+#define HX_85XX_B_SERIES_PWON 2
+#define HX_85XX_C_SERIES_PWON 3
+#define HX_85XX_D_SERIES_PWON 4
+#define HX_85XX_E_SERIES_PWON 5
+#define HX_85XX_ES_SERIES_PWON 6
+#define HX_85XX_F_SERIES_PWON 7
+#define HX_83100_SERIES_PWON 8
+
+#define HX_TP_BIN_CHECKSUM_SW 1
+#define HX_TP_BIN_CHECKSUM_HW 2
+#define HX_TP_BIN_CHECKSUM_CRC 3
+
+enum fw_image_type {
+ fw_image_60k = 0x01,
+ fw_image_64k,
+ fw_image_124k,
+ fw_image_128k,
+};
+
+int himax_hand_shaking(struct i2c_client *client);
+void himax_set_SMWP_enable(struct i2c_client *client,uint8_t SMWP_enable);
+void himax_get_SMWP_enable(struct i2c_client *client,uint8_t *tmp_data);
+void himax_set_HSEN_enable(struct i2c_client *client,uint8_t HSEN_enable);
+void himax_get_HSEN_enable(struct i2c_client *client,uint8_t *tmp_data);
+void himax_diag_register_set(struct i2c_client *client, uint8_t diag_command);
+void himax_flash_dump_func(struct i2c_client *client, uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer);
+int himax_chip_self_test(struct i2c_client *client);
+int himax_burst_enable(struct i2c_client *client, uint8_t auto_add_4_byte); ////himax_83100_BURST_INC0_EN
+void himax_register_read(struct i2c_client *client, uint8_t *read_addr, int read_length, uint8_t *read_data); ////RegisterRead83100
+void himax_flash_read(struct i2c_client *client, uint8_t *reg_byte, uint8_t *read_data); ////himax_83100_Flash_Read
+void himax_flash_write_burst(struct i2c_client *client, uint8_t * reg_byte, uint8_t * write_data); ////himax_83100_Flash_Write_Burst
+int himax_flash_write_burst_lenth(struct i2c_client *client, uint8_t *reg_byte, uint8_t *write_data, int length); ////himax_83100_Flash_Write_Burst_lenth
+int himax_register_write(struct i2c_client *client, uint8_t *write_addr, int write_length, uint8_t *write_data); ////RegisterWrite83100
+void himax_sense_off(struct i2c_client *client); ////himax_83100_SenseOff
+void himax_interface_on(struct i2c_client *client); ////himax_83100_Interface_on
+bool wait_wip(struct i2c_client *client, int Timing);
+void himax_sense_on(struct i2c_client *client, uint8_t FlashMode); ////himax_83100_SenseOn
+void himax_chip_erase(struct i2c_client *client); ////himax_83100_Chip_Erase
+bool himax_block_erase(struct i2c_client *client); ////himax_83100_Block_Erase
+bool himax_sector_erase(struct i2c_client *client, int start_addr); ////himax_83100_Sector_Erase
+void himax_sram_write(struct i2c_client *client, uint8_t *FW_content); ////himax_83100_Sram_Write
+bool himax_sram_verify(struct i2c_client *client, uint8_t *FW_File, int FW_Size); ////himax_83100_Sram_Verify
+void himax_flash_programming(struct i2c_client *client, uint8_t *FW_content, int FW_Size); ////himax_83100_Flash_Programming
+bool himax_check_chip_version(struct i2c_client *client); ////himax_83100_CheckChipVersion
+int himax_check_CRC(struct i2c_client *client, int mode); ////himax_83100_Check_CRC
+bool Calculate_CRC_with_AP(unsigned char *FW_content , int CRC_from_FW, int mode);
+int fts_ctpm_fw_upgrade_with_sys_fs_60k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref);
+int fts_ctpm_fw_upgrade_with_sys_fs_64k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref);
+int fts_ctpm_fw_upgrade_with_sys_fs_124k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref);
+int fts_ctpm_fw_upgrade_with_sys_fs_128k(struct i2c_client *client, unsigned char *fw, int len, bool change_iref);
+void himax_touch_information(struct i2c_client *client);
+void himax_read_FW_ver(struct i2c_client *client);
+bool himax_ic_package_check(struct i2c_client *client);
+void himax_read_event_stack(struct i2c_client *client, uint8_t *buf, uint8_t length);
+int cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max);
+bool read_event_stack(struct i2c_client *client, uint8_t *buf_ts, int length);
+bool post_read_event_stack(struct i2c_client *client);
+bool diag_check_sum( uint8_t hx_touch_info_size, uint8_t *buf_ts); //return checksum value
+void diag_parse_raw_data(int hx_touch_info_size, int RawDataLen, int mul_num, int self_num, uint8_t *buf_ts, uint8_t diag_cmd, int16_t *mutual_data, int16_t *self_data);
+void himax_get_DSRAM_data(struct i2c_client *client, uint8_t *info_data);
\ No newline at end of file
diff --git a/drivers/input/touchscreen/hxchipset/himax_platform.c b/drivers/input/touchscreen/hxchipset/himax_platform.c
new file mode 100644
index 0000000..7e8a1d6
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_platform.c
@@ -0,0 +1,796 @@
+/* Himax Android Driver Sample Code for HIMAX chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include "himax_platform.h"
+#include "himax_common.h"
+
+int irq_enable_count = 0;
+#ifdef HX_SMART_WAKEUP
+#define TS_WAKE_LOCK_TIMEOUT (2 * HZ)
+#endif
+
+#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_ts_release"
+
+extern struct himax_ic_data* ic_data;
+extern void himax_ts_work(struct himax_ts_data *ts);
+extern enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer);
+extern int himax_ts_init(struct himax_ts_data *ts);
+
+extern int tp_rst_gpio;
+
+#ifdef HX_TP_PROC_DIAG
+extern uint8_t getDiagCommand(void);
+#endif
+
+void himax_vk_parser(struct device_node *dt,
+ struct himax_i2c_platform_data *pdata)
+{
+ u32 data = 0;
+ uint8_t cnt = 0, i = 0;
+ uint32_t coords[4] = {0};
+ struct device_node *node, *pp = NULL;
+ struct himax_virtual_key *vk;
+
+ node = of_parse_phandle(dt, "virtualkey", 0);
+ if (node == NULL) {
+ I(" DT-No vk info in DT");
+ return;
+ } else {
+ while ((pp = of_get_next_child(node, pp)))
+ cnt++;
+ if (!cnt)
+ return;
+
+ vk = kzalloc(cnt * (sizeof *vk), GFP_KERNEL);
+ if (!vk)
+ return;
+ pp = NULL;
+ while ((pp = of_get_next_child(node, pp))) {
+ if (of_property_read_u32(pp, "idx", &data) == 0)
+ vk[i].index = data;
+ if (of_property_read_u32_array(pp, "range", coords, 4) == 0) {
+ vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1];
+ vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3];
+ } else
+ I(" range faile");
+ i++;
+ }
+ pdata->virtual_key = vk;
+ for (i = 0; i < cnt; i++)
+ I(" vk[%d] idx:%d x_min:%d, y_max:%d", i,pdata->virtual_key[i].index,
+ pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max);
+ }
+}
+
+int himax_parse_dt(struct himax_ts_data *ts,
+ struct himax_i2c_platform_data *pdata)
+{
+ int rc, coords_size = 0;
+ uint32_t coords[4] = {0};
+ struct property *prop;
+ struct device_node *dt = ts->client->dev.of_node;
+ u32 data = 0;
+
+ prop = of_find_property(dt, "himax,panel-coords", NULL);
+ if (prop) {
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != 4)
+ D(" %s:Invalid panel coords size %d", __func__, coords_size);
+ }
+
+ if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) {
+ pdata->abs_x_min = coords[0], pdata->abs_x_max = coords[1];
+ pdata->abs_y_min = coords[2], pdata->abs_y_max = coords[3];
+ I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min,
+ pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max);
+ }
+
+ prop = of_find_property(dt, "himax,display-coords", NULL);
+ if (prop) {
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != 4)
+ D(" %s:Invalid display coords size %d", __func__, coords_size);
+ }
+ rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size);
+ if (rc && (rc != -EINVAL)) {
+ D(" %s:Fail to read display-coords %d\n", __func__, rc);
+ return rc;
+ }
+ pdata->screenWidth = coords[1];
+ pdata->screenHeight = coords[3];
+ I(" DT-%s:display-coords = (%d, %d)", __func__, pdata->screenWidth,
+ pdata->screenHeight);
+
+ pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0);
+ if (!gpio_is_valid(pdata->gpio_irq)) {
+ I(" DT:gpio_irq value is not valid\n");
+ }
+
+ pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0);
+ if (!gpio_is_valid(pdata->gpio_reset)) {
+ I(" DT:gpio_rst value is not valid\n");
+ }
+ pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0);
+ if (!gpio_is_valid(pdata->gpio_3v3_en)) {
+ I(" DT:gpio_3v3_en value is not valid\n");
+ }
+ I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en);
+
+ if (of_property_read_u32(dt, "report_type", &data) == 0) {
+ pdata->protocol_type = data;
+ I(" DT:protocol_type=%d", pdata->protocol_type);
+ }
+
+ himax_vk_parser(dt, pdata);
+
+ return 0;
+}
+
+int i2c_himax_read(struct i2c_client *client, uint8_t command, uint8_t *data, uint8_t length, uint8_t toRetry)
+{
+ int retry;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &command,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ }
+ };
+
+ for (retry = 0; retry < toRetry; retry++) {
+ if (i2c_transfer(client->adapter, msg, 2) == 2)
+ break;
+ msleep(20);
+ }
+ if (retry == toRetry) {
+ E("%s: i2c_read_block retry over %d\n",
+ __func__, toRetry);
+ return -EIO;
+ }
+ return 0;
+
+}
+
+int i2c_himax_write(struct i2c_client *client, uint8_t command, uint8_t *data, uint8_t length, uint8_t toRetry)
+{
+ int retry/*, loop_i*/;
+ uint8_t buf[length + 1];
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = command;
+ memcpy(buf+1, data, length);
+
+ for (retry = 0; retry < toRetry; retry++) {
+ if (i2c_transfer(client->adapter, msg, 1) == 1)
+ break;
+ msleep(20);
+ }
+
+ if (retry == toRetry) {
+ E("%s: i2c_write_block retry over %d\n",
+ __func__, toRetry);
+ return -EIO;
+ }
+ return 0;
+
+}
+
+int i2c_himax_read_command(struct i2c_client *client, uint8_t length, uint8_t *data, uint8_t *readlength, uint8_t toRetry)
+{
+ int retry;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ }
+ };
+
+ for (retry = 0; retry < toRetry; retry++) {
+ if (i2c_transfer(client->adapter, msg, 1) == 1)
+ break;
+ msleep(20);
+ }
+ if (retry == toRetry) {
+ E("%s: i2c_read_block retry over %d\n",
+ __func__, toRetry);
+ return -EIO;
+ }
+ return 0;
+}
+
+int i2c_himax_write_command(struct i2c_client *client, uint8_t command, uint8_t toRetry)
+{
+ return i2c_himax_write(client, command, NULL, 0, toRetry);
+}
+
+int i2c_himax_master_write(struct i2c_client *client, uint8_t *data, uint8_t length, uint8_t toRetry)
+{
+ int retry/*, loop_i*/;
+ uint8_t buf[length];
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = buf,
+ }
+ };
+
+ memcpy(buf, data, length);
+
+ for (retry = 0; retry < toRetry; retry++) {
+ if (i2c_transfer(client->adapter, msg, 1) == 1)
+ break;
+ msleep(20);
+ }
+
+ if (retry == toRetry) {
+ E("%s: i2c_write_block retry over %d\n",
+ __func__, toRetry);
+ return -EIO;
+ }
+ return 0;
+}
+
+void himax_int_enable(int irqnum, int enable)
+{
+ if (enable == 1 && irq_enable_count == 0) {
+ enable_irq(irqnum);
+ irq_enable_count++;
+ } else if (enable == 0 && irq_enable_count == 1) {
+ disable_irq_nosync(irqnum);
+ irq_enable_count--;
+ }
+ I("irq_enable_count = %d", irq_enable_count);
+}
+
+void himax_rst_gpio_set(int pinnum, uint8_t value)
+{
+ gpio_direction_output(pinnum, value);
+}
+
+uint8_t himax_int_gpio_read(int pinnum)
+{
+ return gpio_get_value(pinnum);
+}
+
+#if defined(CONFIG_HMX_DB)
+static int himax_regulator_configure(struct i2c_client *client,struct himax_i2c_platform_data *pdata)
+{
+ int retval;
+ pdata->vcc_dig = regulator_get(&client->dev,
+ "vdd");
+ if (IS_ERR(pdata->vcc_dig))
+ {
+ E("%s: Failed to get regulator vdd\n",
+ __func__);
+ retval = PTR_ERR(pdata->vcc_dig);
+ return retval;
+ }
+ pdata->vcc_ana = regulator_get(&client->dev,
+ "avdd");
+ if (IS_ERR(pdata->vcc_ana))
+ {
+ E("%s: Failed to get regulator avdd\n",
+ __func__);
+ retval = PTR_ERR(pdata->vcc_ana);
+ regulator_put(pdata->vcc_ana);
+ return retval;
+ }
+
+ return 0;
+};
+
+static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on)
+{
+ int retval;
+
+ if (on)
+ {
+ retval = regulator_enable(pdata->vcc_dig);
+ if (retval)
+ {
+ E("%s: Failed to enable regulator vdd\n",
+ __func__);
+ return retval;
+ }
+ msleep(100);
+ retval = regulator_enable(pdata->vcc_ana);
+ if (retval)
+ {
+ E("%s: Failed to enable regulator avdd\n",
+ __func__);
+ regulator_disable(pdata->vcc_dig);
+ return retval;
+ }
+ }
+ else
+ {
+ regulator_disable(pdata->vcc_dig);
+ regulator_disable(pdata->vcc_ana);
+ }
+
+ return 0;
+}
+
+int himax_ts_pinctrl_init(struct himax_ts_data *ts)
+{
+ int retval;
+
+ /* Get pinctrl if target uses pinctrl */
+ ts->ts_pinctrl = devm_pinctrl_get(&(ts->client->dev));
+ if (IS_ERR_OR_NULL(ts->ts_pinctrl)) {
+ retval = PTR_ERR(ts->ts_pinctrl);
+ dev_dbg(&ts->client->dev,
+ "Target does not use pinctrl %d\n", retval);
+ goto err_pinctrl_get;
+ }
+
+ ts->pinctrl_state_active
+ = pinctrl_lookup_state(ts->ts_pinctrl,
+ PINCTRL_STATE_ACTIVE);
+ if (IS_ERR_OR_NULL(ts->pinctrl_state_active)) {
+ retval = PTR_ERR(ts->pinctrl_state_active);
+ dev_err(&ts->client->dev,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_ACTIVE, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ ts->pinctrl_state_suspend
+ = pinctrl_lookup_state(ts->ts_pinctrl,
+ PINCTRL_STATE_SUSPEND);
+ if (IS_ERR_OR_NULL(ts->pinctrl_state_suspend)) {
+ retval = PTR_ERR(ts->pinctrl_state_suspend);
+ dev_err(&ts->client->dev,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_SUSPEND, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ ts->pinctrl_state_release
+ = pinctrl_lookup_state(ts->ts_pinctrl,
+ PINCTRL_STATE_RELEASE);
+ if (IS_ERR_OR_NULL(ts->pinctrl_state_release)) {
+ retval = PTR_ERR(ts->pinctrl_state_release);
+ dev_dbg(&ts->client->dev,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_RELEASE, retval);
+ }
+
+ return 0;
+
+err_pinctrl_lookup:
+ devm_pinctrl_put(ts->ts_pinctrl);
+err_pinctrl_get:
+ ts->ts_pinctrl = NULL;
+ return retval;
+}
+
+int himax_gpio_power_config(struct i2c_client *client,struct himax_i2c_platform_data *pdata)
+{
+ int error;
+
+ error = himax_regulator_configure(client, pdata);
+ if (error)
+ {
+ E("Failed to intialize hardware\n");
+ goto err_regulator_not_on;
+ }
+
+#ifdef HX_RST_PIN_FUNC
+ if (gpio_is_valid(pdata->gpio_reset))
+ {
+ /* configure touchscreen reset out gpio */
+ error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio");
+ if (error)
+ {
+ E("unable to request gpio [%d]\n",
+ pdata->gpio_reset);
+ goto err_regulator_on;
+ }
+
+ error = gpio_direction_output(pdata->gpio_reset, 0);
+ if (error)
+ {
+ E("unable to set direction for gpio [%d]\n",
+ pdata->gpio_reset);
+ goto err_gpio_reset_req;
+ }
+ }
+#endif
+
+ error = himax_power_on(pdata, true);
+ if (error)
+ {
+ E("Failed to power on hardware\n");
+ goto err_gpio_reset_req;
+ }
+#ifdef HX_IRQ_PIN_FUNC
+ if (gpio_is_valid(pdata->gpio_irq))
+ {
+ /* configure touchscreen irq gpio */
+ error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq");
+ if (error)
+ {
+ E("unable to request gpio [%d]\n",
+ pdata->gpio_irq);
+ goto err_power_on;
+ }
+ error = gpio_direction_input(pdata->gpio_irq);
+ if (error)
+ {
+ E("unable to set direction for gpio [%d]\n",
+ pdata->gpio_irq);
+ goto err_gpio_irq_req;
+ }
+ client->irq = gpio_to_irq(pdata->gpio_irq);
+ }
+ else
+ {
+ E("irq gpio not provided\n");
+ goto err_power_on;
+ }
+#endif
+
+ msleep(20);
+
+#ifdef HX_RST_PIN_FUNC
+ if (gpio_is_valid(pdata->gpio_reset))
+ {
+ error = gpio_direction_output(pdata->gpio_reset, 1);
+ if (error)
+ {
+ E("unable to set direction for gpio [%d]\n",
+ pdata->gpio_reset);
+ goto err_gpio_irq_req;
+ }
+ }
+#endif
+ return 0;
+#ifdef HX_RST_PIN_FUNC
+ err_gpio_irq_req:
+#endif
+#ifdef HX_IRQ_PIN_FUNC
+ if (gpio_is_valid(pdata->gpio_irq))
+ gpio_free(pdata->gpio_irq);
+ err_power_on:
+#endif
+ himax_power_on(pdata, false);
+ err_gpio_reset_req:
+#ifdef HX_RST_PIN_FUNC
+ if (gpio_is_valid(pdata->gpio_reset))
+ gpio_free(pdata->gpio_reset);
+ err_regulator_on:
+#endif
+ err_regulator_not_on:
+
+ return error;
+}
+
+#else
+int himax_gpio_power_config(struct i2c_client *client,struct himax_i2c_platform_data *pdata)
+{
+ int error=0;
+
+#ifdef HX_RST_PIN_FUNC
+ if (pdata->gpio_reset >= 0)
+ {
+ error = gpio_request(pdata->gpio_reset, "himax-reset");
+ if (error < 0)
+ {
+ E("%s: request reset pin failed\n", __func__);
+ return error;
+ }
+ error = gpio_direction_output(pdata->gpio_reset, 0);
+ if (error)
+ {
+ E("unable to set direction for gpio [%d]\n",
+ pdata->gpio_reset);
+ return error;
+ }
+ }
+#endif
+ if (pdata->gpio_3v3_en >= 0)
+ {
+ error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en");
+ if (error < 0)
+ {
+ E("%s: request 3v3_en pin failed\n", __func__);
+ return error;
+ }
+ gpio_direction_output(pdata->gpio_3v3_en, 1);
+ I("3v3_en pin =%d\n", gpio_get_value(pdata->gpio_3v3_en));
+ }
+
+#ifdef HX_IRQ_PIN_FUNC
+ if (gpio_is_valid(pdata->gpio_irq))
+ {
+ /* configure touchscreen irq gpio */
+ error = gpio_request(pdata->gpio_irq, "himax_gpio_irq");
+ if (error)
+ {
+ E("unable to request gpio [%d]\n",pdata->gpio_irq);
+ return error;
+ }
+ error = gpio_direction_input(pdata->gpio_irq);
+ if (error)
+ {
+ E("unable to set direction for gpio [%d]\n",pdata->gpio_irq);
+ return error;
+ }
+ client->irq = gpio_to_irq(pdata->gpio_irq);
+ }
+ else
+ {
+ E("irq gpio not provided\n");
+ return error;
+ }
+#endif
+
+ msleep(20);
+
+#ifdef HX_RST_PIN_FUNC
+ if (pdata->gpio_reset >= 0)
+ {
+ error = gpio_direction_output(pdata->gpio_reset, 1);
+ if (error)
+ {
+ E("unable to set direction for gpio [%d]\n",
+ pdata->gpio_reset);
+ return error;
+ }
+ }
+ msleep(20);
+#endif
+
+ return error;
+ }
+#endif
+
+static void himax_ts_isr_func(struct himax_ts_data *ts)
+{
+ himax_ts_work(ts);
+}
+
+irqreturn_t himax_ts_thread(int irq, void *ptr)
+{
+ uint8_t diag_cmd;
+ struct himax_ts_data *ts = ptr;
+ struct timespec timeStart, timeEnd, timeDelta;
+
+ diag_cmd = getDiagCommand();
+
+ if (ts->debug_log_level & BIT(2)) {
+ getnstimeofday(&timeStart);
+ usleep_range(5000, 7000);
+ //I(" Irq start time = %ld.%06ld s\n",
+ // timeStart.tv_sec, timeStart.tv_nsec/1000);
+ }
+
+#ifdef HX_SMART_WAKEUP
+ if (atomic_read(&ts->suspend_mode)&&(!FAKE_POWER_KEY_SEND)&&(ts->SMWP_enable)&&(!diag_cmd)) {
+ wake_lock_timeout(&ts->ts_SMWP_wake_lock, TS_WAKE_LOCK_TIMEOUT);
+ msleep(200);
+ himax_wake_check_func();
+ return IRQ_HANDLED;
+ }
+#endif
+ himax_ts_isr_func((struct himax_ts_data *)ptr);
+ if(ts->debug_log_level & BIT(2)) {
+ getnstimeofday(&timeEnd);
+ timeDelta.tv_nsec = (timeEnd.tv_sec*1000000000+timeEnd.tv_nsec)
+ -(timeStart.tv_sec*1000000000+timeStart.tv_nsec);
+ //I("Irq finish time = %ld.%06ld s\n",
+ // timeEnd.tv_sec, timeEnd.tv_nsec/1000);
+ //I("Touch latency = %ld us\n", timeDelta.tv_nsec/1000);
+ }
+ return IRQ_HANDLED;
+}
+
+static void himax_ts_work_func(struct work_struct *work)
+{
+ struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work);
+ himax_ts_work(ts);
+}
+
+int tp_irq = -1;
+
+int himax_ts_register_interrupt(struct i2c_client *client)
+{
+ struct himax_ts_data *ts = i2c_get_clientdata(client);
+ int ret = 0;
+
+ ts->irq_enabled = 0;
+ //Work functon
+ if (client->irq) {/*INT mode*/
+ ts->use_irq = 1;
+ if(ic_data->HX_INT_IS_EDGE)
+ {
+ I("%s edge triiger falling\n ",__func__);
+ ret = request_threaded_irq(client->irq, NULL, himax_ts_thread,IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts);
+ }
+ else
+ {
+ I("%s level trigger low\n ",__func__);
+ ret = request_threaded_irq(client->irq, NULL, himax_ts_thread,IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts);
+ }
+ if (ret == 0) {
+ ts->irq_enabled = 1;
+ irq_enable_count = 1;
+ tp_irq = client->irq;
+ I("%s: irq enabled at qpio: %d\n", __func__, client->irq);
+#ifdef HX_SMART_WAKEUP
+ irq_set_irq_wake(client->irq, 1);
+#endif
+ } else {
+ ts->use_irq = 0;
+ E("%s: request_irq failed\n", __func__);
+ }
+ } else {
+ I("%s: client->irq is empty, use polling mode.\n", __func__);
+ }
+
+ if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_WORKAROUND function*/
+ ts->himax_wq = create_singlethread_workqueue("himax_touch");
+
+ INIT_WORK(&ts->work, himax_ts_work_func);
+
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = himax_ts_timer_func;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ I("%s: polling mode enabled\n", __func__);
+ }
+ return ret;
+}
+
+static int himax_common_suspend(struct device *dev)
+{
+ struct himax_ts_data *ts = dev_get_drvdata(dev);
+
+ I("%s: enter \n", __func__);
+
+ himax_chip_common_suspend(ts);
+ return 0;
+}
+
+static int himax_common_resume(struct device *dev)
+{
+ struct himax_ts_data *ts = dev_get_drvdata(dev);
+
+ I("%s: enter \n", __func__);
+
+ himax_chip_common_resume(ts);
+ return 0;
+}
+
+#if defined(CONFIG_FB)
+int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct himax_ts_data *ts=
+ container_of(self, struct himax_ts_data, fb_notif);
+
+ I(" %s\n", __func__);
+ if (evdata && evdata->data && event == FB_EVENT_BLANK && ts &&
+ ts->client) {
+ blank = evdata->data;
+
+ mutex_lock(&ts->fb_mutex);
+ switch (*blank) {
+ case FB_BLANK_UNBLANK:
+ if (!ts->probe_done) {
+ himax_ts_init(ts);
+ ts->probe_done = true;
+ } else {
+ himax_common_resume(&ts->client->dev);
+ }
+ break;
+
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ himax_common_suspend(&ts->client->dev);
+ break;
+ }
+ mutex_unlock(&ts->fb_mutex);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct i2c_device_id himax_common_ts_id[] = {
+ {HIMAX_common_NAME, 0 },
+ {}
+};
+
+static const struct dev_pm_ops himax_common_pm_ops = {
+#if (!defined(CONFIG_FB))
+ .suspend = himax_common_suspend,
+ .resume = himax_common_resume,
+#endif
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id himax_match_table[] = {
+ {.compatible = "himax,hxcommon" },
+ {},
+};
+#else
+#define himax_match_table NULL
+#endif
+
+static struct i2c_driver himax_common_driver = {
+ .id_table = himax_common_ts_id,
+ .probe = himax_chip_common_probe,
+ .remove = himax_chip_common_remove,
+ .driver = {
+ .name = HIMAX_common_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = himax_match_table,
+#ifdef CONFIG_PM
+ .pm = &himax_common_pm_ops,
+#endif
+ },
+};
+
+static void __init himax_common_init_async(void *unused, async_cookie_t cookie)
+{
+ I("%s:Enter \n", __func__);
+ i2c_add_driver(&himax_common_driver);
+}
+
+static int __init himax_common_init(void)
+{
+ I("Himax common touch panel driver init\n");
+ async_schedule(himax_common_init_async, NULL);
+ return 0;
+}
+
+static void __exit himax_common_exit(void)
+{
+ i2c_del_driver(&himax_common_driver);
+}
+
+module_init(himax_common_init);
+module_exit(himax_common_exit);
+
+MODULE_DESCRIPTION("Himax_common driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/touchscreen/hxchipset/himax_platform.h b/drivers/input/touchscreen/hxchipset/himax_platform.h
new file mode 100644
index 0000000..1223685
--- /dev/null
+++ b/drivers/input/touchscreen/hxchipset/himax_platform.h
@@ -0,0 +1,135 @@
+/* Himax Android Driver Sample Code for Himax chipset
+*
+* Copyright (C) 2015 Himax Corporation.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#ifndef HIMAX_PLATFORM_H
+#define HIMAX_PLATFORM_H
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#if defined(CONFIG_HMX_DB)
+#include <linux/regulator/consumer.h>
+#endif
+
+#define QCT
+
+#define HIMAX_I2C_RETRY_TIMES 10
+
+#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
+#define D(x...) pr_debug("[HXTP] " x)
+#define I(x...) pr_info("[HXTP] " x)
+#define W(x...) pr_warning("[HXTP][WARNING] " x)
+#define E(x...) pr_err("[HXTP][ERROR] " x)
+#define DIF(x...) \
+do {\
+ if (debug_flag) \
+ pr_debug("[HXTP][DEBUG] " x) \
+} while(0)
+#else
+#define D(x...)
+#define I(x...)
+#define W(x...)
+#define E(x...)
+#define DIF(x...)
+#endif
+
+#if defined(CONFIG_HMX_DB)
+/* Analog voltage @2.7 V */
+#define HX_VTG_MIN_UV 2700000
+#define HX_VTG_MAX_UV 3300000
+#define HX_ACTIVE_LOAD_UA 15000
+#define HX_LPM_LOAD_UA 10
+/* Digital voltage @1.8 V */
+#define HX_VTG_DIG_MIN_UV 1800000
+#define HX_VTG_DIG_MAX_UV 1800000
+#define HX_ACTIVE_LOAD_DIG_UA 10000
+#define HX_LPM_LOAD_DIG_UA 10
+
+#define HX_I2C_VTG_MIN_UV 1800000
+#define HX_I2C_VTG_MAX_UV 1800000
+#define HX_I2C_LOAD_UA 10000
+#define HX_I2C_LPM_LOAD_UA 10
+#endif
+
+#define HIMAX_common_NAME "himax_tp"
+#define HIMAX_I2C_ADDR 0x48
+#define INPUT_DEV_NAME "himax-touchscreen"
+
+struct himax_i2c_platform_data {
+ int abs_x_min;
+ int abs_x_max;
+ int abs_x_fuzz;
+ int abs_y_min;
+ int abs_y_max;
+ int abs_y_fuzz;
+ int abs_pressure_min;
+ int abs_pressure_max;
+ int abs_pressure_fuzz;
+ int abs_width_min;
+ int abs_width_max;
+ int screenWidth;
+ int screenHeight;
+ uint8_t fw_version;
+ uint8_t tw_id;
+ uint8_t powerOff3V3;
+ uint8_t cable_config[2];
+ uint8_t protocol_type;
+ int gpio_irq;
+ int gpio_reset;
+ int gpio_3v3_en;
+ int (*power)(int on);
+ void (*reset)(void);
+ struct himax_virtual_key *virtual_key;
+ struct kobject *vk_obj;
+ struct kobj_attribute *vk2Use;
+
+ struct himax_config *hx_config;
+ int hx_config_size;
+#if defined(CONFIG_HMX_DB)
+ bool i2c_pull_up;
+ bool digital_pwr_regulator;
+ int reset_gpio;
+ u32 reset_gpio_flags;
+ int irq_gpio;
+ u32 irq_gpio_flags;
+
+ struct regulator *vcc_ana; //For Dragon Board
+ struct regulator *vcc_dig; //For Dragon Board
+ struct regulator *vcc_i2c; //For Dragon Board
+#endif
+};
+
+
+extern int irq_enable_count;
+extern int i2c_himax_read(struct i2c_client *client, uint8_t command, uint8_t *data, uint8_t length, uint8_t toRetry);
+extern int i2c_himax_write(struct i2c_client *client, uint8_t command, uint8_t *data, uint8_t length, uint8_t toRetry);
+extern int i2c_himax_write_command(struct i2c_client *client, uint8_t command, uint8_t toRetry);
+extern int i2c_himax_master_write(struct i2c_client *client, uint8_t *data, uint8_t length, uint8_t toRetry);
+extern int i2c_himax_read_command(struct i2c_client *client, uint8_t length, uint8_t *data, uint8_t *readlength, uint8_t toRetry);
+extern void himax_int_enable(int irqnum, int enable);
+extern int himax_ts_register_interrupt(struct i2c_client *client);
+extern void himax_rst_gpio_set(int pinnum, uint8_t value);
+extern uint8_t himax_int_gpio_read(int pinnum);
+
+extern int himax_gpio_power_config(struct i2c_client *client,struct himax_i2c_platform_data *pdata);
+
+#if defined(CONFIG_FB)
+extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
index 21a9e8f..21236e9 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
@@ -113,6 +113,13 @@
#define F12_WAKEUP_GESTURE_MODE 0x02
#define F12_UDG_DETECT 0x0f
+#define PWR_VTG_MIN_UV 2700000
+#define PWR_VTG_MAX_UV 3600000
+#define PWR_ACTIVE_LOAD_UA 2000
+#define I2C_VTG_MIN_UV 1710000
+#define I2C_VTG_MAX_UV 2000000
+#define I2C_ACTIVE_LOAD_UA 7000
+
static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
bool *was_in_bl_mode);
static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data);
@@ -3407,6 +3414,66 @@
return retval;
}
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_load(reg, load_uA) : 0;
+}
+
+static int synaptics_rmi4_configure_reg(struct synaptics_rmi4_data *rmi4_data,
+ bool on)
+{
+ int retval;
+
+ if (on == false)
+ goto hw_shutdown;
+
+ if (rmi4_data->pwr_reg) {
+ if (regulator_count_voltages(rmi4_data->pwr_reg) > 0) {
+ retval = regulator_set_voltage(rmi4_data->pwr_reg,
+ PWR_VTG_MIN_UV, PWR_VTG_MAX_UV);
+ if (retval) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "regulator set_vtg failed retval =%d\n",
+ retval);
+ goto err_set_vtg_pwr;
+ }
+ }
+ }
+
+ if (rmi4_data->bus_reg) {
+ if (regulator_count_voltages(rmi4_data->bus_reg) > 0) {
+ retval = regulator_set_voltage(rmi4_data->bus_reg,
+ I2C_VTG_MIN_UV, I2C_VTG_MAX_UV);
+ if (retval) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "regulator set_vtg failed retval =%d\n",
+ retval);
+ goto err_set_vtg_bus;
+ }
+ }
+ }
+
+ return 0;
+
+err_set_vtg_bus:
+ if (rmi4_data->pwr_reg &&
+ regulator_count_voltages(rmi4_data->pwr_reg) > 0)
+ regulator_set_voltage(rmi4_data->pwr_reg, 0, PWR_VTG_MAX_UV);
+err_set_vtg_pwr:
+ return retval;
+
+hw_shutdown:
+ if (rmi4_data->pwr_reg &&
+ regulator_count_voltages(rmi4_data->pwr_reg) > 0)
+ regulator_set_voltage(rmi4_data->pwr_reg, 0, PWR_VTG_MAX_UV);
+ if (rmi4_data->bus_reg &&
+ regulator_count_voltages(rmi4_data->bus_reg) > 0)
+ regulator_set_voltage(rmi4_data->bus_reg, 0, I2C_VTG_MAX_UV);
+
+ return 0;
+}
+
static int synaptics_rmi4_get_reg(struct synaptics_rmi4_data *rmi4_data,
bool get)
{
@@ -3472,37 +3539,66 @@
}
if (rmi4_data->bus_reg) {
+ retval = reg_set_optimum_mode_check(rmi4_data->bus_reg,
+ I2C_ACTIVE_LOAD_UA);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Regulator set_opt failed rc=%d\n",
+ __func__, retval);
+ return retval;
+ }
+
retval = regulator_enable(rmi4_data->bus_reg);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to enable bus pullup regulator\n",
__func__);
- goto exit;
+ goto err_bus_reg_en;
}
}
if (rmi4_data->pwr_reg) {
+ retval = reg_set_optimum_mode_check(rmi4_data->pwr_reg,
+ PWR_ACTIVE_LOAD_UA);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Regulator set_opt failed rc=%d\n",
+ __func__, retval);
+ goto disable_bus_reg;
+ }
+
retval = regulator_enable(rmi4_data->pwr_reg);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to enable power regulator\n",
__func__);
- goto disable_bus_reg;
+ goto err_pwr_reg_en;
}
msleep(bdata->power_delay_ms);
}
return 0;
+err_pwr_reg_en:
+ reg_set_optimum_mode_check(rmi4_data->pwr_reg, 0);
+ goto disable_bus_reg;
+err_bus_reg_en:
+ reg_set_optimum_mode_check(rmi4_data->bus_reg, 0);
+
+ return retval;
+
disable_pwr_reg:
- if (rmi4_data->pwr_reg)
+ if (rmi4_data->pwr_reg) {
+ reg_set_optimum_mode_check(rmi4_data->pwr_reg, 0);
regulator_disable(rmi4_data->pwr_reg);
+ }
disable_bus_reg:
- if (rmi4_data->bus_reg)
+ if (rmi4_data->bus_reg) {
+ reg_set_optimum_mode_check(rmi4_data->bus_reg, 0);
regulator_disable(rmi4_data->bus_reg);
+ }
-exit:
return retval;
}
@@ -3976,6 +4072,14 @@
goto err_get_reg;
}
+ retval = synaptics_rmi4_configure_reg(rmi4_data, true);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to configure regulators\n",
+ __func__);
+ goto err_configure_reg;
+ }
+
retval = synaptics_rmi4_enable_reg(rmi4_data, true);
if (retval < 0) {
dev_err(&pdev->dev,
@@ -4202,6 +4306,8 @@
err_enable_reg:
synaptics_rmi4_get_reg(rmi4_data, false);
+err_configure_reg:
+ synaptics_rmi4_configure_reg(rmi4_data, false);
err_get_reg:
kfree(rmi4_data);
@@ -4284,6 +4390,7 @@
}
synaptics_rmi4_enable_reg(rmi4_data, false);
+ synaptics_rmi4_configure_reg(rmi4_data, false);
synaptics_rmi4_get_reg(rmi4_data, false);
kfree(rmi4_data);
@@ -4557,6 +4664,7 @@
struct synaptics_rmi4_exp_fhandler *exp_fhandler;
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
int retval;
+ int lpm_uA;
if (rmi4_data->stay_awake)
return 0;
@@ -4565,6 +4673,17 @@
if (rmi4_data->enable_wakeup_gesture) {
if (!rmi4_data->suspend) {
+ /* Set lpm current for bus regulator */
+ lpm_uA = rmi4_data->hw_if->board_data->bus_lpm_cur_uA;
+ if (lpm_uA) {
+ retval = reg_set_optimum_mode_check(
+ rmi4_data->bus_reg, lpm_uA);
+ if (retval < 0)
+ dev_err(dev,
+ "Bus Regulator set_opt failed rc=%d\n",
+ retval);
+ }
+
synaptics_rmi4_wakeup_gesture(rmi4_data, true);
enable_irq_wake(rmi4_data->irq);
}
@@ -4594,7 +4713,8 @@
}
mutex_unlock(&exp_data.mutex);
- if (!rmi4_data->suspend && !rmi4_data->enable_wakeup_gesture)
+ if (!rmi4_data->suspend && !rmi4_data->enable_wakeup_gesture &&
+ !rmi4_data->hw_if->board_data->dont_disable_regs)
synaptics_rmi4_enable_reg(rmi4_data, false);
rmi4_data->suspend = true;
@@ -4623,6 +4743,18 @@
if (rmi4_data->enable_wakeup_gesture) {
if (rmi4_data->suspend) {
+ /* Set active current for the bus regulator */
+ if (rmi4_data->hw_if->board_data->bus_lpm_cur_uA) {
+ retval = reg_set_optimum_mode_check(
+ rmi4_data->bus_reg,
+ I2C_ACTIVE_LOAD_UA);
+ if (retval < 0)
+ dev_err(dev,
+ "Pwr regulator set_opt failed rc=%d\n",
+ retval);
+ }
+
+
synaptics_rmi4_wakeup_gesture(rmi4_data, false);
disable_irq_wake(rmi4_data->irq);
}
@@ -4631,7 +4763,8 @@
rmi4_data->current_page = MASK_8BIT;
- if (rmi4_data->suspend)
+ if (rmi4_data->suspend &&
+ !rmi4_data->hw_if->board_data->dont_disable_regs)
synaptics_rmi4_enable_reg(rmi4_data, true);
synaptics_rmi4_sleep_enable(rmi4_data, false);
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
index f634f17..e078853 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
@@ -84,6 +84,9 @@
bdata->wakeup_gesture_en = of_property_read_bool(np,
"synaptics,wakeup-gestures-en");
+ bdata->dont_disable_regs = of_property_read_bool(np,
+ "synaptics,do-not-disable-regulators");
+
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
if (retval < 0)
bdata->pwr_reg_name = NULL;
@@ -184,6 +187,10 @@
bdata->max_y_for_2d = -1;
}
+ retval = of_property_read_u32(np, "synaptics,bus-lpm-cur-uA",
+ &value);
+ bdata->bus_lpm_cur_uA = retval < 0 ? 0 : value;
+
bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes");
bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip");
bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 5d0cd51..a4b7b4c 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -455,6 +455,14 @@
tsc2007_stop(ts);
+ /* power down the chip (TSC2007_SETUP does not ACK on I2C) */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to setup chip: %d\n", err);
+ return err; /* usually, chip does not respond */
+ }
+
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev,
diff --git a/drivers/iommu/arm-smmu-errata.c b/drivers/iommu/arm-smmu-errata.c
index 2ee2028..e1cb1a4 100644
--- a/drivers/iommu/arm-smmu-errata.c
+++ b/drivers/iommu/arm-smmu-errata.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -41,7 +41,7 @@
ret = hyp_assign_phys(page_to_phys(page), PAGE_ALIGN(size),
&source_vm, 1,
&dest_vm, &dest_perm, 1);
- if (ret) {
+ if (ret && (ret != -EIO)) {
__free_pages(page, get_order(size));
page = NULL;
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 8005ab8..c3376df 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -292,6 +292,7 @@
#define TTBCR2_SEP_SHIFT 15
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
+#define TTBCR2_AS (1 << 4)
#define TTBRn_ASID_SHIFT 48
@@ -369,6 +370,15 @@
bool valid;
};
+struct arm_smmu_cb {
+ u64 ttbr[2];
+ u32 tcr[2];
+ u32 mair[2];
+ struct arm_smmu_cfg *cfg;
+ u32 actlr;
+ u32 attributes;
+};
+
struct arm_smmu_master_cfg {
struct arm_smmu_device *smmu;
s16 smendx[];
@@ -440,6 +450,7 @@
u32 num_s2_context_banks;
DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
DECLARE_BITMAP(secure_context_map, ARM_SMMU_MAX_CBS);
+ struct arm_smmu_cb *cbs;
atomic_t irptndx;
u32 num_mapping_groups;
@@ -595,6 +606,7 @@
static bool arm_smmu_is_static_cb(struct arm_smmu_device *smmu);
static bool arm_smmu_is_master_side_secure(struct arm_smmu_domain *smmu_domain);
static bool arm_smmu_is_slave_side_secure(struct arm_smmu_domain *smmu_domain);
+static bool arm_smmu_opt_hibernation(struct arm_smmu_device *smmu);
static int msm_secure_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot);
@@ -623,6 +635,13 @@
arm_smmu_options[i].prop);
}
} while (arm_smmu_options[++i].opt);
+
+ if (arm_smmu_opt_hibernation(smmu) &&
+ smmu->options && ARM_SMMU_OPT_SKIP_INIT) {
+ dev_info(smmu->dev,
+ "Disabling incompatible option: skip-init\n");
+ smmu->options &= ~ARM_SMMU_OPT_SKIP_INIT;
+ }
}
static bool is_dynamic_domain(struct iommu_domain *domain)
@@ -693,6 +712,11 @@
mutex_unlock(&smmu_domain->assign_lock);
}
+static bool arm_smmu_opt_hibernation(struct arm_smmu_device *smmu)
+{
+ return IS_ENABLED(CONFIG_HIBERNATION);
+}
+
/*
* init()
* Hook for additional device tree parsing at probe time.
@@ -1625,17 +1649,76 @@
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg)
{
- u32 reg, reg2;
- u64 reg64;
- bool stage1;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
+ bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
+
+ cb->cfg = cfg;
+
+ /* TTBCR */
+ if (stage1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr;
+ } else {
+ cb->tcr[0] = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+ cb->tcr[1] = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
+ cb->tcr[1] |= TTBCR2_SEP_UPSTREAM;
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
+ cb->tcr[1] |= TTBCR2_AS;
+ }
+ } else {
+ cb->tcr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+ }
+
+ /* TTBRs */
+ if (stage1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
+ cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
+ } else {
+ cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+ cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
+ cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
+ cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
+ }
+ } else {
+ cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+ }
+
+ /* MAIRs (stage-1 only) */
+ if (stage1) {
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ cb->mair[0] = pgtbl_cfg->arm_v7s_cfg.prrr;
+ cb->mair[1] = pgtbl_cfg->arm_v7s_cfg.nmrr;
+ } else {
+ cb->mair[0] = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
+ cb->mair[1] = pgtbl_cfg->arm_lpae_s1_cfg.mair[1];
+ }
+ }
+
+ cb->attributes = smmu_domain->attributes;
+}
+
+static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
+{
+ u32 reg;
+ bool stage1;
+ struct arm_smmu_cb *cb = &smmu->cbs[idx];
+ struct arm_smmu_cfg *cfg = cb->cfg;
void __iomem *cb_base, *gr1_base;
+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, idx);
+
+ /* Unassigned context banks only need disabling */
+ if (!cfg) {
+ writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+ return;
+ }
+
gr1_base = ARM_SMMU_GR1(smmu);
stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+ /* CBA2R */
if (smmu->version > ARM_SMMU_V1) {
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
reg = CBA2R_RW64_64BIT;
@@ -1645,7 +1728,7 @@
if (smmu->features & ARM_SMMU_FEAT_VMID16)
reg |= ARM_SMMU_CB_VMID(smmu, cfg) << CBA2R_VMID_SHIFT;
- writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx));
+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(idx));
}
/* CBAR */
@@ -1664,81 +1747,57 @@
/* 8-bit VMIDs live in CBAR */
reg |= ARM_SMMU_CB_VMID(smmu, cfg) << CBAR_VMID_SHIFT;
}
- writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx));
+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(idx));
+
+ /*
+ * TTBCR
+ * We must write this before the TTBRs, since it determines the
+ * access behaviour of some fields (in particular, ASID[15:8]).
+ */
+ if (stage1 && smmu->version > ARM_SMMU_V1)
+ writel_relaxed(cb->tcr[1], cb_base + ARM_SMMU_CB_TTBCR2);
+ writel_relaxed(cb->tcr[0], cb_base + ARM_SMMU_CB_TTBCR);
/* TTBRs */
- if (stage1) {
- u16 asid = ARM_SMMU_CB_ASID(smmu, cfg);
-
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- reg = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0);
- reg = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1);
- writel_relaxed(asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);
- } else {
- reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
- reg64 |= (u64)asid << TTBRn_ASID_SHIFT;
- writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0);
- reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
- reg64 |= (u64)asid << TTBRn_ASID_SHIFT;
- writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR1);
- }
+ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
+ writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);
+ writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
+ writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
} else {
- reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
- writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0);
+ writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
+ if (stage1)
+ writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
}
- /* TTBCR */
- if (stage1) {
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- reg = pgtbl_cfg->arm_v7s_cfg.tcr;
- reg2 = 0;
- } else {
- reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
- reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
- reg2 |= TTBCR2_SEP_UPSTREAM;
- }
- if (smmu->version > ARM_SMMU_V1)
- writel_relaxed(reg2, cb_base + ARM_SMMU_CB_TTBCR2);
- } else {
- reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
- }
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
-
/* MAIRs (stage-1 only) */
if (stage1) {
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
- reg = pgtbl_cfg->arm_v7s_cfg.prrr;
- reg2 = pgtbl_cfg->arm_v7s_cfg.nmrr;
- } else {
- reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
- reg2 = pgtbl_cfg->arm_lpae_s1_cfg.mair[1];
- }
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
- writel_relaxed(reg2, cb_base + ARM_SMMU_CB_S1_MAIR1);
+ writel_relaxed(cb->mair[0], cb_base + ARM_SMMU_CB_S1_MAIR0);
+ writel_relaxed(cb->mair[1], cb_base + ARM_SMMU_CB_S1_MAIR1);
}
+ /* ACTLR (implementation defined) */
+ writel_relaxed(cb->actlr, cb_base + ARM_SMMU_CB_ACTLR);
+
/* SCTLR */
reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE;
/* Ensure bypass transactions are Non-shareable */
reg |= SCTLR_SHCFG_NSH << SCTLR_SHCFG_SHIFT;
- if (smmu_domain->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
+ if (cb->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
reg &= ~SCTLR_CFCFG;
reg |= SCTLR_HUPCF;
}
- if ((!(smmu_domain->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) &&
- !(smmu_domain->attributes & (1 << DOMAIN_ATTR_EARLY_MAP))) ||
+ if ((!(cb->attributes & (1 << DOMAIN_ATTR_S1_BYPASS)) &&
+ !(cb->attributes & (1 << DOMAIN_ATTR_EARLY_MAP))) ||
!stage1)
reg |= SCTLR_M;
if (stage1)
reg |= SCTLR_S1_ASIDPNE;
-#ifdef __BIG_ENDIAN
- reg |= SCTLR_E;
-#endif
+ if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ reg |= SCTLR_E;
+
writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
}
@@ -1813,6 +1872,14 @@
goto out_unlock;
}
+ if (arm_smmu_has_secure_vmid(smmu_domain) &&
+ arm_smmu_opt_hibernation(smmu)) {
+ dev_err(smmu->dev,
+ "Secure usecases not supported with hibernation\n");
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
/*
* Mapping the requested stage onto what we support is surprisingly
* complicated, mainly because the spec allows S1+S2 SMMUs without
@@ -1988,7 +2055,9 @@
if (!dynamic) {
/* Initialise the context bank with our page table cfg */
arm_smmu_init_context_bank(smmu_domain,
- &smmu_domain->pgtbl_cfg);
+ &smmu_domain->pgtbl_cfg);
+ arm_smmu_arch_init_context_bank(smmu_domain, dev);
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
/* for slave side secure, we may have to force the pagetable
* format to V8L.
*/
@@ -1997,8 +2066,6 @@
if (ret)
goto out_clear_smmu;
- arm_smmu_arch_init_context_bank(smmu_domain, dev);
-
/*
* Request context fault interrupt. Do this last to avoid the
* handler seeing a half-initialised domain state.
@@ -2046,7 +2113,6 @@
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- void __iomem *cb_base;
int irq;
bool dynamic;
int ret;
@@ -2078,8 +2144,8 @@
* Disable the context bank and free the page tables before freeing
* it.
*/
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
- writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+ smmu->cbs[cfg->cbndx].cfg = NULL;
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
if (cfg->irptndx != INVALID_IRPTNDX) {
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
@@ -3502,19 +3568,16 @@
{
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
struct arm_smmu_device *smmu = smmu_domain->smmu;
- void __iomem *cb_base;
- u32 reg;
+ struct arm_smmu_cb *cb = &smmu->cbs[cfg->cbndx];
int ret;
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
ret = arm_smmu_power_on(smmu->pwr);
if (ret)
return ret;
- reg = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR);
- reg |= SCTLR_M;
+ cb->attributes &= ~(1 << DOMAIN_ATTR_EARLY_MAP);
+ arm_smmu_write_context_bank(smmu, cfg->cbndx);
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
arm_smmu_power_off(smmu->pwr);
return ret;
}
@@ -3701,18 +3764,17 @@
int i;
u32 val;
struct arm_smmu_impl_def_reg *regs = smmu->impl_def_attach_registers;
- void __iomem *cb_base;
-
/*
* SCTLR.M must be disabled here per ARM SMMUv2 spec
* to prevent table walks with an inconsistent state.
*/
for (i = 0; i < smmu->num_context_banks; ++i) {
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
+ struct arm_smmu_cb *cb = &smmu->cbs[i];
+
val = ACTLR_QCOM_ISH << ACTLR_QCOM_ISH_SHIFT |
ACTLR_QCOM_OSH << ACTLR_QCOM_OSH_SHIFT |
ACTLR_QCOM_NSH << ACTLR_QCOM_NSH_SHIFT;
- writel_relaxed(val, cb_base + ARM_SMMU_CB_ACTLR);
+ cb->actlr = val;
}
/* Program implementation defined registers */
@@ -3777,7 +3839,6 @@
int i;
u32 reg, major;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
- void __iomem *cb_base;
/*
* Before clearing ARM_MMU500_ACTLR_CPRE, need to
@@ -3794,8 +3855,10 @@
/* Make sure all context banks are disabled and clear CB_FSR */
for (i = 0; i < smmu->num_context_banks; ++i) {
- cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
- writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+ void __iomem *cb_base = ARM_SMMU_CB_BASE(smmu) +
+ ARM_SMMU_CB(smmu, i);
+
+ arm_smmu_write_context_bank(smmu, i);
writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR);
/*
* Disable MMU-500's not-particularly-beneficial next-page
@@ -3952,6 +4015,33 @@
return cb;
}
+static void parse_static_cb_cfg(struct arm_smmu_device *smmu)
+{
+ u32 idx = 0;
+ u32 val;
+ int ret;
+
+ if (!(arm_smmu_is_static_cb(smmu) &&
+ arm_smmu_opt_hibernation(smmu)))
+ return;
+
+ /*
+ * Context banks may be xpu-protected. Require a devicetree property to
+ * indicate which context banks HLOS has access to.
+ */
+ bitmap_set(smmu->secure_context_map, 0, ARM_SMMU_MAX_CBS);
+ while (idx < ARM_SMMU_MAX_CBS) {
+ ret = of_property_read_u32_index(
+ smmu->dev->of_node, "qcom,static-ns-cbs",
+ idx++, &val);
+ if (ret)
+ break;
+
+ bitmap_clear(smmu->secure_context_map, val, 1);
+ dev_dbg(smmu->dev, "Detected NS context bank: %d\n", idx);
+ }
+}
+
static int arm_smmu_handoff_cbs(struct arm_smmu_device *smmu)
{
u32 i, raw_smr, raw_s2cr;
@@ -4425,6 +4515,10 @@
&cavium_smmu_context_count);
smmu->cavium_id_base -= smmu->num_context_banks;
}
+ smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks,
+ sizeof(*smmu->cbs), GFP_KERNEL);
+ if (!smmu->cbs)
+ return -ENOMEM;
/* ID2 */
id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2);
@@ -4647,6 +4741,7 @@
}
parse_driver_options(smmu);
+ parse_static_cb_cfg(smmu);
smmu->pwr = arm_smmu_init_power_resources(pdev);
if (IS_ERR(smmu->pwr))
@@ -4753,8 +4848,9 @@
if (arm_smmu_power_on(smmu->pwr))
return -EINVAL;
- if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS) ||
- !bitmap_empty(smmu->secure_context_map, ARM_SMMU_MAX_CBS))
+ if (!(bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS) &&
+ (bitmap_empty(smmu->secure_context_map, ARM_SMMU_MAX_CBS) ||
+ arm_smmu_opt_hibernation(smmu))))
dev_err(&pdev->dev, "removing device with active domains!\n");
idr_destroy(&smmu->asid_idr);
@@ -4768,10 +4864,43 @@
return 0;
}
+static int arm_smmu_pm_freeze(struct device *dev)
+{
+ struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+
+ if (!arm_smmu_opt_hibernation(smmu)) {
+ dev_err(smmu->dev, "Aborting: Hibernation not supported\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int arm_smmu_pm_restore(struct device *dev)
+{
+ struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+ int ret;
+
+ ret = arm_smmu_power_on(smmu->pwr);
+ if (ret)
+ return ret;
+
+ arm_smmu_device_reset(smmu);
+ arm_smmu_power_off(smmu->pwr);
+ return 0;
+}
+
+static const struct dev_pm_ops arm_smmu_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .freeze = arm_smmu_pm_freeze,
+ .restore = arm_smmu_pm_restore,
+#endif
+};
+
static struct platform_driver arm_smmu_driver = {
.driver = {
.name = "arm-smmu",
.of_match_table = of_match_ptr(arm_smmu_of_match),
+ .pm = &arm_smmu_pm_ops,
},
.probe = arm_smmu_device_dt_probe,
.remove = arm_smmu_device_remove,
@@ -4782,6 +4911,7 @@
{
static bool registered;
int ret = 0;
+ struct device_node *node;
ktime_t cur;
if (registered)
@@ -4793,9 +4923,12 @@
return ret;
ret = platform_driver_register(&arm_smmu_driver);
-#ifdef CONFIG_MSM_TZ_SMMU
- ret = register_iommu_sec_ptbl();
-#endif
+ /* Disable secure usecases if hibernation support is enabled */
+ node = of_find_compatible_node(NULL, NULL, "qcom,qsmmu-v500");
+ if (IS_ENABLED(CONFIG_MSM_TZ_SMMU) && node &&
+ !of_find_property(node, "qcom,hibernation-support", NULL))
+ ret = register_iommu_sec_ptbl();
+
registered = !ret;
trace_smmu_init(ktime_us_delta(ktime_get(), cur));
@@ -5385,20 +5518,14 @@
struct device *dev)
{
struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cb *cb = &smmu->cbs[smmu_domain->cfg.cbndx];
struct qsmmuv500_group_iommudata *iommudata =
to_qsmmuv500_group_iommudata(dev->iommu_group);
- void __iomem *cb_base;
- const struct iommu_gather_ops *tlb;
if (!iommudata->has_actlr)
return;
- tlb = smmu_domain->pgtbl_cfg.tlb;
- cb_base = ARM_SMMU_CB_BASE(smmu) +
- ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx);
-
- writel_relaxed(iommudata->actlr, cb_base + ARM_SMMU_CB_ACTLR);
-
+ cb->actlr = iommudata->actlr;
/*
* Prefetch only works properly if the start and end of all
* buffers in the page table are aligned to 16 Kb.
@@ -5406,12 +5533,6 @@
if ((iommudata->actlr >> QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT) &
QSMMUV500_ACTLR_DEEP_PREFETCH_MASK)
smmu_domain->qsmmuv500_errata2_min_align = true;
-
- /*
- * Flush the context bank after modifying ACTLR to ensure there
- * are no cache entries with stale state
- */
- tlb->tlb_flush_all(smmu_domain);
}
static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index cb72e00..f846f01 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -127,6 +127,7 @@
pr_err("IOMMU: %s: Failed to request IRQ for page request queue\n",
iommu->name);
dmar_free_hwirq(irq);
+ iommu->pr_irq = 0;
goto err;
}
dmar_writeq(iommu->reg + DMAR_PQH_REG, 0ULL);
@@ -142,9 +143,11 @@
dmar_writeq(iommu->reg + DMAR_PQT_REG, 0ULL);
dmar_writeq(iommu->reg + DMAR_PQA_REG, 0ULL);
- free_irq(iommu->pr_irq, iommu);
- dmar_free_hwirq(iommu->pr_irq);
- iommu->pr_irq = 0;
+ if (iommu->pr_irq) {
+ free_irq(iommu->pr_irq, iommu);
+ dmar_free_hwirq(iommu->pr_irq);
+ iommu->pr_irq = 0;
+ }
free_pages((unsigned long)iommu->prq, PRQ_ORDER);
iommu->prq = NULL;
@@ -386,6 +389,7 @@
pasid_max - 1, GFP_KERNEL);
if (ret < 0) {
kfree(svm);
+ kfree(sdev);
goto out;
}
svm->pasid = ret;
diff --git a/drivers/iommu/io-pgtable-msm-secure.c b/drivers/iommu/io-pgtable-msm-secure.c
index d0a8a79..e0314f9 100644
--- a/drivers/iommu/io-pgtable-msm-secure.c
+++ b/drivers/iommu/io-pgtable-msm-secure.c
@@ -47,6 +47,11 @@
int msm_iommu_sec_pgtbl_init(void)
{
+ struct msm_scm_ptbl_init {
+ unsigned int paddr;
+ unsigned int size;
+ unsigned int spare;
+ } pinit = {0};
int psize[2] = {0, 0};
unsigned int spare = 0;
int ret, ptbl_ret = 0;
@@ -55,7 +60,12 @@
dma_addr_t paddr;
unsigned long attrs = 0;
- if (is_scm_armv8()) {
+ struct scm_desc desc = {0};
+
+ if (!is_scm_armv8()) {
+ ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_PTBL_SIZE, &spare,
+ sizeof(spare), psize, sizeof(psize));
+ } else {
struct scm_desc desc = {0};
desc.args[0] = spare;
@@ -64,12 +74,11 @@
IOMMU_SECURE_PTBL_SIZE), &desc);
psize[0] = desc.ret[0];
psize[1] = desc.ret[1];
- if (ret || psize[1]) {
- pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
- return ret;
- }
}
-
+ if (ret || psize[1]) {
+ pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+ goto fail;
+ }
/* Now allocate memory for the secure page tables */
attrs = DMA_ATTR_NO_KERNEL_MAPPING;
dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
@@ -78,34 +87,42 @@
if (!cpu_addr) {
pr_err("%s: Failed to allocate %d bytes for PTBL\n",
__func__, psize[0]);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail;
}
- if (is_scm_armv8()) {
- struct scm_desc desc = {0};
+ pinit.paddr = (unsigned int)paddr;
+ /* paddr may be a physical address > 4GB */
+ desc.args[0] = paddr;
+ desc.args[1] = pinit.size = psize[0];
+ desc.args[2] = pinit.spare;
+ desc.arginfo = SCM_ARGS(3, SCM_RW, SCM_VAL, SCM_VAL);
- desc.args[0] = paddr;
- desc.args[1] = psize[0];
- desc.args[2] = 0;
- desc.arginfo = SCM_ARGS(3, SCM_RW, SCM_VAL, SCM_VAL);
-
+ if (!is_scm_armv8()) {
+ ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_PTBL_INIT, &pinit,
+ sizeof(pinit), &ptbl_ret, sizeof(ptbl_ret));
+ } else {
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
IOMMU_SECURE_PTBL_INIT), &desc);
ptbl_ret = desc.ret[0];
-
- if (ret) {
- pr_err("scm call IOMMU_SECURE_PTBL_INIT failed\n");
- return ret;
- }
-
- if (ptbl_ret) {
- pr_err("scm call IOMMU_SECURE_PTBL_INIT extended ret fail\n");
- return ret;
- }
+ }
+ if (ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_INIT failed\n");
+ goto fail_mem;
+ }
+ if (ptbl_ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_INIT extended ret fail\n");
+ goto fail_mem;
}
return 0;
+
+fail_mem:
+ dma_free_attrs(&dev, psize[0], cpu_addr, paddr, attrs);
+fail:
+ return ret;
}
+
EXPORT_SYMBOL(msm_iommu_sec_pgtbl_init);
static int msm_secure_map(struct io_pgtable_ops *ops, unsigned long iova,
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index e2583cc..5455671 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1299,6 +1299,7 @@
const unsigned long flags = SLAB_HWCACHE_ALIGN;
size_t align = 1 << 10; /* L2 pagetable alignement */
struct device_node *np;
+ int ret;
np = of_find_matching_node(NULL, omap_iommu_of_match);
if (!np)
@@ -1312,11 +1313,25 @@
return -ENOMEM;
iopte_cachep = p;
- bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
-
omap_iommu_debugfs_init();
- return platform_driver_register(&omap_iommu_driver);
+ ret = platform_driver_register(&omap_iommu_driver);
+ if (ret) {
+ pr_err("%s: failed to register driver\n", __func__);
+ goto fail_driver;
+ }
+
+ ret = bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
+ if (ret)
+ goto fail_bus;
+
+ return 0;
+
+fail_bus:
+ platform_driver_unregister(&omap_iommu_driver);
+fail_driver:
+ kmem_cache_destroy(iopte_cachep);
+ return ret;
}
subsys_initcall(omap_iommu_init);
/* must be ready before omap3isp is probed */
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index 9ae7180..1c2ca8d 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -21,6 +21,8 @@
#include "irq-gic-common.h"
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
static const struct gic_kvm_info *gic_kvm_info;
const struct gic_kvm_info *gic_get_kvm_info(void)
@@ -52,11 +54,13 @@
u32 confoff = (irq / 16) * 4;
u32 val, oldval;
int ret = 0;
+ unsigned long flags;
/*
* Read current configuration register, and insert the config
* for "irq", depending on "type".
*/
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
if (type & IRQ_TYPE_LEVEL_MASK)
val &= ~confmask;
@@ -64,8 +68,10 @@
val |= confmask;
/* If the current configuration is the same, then we are done */
- if (val == oldval)
+ if (val == oldval) {
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
return 0;
+ }
/*
* Write back the new configuration, and possibly re-enable
@@ -83,6 +89,7 @@
pr_warn("GIC: PPI%d is secure or misconfigured\n",
irq - 16);
}
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
if (sync_access)
sync_access();
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index acb9d25..ac15e5d 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -684,7 +684,7 @@
* This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations.
*/
#define IRQS_PER_CHUNK_SHIFT 5
-#define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT)
+#define IRQS_PER_CHUNK (1UL << IRQS_PER_CHUNK_SHIFT)
static unsigned long *lpi_bitmap;
static u32 lpi_chunks;
@@ -1320,11 +1320,10 @@
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
/*
- * At least one bit of EventID is being used, hence a minimum
- * of two entries. No, the architecture doesn't let you
- * express an ITT with a single entry.
+ * We allocate at least one chunk worth of LPIs bet device,
+ * and thus that many ITEs. The device may require less though.
*/
- nr_ites = max(2UL, roundup_pow_of_two(nvecs));
+ nr_ites = max(IRQS_PER_CHUNK, roundup_pow_of_two(nvecs));
sz = nr_ites * its->ite_size;
sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
itt = kzalloc(sz, GFP_KERNEL);
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 2519c92..b0e9fe6 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -44,6 +44,34 @@
#include "irq-gic-common.h"
+#define MAX_IRQ 1020U /* Max number of SGI+PPI+SPI */
+#define SPI_START_IRQ 32 /* SPI start irq number */
+#define GICD_ICFGR_BITS 2 /* 2 bits per irq in GICD_ICFGR */
+#define GICD_ISENABLER_BITS 1 /* 1 bit per irq in GICD_ISENABLER */
+#define GICD_IPRIORITYR_BITS 8 /* 8 bits per irq in GICD_IPRIORITYR */
+
+/* 32 bit mask with lower n bits set */
+#define UMASK_LOW(n) (~0U >> (32 - (n)))
+
+/* Number of 32-bit words required to store all irqs, for
+ * registers where each word stores configuration for each irq
+ * in bits_per_irq bits.
+ */
+#define NUM_IRQ_WORDS(bits_per_irq) (DIV_ROUND_UP(MAX_IRQ, \
+ 32 / (bits_per_irq)))
+#define MAX_IRQS_IGNORE 10
+
+#define IRQ_NR_BOUND(nr) min((nr), MAX_IRQ)
+
+/* Bitmap to irqs, which are restored */
+static DECLARE_BITMAP(irqs_restore, MAX_IRQ);
+
+/* Bitmap to irqs, for which restore is ignored.
+ * Presently, only GICD_IROUTER mismatches are
+ * ignored.
+ */
+static DECLARE_BITMAP(irqs_ignore_restore, MAX_IRQ);
+
struct redist_region {
void __iomem *redist_base;
phys_addr_t phys_base;
@@ -60,6 +88,16 @@
u32 nr_redist_regions;
unsigned int irq_nr;
struct partition_desc *ppi_descs[16];
+
+ u64 saved_spi_router[MAX_IRQ];
+ u32 saved_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)];
+ u32 saved_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)];
+ u32 saved_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)];
+
+ u64 changed_spi_router[MAX_IRQ];
+ u32 changed_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)];
+ u32 changed_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)];
+ u32 changed_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)];
};
static struct gic_chip_data gic_data __read_mostly;
@@ -67,6 +105,58 @@
static struct gic_kvm_info gic_v3_kvm_info;
+enum gicd_save_restore_reg {
+ SAVED_ICFGR,
+ SAVED_IS_ENABLER,
+ SAVED_IPRIORITYR,
+ NUM_SAVED_GICD_REGS,
+};
+
+/* Stores start address of spi config for saved gicd regs */
+static u32 *saved_spi_regs_start[NUM_SAVED_GICD_REGS] = {
+ [SAVED_ICFGR] = gic_data.saved_spi_cfg,
+ [SAVED_IS_ENABLER] = gic_data.saved_spi_enable,
+ [SAVED_IPRIORITYR] = gic_data.saved_spi_priority,
+};
+
+/* Stores start address of spi config for changed gicd regs */
+static u32 *changed_spi_regs_start[NUM_SAVED_GICD_REGS] = {
+ [SAVED_ICFGR] = gic_data.changed_spi_cfg,
+ [SAVED_IS_ENABLER] = gic_data.changed_spi_enable,
+ [SAVED_IPRIORITYR] = gic_data.changed_spi_priority,
+};
+
+/* GICD offset for saved registers */
+static u32 gicd_offset[NUM_SAVED_GICD_REGS] = {
+ [SAVED_ICFGR] = GICD_ICFGR,
+ [SAVED_IS_ENABLER] = GICD_ISENABLER,
+ [SAVED_IPRIORITYR] = GICD_IPRIORITYR,
+};
+
+/* Bits per irq word, for gicd saved registers */
+static u32 gicd_reg_bits_per_irq[NUM_SAVED_GICD_REGS] = {
+ [SAVED_ICFGR] = GICD_ICFGR_BITS,
+ [SAVED_IS_ENABLER] = GICD_ISENABLER_BITS,
+ [SAVED_IPRIORITYR] = GICD_IPRIORITYR_BITS,
+};
+
+#define for_each_spi_irq_word(i, reg) \
+ for (i = 0; \
+ i < DIV_ROUND_UP(IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ, \
+ 32 / gicd_reg_bits_per_irq[reg]); \
+ i++)
+
+#define read_spi_word_offset(base, reg, i) \
+ readl_relaxed_no_log( \
+ base + gicd_offset[reg] + i * 4 + \
+ SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8)
+
+#define restore_spi_word_offset(base, reg, i) \
+ writel_relaxed_no_log( \
+ saved_spi_regs_start[reg][i],\
+ base + gicd_offset[reg] + i * 4 + \
+ SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8)
+
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
@@ -134,6 +224,229 @@
}
#endif
+void gic_v3_dist_save(void)
+{
+ void __iomem *base = gic_data.dist_base;
+ int reg, i;
+
+ for (reg = SAVED_ICFGR; reg < NUM_SAVED_GICD_REGS; reg++) {
+ for_each_spi_irq_word(i, reg) {
+ saved_spi_regs_start[reg][i] =
+ read_spi_word_offset(base, reg, i);
+ }
+ }
+
+ for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++)
+ gic_data.saved_spi_router[i] =
+ gic_read_irouter(base + GICD_IROUTER + i * 8);
+}
+
+static void _gicd_check_reg(enum gicd_save_restore_reg reg)
+{
+ void __iomem *base = gic_data.dist_base;
+ u32 *saved_spi_cfg = saved_spi_regs_start[reg];
+ u32 *changed_spi_cfg = changed_spi_regs_start[reg];
+ u32 bits_per_irq = gicd_reg_bits_per_irq[reg];
+ u32 current_cfg = 0;
+ int i, j = SPI_START_IRQ, l;
+ u32 k;
+
+ for_each_spi_irq_word(i, reg) {
+ current_cfg = read_spi_word_offset(base, reg, i);
+ if (current_cfg != saved_spi_cfg[i]) {
+ for (k = current_cfg ^ saved_spi_cfg[i],
+ l = 0; k ; k >>= bits_per_irq, l++) {
+ if (k & UMASK_LOW(bits_per_irq))
+ set_bit(j+l, irqs_restore);
+ }
+ changed_spi_cfg[i] = current_cfg ^ saved_spi_cfg[i];
+ }
+ j += 32 / bits_per_irq;
+ }
+}
+
+#define _gic_v3_dist_check_icfgr() \
+ _gicd_check_reg(SAVED_ICFGR)
+#define _gic_v3_dist_check_ipriorityr() \
+ _gicd_check_reg(SAVED_IPRIORITYR)
+#define _gic_v3_dist_check_isenabler() \
+ _gicd_check_reg(SAVED_IS_ENABLER)
+
+static void _gic_v3_dist_check_irouter(void)
+{
+ void __iomem *base = gic_data.dist_base;
+ u64 current_irouter_cfg = 0;
+ int i;
+
+ for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) {
+ if (test_bit(i, irqs_ignore_restore))
+ continue;
+ current_irouter_cfg = gic_read_irouter(
+ base + GICD_IROUTER + i * 8);
+ if (current_irouter_cfg != gic_data.saved_spi_router[i]) {
+ set_bit(i, irqs_restore);
+ gic_data.changed_spi_router[i] =
+ current_irouter_cfg ^ gic_data.saved_spi_router[i];
+ }
+ }
+}
+
+static void _gic_v3_dist_restore_reg(enum gicd_save_restore_reg reg)
+{
+ void __iomem *base = gic_data.dist_base;
+ int i;
+
+ for_each_spi_irq_word(i, reg) {
+ if (changed_spi_regs_start[reg][i])
+ restore_spi_word_offset(base, reg, i);
+ }
+
+ /* Commit all restored configurations before subsequent writes */
+ wmb();
+}
+
+#define _gic_v3_dist_restore_icfgr() _gic_v3_dist_restore_reg(SAVED_ICFGR)
+#define _gic_v3_dist_restore_ipriorityr() \
+ _gic_v3_dist_restore_reg(SAVED_IPRIORITYR)
+
+static void _gic_v3_dist_restore_set_reg(u32 offset)
+{
+ void __iomem *base = gic_data.dist_base;
+ int i, j = SPI_START_IRQ, l;
+ int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ;
+
+ for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) {
+ u32 reg_val = readl_relaxed_no_log(base + offset + i * 4 + 4);
+ bool irqs_restore_updated = 0;
+
+ for (l = 0; l < 32; l++) {
+ if (test_bit(j+l, irqs_restore)) {
+ reg_val |= BIT(l);
+ irqs_restore_updated = 1;
+ }
+ }
+
+ if (irqs_restore_updated) {
+ writel_relaxed_no_log(
+ reg_val, base + offset + i * 4 + 4);
+ }
+ }
+
+ /* Commit restored configuration updates before subsequent writes */
+ wmb();
+}
+
+#define _gic_v3_dist_restore_isenabler() \
+ _gic_v3_dist_restore_set_reg(GICD_ISENABLER)
+
+#define _gic_v3_dist_restore_ispending() \
+ _gic_v3_dist_restore_set_reg(GICD_ISPENDR)
+
+static void _gic_v3_dist_restore_irouter(void)
+{
+ void __iomem *base = gic_data.dist_base;
+ int i;
+
+ for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) {
+ if (test_bit(i, irqs_ignore_restore))
+ continue;
+ if (gic_data.changed_spi_router[i]) {
+ gic_write_irouter(gic_data.saved_spi_router[i],
+ base + GICD_IROUTER + i * 8);
+ }
+ }
+
+ /* Commit GICD_IROUTER writes before subsequent writes */
+ wmb();
+}
+
+static void _gic_v3_dist_clear_reg(u32 offset)
+{
+ void __iomem *base = gic_data.dist_base;
+ int i, j = SPI_START_IRQ, l;
+ int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ;
+
+ for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) {
+ u32 clear = 0;
+ bool irqs_restore_updated = 0;
+
+ for (l = 0; l < 32; l++) {
+ if (test_bit(j+l, irqs_restore)) {
+ clear |= BIT(l);
+ irqs_restore_updated = 1;
+ }
+ }
+
+ if (irqs_restore_updated) {
+ writel_relaxed_no_log(
+ clear, base + offset + i * 4 + 4);
+ }
+ }
+
+ /* Commit clearing of irq config before subsequent writes */
+ wmb();
+}
+
+#define _gic_v3_dist_set_icenabler() \
+ _gic_v3_dist_clear_reg(GICD_ICENABLER)
+
+#define _gic_v3_dist_set_icpending() \
+ _gic_v3_dist_clear_reg(GICD_ICPENDR)
+
+#define _gic_v3_dist_set_icactive() \
+ _gic_v3_dist_clear_reg(GICD_ICACTIVER)
+
+/* Restore GICD state for SPIs. SPI configuration is restored
+ * for GICD_ICFGR, GICD_ISENABLER, GICD_IPRIORITYR, GICD_IROUTER
+ * registers. Following is the sequence for restore:
+ *
+ * 1. For SPIs, check whether any of GICD_ICFGR, GICD_ISENABLER,
+ * GICD_IPRIORITYR, GICD_IROUTER, current configuration is
+ * different from saved configuration.
+ *
+ * For all irqs, with mismatched configurations,
+ *
+ * 2. Set GICD_ICENABLER and wait for its completion.
+ *
+ * 3. Restore any changed GICD_ICFGR, GICD_IPRIORITYR, GICD_IROUTER
+ * configurations.
+ *
+ * 4. Set GICD_ICACTIVER.
+ *
+ * 5. Set pending for the interrupt.
+ *
+ * 6. Enable interrupt and wait for its completion.
+ *
+ */
+void gic_v3_dist_restore(void)
+{
+ _gic_v3_dist_check_icfgr();
+ _gic_v3_dist_check_ipriorityr();
+ _gic_v3_dist_check_isenabler();
+ _gic_v3_dist_check_irouter();
+
+ if (bitmap_empty(irqs_restore, IRQ_NR_BOUND(gic_data.irq_nr)))
+ return;
+
+ _gic_v3_dist_set_icenabler();
+ gic_dist_wait_for_rwp();
+
+ _gic_v3_dist_restore_icfgr();
+ _gic_v3_dist_restore_ipriorityr();
+ _gic_v3_dist_restore_irouter();
+
+ _gic_v3_dist_set_icactive();
+
+ _gic_v3_dist_set_icpending();
+ _gic_v3_dist_restore_ispending();
+
+ _gic_v3_dist_restore_isenabler();
+ gic_dist_wait_for_rwp();
+
+ /* Commit all writes before proceeding */
+ wmb();
+}
+
/*
* gic_show_pending_irq - Shows the pending interrupts
* Note: Interrupts should be disabled on the cpu from which
@@ -721,7 +1034,7 @@
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
- smp_wmb();
+ wmb();
for_each_cpu(cpu, mask) {
unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL;
@@ -1244,7 +1557,8 @@
struct redist_region *rdist_regs;
u64 redist_stride;
u32 nr_redist_regions;
- int err, i;
+ int err, i, ignore_irqs_len;
+ u32 ignore_restore_irqs[MAX_IRQS_IGNORE] = {0};
dist_base = of_iomap(node, 0);
if (!dist_base) {
@@ -1294,6 +1608,14 @@
gic_populate_ppi_partitions(node);
gic_of_setup_kvm_info(node);
+
+ ignore_irqs_len = of_property_read_variable_u32_array(node,
+ "ignored-save-restore-irqs",
+ ignore_restore_irqs,
+ 0, MAX_IRQS_IGNORE);
+ for (i = 0; i < ignore_irqs_len; i++)
+ set_bit(ignore_restore_irqs[i], irqs_ignore_restore);
+
return 0;
out_unmap_rdist:
@@ -1359,6 +1681,10 @@
u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2;
void __iomem *redist_base;
+ /* GICC entry which has !ACPI_MADT_ENABLED is not unusable so skip */
+ if (!(gicc->flags & ACPI_MADT_ENABLED))
+ return 0;
+
redist_base = ioremap(gicc->gicr_base_address, size);
if (!redist_base)
return -ENOMEM;
@@ -1408,6 +1734,13 @@
if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address)
return 0;
+ /*
+ * It's perfectly valid firmware can pass disabled GICC entry, driver
+ * should not treat as errors, skip the entry instead of probe fail.
+ */
+ if (!(gicc->flags & ACPI_MADT_ENABLED))
+ return 0;
+
return -ENODEV;
}
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index bd189a4..8f3aa54 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -41,6 +41,7 @@
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/msm_rtb.h>
#include <asm/cputype.h>
#include <asm/irq.h>
@@ -364,6 +365,7 @@
writel_relaxed_no_log(irqstat,
cpu_base + GIC_CPU_EOI);
handle_domain_irq(gic->domain, irqnr, regs);
+ uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
continue;
}
if (irqnr < 16) {
@@ -382,6 +384,7 @@
smp_rmb();
handle_IPI(irqnr, regs);
#endif
+ uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
continue;
}
break;
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index 03b79b0..05d87f6 100644
--- a/drivers/irqchip/irq-mbigen.c
+++ b/drivers/irqchip/irq-mbigen.c
@@ -105,10 +105,7 @@
static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq,
u32 *mask, u32 *addr)
{
- unsigned int ofst;
-
- hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
- ofst = hwirq / 32 * 4;
+ unsigned int ofst = (hwirq / 32) * 4;
*mask = 1 << (hwirq % 32);
*addr = ofst + REG_MBIGEN_CLEAR_OFFSET;
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index d74374f..abf696b 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -55,6 +55,7 @@
static unsigned int timer_cpu_pin;
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
+DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
static void __gic_irq_dispatch(void);
@@ -746,17 +747,17 @@
return gic_setup_dev_chip(d, virq, spec->hwirq);
} else {
- base_hwirq = find_first_bit(ipi_resrv, gic_shared_intrs);
+ base_hwirq = find_first_bit(ipi_available, gic_shared_intrs);
if (base_hwirq == gic_shared_intrs) {
return -ENOMEM;
}
/* check that we have enough space */
for (i = base_hwirq; i < nr_irqs; i++) {
- if (!test_bit(i, ipi_resrv))
+ if (!test_bit(i, ipi_available))
return -EBUSY;
}
- bitmap_clear(ipi_resrv, base_hwirq, nr_irqs);
+ bitmap_clear(ipi_available, base_hwirq, nr_irqs);
/* map the hwirq for each cpu consecutively */
i = 0;
@@ -787,7 +788,7 @@
return 0;
error:
- bitmap_set(ipi_resrv, base_hwirq, nr_irqs);
+ bitmap_set(ipi_available, base_hwirq, nr_irqs);
return ret;
}
@@ -802,7 +803,7 @@
return;
base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data));
- bitmap_set(ipi_resrv, base_hwirq, nr_irqs);
+ bitmap_set(ipi_available, base_hwirq, nr_irqs);
}
int gic_irq_domain_match(struct irq_domain *d, struct device_node *node,
@@ -1066,6 +1067,7 @@
2 * gic_vpes);
}
+ bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
gic_basic_init();
}
diff --git a/drivers/irqchip/qcom/Makefile b/drivers/irqchip/qcom/Makefile
index 8871f9a..e30f074 100644
--- a/drivers/irqchip/qcom/Makefile
+++ b/drivers/irqchip/qcom/Makefile
@@ -2,4 +2,4 @@
obj-$(CONFIG_QTI_PDC_SDM845) += pdc-sdm845.o
obj-$(CONFIG_QTI_PDC_SDM670) += pdc-sdm670.o
obj-$(CONFIG_QTI_PDC_SDXPOORWILLS) += pdc-sdxpoorwills.o
-obj-$(CONFIG_QTI_MPM) += mpm.o mpm-8953.o mpm-8937.o
+obj-$(CONFIG_QTI_MPM) += mpm.o mpm-8909.o mpm-8953.o mpm-8937.o
diff --git a/drivers/irqchip/qcom/mpm-8909.c b/drivers/irqchip/qcom/mpm-8909.c
new file mode 100644
index 0000000..2767171
--- /dev/null
+++ b/drivers/irqchip/qcom/mpm-8909.c
@@ -0,0 +1,70 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mpm.h"
+
+const struct mpm_pin mpm_msm8909_gic_chip_data[] = {
+ {2, 216}, /* tsens_upper_lower_int */
+ {48, 168}, /* usb_phy_id_irq */
+ {49, 172}, /* usb1_hs_async_wakeup_irq */
+ {58, 166}, /* usb_hs_irq */
+ {53, 104}, /* mdss_irq */
+ {62, 222}, /* ee0_krait_hlos_spmi_periph_irq */
+ {-1},
+};
+
+const struct mpm_pin mpm_msm8909_gpio_chip_data[] = {
+ {3, 65 },
+ {4, 5},
+ {5, 11},
+ {6, 12},
+ {7, 64},
+ {8, 58},
+ {9, 50},
+ {10, 13},
+ {11, 49},
+ {12, 20},
+ {13, 21},
+ {14, 25},
+ {15, 46},
+ {16, 45},
+ {17, 28},
+ {18, 44},
+ {19, 31},
+ {20, 43},
+ {21, 42},
+ {22, 34},
+ {23, 35},
+ {24, 36},
+ {25, 37},
+ {26, 38},
+ {27, 39},
+ {28, 40},
+ {29, 41},
+ {30, 90},
+ {32, 91},
+ {33, 92},
+ {34, 94},
+ {35, 95},
+ {36, 96},
+ {37, 97},
+ {38, 98},
+ {39, 110},
+ {40, 111},
+ {41, 112},
+ {42, 105},
+ {43, 107},
+ {50, 47},
+ {51, 48},
+ {-1},
+};
diff --git a/drivers/irqchip/qcom/mpm.c b/drivers/irqchip/qcom/mpm.c
index 2de64b6..5e352f4 100644
--- a/drivers/irqchip/qcom/mpm.c
+++ b/drivers/irqchip/qcom/mpm.c
@@ -597,6 +597,10 @@
.compatible = "qcom,mpm-gic-msm8937",
.data = mpm_msm8937_gic_chip_data,
},
+ {
+ .compatible = "qcom,mpm-gic-msm8909",
+ .data = mpm_msm8909_gic_chip_data,
+ },
{}
};
@@ -611,6 +615,10 @@
.compatible = "qcom,mpm-gpio-msm8937",
.data = mpm_msm8937_gpio_chip_data,
},
+ {
+ .compatible = "qcom,mpm-gpio-msm8909",
+ .data = mpm_msm8909_gpio_chip_data,
+ },
{}
};
diff --git a/drivers/irqchip/qcom/mpm.h b/drivers/irqchip/qcom/mpm.h
index 50a127f..c82191e 100644
--- a/drivers/irqchip/qcom/mpm.h
+++ b/drivers/irqchip/qcom/mpm.h
@@ -27,4 +27,7 @@
extern const struct mpm_pin mpm_msm8937_gic_chip_data[];
extern const struct mpm_pin mpm_msm8937_gpio_chip_data[];
+extern const struct mpm_pin mpm_msm8909_gic_chip_data[];
+extern const struct mpm_pin mpm_msm8909_gpio_chip_data[];
+
#endif /* __QCOM_MPM_H__ */
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
index e3fa1cd..a57b04f 100644
--- a/drivers/isdn/hardware/mISDN/avmfritz.c
+++ b/drivers/isdn/hardware/mISDN/avmfritz.c
@@ -156,7 +156,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct fritzcard *card;
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
index d5bdbaf..1fc2906 100644
--- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c
+++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
@@ -244,7 +244,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct inf_hw *card;
diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c
index afde4ed..e9fcae4 100644
--- a/drivers/isdn/hardware/mISDN/netjet.c
+++ b/drivers/isdn/hardware/mISDN/netjet.c
@@ -111,7 +111,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct tiger_hw *card;
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
index 9815bb4..1f1446e 100644
--- a/drivers/isdn/hardware/mISDN/speedfax.c
+++ b/drivers/isdn/hardware/mISDN/speedfax.c
@@ -94,7 +94,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct sfax_hw *card;
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
index 3b067ea..0db6783 100644
--- a/drivers/isdn/hardware/mISDN/w6692.c
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -101,7 +101,7 @@
}
static int
-set_debug(const char *val, struct kernel_param *kp)
+set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct w6692_hw *card;
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 9cb4b62..b92a19a 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -72,7 +72,7 @@
if (sk->sk_state != MISDN_BOUND)
continue;
if (!cskb)
- cskb = skb_copy(skb, GFP_KERNEL);
+ cskb = skb_copy(skb, GFP_ATOMIC);
if (!cskb) {
printk(KERN_WARNING "%s no skb\n", __func__);
break;
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 787bda3..f931a2c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -723,6 +723,16 @@
peripheral found on Qualcomm Technologies, Inc. QPNP PMICs.
The vibrator-ldo peripheral is capable of driving ERM vibrators.
+config LEDS_QPNP_VIBRATOR
+ tristate "Vibrator support for QPNP PMIC"
+ depends on LEDS_CLASS && MFD_SPMI_PMIC
+ help
+ This option enables device driver support for the vibrator
+ on the Qualcomm technologies Inc's QPNP PMICs. The vibrator
+ is connected on the VIB_DRV_N line and can be controlled
+ manually or by the DTEST lines.It uses the android timed-output
+ framework.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index e9eaa50..31d29f0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -78,6 +78,7 @@
obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o
obj-$(CONFIG_LEDS_QPNP_HAPTICS) += leds-qpnp-haptics.o
obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o
+obj-$(CONFIG_LEDS_QPNP_VIBRATOR) += leds-qpnp-vibrator.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 8cb3265..2522a3d 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -56,6 +56,7 @@
if (state == LED_OFF && !(led_cdev->flags & LED_KEEP_TRIGGER))
led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state);
+ led_cdev->usr_brightness_req = state;
ret = size;
unlock:
@@ -71,7 +72,24 @@
return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
-static DEVICE_ATTR_RO(max_brightness);
+
+static ssize_t max_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ unsigned long state;
+ ssize_t ret = -EINVAL;
+
+ ret = kstrtoul(buf, 10, &state);
+ if (ret)
+ return ret;
+
+ led_cdev->max_brightness = state;
+ led_set_brightness(led_cdev, led_cdev->usr_brightness_req);
+
+ return size;
+}
+static DEVICE_ATTR_RW(max_brightness);
#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 3bce448..454ed4d 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -188,6 +188,7 @@
{
del_timer_sync(&led_cdev->blink_timer);
+ led_cdev->flags &= ~LED_BLINK_SW;
led_cdev->flags &= ~LED_BLINK_ONESHOT;
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 840401a..f672648 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -266,7 +266,7 @@
"slave address 0x%02x\n",
id->name, chip->bits, client->addr);
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
if (pdata) {
diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c
index a526743..8988ba3 100644
--- a/drivers/leds/leds-pm8058.c
+++ b/drivers/leds/leds-pm8058.c
@@ -106,7 +106,7 @@
if (!led)
return -ENOMEM;
- led->ledtype = (u32)of_device_get_match_data(&pdev->dev);
+ led->ledtype = (u32)(unsigned long)of_device_get_match_data(&pdev->dev);
map = dev_get_regmap(pdev->dev.parent, NULL);
if (!map) {
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index d4b64ba..759d853 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -353,7 +353,7 @@
if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000)))
return 0;
- return DIV_ROUND_UP(target_curr_ma * 1000, ires_ua) - 1;
+ return DIV_ROUND_CLOSEST(target_curr_ma * 1000, ires_ua) - 1;
}
static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
diff --git a/drivers/leds/leds-qpnp-haptics.c b/drivers/leds/leds-qpnp-haptics.c
index 764657a..8a850c5 100644
--- a/drivers/leds/leds-qpnp-haptics.c
+++ b/drivers/leds/leds-qpnp-haptics.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/qpnp/qpnp-misc.h>
#include <linux/qpnp/qpnp-revid.h>
@@ -321,6 +322,7 @@
int sc_irq;
struct pwm_param pwm_data;
struct hap_lra_ares_param ares_cfg;
+ struct regulator *vcc_pon;
u32 play_time_ms;
u32 max_play_time_ms;
u32 vmax_mv;
@@ -355,6 +357,7 @@
bool lra_auto_mode;
bool play_irq_en;
bool auto_res_err_recovery_hw;
+ bool vcc_pon_enabled;
};
static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip);
@@ -801,10 +804,29 @@
enable = atomic_read(&chip->state);
pr_debug("state: %d\n", enable);
+
+ if (chip->vcc_pon && enable && !chip->vcc_pon_enabled) {
+ rc = regulator_enable(chip->vcc_pon);
+ if (rc < 0)
+ pr_err("%s: could not enable vcc_pon regulator rc=%d\n",
+ __func__, rc);
+ else
+ chip->vcc_pon_enabled = true;
+ }
+
rc = qpnp_haptics_play(chip, enable);
if (rc < 0)
pr_err("Error in %sing haptics, rc=%d\n",
enable ? "play" : "stopp", rc);
+
+ if (chip->vcc_pon && !enable && chip->vcc_pon_enabled) {
+ rc = regulator_disable(chip->vcc_pon);
+ if (rc)
+ pr_err("%s: could not disable vcc_pon regulator rc=%d\n",
+ __func__, rc);
+ else
+ chip->vcc_pon_enabled = false;
+ }
}
static enum hrtimer_restart hap_stop_timer(struct hrtimer *timer)
@@ -2054,6 +2076,7 @@
struct device_node *revid_node, *misc_node;
const char *temp_str;
int rc, temp;
+ struct regulator *vcc_pon;
rc = of_property_read_u32(node, "reg", &temp);
if (rc < 0) {
@@ -2381,6 +2404,16 @@
else if (chip->play_mode == HAP_PWM)
rc = qpnp_haptics_parse_pwm_dt(chip);
+ if (of_find_property(node, "vcc_pon-supply", NULL)) {
+ vcc_pon = regulator_get(&chip->pdev->dev, "vcc_pon");
+ if (IS_ERR(vcc_pon)) {
+ rc = PTR_ERR(vcc_pon);
+ dev_err(&chip->pdev->dev,
+ "regulator get failed vcc_pon rc=%d\n", rc);
+ }
+ chip->vcc_pon = vcc_pon;
+ }
+
return rc;
}
diff --git a/drivers/leds/leds-qpnp-vibrator-ldo.c b/drivers/leds/leds-qpnp-vibrator-ldo.c
index 6a14324..dd19dd1 100644
--- a/drivers/leds/leds-qpnp-vibrator-ldo.c
+++ b/drivers/leds/leds-qpnp-vibrator-ldo.c
@@ -65,9 +65,29 @@
bool disable_overdrive;
};
-static int qpnp_vib_ldo_set_voltage(struct vib_ldo_chip *chip, int new_uV)
+static inline int qpnp_vib_ldo_poll_status(struct vib_ldo_chip *chip)
{
unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(chip->regmap,
+ chip->base + QPNP_VIB_LDO_REG_STATUS1, val,
+ val & QPNP_VIB_LDO_VREG_READY, 100, 1000);
+ if (ret < 0) {
+ pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n",
+ val, ret);
+
+ /* Keep VIB_LDO disabled */
+ regmap_update_bits(chip->regmap,
+ chip->base + QPNP_VIB_LDO_REG_EN_CTL,
+ QPNP_VIB_LDO_EN, 0);
+ }
+
+ return ret;
+}
+
+static int qpnp_vib_ldo_set_voltage(struct vib_ldo_chip *chip, int new_uV)
+{
u32 vlevel;
u8 reg[2];
int ret;
@@ -86,13 +106,9 @@
}
if (chip->vib_enabled) {
- ret = regmap_read_poll_timeout(chip->regmap,
- chip->base + QPNP_VIB_LDO_REG_STATUS1,
- val, val & QPNP_VIB_LDO_VREG_READY,
- 100, 1000);
+ ret = qpnp_vib_ldo_poll_status(chip);
if (ret < 0) {
- pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n",
- val, ret);
+ pr_err("Vibrator LDO status polling timedout\n");
return ret;
}
}
@@ -103,7 +119,6 @@
static inline int qpnp_vib_ldo_enable(struct vib_ldo_chip *chip, bool enable)
{
- unsigned int val;
int ret;
if (chip->vib_enabled == enable)
@@ -120,13 +135,9 @@
}
if (enable) {
- ret = regmap_read_poll_timeout(chip->regmap,
- chip->base + QPNP_VIB_LDO_REG_STATUS1,
- val, val & QPNP_VIB_LDO_VREG_READY,
- 100, 1000);
+ ret = qpnp_vib_ldo_poll_status(chip);
if (ret < 0) {
- pr_err("Vibrator LDO vreg_ready timeout, status=0x%02x, ret=%d\n",
- val, ret);
+ pr_err("Vibrator LDO status polling timedout\n");
return ret;
}
}
@@ -430,6 +441,7 @@
}
hrtimer_cancel(&chip->stop_timer);
cancel_work_sync(&chip->vib_work);
+ qpnp_vib_ldo_enable(chip, false);
mutex_unlock(&chip->lock);
return 0;
diff --git a/drivers/leds/leds-qpnp-vibrator.c b/drivers/leds/leds-qpnp-vibrator.c
new file mode 100644
index 0000000..cc2615d
--- /dev/null
+++ b/drivers/leds/leds-qpnp-vibrator.c
@@ -0,0 +1,526 @@
+/* Copyright (c) 2013-2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/device.h>
+#include <linux/qpnp/pwm.h>
+
+#define QPNP_VIB_VTG_CTL(base) (base + 0x41)
+#define QPNP_VIB_EN_CTL(base) (base + 0x46)
+
+#define QPNP_VIB_MAX_LEVEL 31
+#define QPNP_VIB_MIN_LEVEL 12
+
+#define QPNP_VIB_DEFAULT_TIMEOUT 15000
+#define QPNP_VIB_DEFAULT_VTG_LVL 3100
+
+#define QPNP_VIB_EN BIT(7)
+#define QPNP_VIB_VTG_SET_MASK 0x1F
+#define QPNP_VIB_LOGIC_SHIFT 4
+
+enum qpnp_vib_mode {
+ QPNP_VIB_MANUAL,
+ QPNP_VIB_DTEST1,
+ QPNP_VIB_DTEST2,
+ QPNP_VIB_DTEST3,
+};
+
+struct qpnp_pwm_info {
+ struct pwm_device *pwm_dev;
+ u32 pwm_channel;
+ u32 duty_us;
+ u32 period_us;
+};
+
+struct qpnp_vib {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct hrtimer vib_timer;
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct qpnp_pwm_info pwm_info;
+ enum qpnp_vib_mode mode;
+
+ u8 reg_vtg_ctl;
+ u8 reg_en_ctl;
+ u8 active_low;
+ u16 base;
+ int state;
+ int vtg_level;
+ int timeout;
+ u32 vib_play_ms;
+ struct mutex lock;
+};
+
+static int qpnp_vib_read_u8(struct qpnp_vib *vib, u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = regmap_read(vib->regmap, reg, (unsigned int *)data);
+ if (rc < 0)
+ dev_err(&vib->pdev->dev,
+ "Error reading address: %X - ret %X\n", reg, rc);
+
+ return rc;
+}
+
+static int qpnp_vib_write_u8(struct qpnp_vib *vib, u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = regmap_write(vib->regmap, reg, (unsigned int)*data);
+ if (rc < 0)
+ dev_err(&vib->pdev->dev,
+ "Error writing address: %X - ret %X\n", reg, rc);
+
+ return rc;
+}
+
+static int qpnp_vibrator_config(struct qpnp_vib *vib)
+{
+ u8 reg = 0;
+ int rc;
+
+ /* Configure the VTG CTL regiser */
+ rc = qpnp_vib_read_u8(vib, ®, QPNP_VIB_VTG_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ reg &= ~QPNP_VIB_VTG_SET_MASK;
+ reg |= (vib->vtg_level & QPNP_VIB_VTG_SET_MASK);
+ rc = qpnp_vib_write_u8(vib, ®, QPNP_VIB_VTG_CTL(vib->base));
+ if (rc)
+ return rc;
+ vib->reg_vtg_ctl = reg;
+
+ /* Configure the VIB ENABLE regiser */
+ rc = qpnp_vib_read_u8(vib, ®, QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ reg |= (!!vib->active_low) << QPNP_VIB_LOGIC_SHIFT;
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ vib->pwm_info.pwm_dev = pwm_request(vib->pwm_info.pwm_channel,
+ "qpnp-vib");
+ if (IS_ERR_OR_NULL(vib->pwm_info.pwm_dev)) {
+ dev_err(&vib->pdev->dev, "vib pwm request failed\n");
+ return -ENODEV;
+ }
+
+ rc = pwm_config(vib->pwm_info.pwm_dev, vib->pwm_info.duty_us,
+ vib->pwm_info.period_us);
+ if (rc < 0) {
+ dev_err(&vib->pdev->dev, "vib pwm config failed\n");
+ pwm_free(vib->pwm_info.pwm_dev);
+ return -ENODEV;
+ }
+
+ reg |= BIT(vib->mode - 1);
+ }
+
+ rc = qpnp_vib_write_u8(vib, ®, QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ vib->reg_en_ctl = reg;
+
+ return rc;
+}
+
+static int qpnp_vib_set(struct qpnp_vib *vib, int on)
+{
+ int rc;
+ u8 val;
+
+ if (on) {
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ pwm_enable(vib->pwm_info.pwm_dev);
+ } else {
+ val = vib->reg_en_ctl;
+ val |= QPNP_VIB_EN;
+ rc = qpnp_vib_write_u8(vib, &val,
+ QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ vib->reg_en_ctl = val;
+ }
+ } else {
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ pwm_disable(vib->pwm_info.pwm_dev);
+ } else {
+ val = vib->reg_en_ctl;
+ val &= ~QPNP_VIB_EN;
+ rc = qpnp_vib_write_u8(vib, &val,
+ QPNP_VIB_EN_CTL(vib->base));
+ if (rc < 0)
+ return rc;
+ vib->reg_en_ctl = val;
+ }
+ }
+
+ return 0;
+}
+
+static void qpnp_vib_update(struct work_struct *work)
+{
+ struct qpnp_vib *vib = container_of(work, struct qpnp_vib,
+ work);
+ qpnp_vib_set(vib, vib->state);
+}
+
+static enum hrtimer_restart qpnp_vib_timer_func(struct hrtimer *timer)
+{
+ struct qpnp_vib *vib = container_of(timer, struct qpnp_vib,
+ vib_timer);
+
+ vib->state = 0;
+ schedule_work(&vib->work);
+
+ return HRTIMER_NORESTART;
+}
+
+#ifdef CONFIG_PM
+static int qpnp_vibrator_suspend(struct device *dev)
+{
+ struct qpnp_vib *vib = dev_get_drvdata(dev);
+
+ hrtimer_cancel(&vib->vib_timer);
+ cancel_work_sync(&vib->work);
+ /* turn-off vibrator */
+ qpnp_vib_set(vib, 0);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qpnp_vibrator_pm_ops, qpnp_vibrator_suspend, NULL);
+
+static int qpnp_vib_parse_dt(struct qpnp_vib *vib)
+{
+ struct platform_device *pdev = vib->pdev;
+ struct device_node *node = pdev->dev.of_node;
+ const char *mode;
+ u32 temp_val;
+ int rc;
+
+ vib->timeout = QPNP_VIB_DEFAULT_TIMEOUT;
+
+ rc = of_property_read_u32(node, "reg", &temp_val);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ node->full_name, rc);
+ return rc;
+ }
+ vib->base = temp_val;
+
+ rc = of_property_read_u32(node,
+ "qcom,vib-timeout-ms", &temp_val);
+ if (!rc) {
+ vib->timeout = temp_val;
+ } else if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read vib timeout\n");
+ return rc;
+ }
+
+ vib->vtg_level = QPNP_VIB_DEFAULT_VTG_LVL;
+ rc = of_property_read_u32(node,
+ "qcom,vib-vtg-level-mV", &temp_val);
+ if (!rc) {
+ vib->vtg_level = temp_val;
+ } else if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read vtg level\n");
+ return rc;
+ }
+
+ vib->vtg_level /= 100;
+ if (vib->vtg_level < QPNP_VIB_MIN_LEVEL)
+ vib->vtg_level = QPNP_VIB_MIN_LEVEL;
+ else if (vib->vtg_level > QPNP_VIB_MAX_LEVEL)
+ vib->vtg_level = QPNP_VIB_MAX_LEVEL;
+
+ vib->mode = QPNP_VIB_MANUAL;
+ rc = of_property_read_string(node, "qcom,mode", &mode);
+ if (!rc) {
+ if (strcmp(mode, "manual") == 0) {
+ vib->mode = QPNP_VIB_MANUAL;
+ } else if (strcmp(mode, "dtest1") == 0) {
+ vib->mode = QPNP_VIB_DTEST1;
+ } else if (strcmp(mode, "dtest2") == 0) {
+ vib->mode = QPNP_VIB_DTEST2;
+ } else if (strcmp(mode, "dtest3") == 0) {
+ vib->mode = QPNP_VIB_DTEST3;
+ } else {
+ dev_err(&pdev->dev, "Invalid mode\n");
+ return -EINVAL;
+ }
+ } else if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read mode\n");
+ return rc;
+ }
+
+ if (vib->mode != QPNP_VIB_MANUAL) {
+ rc = of_property_read_u32(node,
+ "qcom,pwm-channel", &temp_val);
+ if (!rc)
+ vib->pwm_info.pwm_channel = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(node,
+ "qcom,period-us", &temp_val);
+ if (!rc)
+ vib->pwm_info.period_us = temp_val;
+ else
+ return rc;
+
+ rc = of_property_read_u32(node,
+ "qcom,duty-us", &temp_val);
+ if (!rc)
+ vib->pwm_info.duty_us = temp_val;
+ else
+ return rc;
+ }
+
+ vib->active_low = of_property_read_bool(node,
+ "qcom,active-low");
+
+ return 0;
+}
+
+static ssize_t qpnp_vib_get_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *chip = container_of(cdev, struct qpnp_vib, cdev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", !!chip->reg_en_ctl);
+}
+
+static ssize_t qpnp_vib_get_duration(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+ ktime_t time_rem;
+ s64 time_us = 0;
+
+ if (hrtimer_active(&vib->vib_timer)) {
+ time_rem = hrtimer_get_remaining(&vib->vib_timer);
+ time_us = ktime_to_us(time_rem);
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%lld\n", div_s64(time_us, 1000));
+}
+
+
+static ssize_t qpnp_vib_set_duration(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+ u32 value;
+ int rc;
+
+ rc = kstrtouint(buf, 0, &value);
+ if (rc < 0)
+ return rc;
+
+ /* setting 0 on duration is NOP for now */
+ if (value <= 0)
+ return count;
+
+ if (value > vib->timeout)
+ value = vib->timeout;
+
+ mutex_lock(&vib->lock);
+ vib->vib_play_ms = value;
+ mutex_unlock(&vib->lock);
+ return count;
+}
+
+static ssize_t qpnp_vib_get_activate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", vib->state);
+}
+
+static ssize_t qpnp_vib_set_activate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct qpnp_vib *vib = container_of(cdev, struct qpnp_vib, cdev);
+ u32 value;
+ int rc;
+
+ rc = kstrtouint(buf, 0, &value);
+ if (rc < 0)
+ return rc;
+
+ if (value != 0 && value != 1)
+ return count;
+
+ vib->state = value;
+ pr_debug("state = %d, time = %ums\n", vib->state,
+ vib->vib_play_ms);
+ mutex_lock(&vib->lock);
+ if (vib->state) {
+ hrtimer_cancel(&vib->vib_timer);
+ hrtimer_start(&vib->vib_timer,
+ ktime_set(vib->vib_play_ms / 1000,
+ (vib->vib_play_ms % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+ vib->vib_play_ms = 0;
+ mutex_unlock(&vib->lock);
+ schedule_work(&vib->work);
+
+ return count;
+}
+
+static struct device_attribute qpnp_vib_attrs[] = {
+ __ATTR(state, 0444, qpnp_vib_get_state, NULL),
+ __ATTR(duration, 0664, qpnp_vib_get_duration, qpnp_vib_set_duration),
+ __ATTR(activate, 0664, qpnp_vib_get_activate, qpnp_vib_set_activate),
+};
+
+/* Dummy functions for brightness */
+static
+enum led_brightness qpnp_brightness_get(struct led_classdev *cdev)
+{
+ return 0;
+}
+
+static void qpnp_brightness_set(struct led_classdev *cdev,
+ enum led_brightness level)
+{
+
+}
+
+static int qpnp_vibrator_probe(struct platform_device *pdev)
+{
+ struct qpnp_vib *vib;
+ int rc;
+ int i;
+
+ vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL);
+ if (!vib)
+ return -ENOMEM;
+
+ vib->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!vib->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ vib->pdev = pdev;
+
+ rc = qpnp_vib_parse_dt(vib);
+ if (rc) {
+ dev_err(&pdev->dev, "DT parsing failed\n");
+ return rc;
+ }
+
+ rc = qpnp_vibrator_config(vib);
+ if (rc) {
+ dev_err(&pdev->dev, "vib config failed\n");
+ return rc;
+ }
+
+ mutex_init(&vib->lock);
+ INIT_WORK(&vib->work, qpnp_vib_update);
+
+ hrtimer_init(&vib->vib_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vib->vib_timer.function = qpnp_vib_timer_func;
+
+ vib->cdev.name = "vibrator";
+ vib->cdev.brightness_get = qpnp_brightness_get;
+ vib->cdev.brightness_set = qpnp_brightness_set;
+ vib->cdev.max_brightness = 100;
+ rc = devm_led_classdev_register(&pdev->dev, &vib->cdev);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Error in registering led class device, rc=%d\n",
+ rc);
+ goto led_reg_fail;
+ }
+
+ /* Enabling sysfs entries */
+ for (i = 0; i < ARRAY_SIZE(qpnp_vib_attrs); i++) {
+ rc = sysfs_create_file(&vib->cdev.dev->kobj,
+ &qpnp_vib_attrs[i].attr);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Error in creating sysfs file, rc=%d\n",
+ rc);
+ goto sysfs_fail;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, vib);
+ return rc;
+
+sysfs_fail:
+ dev_set_drvdata(&pdev->dev, NULL);
+led_reg_fail:
+ hrtimer_cancel(&vib->vib_timer);
+ cancel_work_sync(&vib->work);
+ mutex_destroy(&vib->lock);
+ return -EINVAL;
+}
+
+static int qpnp_vibrator_remove(struct platform_device *pdev)
+{
+ struct qpnp_vib *vib = dev_get_drvdata(&pdev->dev);
+ int i;
+
+/* Removing sysfs entries */
+ for (i = 0; i < ARRAY_SIZE(qpnp_vib_attrs); i++)
+ sysfs_remove_file(&vib->cdev.dev->kobj,
+ &qpnp_vib_attrs[i].attr);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ hrtimer_cancel(&vib->vib_timer);
+ cancel_work_sync(&vib->work);
+ mutex_destroy(&vib->lock);
+ return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+ { .compatible = "qcom,qpnp-vibrator",
+ },
+ {}
+};
+
+static struct platform_driver qpnp_vibrator_driver = {
+ .driver = {
+ .name = "qcom,qpnp-vibrator",
+ .of_match_table = spmi_match_table,
+ .pm = &qpnp_vibrator_pm_ops,
+ },
+ .probe = qpnp_vibrator_probe,
+ .remove = qpnp_vibrator_remove,
+};
+
+module_platform_driver(qpnp_vibrator_driver);
+
+MODULE_DESCRIPTION("qpnp vibrator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index d2e576d..861d987 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2215,12 +2215,16 @@
return rc;
if (wled->en_ext_pfet_sc_pro) {
- reg = QPNP_WLED_EXT_FET_DTEST2;
- rc = qpnp_wled_sec_write_reg(wled,
+ if (!(wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE
+ && wled->pmic_rev_id->rev4 ==
+ PMI8998_V2P0_REV4)) {
+ reg = QPNP_WLED_EXT_FET_DTEST2;
+ rc = qpnp_wled_sec_write_reg(wled,
QPNP_WLED_TEST1_REG(wled->ctrl_base),
reg);
- if (rc)
- return rc;
+ if (rc)
+ return rc;
+ }
}
} else {
rc = qpnp_wled_read_reg(wled,
diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
index 537903b..d23337e 100644
--- a/drivers/md/bcache/alloc.c
+++ b/drivers/md/bcache/alloc.c
@@ -512,15 +512,21 @@
/*
* We keep multiple buckets open for writes, and try to segregate different
- * write streams for better cache utilization: first we look for a bucket where
- * the last write to it was sequential with the current write, and failing that
- * we look for a bucket that was last used by the same task.
+ * write streams for better cache utilization: first we try to segregate flash
+ * only volume write streams from cached devices, secondly we look for a bucket
+ * where the last write to it was sequential with the current write, and
+ * failing that we look for a bucket that was last used by the same task.
*
* The ideas is if you've got multiple tasks pulling data into the cache at the
* same time, you'll get better cache utilization if you try to segregate their
* data and preserve locality.
*
- * For example, say you've starting Firefox at the same time you're copying a
+ * For example, dirty sectors of flash only volume is not reclaimable, if their
+ * dirty sectors mixed with dirty sectors of cached device, such buckets will
+ * be marked as dirty and won't be reclaimed, though the dirty data of cached
+ * device have been written back to backend device.
+ *
+ * And say you've starting Firefox at the same time you're copying a
* bunch of files. Firefox will likely end up being fairly hot and stay in the
* cache awhile, but the data you copied might not be; if you wrote all that
* data to the same buckets it'd get invalidated at the same time.
@@ -537,7 +543,10 @@
struct open_bucket *ret, *ret_task = NULL;
list_for_each_entry_reverse(ret, &c->data_buckets, list)
- if (!bkey_cmp(&ret->key, search))
+ if (UUID_FLASH_ONLY(&c->uuids[KEY_INODE(&ret->key)]) !=
+ UUID_FLASH_ONLY(&c->uuids[KEY_INODE(search)]))
+ continue;
+ else if (!bkey_cmp(&ret->key, search))
goto found;
else if (ret->last_write_point == write_point)
ret_task = ret;
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index e44816f..e4c2d5d 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -892,6 +892,12 @@
mutex_lock(&bch_register_lock);
+ cancel_delayed_work_sync(&dc->writeback_rate_update);
+ if (!IS_ERR_OR_NULL(dc->writeback_thread)) {
+ kthread_stop(dc->writeback_thread);
+ dc->writeback_thread = NULL;
+ }
+
memset(&dc->sb.set_uuid, 0, 16);
SET_BDEV_STATE(&dc->sb, BDEV_STATE_NONE);
@@ -937,6 +943,7 @@
uint32_t rtime = cpu_to_le32(get_seconds());
struct uuid_entry *u;
char buf[BDEVNAME_SIZE];
+ struct cached_dev *exist_dc, *t;
bdevname(dc->bdev, buf);
@@ -960,6 +967,16 @@
return -EINVAL;
}
+ /* Check whether already attached */
+ list_for_each_entry_safe(exist_dc, t, &c->cached_devs, list) {
+ if (!memcmp(dc->sb.uuid, exist_dc->sb.uuid, 16)) {
+ pr_err("Tried to attach %s but duplicate UUID already attached",
+ buf);
+
+ return -EINVAL;
+ }
+ }
+
u = uuid_find(c, dc->sb.uuid);
if (u &&
@@ -1182,7 +1199,7 @@
return;
err:
- pr_notice("error opening %s: %s", bdevname(bdev, name), err);
+ pr_notice("error %s: %s", bdevname(bdev, name), err);
bcache_device_stop(&dc->disk);
}
@@ -1853,6 +1870,8 @@
const char *err = NULL; /* must be set for any error case */
int ret = 0;
+ bdevname(bdev, name);
+
memcpy(&ca->sb, sb, sizeof(struct cache_sb));
ca->bdev = bdev;
ca->bdev->bd_holder = ca;
@@ -1863,11 +1882,12 @@
ca->sb_bio.bi_io_vec[0].bv_page = sb_page;
get_page(sb_page);
- if (blk_queue_discard(bdev_get_queue(ca->bdev)))
+ if (blk_queue_discard(bdev_get_queue(bdev)))
ca->discard = CACHE_DISCARD(&ca->sb);
ret = cache_alloc(ca);
if (ret != 0) {
+ blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
if (ret == -ENOMEM)
err = "cache_alloc(): -ENOMEM";
else
@@ -1890,14 +1910,14 @@
goto out;
}
- pr_info("registered cache device %s", bdevname(bdev, name));
+ pr_info("registered cache device %s", name);
out:
kobject_put(&ca->kobj);
err:
if (err)
- pr_notice("error opening %s: %s", bdevname(bdev, name), err);
+ pr_notice("error %s: %s", name, err);
return ret;
}
@@ -1986,6 +2006,7 @@
if (err)
goto err_close;
+ err = "failed to register device";
if (SB_IS_BDEV(sb)) {
struct cached_dev *dc = kzalloc(sizeof(*dc), GFP_KERNEL);
if (!dc)
@@ -2000,7 +2021,7 @@
goto err_close;
if (register_cache(sb, sb_page, bdev, ca) != 0)
- goto err_close;
+ goto err;
}
out:
if (sb_page)
@@ -2013,7 +2034,7 @@
err_close:
blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
err:
- pr_info("error opening %s: %s", path, err);
+ pr_info("error %s: %s", path, err);
ret = -EINVAL;
goto out;
}
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 3ec647e..809a4df 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -827,7 +827,8 @@
* dm-bufio is resistant to allocation failures (it just keeps
* one buffer reserved in cases all the allocations fail).
* So set flags to not try too hard:
- * GFP_NOIO: don't recurse into the I/O layer
+ * GFP_NOWAIT: don't wait; if we need to sleep we'll release our
+ * mutex and wait ourselves.
* __GFP_NORETRY: don't retry and rather return failure
* __GFP_NOMEMALLOC: don't use emergency reserves
* __GFP_NOWARN: don't print a warning in case of failure
@@ -837,7 +838,7 @@
*/
while (1) {
if (dm_bufio_cache_size_latch != 1) {
- b = alloc_buffer(c, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
+ b = alloc_buffer(c, GFP_NOWAIT | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
if (b)
return b;
}
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 0bf1a12..ee6045d 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -302,6 +302,7 @@
special_cmd_max_sectors = q->limits.max_write_same_sectors;
if ((op == REQ_OP_DISCARD || op == REQ_OP_WRITE_SAME) &&
special_cmd_max_sectors == 0) {
+ atomic_inc(&io->count);
dec_count(io, region, -EOPNOTSUPP);
return;
}
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index be13ebf..f688bfe 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1777,12 +1777,12 @@
cmd == DM_LIST_VERSIONS_CMD)
return 0;
- if ((cmd == DM_DEV_CREATE_CMD)) {
+ if (cmd == DM_DEV_CREATE_CMD) {
if (!*param->name) {
DMWARN("name not supplied when creating device");
return -EINVAL;
}
- } else if ((*param->uuid && *param->name)) {
+ } else if (*param->uuid && *param->name) {
DMWARN("only supply one of name or uuid, cmd(%u)", cmd);
return -EINVAL;
}
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 5d0a996..d96aa84 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/reboot.h>
+#include <linux/vmalloc.h>
#define DM_MSG_PREFIX "verity"
@@ -32,6 +33,7 @@
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
+#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
@@ -395,6 +397,18 @@
}
/*
+ * Moves the bio iter one data block forward.
+ */
+static inline void verity_bv_skip_block(struct dm_verity *v,
+ struct dm_verity_io *io,
+ struct bvec_iter *iter)
+{
+ struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
+
+ bio_advance_iter(bio, iter, 1 << v->data_dev_block_bits);
+}
+
+/*
* Verify one "dm_verity_io" structure.
*/
static int verity_verify_io(struct dm_verity_io *io)
@@ -406,9 +420,16 @@
for (b = 0; b < io->n_blocks; b++) {
int r;
+ sector_t cur_block = io->block + b;
struct shash_desc *desc = verity_io_hash_desc(v, io);
- r = verity_hash_for_block(v, io, io->block + b,
+ if (v->validated_blocks &&
+ likely(test_bit(cur_block, v->validated_blocks))) {
+ verity_bv_skip_block(v, io, &io->iter);
+ continue;
+ }
+
+ r = verity_hash_for_block(v, io, cur_block,
verity_io_want_digest(v, io),
&is_zero);
if (unlikely(r < 0))
@@ -441,13 +462,16 @@
return r;
if (likely(memcmp(verity_io_real_digest(v, io),
- verity_io_want_digest(v, io), v->digest_size) == 0))
+ verity_io_want_digest(v, io), v->digest_size) == 0)) {
+ if (v->validated_blocks)
+ set_bit(cur_block, v->validated_blocks);
continue;
+ }
else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
- io->block + b, NULL, &start) == 0)
+ cur_block, NULL, &start) == 0)
continue;
else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
- io->block + b))
+ cur_block))
return -EIO;
}
@@ -641,6 +665,8 @@
args += DM_VERITY_OPTS_FEC;
if (v->zero_digest)
args++;
+ if (v->validated_blocks)
+ args++;
if (!args)
return;
DMEMIT(" %u", args);
@@ -659,6 +685,8 @@
}
if (v->zero_digest)
DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
+ if (v->validated_blocks)
+ DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
sz = verity_fec_status_table(v, sz, result, maxlen);
break;
}
@@ -712,6 +740,7 @@
if (v->bufio)
dm_bufio_client_destroy(v->bufio);
+ vfree(v->validated_blocks);
kfree(v->salt);
kfree(v->root_digest);
kfree(v->zero_digest);
@@ -733,6 +762,26 @@
}
EXPORT_SYMBOL_GPL(verity_dtr);
+static int verity_alloc_most_once(struct dm_verity *v)
+{
+ struct dm_target *ti = v->ti;
+
+ /* the bitset can only handle INT_MAX blocks */
+ if (v->data_blocks > INT_MAX) {
+ ti->error = "device too large to use check_at_most_once";
+ return -E2BIG;
+ }
+
+ v->validated_blocks = vzalloc(BITS_TO_LONGS(v->data_blocks) *
+ sizeof(unsigned long));
+ if (!v->validated_blocks) {
+ ti->error = "failed to allocate bitset for check_at_most_once";
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int verity_alloc_zero_digest(struct dm_verity *v)
{
int r = -ENOMEM;
@@ -802,6 +851,12 @@
}
continue;
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_AT_MOST_ONCE)) {
+ r = verity_alloc_most_once(v);
+ if (r)
+ return r;
+ continue;
+
} else if (verity_is_fec_opt_arg(arg_name)) {
r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
if (r)
@@ -1070,7 +1125,7 @@
static struct target_type verity_target = {
.name = "verity",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index 75effca..6d6d8df 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -63,6 +63,7 @@
sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
struct dm_verity_fec *fec; /* forward error correction */
+ unsigned long *validated_blocks; /* bitset blocks validated */
};
struct dm_verity_io {
diff --git a/drivers/md/md-cluster.c b/drivers/md/md-cluster.c
index ba7edcd..fcc2b57 100644
--- a/drivers/md/md-cluster.c
+++ b/drivers/md/md-cluster.c
@@ -1122,8 +1122,10 @@
cmsg.raid_slot = cpu_to_le32(rdev->desc_nr);
lock_comm(cinfo);
ret = __sendmsg(cinfo, &cmsg);
- if (ret)
+ if (ret) {
+ unlock_comm(cinfo);
return ret;
+ }
cinfo->no_new_dev_lockres->flags |= DLM_LKF_NOQUEUE;
ret = dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_EX);
cinfo->no_new_dev_lockres->flags &= ~DLM_LKF_NOQUEUE;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index df7d606..93059dd 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -4826,8 +4826,10 @@
return err;
/* cluster raid doesn't support change array_sectors */
- if (mddev_is_clustered(mddev))
+ if (mddev_is_clustered(mddev)) {
+ mddev_unlock(mddev);
return -EINVAL;
+ }
if (strncmp(buf, "default", 7) == 0) {
if (mddev->pers)
@@ -5095,7 +5097,7 @@
return NULL;
}
-static int add_named_array(const char *val, struct kernel_param *kp)
+static int add_named_array(const char *val, const struct kernel_param *kp)
{
/* val must be "md_*" where * is not all digits.
* We allocate an array with a large free minor number, and
@@ -8224,6 +8226,10 @@
int removed = 0;
bool remove_some = false;
+ if (this && test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+ /* Mustn't remove devices when resync thread is running */
+ return 0;
+
rdev_for_each(rdev, mddev) {
if ((this == NULL || rdev == this) &&
rdev->raid_disk >= 0 &&
@@ -8965,11 +8971,11 @@
subsys_initcall(md_init);
module_exit(md_exit)
-static int get_ro(char *buffer, struct kernel_param *kp)
+static int get_ro(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, "%d", start_readonly);
}
-static int set_ro(const char *val, struct kernel_param *kp)
+static int set_ro(const char *val, const struct kernel_param *kp)
{
return kstrtouint(val, 10, (unsigned int *)&start_readonly);
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 8d40238..2b04c720 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2704,6 +2704,11 @@
list_add(&r10_bio->retry_list, &conf->bio_end_io_list);
conf->nr_queued++;
spin_unlock_irq(&conf->device_lock);
+ /*
+ * In case freeze_array() is waiting for condition
+ * nr_pending == nr_queued + extra to be true.
+ */
+ wake_up(&conf->wait_barrier);
md_wakeup_thread(conf->mddev->thread);
} else {
if (test_bit(R10BIO_WriteError,
@@ -3676,6 +3681,7 @@
if (blk_queue_discard(bdev_get_queue(rdev->bdev)))
discard_supported = true;
+ first = 0;
}
if (mddev->queue) {
@@ -4084,6 +4090,7 @@
diff = 0;
if (first || diff < min_offset_diff)
min_offset_diff = diff;
+ first = 0;
}
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 90d863e..a7549a4 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -110,8 +110,7 @@
static inline void lock_all_device_hash_locks_irq(struct r5conf *conf)
{
int i;
- local_irq_disable();
- spin_lock(conf->hash_locks);
+ spin_lock_irq(conf->hash_locks);
for (i = 1; i < NR_STRIPE_HASH_LOCKS; i++)
spin_lock_nest_lock(conf->hash_locks + i, conf->hash_locks);
spin_lock(&conf->device_lock);
@@ -121,9 +120,9 @@
{
int i;
spin_unlock(&conf->device_lock);
- for (i = NR_STRIPE_HASH_LOCKS; i; i--)
- spin_unlock(conf->hash_locks + i - 1);
- local_irq_enable();
+ for (i = NR_STRIPE_HASH_LOCKS - 1; i; i--)
+ spin_unlock(conf->hash_locks + i);
+ spin_unlock_irq(conf->hash_locks);
}
/* bio's attached to a stripe+device for I/O are linked together in bi_sector
@@ -732,12 +731,11 @@
static void lock_two_stripes(struct stripe_head *sh1, struct stripe_head *sh2)
{
- local_irq_disable();
if (sh1 > sh2) {
- spin_lock(&sh2->stripe_lock);
+ spin_lock_irq(&sh2->stripe_lock);
spin_lock_nested(&sh1->stripe_lock, 1);
} else {
- spin_lock(&sh1->stripe_lock);
+ spin_lock_irq(&sh1->stripe_lock);
spin_lock_nested(&sh2->stripe_lock, 1);
}
}
@@ -745,8 +743,7 @@
static void unlock_two_stripes(struct stripe_head *sh1, struct stripe_head *sh2)
{
spin_unlock(&sh1->stripe_lock);
- spin_unlock(&sh2->stripe_lock);
- local_irq_enable();
+ spin_unlock_irq(&sh2->stripe_lock);
}
/* Only freshly new full stripe normal write stripe can be added to a batch list */
@@ -3391,9 +3388,20 @@
BUG_ON(test_bit(R5_Wantcompute, &dev->flags));
BUG_ON(test_bit(R5_Wantread, &dev->flags));
BUG_ON(sh->batch_head);
+
+ /*
+ * In the raid6 case if the only non-uptodate disk is P
+ * then we already trusted P to compute the other failed
+ * drives. It is safe to compute rather than re-read P.
+ * In other cases we only compute blocks from failed
+ * devices, otherwise check/repair might fail to detect
+ * a real inconsistency.
+ */
+
if ((s->uptodate == disks - 1) &&
+ ((sh->qd_idx >= 0 && sh->pd_idx == disk_idx) ||
(s->failed && (disk_idx == s->failed_num[0] ||
- disk_idx == s->failed_num[1]))) {
+ disk_idx == s->failed_num[1])))) {
/* have disk failed, and we're requested to fetch it;
* do compute it
*/
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index b5b5b19..42ce88c 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -779,6 +779,29 @@
goto exit;
}
+ /*
+ * It may need some time for the CAM to settle down, or there might
+ * be a race condition between the CAM, writing HC and our last
+ * check for DA. This happens, if the CAM asserts DA, just after
+ * checking DA before we are setting HC. In this case it might be
+ * a bug in the CAM to keep the FR bit, the lower layer/HW
+ * communication requires a longer timeout or the CAM needs more
+ * time internally. But this happens in reality!
+ * We need to read the status from the HW again and do the same
+ * we did for the previous check for DA
+ */
+ status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+
+ if (status & (STATUSREG_DA | STATUSREG_RE)) {
+ if (status & STATUSREG_DA)
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ status = -EAGAIN;
+ goto exit;
+ }
+
/* send the amount of data */
if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0)
goto exit;
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index e0fe5bc..31f1610 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1262,11 +1262,12 @@
* New users must use I2C client binding directly!
*/
struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
- struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter)
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c_adapter)
{
struct i2c_client *client;
struct i2c_board_info board_info;
- struct m88ds3103_platform_data pdata;
+ struct m88ds3103_platform_data pdata = {};
pdata.clk = cfg->clock;
pdata.i2c_wr_max = cfg->i2c_wr_max;
@@ -1409,6 +1410,8 @@
case M88DS3103_CHIP_ID:
break;
default:
+ ret = -ENODEV;
+ dev_err(&client->dev, "Unknown device. Chip_id=%02x\n", dev->chip_id);
goto err_kfree;
}
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index 20b4a65..0fe69f7 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -14,6 +14,8 @@
* GNU General Public License for more details.
*/
+#include <linux/delay.h>
+
#include "si2168_priv.h"
static const struct dvb_frontend_ops si2168_ops;
@@ -378,6 +380,7 @@
if (ret)
goto err;
+ udelay(100);
memcpy(cmd.args, "\x85", 1);
cmd.wlen = 1;
cmd.rlen = 1;
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 142ae28..d558ed3 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -420,11 +420,13 @@
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
- prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
- queue_work(q, &state->fw_work);
- schedule();
- finish_wait(&state->fw_wait, &wait);
- destroy_workqueue(q);
+ if (q) {
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+ }
/* 6. */
cx25840_write(client, 0x115, 0x8c);
@@ -634,11 +636,13 @@
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
- prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
- queue_work(q, &state->fw_work);
- schedule();
- finish_wait(&state->fw_wait, &wait);
- destroy_workqueue(q);
+ if (q) {
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+ }
/* Call the cx23888 specific std setup func, we no longer rely on
* the generic cx24840 func.
@@ -752,11 +756,13 @@
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
- prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
- queue_work(q, &state->fw_work);
- schedule();
- finish_wait(&state->fw_wait, &wait);
- destroy_workqueue(q);
+ if (q) {
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+ }
cx25840_std_setup(client);
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index 4bf2995..8f85910 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -1033,7 +1033,7 @@
priv->code = MEDIA_BUS_FMT_YUYV8_2X8;
priv->colorspace = V4L2_COLORSPACE_JPEG;
- priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ priv->clk = v4l2_clk_get(&client->dev, NULL);
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto eclkget;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 26d999c..0f572bf 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -222,7 +222,7 @@
static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg,
u8 mask, u8 val)
{
- i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2);
+ i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 1) & mask) | val, 1);
}
static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg)
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 4462d8c..cc8de56 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -58,9 +58,10 @@
return 0;
}
-static int media_device_get_info(struct media_device *dev,
- struct media_device_info *info)
+static long media_device_get_info(struct media_device *dev, void *arg)
{
+ struct media_device_info *info = (struct media_device_info *)arg;
+
memset(info, 0, sizeof(*info));
if (dev->driver_name[0])
@@ -97,9 +98,9 @@
return NULL;
}
-static long media_device_enum_entities(struct media_device *mdev,
- struct media_entity_desc *entd)
+static long media_device_enum_entities(struct media_device *mdev, void *arg)
{
+ struct media_entity_desc *entd = (struct media_entity_desc *)arg;
struct media_entity *ent;
ent = find_entity(mdev, entd->id);
@@ -150,9 +151,9 @@
upad->flags = kpad->flags;
}
-static long media_device_enum_links(struct media_device *mdev,
- struct media_links_enum *links)
+static long media_device_enum_links(struct media_device *mdev, void *arg)
{
+ struct media_links_enum *links = (struct media_links_enum *)arg;
struct media_entity *entity;
entity = find_entity(mdev, links->entity);
@@ -198,9 +199,9 @@
return 0;
}
-static long media_device_setup_link(struct media_device *mdev,
- struct media_link_desc *linkd)
+static long media_device_setup_link(struct media_device *mdev, void *arg)
{
+ struct media_link_desc *linkd = (struct media_link_desc *)arg;
struct media_link *link = NULL;
struct media_entity *source;
struct media_entity *sink;
@@ -226,9 +227,9 @@
return __media_entity_setup_link(link, linkd->flags);
}
-static long media_device_get_topology(struct media_device *mdev,
- struct media_v2_topology *topo)
+static long media_device_get_topology(struct media_device *mdev, void *arg)
{
+ struct media_v2_topology *topo = (struct media_v2_topology *)arg;
struct media_entity *entity;
struct media_interface *intf;
struct media_pad *pad;
diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c
index 8aa7266..90fcccc 100644
--- a/drivers/media/pci/bt8xx/bt878.c
+++ b/drivers/media/pci/bt8xx/bt878.c
@@ -422,8 +422,7 @@
bt878_num);
if (bt878_num >= BT878_MAX) {
printk(KERN_ERR "bt878: Too many devices inserted\n");
- result = -ENOMEM;
- goto fail0;
+ return -ENOMEM;
}
if (pci_enable_device(dev))
return -EIO;
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 99ba8d6..427ece1 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -2282,6 +2282,10 @@
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
if (dev->sd_cx25840) {
+ /* set host data for clk_freq configuration */
+ v4l2_set_subdev_hostdata(dev->sd_cx25840,
+ &dev->clk_freq);
+
dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
}
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index c86b109..dcbb3a2 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -872,6 +872,16 @@
if (cx23885_boards[dev->board].clk_freq > 0)
dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE &&
+ dev->pci->subsystem_device == 0x7137) {
+ /* Hauppauge ImpactVCBe device ID 0x7137 is populated
+ * with an 888, and a 25Mhz crystal, instead of the
+ * usual third overtone 50Mhz. The default clock rate must
+ * be overridden so the cx25840 is properly configured
+ */
+ dev->clk_freq = 25000000;
+ }
+
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
cx23885_irq_add(dev, 0x001f00);
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index 9a5f912..0d4cacb 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -871,6 +871,10 @@
dev->nr = ++cx25821_devcount;
sprintf(dev->name, "cx25821[%d]", dev->nr);
+ if (dev->nr >= ARRAY_SIZE(card)) {
+ CX25821_INFO("dev->nr >= %zd", ARRAY_SIZE(card));
+ return -ENODEV;
+ }
if (dev->pci->device != 0x8210) {
pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
__func__, dev->pci->device);
@@ -886,9 +890,6 @@
dev->channels[i].sram_channels = &cx25821_sram_channels[i];
}
- if (dev->nr > 1)
- CX25821_INFO("dev->nr > 1!");
-
/* board config */
dev->board = 1; /* card[dev->nr]; */
dev->_max_num_decoders = MAX_DECODERS;
diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c
index b4be479..e17d6b9 100644
--- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c
+++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c
@@ -341,6 +341,17 @@
struct solo_dev *solo_dev = vb2_get_drv_priv(q);
solo_stop_thread(solo_dev);
+
+ spin_lock(&solo_dev->slock);
+ while (!list_empty(&solo_dev->vidq_active)) {
+ struct solo_vb2_buf *buf = list_entry(
+ solo_dev->vidq_active.next,
+ struct solo_vb2_buf, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock(&solo_dev->slock);
INIT_LIST_HEAD(&solo_dev->vidq_active);
}
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index 71a0453..279d447 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -72,12 +72,12 @@
}
}
-static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp)
+static int tw686x_dma_mode_get(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, dma_mode_name(dma_mode));
}
-static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp)
+static int tw686x_dma_mode_set(const char *val, const struct kernel_param *kp)
{
if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY)))
dma_mode = TW686X_DMA_MODE_MEMCPY;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index 8beffc4..891b738 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -355,7 +355,7 @@
{
int rc = 0;
- if (!ctx->state_machine) {
+ if (!ctx || !ctx->state_machine) {
CAM_ERR(CAM_CORE, "Context is not ready");
return -EINVAL;
}
@@ -384,7 +384,7 @@
{
int rc = 0;
- if (!ctx->state_machine) {
+ if (!ctx || !ctx->state_machine) {
CAM_ERR(CAM_CORE, "Context is not ready");
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
index 4168ce6..f7990b6 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
@@ -183,6 +183,7 @@
uint32_t num_out_map_entries;
void *priv;
uint64_t request_id;
+ bool init_packet;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index a943680..0a9fabc 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -18,6 +18,34 @@
#include "cam_trace.h"
#include "cam_debug_util.h"
+static void cam_node_print_ctx_state(
+ struct cam_node *node)
+{
+ int i;
+ struct cam_context *ctx;
+
+ CAM_INFO(CAM_CORE, "[%s] state=%d, ctx_size %d",
+ node->name, node->state, node->ctx_size);
+
+ mutex_lock(&node->list_mutex);
+ for (i = 0; i < node->ctx_size; i++) {
+ ctx = &node->ctx_list[i];
+
+ spin_lock(&ctx->lock);
+ CAM_INFO(CAM_CORE,
+ "[%s][%d] : state=%d, refcount=%d, active_req_list=%d, pending_req_list=%d, wait_req_list=%d, free_req_list=%d",
+ ctx->dev_name ? ctx->dev_name : "null",
+ i, ctx->state,
+ atomic_read(&(ctx->refcount.refcount)),
+ list_empty(&ctx->active_req_list),
+ list_empty(&ctx->pending_req_list),
+ list_empty(&ctx->wait_req_list),
+ list_empty(&ctx->free_req_list));
+ spin_unlock(&ctx->lock);
+ }
+ mutex_unlock(&node->list_mutex);
+}
+
static struct cam_context *cam_node_get_ctxt_from_free_list(
struct cam_node *node)
{
@@ -75,6 +103,10 @@
ctx = cam_node_get_ctxt_from_free_list(node);
if (!ctx) {
+ CAM_ERR(CAM_CORE, "No free ctx in free list node %s",
+ node->name);
+ cam_node_print_ctx_state(node);
+
rc = -ENOMEM;
goto err;
}
@@ -86,6 +118,9 @@
goto free_ctx;
}
+ CAM_DBG(CAM_CORE, "[%s] Acquire ctx_id %d",
+ node->name, ctx->ctx_id);
+
return 0;
free_ctx:
cam_context_putref(ctx);
@@ -260,6 +295,10 @@
CAM_ERR(CAM_CORE, "destroy device handle is failed node %s",
node->name);
+ CAM_DBG(CAM_CORE, "[%s] Release ctx_id=%d, refcount=%d",
+ node->name, ctx->ctx_id,
+ atomic_read(&(ctx->refcount.refcount)));
+
cam_context_putref(ctx);
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index 5e4ff0d..90603de 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -22,11 +22,6 @@
#include "cam_cpas_hw_intf.h"
#include "cam_cpas_soc.h"
-#define CAM_CPAS_AXI_MIN_MNOC_AB_BW (2048 * 1024)
-#define CAM_CPAS_AXI_MIN_MNOC_IB_BW (2048 * 1024)
-#define CAM_CPAS_AXI_MIN_CAMNOC_AB_BW (2048 * 1024)
-#define CAM_CPAS_AXI_MIN_CAMNOC_IB_BW (3000000000L)
-
static uint cam_min_camnoc_ib_bw;
module_param(cam_min_camnoc_ib_bw, uint, 0644);
@@ -82,8 +77,8 @@
if (level == bus_client->curr_vote_level)
return 0;
- CAM_DBG(CAM_CPAS, "Bus client[%d] index[%d]", bus_client->client_id,
- level);
+ CAM_DBG(CAM_CPAS, "Bus client=[%d][%s] index[%d]",
+ bus_client->client_id, bus_client->name, level);
msm_bus_scale_client_update_request(bus_client->client_id, level);
bus_client->curr_vote_level = level;
@@ -152,8 +147,8 @@
path->vectors[0].ab = ab;
path->vectors[0].ib = ib;
- CAM_DBG(CAM_CPAS, "Bus client[%d] :ab[%llu] ib[%llu], index[%d]",
- bus_client->client_id, ab, ib, idx);
+ CAM_DBG(CAM_CPAS, "Bus client=[%d][%s] :ab[%llu] ib[%llu], index[%d]",
+ bus_client->client_id, bus_client->name, ab, ib, idx);
msm_bus_scale_client_update_request(bus_client->client_id, idx);
return 0;
@@ -208,10 +203,12 @@
bus_client->num_paths = pdata->usecase[0].num_paths;
bus_client->curr_vote_level = 0;
bus_client->valid = true;
+ bus_client->name = pdata->name;
mutex_init(&bus_client->lock);
- CAM_DBG(CAM_CPAS, "Bus Client : src=%d, dst=%d, bus_client=%d",
- bus_client->src, bus_client->dst, bus_client->client_id);
+ CAM_DBG(CAM_CPAS, "Bus Client=[%d][%s] : src=%d, dst=%d",
+ bus_client->client_id, bus_client->name,
+ bus_client->src, bus_client->dst);
return 0;
fail_unregister_client:
@@ -463,6 +460,7 @@
{
struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+ struct cam_cpas_client *cpas_client = NULL;
int reg_base_index = cpas_core->regbase_index[reg_base];
uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
int rc = 0;
@@ -478,9 +476,12 @@
return -EINVAL;
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "client has not started%d", client_indx);
+ CAM_ERR(CAM_CPAS, "client=[%d][%s][%d] has not started",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto unlock_client;
}
@@ -503,6 +504,7 @@
{
struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+ struct cam_cpas_client *cpas_client = NULL;
int reg_base_index = cpas_core->regbase_index[reg_base];
uint32_t reg_value;
uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
@@ -522,9 +524,12 @@
return -EINVAL;
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "client has not started%d", client_indx);
+ CAM_ERR(CAM_CPAS, "client=[%d][%s][%d] has not started",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto unlock_client;
}
@@ -580,8 +585,9 @@
soc_private->camnoc_axi_clk_bw_margin) / 100;
if ((required_camnoc_bw > 0) &&
- (required_camnoc_bw < CAM_CPAS_AXI_MIN_CAMNOC_IB_BW))
- required_camnoc_bw = CAM_CPAS_AXI_MIN_CAMNOC_IB_BW;
+ (required_camnoc_bw <
+ soc_private->camnoc_axi_min_ib_bw))
+ required_camnoc_bw = soc_private->camnoc_axi_min_ib_bw;
clk_rate = required_camnoc_bw / soc_private->camnoc_bus_width;
@@ -695,6 +701,7 @@
{
struct cam_axi_vote axi_vote;
struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+ struct cam_cpas_client *cpas_client = NULL;
uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
int rc = 0;
@@ -719,16 +726,20 @@
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "client has not started %d", client_indx);
+ CAM_ERR(CAM_CPAS, "client=[%d][%s][%d] has not started",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto unlock_client;
}
CAM_DBG(CAM_CPAS,
- "Client[%d] Requested compressed[%llu], uncompressed[%llu]",
- client_indx, axi_vote.compressed_bw,
+ "Client=[%d][%s][%d] Requested compressed[%llu], uncompressed[%llu]",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, axi_vote.compressed_bw,
axi_vote.uncompressed_bw);
rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
@@ -809,7 +820,8 @@
mutex_lock(&ahb_bus_client->lock);
cpas_client->ahb_level = required_level;
- CAM_DBG(CAM_CPAS, "Clients required level[%d], curr_level[%d]",
+ CAM_DBG(CAM_CPAS, "Client=[%d][%s] required level[%d], curr_level[%d]",
+ ahb_bus_client->client_id, ahb_bus_client->name,
required_level, ahb_bus_client->curr_vote_level);
if (required_level == ahb_bus_client->curr_vote_level)
@@ -853,6 +865,7 @@
{
struct cam_ahb_vote ahb_vote;
struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+ struct cam_cpas_client *cpas_client = NULL;
uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
int rc = 0;
@@ -875,17 +888,21 @@
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "client has not started %d", client_indx);
+ CAM_ERR(CAM_CPAS, "client=[%d][%s][%d] has not started",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto unlock_client;
}
CAM_DBG(CAM_CPAS,
- "client[%d] : type[%d], level[%d], freq[%ld], applied[%d]",
- client_indx, ahb_vote.type, ahb_vote.vote.level,
- ahb_vote.vote.freq,
+ "client=[%d][%s][%d] : type[%d], level[%d], freq[%ld], applied[%d]",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, ahb_vote.type,
+ ahb_vote.vote.level, ahb_vote.vote.freq,
cpas_core->cpas_client[client_indx]->ahb_level);
rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw,
@@ -948,32 +965,37 @@
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
if (!CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "client is not registered %d", client_indx);
+ CAM_ERR(CAM_CPAS, "client=[%d] is not registered",
+ client_indx);
rc = -EPERM;
goto done;
}
if (CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "Client %d is in start state", client_indx);
+ CAM_ERR(CAM_CPAS, "client=[%d][%s][%d] is in start state",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto done;
}
- cpas_client = cpas_core->cpas_client[client_indx];
-
- CAM_DBG(CAM_CPAS, "AHB :client[%d] type[%d], level[%d], applied[%d]",
- client_indx, ahb_vote->type, ahb_vote->vote.level,
- cpas_client->ahb_level);
+ CAM_DBG(CAM_CPAS,
+ "AHB :client=[%d][%s][%d] type[%d], level[%d], applied[%d]",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index,
+ ahb_vote->type, ahb_vote->vote.level, cpas_client->ahb_level);
rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
ahb_vote, &applied_level);
if (rc)
goto done;
CAM_DBG(CAM_CPAS,
- "AXI client[%d] compressed_bw[%llu], uncompressed_bw[%llu]",
- client_indx, axi_vote->compressed_bw,
+ "AXI client=[%d][%s][%d] compressed_bw[%llu], uncompressed_bw[%llu]",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, axi_vote->compressed_bw,
axi_vote->uncompressed_bw);
rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
cpas_client, axi_vote);
@@ -1010,9 +1032,9 @@
cpas_client->started = true;
cpas_core->streamon_clients++;
- CAM_DBG(CAM_CPAS, "client=%s, streamon_clients=%d",
- soc_private->client_name[client_indx],
- cpas_core->streamon_clients);
+ CAM_DBG(CAM_CPAS, "client=[%d][%s][%d] streamon_clients=%d",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, cpas_core->streamon_clients);
done:
mutex_unlock(&cpas_core->client_mutex[client_indx]);
mutex_unlock(&cpas_hw->hw_mutex);
@@ -1062,18 +1084,20 @@
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
- CAM_DBG(CAM_CPAS, "client=%s, streamon_clients=%d",
- soc_private->client_name[client_indx],
- cpas_core->streamon_clients);
+ CAM_DBG(CAM_CPAS, "Client=[%d][%s][%d] streamon_clients=%d",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, cpas_core->streamon_clients);
if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "Client %d is not started", client_indx);
+ CAM_ERR(CAM_CPAS, "Client=[%d][%s][%d] is not started",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto done;
}
- cpas_client = cpas_core->cpas_client[client_indx];
cpas_client->started = false;
cpas_core->streamon_clients--;
@@ -1207,8 +1231,9 @@
cpas_client, client_indx);
if (rc) {
CAM_ERR(CAM_CPAS,
- "axi_port_insert failed client_indx=%d, rc=%d",
- client_indx, rc);
+ "axi_port_insert failed Client=[%d][%s][%d], rc=%d",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, rc);
kfree(cpas_client);
mutex_unlock(&cpas_hw->hw_mutex);
return -EINVAL;
@@ -1223,8 +1248,9 @@
mutex_unlock(&cpas_hw->hw_mutex);
- CAM_DBG(CAM_CPAS, "client_indx=%d, registered_clients=%d",
- client_indx, cpas_core->registered_clients);
+ CAM_DBG(CAM_CPAS, "client=[%d][%s][%d], registered_clients=%d",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, cpas_core->registered_clients);
return 0;
}
@@ -1233,6 +1259,7 @@
uint32_t client_handle)
{
struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+ struct cam_cpas_client *cpas_client = NULL;
uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
int rc = 0;
@@ -1241,15 +1268,20 @@
mutex_lock(&cpas_hw->hw_mutex);
mutex_lock(&cpas_core->client_mutex[client_indx]);
+ cpas_client = cpas_core->cpas_client[client_indx];
if (!CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "client not registered %d", client_indx);
+ CAM_ERR(CAM_CPAS, "Client=[%d][%s][%d] not registered",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto done;
}
if (CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
- CAM_ERR(CAM_CPAS, "Client %d is not stopped", client_indx);
+ CAM_ERR(CAM_CPAS, "Client=[%d][%s][%d] is not stopped",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index);
rc = -EPERM;
goto done;
}
@@ -1257,8 +1289,9 @@
cam_cpas_util_remove_client_from_axi_port(
cpas_core->cpas_client[client_indx]);
- CAM_DBG(CAM_CPAS, "client_indx=%d, registered_clients=%d",
- client_indx, cpas_core->registered_clients);
+ CAM_DBG(CAM_CPAS, "client=[%d][%s][%d], registered_clients=%d",
+ client_indx, cpas_client->data.identifier,
+ cpas_client->data.cell_index, cpas_core->registered_clients);
kfree(cpas_core->cpas_client[client_indx]);
cpas_core->cpas_client[client_indx] = NULL;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
index 2e660b1..d51b152 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
@@ -20,6 +20,11 @@
#define CAM_CPAS_MAX_CLIENTS 30
#define CAM_CPAS_INFLIGHT_WORKS 5
+#define CAM_CPAS_AXI_MIN_MNOC_AB_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_MNOC_IB_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_CAMNOC_AB_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_CAMNOC_IB_BW (3000000000L)
+
#define CAM_CPAS_GET_CLIENT_IDX(handle) (handle)
#define CAM_CPAS_GET_CLIENT_HANDLE(indx) (indx)
@@ -118,6 +123,7 @@
* @dyn_vote: Whether dynamic voting enabled
* @lock: Mutex lock used while voting on this client
* @valid: Whether bus client is valid
+ * @name: Name of the bus client
*
*/
struct cam_cpas_bus_client {
@@ -131,6 +137,7 @@
bool dyn_vote;
struct mutex lock;
bool valid;
+ const char *name;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
index 8f9ec14..b73b32a 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
@@ -56,6 +56,27 @@
CAM_DBG(CAM_CPAS, "CPAS HW VERSION %x", soc_private->hw_version);
+ soc_private->camnoc_axi_min_ib_bw = 0;
+ rc = of_property_read_u64(of_node,
+ "camnoc-axi-min-ib-bw",
+ &soc_private->camnoc_axi_min_ib_bw);
+ if (rc == -EOVERFLOW) {
+ soc_private->camnoc_axi_min_ib_bw = 0;
+ rc = of_property_read_u32(of_node,
+ "camnoc-axi-min-ib-bw",
+ (u32 *)&soc_private->camnoc_axi_min_ib_bw);
+ }
+
+ if (rc) {
+ CAM_DBG(CAM_CPAS,
+ "failed to read camnoc-axi-min-ib-bw rc:%d", rc);
+ soc_private->camnoc_axi_min_ib_bw =
+ CAM_CPAS_AXI_MIN_CAMNOC_IB_BW;
+ }
+
+ CAM_DBG(CAM_CPAS, "camnoc-axi-min-ib-bw = %llu",
+ soc_private->camnoc_axi_min_ib_bw);
+
soc_private->client_id_based = of_property_read_bool(of_node,
"client-id-based");
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index 91e8d0c0..f6ae8a8 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -48,6 +48,7 @@
* @camnoc_bus_width : CAMNOC Bus width
* @camnoc_axi_clk_bw_margin : BW Margin in percentage to add while calculating
* camnoc axi clock
+ * @camnoc_axi_min_ib_bw: Min camnoc BW which varies based on target
*
*/
struct cam_cpas_private_soc {
@@ -66,6 +67,7 @@
bool control_camnoc_axi_clk;
uint32_t camnoc_bus_width;
uint32_t camnoc_axi_clk_bw_margin;
+ uint64_t camnoc_axi_min_ib_bw;
};
int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
index f556780..2c364e01 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
@@ -121,6 +121,12 @@
int hfi_set_debug_level(u64 a5_dbg_type, uint32_t lvl);
/**
+ * hfi_set_fw_dump_level() - set firmware dump level
+ * @lvl: level of firmware dump level
+ */
+int hfi_set_fw_dump_level(uint32_t lvl);
+
+/**
* hfi_enable_ipe_bps_pc() - Enable interframe pc
* Host sends a command to firmware to enable interframe
* power collapse for IPE and BPS hardware.
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
index 91190b6..311886f 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
@@ -160,6 +160,7 @@
#define HFI_PROP_SYS_IMAGE_VER (HFI_PROPERTY_ICP_COMMON_START + 0x3)
#define HFI_PROP_SYS_SUPPORTED (HFI_PROPERTY_ICP_COMMON_START + 0x4)
#define HFI_PROP_SYS_IPEBPS_PC (HFI_PROPERTY_ICP_COMMON_START + 0x5)
+#define HFI_PROP_SYS_FW_DUMP_CFG (HFI_PROPERTY_ICP_COMMON_START + 0x8)
/* Capabilities reported at sys init */
#define HFI_CAPS_PLACEHOLDER_1 (HFI_COMMON_BASE + 0x1)
@@ -180,6 +181,18 @@
/* Disable ARM9 watchdog. */
#define HFI_DEBUG_CFG_ARM9WD 0x10000000
+
+/*
+ * HFI_FW_DUMP levels
+ * HFI_FW_DUMP_xx
+ */
+#define HFI_FW_DUMP_DISABLED 0x00000000
+#define HFI_FW_DUMP_ON_FAILURE 0x00000001
+#define HFI_FW_DUMP_ALWAYS 0x00000002
+
+/* Number of available dump levels. */
+#define NUM_HFI_DUMP_LVL 0x00000003
+
/* Debug Msg Communication types:
* Section describes different modes (HFI_DEBUG_MODE_X)
* available to communicate the debug messages
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index b75719b..a0752f5 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -324,6 +324,42 @@
return 0;
}
+int hfi_set_fw_dump_level(uint32_t lvl)
+{
+ uint8_t *prop = NULL;
+ struct hfi_cmd_prop *fw_dump_level_switch_prop = NULL;
+ uint32_t size = 0;
+
+ CAM_DBG(CAM_HFI, "fw dump ENTER");
+
+ size = sizeof(struct hfi_cmd_prop) + sizeof(lvl);
+ prop = kzalloc(size, GFP_KERNEL);
+ if (!prop)
+ return -ENOMEM;
+
+ fw_dump_level_switch_prop = (struct hfi_cmd_prop *)prop;
+ fw_dump_level_switch_prop->size = size;
+ fw_dump_level_switch_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ fw_dump_level_switch_prop->num_prop = 1;
+ fw_dump_level_switch_prop->prop_data[0] = HFI_PROP_SYS_FW_DUMP_CFG;
+ fw_dump_level_switch_prop->prop_data[1] = lvl;
+
+ CAM_DBG(CAM_HFI, "prop->size = %d\n"
+ "prop->pkt_type = %d\n"
+ "prop->num_prop = %d\n"
+ "prop->prop_data[0] = %d\n"
+ "prop->prop_data[1] = %d\n",
+ fw_dump_level_switch_prop->size,
+ fw_dump_level_switch_prop->pkt_type,
+ fw_dump_level_switch_prop->num_prop,
+ fw_dump_level_switch_prop->prop_data[0],
+ fw_dump_level_switch_prop->prop_data[1]);
+
+ hfi_write_cmd(prop);
+ kfree(prop);
+ return 0;
+}
+
void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size)
{
switch (type) {
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index ba4385f5..16e97ea 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -1285,6 +1285,22 @@
DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_type_fs, cam_icp_get_a5_dbg_type,
cam_icp_set_a5_dbg_type, "%08llu");
+static int cam_icp_set_a5_fw_dump_lvl(void *data, u64 val)
+{
+ if (val < NUM_HFI_DUMP_LVL)
+ icp_hw_mgr.a5_fw_dump_lvl = val;
+ return 0;
+}
+
+static int cam_icp_get_a5_fw_dump_lvl(void *data, u64 *val)
+{
+ *val = icp_hw_mgr.a5_fw_dump_lvl;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_fw_dump, cam_icp_get_a5_fw_dump_lvl,
+ cam_icp_set_a5_fw_dump_lvl, "%08llu");
+
static int cam_icp_hw_mgr_create_debugfs_entry(void)
{
int rc = 0;
@@ -1335,7 +1351,7 @@
0644,
icp_hw_mgr.dentry,
NULL, &cam_icp_debug_type_fs)) {
- CAM_ERR(CAM_ICP, "failed to create a5_debug_type\n");
+ CAM_ERR(CAM_ICP, "failed to create a5_debug_type");
rc = -ENOMEM;
goto err;
}
@@ -1344,7 +1360,16 @@
0644,
icp_hw_mgr.dentry,
NULL, &cam_icp_debug_fs)) {
- CAM_ERR(CAM_ICP, "failed to create a5_dbg_lvl\n");
+ CAM_ERR(CAM_ICP, "failed to create a5_dbg_lvl");
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ if (!debugfs_create_file("a5_fw_dump_lvl",
+ 0644,
+ icp_hw_mgr.dentry,
+ NULL, &cam_icp_debug_fw_dump)) {
+ CAM_ERR(CAM_ICP, "failed to create a5_fw_dump_lvl");
rc = -ENOMEM;
goto err;
}
@@ -2260,13 +2285,7 @@
unsigned long rem_jiffies;
size_t packet_size;
int timeout = 100;
- struct hfi_cmd_work_data *task_data;
struct hfi_cmd_ipebps_async *abort_cmd;
- struct crm_workq_task *task;
-
- task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
- if (!task)
- return -ENOMEM;
packet_size =
sizeof(struct hfi_cmd_ipebps_async) +
@@ -2292,13 +2311,7 @@
abort_cmd->user_data1 = (uint64_t)ctx_data;
abort_cmd->user_data2 = (uint64_t)0x0;
- task_data = (struct hfi_cmd_work_data *)task->payload;
- task_data->data = (void *)abort_cmd;
- task_data->request_id = 0;
- task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
- rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
- CRM_TASK_PRIORITY_0);
+ rc = hfi_write_cmd(abort_cmd);
if (rc) {
kfree(abort_cmd);
return rc;
@@ -2312,6 +2325,7 @@
CAM_ERR(CAM_ICP, "FW timeout/err in abort handle command");
}
+ kfree(abort_cmd);
return rc;
}
@@ -2322,13 +2336,7 @@
int timeout = 100;
unsigned long rem_jiffies;
size_t packet_size;
- struct hfi_cmd_work_data *task_data;
struct hfi_cmd_ipebps_async *destroy_cmd;
- struct crm_workq_task *task;
-
- task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
- if (!task)
- return -ENOMEM;
packet_size =
sizeof(struct hfi_cmd_ipebps_async) +
@@ -2355,13 +2363,7 @@
memcpy(destroy_cmd->payload.direct, &ctx_data->temp_payload,
sizeof(uint64_t));
- task_data = (struct hfi_cmd_work_data *)task->payload;
- task_data->data = (void *)destroy_cmd;
- task_data->request_id = 0;
- task_data->type = ICP_WORKQ_TASK_CMD_TYPE;
- task->process_cb = cam_icp_mgr_process_cmd;
- rc = cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
- CRM_TASK_PRIORITY_0);
+ rc = hfi_write_cmd(destroy_cmd);
if (rc) {
kfree(destroy_cmd);
return rc;
@@ -2378,6 +2380,7 @@
HFI_DEBUG_MODE_QUEUE)
cam_icp_mgr_process_dbg_buf();
}
+ kfree(destroy_cmd);
return rc;
}
@@ -3866,6 +3869,8 @@
hfi_set_debug_level(icp_hw_mgr.a5_debug_type,
icp_hw_mgr.a5_dbg_lvl);
+ hfi_set_fw_dump_level(icp_hw_mgr.a5_fw_dump_lvl);
+
rc = cam_icp_send_ubwc_cfg(hw_mgr);
if (rc)
goto ubwc_cfg_failed;
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
index c94550d..8746ee2 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -279,6 +279,7 @@
* @a5_jtag_debug: entry to enable A5 JTAG debugging
* @a5_debug_type : entry to enable FW debug message/qdss
* @a5_dbg_lvl : debug level set to FW.
+ * @a5_fw_dump_lvl : level set for dumping the FW data
* @ipe0_enable: Flag for IPE0
* @ipe1_enable: Flag for IPE1
* @bps_enable: Flag for BPS
@@ -325,6 +326,7 @@
bool a5_jtag_debug;
u64 a5_debug_type;
u64 a5_dbg_lvl;
+ u64 a5_fw_dump_lvl;
bool ipe0_enable;
bool ipe1_enable;
bool bps_enable;
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index 97e977d..7b02aac 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -504,6 +504,18 @@
}
+static int __cam_isp_ctx_reg_upd_in_epoch_state(
+ struct cam_isp_context *ctx_isp, void *evt_data)
+{
+ if (ctx_isp->frame_id == 1)
+ CAM_DBG(CAM_ISP, "Reg update for early PCR");
+ else
+ CAM_WARN(CAM_ISP,
+ "Unexpected reg update in activated substate:%d for frame_id:%lld",
+ ctx_isp->substate_activated, ctx_isp->frame_id);
+ return 0;
+}
+
static int __cam_isp_ctx_reg_upd_in_activated_state(
struct cam_isp_context *ctx_isp, void *evt_data)
{
@@ -1119,7 +1131,7 @@
.irq_ops = {
__cam_isp_ctx_handle_error,
__cam_isp_ctx_sof_in_epoch,
- NULL,
+ __cam_isp_ctx_reg_upd_in_epoch_state,
__cam_isp_ctx_notify_sof_in_actived_state,
__cam_isp_ctx_notify_eof_in_actived_state,
__cam_isp_ctx_buf_done_in_epoch,
@@ -1244,6 +1256,7 @@
cfg.hw_update_entries = req_isp->cfg;
cfg.num_hw_update_entries = req_isp->num_cfg;
cfg.priv = &req_isp->hw_update_data;
+ cfg.init_packet = 0;
rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
if (rc) {
@@ -1324,9 +1337,7 @@
struct list_head flush_list;
INIT_LIST_HEAD(&flush_list);
- spin_lock_bh(&ctx->lock);
if (list_empty(req_list)) {
- spin_unlock_bh(&ctx->lock);
CAM_DBG(CAM_ISP, "request list is empty");
return 0;
}
@@ -1345,7 +1356,6 @@
list_del_init(&req->list);
list_add_tail(&req->list, &flush_list);
}
- spin_unlock_bh(&ctx->lock);
list_for_each_entry_safe(req, req_temp, &flush_list, list) {
req_isp = (struct cam_isp_ctx_req *) req->req_priv;
@@ -1363,16 +1373,16 @@
req_isp->fence_map_out[i].sync_id = -1;
}
}
- spin_lock_bh(&ctx->lock);
list_add_tail(&req->list, &ctx->free_req_list);
- spin_unlock_bh(&ctx->lock);
}
if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ &&
- !cancel_req_id_found)
- CAM_DBG(CAM_ISP,
+ !cancel_req_id_found) {
+ CAM_INFO(CAM_ISP,
"Flush request id:%lld is not found in the list",
flush_req->req_id);
+ return -EINVAL;
+ }
return 0;
}
@@ -1384,7 +1394,9 @@
int rc = 0;
CAM_DBG(CAM_ISP, "try to flush pending list");
+ spin_lock_bh(&ctx->lock);
rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req);
+ spin_unlock_bh(&ctx->lock);
CAM_DBG(CAM_ISP, "Flush request in top state %d",
ctx->state);
return rc;
@@ -1398,13 +1410,17 @@
struct cam_isp_context *ctx_isp;
ctx_isp = (struct cam_isp_context *) ctx->ctx_priv;
- spin_lock_bh(&ctx->lock);
- ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_FLUSH;
- ctx_isp->frame_skip_count = 2;
- spin_unlock_bh(&ctx->lock);
CAM_DBG(CAM_ISP, "Flush request in state %d", ctx->state);
rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req);
+
+ /* only if request is found in pending queue, move to flush state*/
+ if (!rc) {
+ spin_lock_bh(&ctx->lock);
+ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_FLUSH;
+ ctx_isp->frame_skip_count = 2;
+ spin_unlock_bh(&ctx->lock);
+ }
return rc;
}
@@ -1415,10 +1431,10 @@
int rc = 0;
CAM_DBG(CAM_ISP, "try to flush pending list");
+ spin_lock_bh(&ctx->lock);
rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req);
/* if nothing is in pending req list, change state to acquire*/
- spin_lock_bh(&ctx->lock);
if (list_empty(&ctx->pending_req_list))
ctx->state = CAM_CTX_ACQUIRED;
spin_unlock_bh(&ctx->lock);
@@ -1994,8 +2010,9 @@
flush_req.dev_hdl = ctx->dev_hdl;
CAM_DBG(CAM_ISP, "try to flush pending list");
+ spin_lock_bh(&ctx->lock);
rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, &flush_req);
-
+ spin_unlock_bh(&ctx->lock);
ctx->state = CAM_CTX_AVAILABLE;
trace_cam_context_state("ISP", ctx);
@@ -2374,12 +2391,15 @@
arg.hw_update_entries = req_isp->cfg;
arg.num_hw_update_entries = req_isp->num_cfg;
arg.priv = &req_isp->hw_update_data;
+ arg.init_packet = 1;
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
ctx_isp->reported_req_id = 0;
ctx_isp->substate_activated = ctx_isp->rdi_only_context ?
- CAM_ISP_CTX_ACTIVATED_APPLIED : CAM_ISP_CTX_ACTIVATED_SOF;
+ CAM_ISP_CTX_ACTIVATED_APPLIED :
+ (req_isp->num_fence_map_out) ? CAM_ISP_CTX_ACTIVATED_EPOCH :
+ CAM_ISP_CTX_ACTIVATED_SOF;
/*
* Only place to change state before calling the hw due to
@@ -2397,6 +2417,11 @@
goto end;
}
CAM_DBG(CAM_ISP, "start device success");
+
+ if (req_isp->num_fence_map_out) {
+ list_del_init(&req->list);
+ list_add_tail(&req->list, &ctx->active_req_list);
+ }
end:
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 0a127ef..c1aa501 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -210,7 +210,8 @@
int rc = -1;
struct cam_hw_intf *hw_intf;
- for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ /* Start slave (which is right split) first */
+ for (i = CAM_ISP_HW_SPLIT_MAX - 1; i >= 0; i--) {
if (!isp_hw_res->hw_res[i])
continue;
hw_intf = isp_hw_res->hw_res[i]->hw_intf;
@@ -959,6 +960,7 @@
{
int rc = -1;
int i;
+ int master_idx = -1;
struct cam_ife_hw_mgr *ife_hw_mgr;
struct cam_ife_hw_mgr_res *csid_res;
@@ -1010,18 +1012,27 @@
if (!cid_res->hw_res[i])
continue;
- csid_acquire.node_res = NULL;
- if (csid_res->is_dual_vfe) {
- if (i == CAM_ISP_HW_SPLIT_LEFT)
- csid_acquire.sync_mode =
- CAM_ISP_HW_SYNC_MASTER;
- else
- csid_acquire.sync_mode =
- CAM_ISP_HW_SYNC_SLAVE;
- }
-
hw_intf = ife_hw_mgr->csid_devices[
cid_res->hw_res[i]->hw_intf->hw_idx];
+
+ csid_acquire.node_res = NULL;
+ if (csid_res->is_dual_vfe) {
+ if (i == CAM_ISP_HW_SPLIT_LEFT) {
+ master_idx = hw_intf->hw_idx;
+ csid_acquire.sync_mode =
+ CAM_ISP_HW_SYNC_MASTER;
+ } else {
+ if (master_idx == -1) {
+ CAM_ERR(CAM_ISP,
+ "No Master found");
+ goto err;
+ }
+ csid_acquire.sync_mode =
+ CAM_ISP_HW_SYNC_SLAVE;
+ csid_acquire.master_idx = master_idx;
+ }
+ }
+
rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
&csid_acquire, sizeof(csid_acquire));
if (rc) {
@@ -1356,6 +1367,7 @@
uint32_t num_rdi_port_per_in = 0;
uint32_t total_pix_port = 0;
uint32_t total_rdi_port = 0;
+ uint32_t in_port_length = 0;
CAM_DBG(CAM_ISP, "Enter...");
@@ -1416,9 +1428,27 @@
isp_resource[i].res_hdl,
isp_resource[i].length);
+ in_port_length = sizeof(struct cam_isp_in_port_info);
+
+ if (in_port_length > isp_resource[i].length) {
+ CAM_ERR(CAM_ISP, "buffer size is not enough");
+ rc = -EINVAL;
+ goto free_res;
+ }
+
in_port = memdup_user((void __user *)isp_resource[i].res_hdl,
isp_resource[i].length);
if (!IS_ERR(in_port)) {
+ in_port_length = sizeof(struct cam_isp_in_port_info) +
+ (in_port->num_out_res - 1) *
+ sizeof(struct cam_isp_out_port_info);
+ if (in_port_length > isp_resource[i].length) {
+ CAM_ERR(CAM_ISP, "buffer size is not enough");
+ rc = -EINVAL;
+ kfree(in_port);
+ goto free_res;
+ }
+
rc = cam_ife_mgr_acquire_hw_for_ctx(ife_ctx, in_port,
&num_pix_port_per_in, &num_rdi_port_per_in);
total_pix_port += num_pix_port_per_in;
@@ -1611,7 +1641,7 @@
cdm_cmd->cmd[i].len = cmd->len;
}
- if (cfg->request_id == 1)
+ if (cfg->init_packet)
init_completion(&ctx->config_done_complete);
CAM_DBG(CAM_ISP, "Submit to CDM");
@@ -1621,7 +1651,7 @@
return rc;
}
- if (cfg->request_id == 1) {
+ if (cfg->init_packet) {
rc = wait_for_completion_timeout(
&ctx->config_done_complete,
msecs_to_jiffies(30));
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
index 1d38f3b..e869e2b 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
@@ -579,8 +579,15 @@
io_addr[plane_id] +=
io_cfg[i].offsets[plane_id];
CAM_DBG(CAM_ISP,
- "get io_addr for plane %d: 0x%llx",
- plane_id, io_addr[plane_id]);
+ "get io_addr for plane %d: 0x%llx, mem_hdl=0x%x",
+ plane_id, io_addr[plane_id],
+ io_cfg[i].mem_handle[plane_id]);
+
+ CAM_DBG(CAM_ISP,
+ "mmu_hdl=0x%x, size=%d, end=0x%x",
+ mmu_hdl, (int)size,
+ io_addr[plane_id]+size);
+
}
if (!plane_id) {
CAM_ERR(CAM_ISP, "No valid planes for res%d",
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
index 8863275..3a89732 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -273,8 +273,7 @@
int cam_tasklet_start(void *tasklet_info)
{
struct cam_tasklet_info *tasklet = tasklet_info;
- struct cam_tasklet_queue_cmd *tasklet_cmd;
- struct cam_tasklet_queue_cmd *tasklet_cmd_temp;
+ int i = 0;
if (atomic_read(&tasklet->tasklet_active)) {
CAM_ERR(CAM_ISP, "Tasklet already active. idx = %d",
@@ -283,11 +282,11 @@
}
atomic_set(&tasklet->tasklet_active, 1);
- /* flush the command queue first */
- list_for_each_entry_safe(tasklet_cmd, tasklet_cmd_temp,
- &tasklet->used_cmd_list, list) {
- list_del_init(&tasklet_cmd->list);
- list_add_tail(&tasklet_cmd->list, &tasklet->free_cmd_list);
+ /* clean up the command queue first */
+ for (i = 0; i < CAM_TASKLETQ_SIZE; i++) {
+ list_del_init(&tasklet->cmd_queue[i].list);
+ list_add_tail(&tasklet->cmd_queue[i].list,
+ &tasklet->free_cmd_list);
}
tasklet_enable(&tasklet->tasklet);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index 053eb00..3edae4a 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -851,7 +851,6 @@
path_data->cid = reserve->cid;
path_data->in_format = reserve->in_port->format;
path_data->out_format = reserve->out_port->format;
- path_data->master_idx = reserve->master_idx;
path_data->sync_mode = reserve->sync_mode;
path_data->height = reserve->in_port->height;
path_data->start_line = reserve->in_port->line_start;
@@ -877,9 +876,11 @@
goto end;
}
- CAM_DBG(CAM_ISP, "Res id: %d height:%d line_start %d line_stop %d",
+ CAM_DBG(CAM_ISP,
+ "Res id: %d height:%d line_start %d line_stop %d crop_en %d",
reserve->res_id, reserve->in_port->height,
- reserve->in_port->line_start, reserve->in_port->line_stop);
+ reserve->in_port->line_start, reserve->in_port->line_stop,
+ path_data->crop_enable);
if (reserve->in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) {
path_data->dt = CAM_IFE_CSID_TPG_DT_VAL;
@@ -893,20 +894,23 @@
path_data->start_pixel = reserve->in_port->left_start;
path_data->end_pixel = reserve->in_port->left_stop;
path_data->width = reserve->in_port->left_width;
- CAM_DBG(CAM_ISP, "CSID:%dmaster:startpixel 0x%x endpixel:0x%x",
+ CAM_DBG(CAM_ISP, "CSID:%d master:startpixel 0x%x endpixel:0x%x",
csid_hw->hw_intf->hw_idx, path_data->start_pixel,
path_data->end_pixel);
- CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x",
+ CAM_DBG(CAM_ISP, "CSID:%d master:line start:0x%x line end:0x%x",
csid_hw->hw_intf->hw_idx, path_data->start_line,
path_data->end_line);
} else if (reserve->sync_mode == CAM_ISP_HW_SYNC_SLAVE) {
+ path_data->master_idx = reserve->master_idx;
+ CAM_DBG(CAM_ISP, "CSID:%d master_idx=%d",
+ csid_hw->hw_intf->hw_idx, path_data->master_idx);
path_data->start_pixel = reserve->in_port->right_start;
path_data->end_pixel = reserve->in_port->right_stop;
path_data->width = reserve->in_port->right_width;
CAM_DBG(CAM_ISP, "CSID:%d slave:start:0x%x end:0x%x width 0x%x",
csid_hw->hw_intf->hw_idx, path_data->start_pixel,
path_data->end_pixel, path_data->width);
- CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x",
+ CAM_DBG(CAM_ISP, "CSID:%d slave:line start:0x%x line end:0x%x",
csid_hw->hw_intf->hw_idx, path_data->start_line,
path_data->end_line);
} else {
@@ -989,10 +993,6 @@
cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
csid_reg->cmn_reg->csid_irq_cmd_addr);
- /* Enable the top IRQ interrupt */
- cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
- csid_reg->cmn_reg->csid_top_irq_mask_addr);
-
val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
csid_reg->cmn_reg->csid_hw_version_addr);
CAM_DBG(CAM_ISP, "CSID:%d CSID HW version: 0x%x",
@@ -1435,19 +1435,6 @@
cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
csid_reg->ipp_reg->csid_ipp_line_drop_period_addr);
- /*Set master or slave IPP */
- if (path_data->sync_mode == CAM_ISP_HW_SYNC_MASTER)
- /*Set halt mode as master */
- val = CSID_HALT_MODE_MASTER << 2;
- else if (path_data->sync_mode == CAM_ISP_HW_SYNC_SLAVE)
- /*Set halt mode as slave and set master idx */
- val = path_data->master_idx << 4 | CSID_HALT_MODE_SLAVE << 2;
- else
- /* Default is internal halt mode */
- val = 0;
-
- cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
- csid_reg->ipp_reg->csid_ipp_ctrl_addr);
/* Enable the IPP path */
val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
@@ -1549,19 +1536,31 @@
CAM_DBG(CAM_ISP, "Enable IPP path");
- /* Resume at frame boundary */
- if (path_data->sync_mode == CAM_ISP_HW_SYNC_MASTER) {
- val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
- csid_reg->ipp_reg->csid_ipp_ctrl_addr);
+
+ /* Set master or slave IPP */
+ if (path_data->sync_mode == CAM_ISP_HW_SYNC_MASTER)
+ /*Set halt mode as master */
+ val = CSID_HALT_MODE_MASTER << 2;
+ else if (path_data->sync_mode == CAM_ISP_HW_SYNC_SLAVE)
+ /*Set halt mode as slave and set master idx */
+ val = path_data->master_idx << 4 | CSID_HALT_MODE_SLAVE << 2;
+ else
+ /* Default is internal halt mode */
+ val = 0;
+
+ /*
+ * Resume at frame boundary if Master or No Sync.
+ * Slave will get resume command from Master.
+ */
+ if (path_data->sync_mode == CAM_ISP_HW_SYNC_MASTER ||
+ path_data->sync_mode == CAM_ISP_HW_SYNC_NONE)
val |= CAM_CSID_RESUME_AT_FRAME_BOUNDARY;
- cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
- csid_reg->ipp_reg->csid_ipp_ctrl_addr);
- } else if (path_data->sync_mode == CAM_ISP_HW_SYNC_NONE) {
- cam_io_w_mb(CAM_CSID_RESUME_AT_FRAME_BOUNDARY,
- soc_info->reg_map[0].mem_base +
- csid_reg->ipp_reg->csid_ipp_ctrl_addr);
- }
- /* for slave mode, not need to resume for slave device */
+
+ cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
+ csid_reg->ipp_reg->csid_ipp_ctrl_addr);
+
+ CAM_DBG(CAM_ISP, "CSID:%d IPP Ctrl val: 0x%x",
+ csid_hw->hw_intf->hw_idx, val);
/* Enable the required ipp interrupts */
val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW;
@@ -1573,6 +1572,7 @@
cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
csid_reg->ipp_reg->csid_ipp_irq_mask_addr);
+ CAM_DBG(CAM_ISP, "Enable IPP IRQ mask 0x%x", val);
res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
@@ -2171,25 +2171,38 @@
struct cam_ife_csid_hw *csid_hw)
{
int rc = 0;
+ uint32_t status;
struct cam_ife_csid_reg_offset *csid_reg =
csid_hw->csid_info->csid_reg;
+ struct cam_hw_soc_info *soc_info;
- init_completion(&csid_hw->csid_top_complete);
+ soc_info = &csid_hw->hw_info->soc_info;
+ /* clear the top interrupt first */
+ cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
+ csid_reg->cmn_reg->csid_top_irq_clear_addr);
+ cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
+ csid_reg->cmn_reg->csid_irq_cmd_addr);
+
cam_io_w_mb(csid_reg->cmn_reg->csid_rst_stb,
- csid_hw->hw_info->soc_info.reg_map[0].mem_base +
+ soc_info->reg_map[0].mem_base +
csid_reg->cmn_reg->csid_rst_strobes_addr);
-
- CAM_DBG(CAM_ISP, " Waiting for SW reset complete from irq handler");
- rc = wait_for_completion_timeout(&csid_hw->csid_top_complete,
- msecs_to_jiffies(IFE_CSID_TIMEOUT));
- if (rc <= 0) {
- CAM_ERR(CAM_ISP, "CSID:%d reset completion in fail rc = %d",
- csid_hw->hw_intf->hw_idx, rc);
- if (rc == 0)
- rc = -ETIMEDOUT;
+ rc = readl_poll_timeout(soc_info->reg_map[0].mem_base +
+ csid_reg->cmn_reg->csid_top_irq_status_addr,
+ status, (status & 0x1) == 0x1,
+ CAM_IFE_CSID_TIMEOUT_SLEEP_US, CAM_IFE_CSID_TIMEOUT_ALL_US);
+ if (rc < 0) {
+ CAM_ERR(CAM_ISP, "CSID:%d csid_reset fail rc = %d",
+ csid_hw->hw_intf->hw_idx, rc);
+ rc = -ETIMEDOUT;
} else {
+ CAM_DBG(CAM_ISP, "CSID:%d hw reset completed %d",
+ csid_hw->hw_intf->hw_idx, rc);
rc = 0;
}
+ cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
+ csid_reg->cmn_reg->csid_top_irq_clear_addr);
+ cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
+ csid_reg->cmn_reg->csid_irq_cmd_addr);
return rc;
}
@@ -2529,8 +2542,6 @@
csid_reg->rdi_reg[i]->csid_rdi_irq_status_addr);
/* clear */
- cam_io_w_mb(irq_status_top, soc_info->reg_map[0].mem_base +
- csid_reg->cmn_reg->csid_top_irq_clear_addr);
cam_io_w_mb(irq_status_rx, soc_info->reg_map[0].mem_base +
csid_reg->csi2_reg->csid_csi2_rx_irq_clear_addr);
if (csid_reg->cmn_reg->no_pix)
@@ -2551,13 +2562,6 @@
CAM_DBG(CAM_ISP, "irq_status_rdi1= 0x%x", irq_status_rdi[1]);
CAM_DBG(CAM_ISP, "irq_status_rdi2= 0x%x", irq_status_rdi[2]);
- if (irq_status_top) {
- CAM_DBG(CAM_ISP, "CSID global reset complete......Exit");
- complete(&csid_hw->csid_top_complete);
- return IRQ_HANDLED;
- }
-
-
if (irq_status_rx & BIT(csid_reg->csi2_reg->csi2_rst_done_shift_val)) {
CAM_DBG(CAM_ISP, "csi rx reset complete");
complete(&csid_hw->csid_csi2_complete);
@@ -2618,38 +2622,46 @@
if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ) {
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_EOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL0_EOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_EOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL1_EOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_EOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL2_EOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_EOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL3_EOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
}
if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ) {
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_SOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL0_SOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_SOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL1_SOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_SOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL2_SOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED) {
- CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_SOT_CAPTURED",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d PHY_DL3_SOT_CAPTURED",
csid_hw->hw_intf->hw_idx);
}
}
@@ -2709,16 +2721,17 @@
if ((irq_status_ipp & CSID_PATH_INFO_INPUT_SOF) &&
(csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ))
- CAM_ERR(CAM_ISP, "CSID:%d IPP SOF received",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d IPP SOF received",
csid_hw->hw_intf->hw_idx);
if ((irq_status_ipp & CSID_PATH_INFO_INPUT_EOF) &&
(csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ))
- CAM_ERR(CAM_ISP, "CSID:%d IPP EOF received",
+ CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d IPP EOF received",
csid_hw->hw_intf->hw_idx);
if (irq_status_ipp & CSID_PATH_ERROR_FIFO_OVERFLOW) {
- CAM_ERR(CAM_ISP, "CSID:%d IPP fifo over flow",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d IPP fifo over flow",
csid_hw->hw_intf->hw_idx);
/*Stop IPP path immediately */
cam_io_w_mb(CAM_CSID_HALT_IMMEDIATELY,
@@ -2736,14 +2749,17 @@
if ((irq_status_rdi[i] & CSID_PATH_INFO_INPUT_SOF) &&
(csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ))
- CAM_ERR(CAM_ISP, "CSID RDI:%d SOF received", i);
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID RDI:%d SOF received", i);
if ((irq_status_rdi[i] & CSID_PATH_INFO_INPUT_EOF) &&
(csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ))
- CAM_ERR(CAM_ISP, "CSID RDI:%d EOF received", i);
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID RDI:%d EOF received", i);
if (irq_status_rdi[i] & CSID_PATH_ERROR_FIFO_OVERFLOW) {
- CAM_ERR(CAM_ISP, "CSID:%d RDI fifo over flow",
+ CAM_ERR_RATE_LIMIT(CAM_ISP,
+ "CSID:%d RDI fifo over flow",
csid_hw->hw_intf->hw_idx);
/*Stop RDI path immediately */
cam_io_w_mb(CAM_CSID_HALT_IMMEDIATELY,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
index a4ba2e1..984adf7 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index be4db8a..3c37b83 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -106,6 +106,7 @@
struct cam_vfe_bus_irq_evt_payload evt_payload[
CAM_VFE_BUS_VER2_PAYLOAD_MAX];
struct list_head free_payload_list;
+ spinlock_t spin_lock;
struct mutex bus_mutex;
uint32_t secure_mode;
uint32_t num_sec_out;
@@ -214,16 +215,23 @@
struct cam_vfe_bus_ver2_common_data *common_data,
struct cam_vfe_bus_irq_evt_payload **evt_payload)
{
+ int rc;
+
+ spin_lock(&common_data->spin_lock);
if (list_empty(&common_data->free_payload_list)) {
*evt_payload = NULL;
CAM_ERR_RATE_LIMIT(CAM_ISP, "No free payload");
- return -ENODEV;
+ rc = -ENODEV;
+ goto done;
}
*evt_payload = list_first_entry(&common_data->free_payload_list,
struct cam_vfe_bus_irq_evt_payload, list);
list_del_init(&(*evt_payload)->list);
- return 0;
+ rc = 0;
+done:
+ spin_unlock(&common_data->spin_lock);
+ return rc;
}
static enum cam_vfe_bus_comp_grp_id
@@ -254,6 +262,7 @@
struct cam_vfe_bus_ver2_common_data *common_data = NULL;
uint32_t *ife_irq_regs = NULL;
uint32_t status_reg0, status_reg1, status_reg2;
+ unsigned long flags;
if (!core_info) {
CAM_ERR(CAM_ISP, "Invalid param core_info NULL");
@@ -276,8 +285,12 @@
}
common_data = core_info;
+
+ spin_lock_irqsave(&common_data->spin_lock, flags);
list_add_tail(&(*evt_payload)->list,
&common_data->free_payload_list);
+ spin_unlock_irqrestore(&common_data->spin_lock, flags);
+
*evt_payload = NULL;
CAM_DBG(CAM_ISP, "Done");
@@ -2943,6 +2956,7 @@
}
}
+ spin_lock_init(&bus_priv->common_data.spin_lock);
INIT_LIST_HEAD(&bus_priv->common_data.free_payload_list);
for (i = 0; i < CAM_VFE_BUS_VER2_PAYLOAD_MAX; i++) {
INIT_LIST_HEAD(&bus_priv->common_data.evt_payload[i].list);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
index 734cbdb..8bc9bd2 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -138,6 +138,9 @@
camif_data->first_line = acquire_data->vfe_in.in_port->line_start;
camif_data->last_line = acquire_data->vfe_in.in_port->line_stop;
+ CAM_DBG(CAM_ISP, "hw id:%d pix_pattern:%d dsp_mode=%d",
+ camif_res->hw_intf->hw_idx,
+ camif_data->pix_pattern, camif_data->dsp_mode);
return rc;
}
@@ -201,6 +204,9 @@
{
struct cam_vfe_mux_camif_data *rsrc_data;
uint32_t val = 0;
+ uint32_t epoch0_irq_mask;
+ uint32_t epoch1_irq_mask;
+ uint32_t computed_epoch_line_cfg;
if (!camif_res) {
CAM_ERR(CAM_ISP, "Error! Invalid input arguments");
@@ -240,15 +246,24 @@
rsrc_data->common_reg->module_ctrl[
CAM_VFE_TOP_VER2_MODULE_STATS]->cgc_ovd);
- /* epoch config with 20 line */
- cam_io_w_mb(rsrc_data->reg_data->epoch_line_cfg,
+ /* epoch config */
+ epoch0_irq_mask = ((rsrc_data->last_line - rsrc_data->first_line) / 2) +
+ rsrc_data->first_line;
+ epoch1_irq_mask = rsrc_data->reg_data->epoch_line_cfg & 0xFFFF;
+ computed_epoch_line_cfg = (epoch0_irq_mask << 16) | epoch1_irq_mask;
+ cam_io_w_mb(computed_epoch_line_cfg,
rsrc_data->mem_base + rsrc_data->camif_reg->epoch_irq);
+ CAM_DBG(CAM_ISP, "first_line:%u last_line:%u epoch_line_cfg: 0x%x",
+ rsrc_data->first_line, rsrc_data->last_line,
+ computed_epoch_line_cfg);
camif_res->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
/* Reg Update */
cam_io_w_mb(rsrc_data->reg_data->reg_update_cmd_data,
rsrc_data->mem_base + rsrc_data->camif_reg->reg_update_cmd);
+ CAM_DBG(CAM_ISP, "hw id:%d RUP val:%d", camif_res->hw_intf->hw_idx,
+ rsrc_data->reg_data->reg_update_cmd_data);
CAM_DBG(CAM_ISP, "Start Camif IFE %d Done", camif_res->hw_intf->hw_idx);
return 0;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
index 5d6045b..be0ca18 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
@@ -34,11 +34,12 @@
struct cam_vfe_top_ver2_common_data common_data;
struct cam_isp_resource_node mux_rsrc[CAM_VFE_TOP_VER2_MUX_MAX];
unsigned long hw_clk_rate;
- struct cam_axi_vote to_be_applied_axi_vote;
struct cam_axi_vote applied_axi_vote;
- uint32_t counter_to_update_axi_vote;
struct cam_axi_vote req_axi_vote[CAM_VFE_TOP_VER2_MUX_MAX];
unsigned long req_clk_rate[CAM_VFE_TOP_VER2_MUX_MAX];
+ struct cam_axi_vote last_vote[CAM_VFE_TOP_VER2_MUX_MAX *
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES];
+ uint32_t last_counter;
enum cam_vfe_bw_control_action
axi_vote_control[CAM_VFE_TOP_VER2_MUX_MAX];
};
@@ -128,6 +129,7 @@
bool start_stop)
{
struct cam_axi_vote sum = {0, 0};
+ struct cam_axi_vote to_be_applied_axi_vote = {0, 0};
int i, rc = 0;
struct cam_hw_soc_info *soc_info =
top_priv->common_data.soc_info;
@@ -156,6 +158,11 @@
sum.uncompressed_bw,
sum.compressed_bw);
+ top_priv->last_vote[top_priv->last_counter] = sum;
+ top_priv->last_counter = (top_priv->last_counter + 1) %
+ (CAM_VFE_TOP_VER2_MUX_MAX *
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES);
+
if ((top_priv->applied_axi_vote.uncompressed_bw ==
sum.uncompressed_bw) &&
(top_priv->applied_axi_vote.compressed_bw ==
@@ -163,77 +170,60 @@
CAM_DBG(CAM_ISP, "BW config unchanged %llu %llu",
top_priv->applied_axi_vote.uncompressed_bw,
top_priv->applied_axi_vote.compressed_bw);
- top_priv->counter_to_update_axi_vote = 0;
return 0;
}
- if ((top_priv->to_be_applied_axi_vote.uncompressed_bw !=
- sum.uncompressed_bw) ||
- (top_priv->to_be_applied_axi_vote.compressed_bw !=
- sum.compressed_bw)) {
- // we got a new bw value to apply
- top_priv->counter_to_update_axi_vote = 0;
-
- top_priv->to_be_applied_axi_vote.uncompressed_bw =
- sum.uncompressed_bw;
- top_priv->to_be_applied_axi_vote.compressed_bw =
- sum.compressed_bw;
- }
-
if (start_stop == true) {
- CAM_DBG(CAM_ISP,
- "New bw in start/stop, applying bw now, counter=%d",
- top_priv->counter_to_update_axi_vote);
- top_priv->counter_to_update_axi_vote = 0;
- apply_bw_update = true;
- } else if ((top_priv->to_be_applied_axi_vote.uncompressed_bw <
- top_priv->applied_axi_vote.uncompressed_bw) ||
- (top_priv->to_be_applied_axi_vote.compressed_bw <
- top_priv->applied_axi_vote.compressed_bw)) {
- if (top_priv->counter_to_update_axi_vote >=
+ /* need to vote current request immediately */
+ to_be_applied_axi_vote = sum;
+ /* Reset everything, we can start afresh */
+ memset(top_priv->last_vote, 0x0, sizeof(struct cam_axi_vote) *
(CAM_VFE_TOP_VER2_MUX_MAX *
- CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES)) {
- CAM_DBG(CAM_ISP,
- "New bw is less, applying bw now, counter=%d",
- top_priv->counter_to_update_axi_vote);
- top_priv->counter_to_update_axi_vote = 0;
- apply_bw_update = true;
- } else {
- CAM_DBG(CAM_ISP,
- "New bw is less, Defer applying bw, counter=%d",
- top_priv->counter_to_update_axi_vote);
-
- top_priv->counter_to_update_axi_vote++;
- apply_bw_update = false;
- }
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES));
+ top_priv->last_counter = 0;
+ top_priv->last_vote[top_priv->last_counter] = sum;
+ top_priv->last_counter = (top_priv->last_counter + 1) %
+ (CAM_VFE_TOP_VER2_MUX_MAX *
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES);
} else {
- CAM_DBG(CAM_ISP,
- "New bw is more, applying bw now, counter=%d",
- top_priv->counter_to_update_axi_vote);
- top_priv->counter_to_update_axi_vote = 0;
- apply_bw_update = true;
+ /*
+ * Find max bw request in last few frames. This will the bw
+ *that we want to vote to CPAS now.
+ */
+ for (i = 0; i < (CAM_VFE_TOP_VER2_MUX_MAX *
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES); i++) {
+ if (to_be_applied_axi_vote.compressed_bw <
+ top_priv->last_vote[i].compressed_bw)
+ to_be_applied_axi_vote.compressed_bw =
+ top_priv->last_vote[i].compressed_bw;
+
+ if (to_be_applied_axi_vote.uncompressed_bw <
+ top_priv->last_vote[i].uncompressed_bw)
+ to_be_applied_axi_vote.uncompressed_bw =
+ top_priv->last_vote[i].uncompressed_bw;
+ }
}
- CAM_DBG(CAM_ISP,
- "counter=%d, apply_bw_update=%d",
- top_priv->counter_to_update_axi_vote,
- apply_bw_update);
+ if ((to_be_applied_axi_vote.uncompressed_bw !=
+ top_priv->applied_axi_vote.uncompressed_bw) ||
+ (to_be_applied_axi_vote.compressed_bw !=
+ top_priv->applied_axi_vote.compressed_bw))
+ apply_bw_update = true;
+
+ CAM_DBG(CAM_ISP, "apply_bw_update=%d", apply_bw_update);
if (apply_bw_update == true) {
rc = cam_cpas_update_axi_vote(
soc_private->cpas_handle,
- &top_priv->to_be_applied_axi_vote);
+ &to_be_applied_axi_vote);
if (!rc) {
top_priv->applied_axi_vote.uncompressed_bw =
- top_priv->
- to_be_applied_axi_vote.uncompressed_bw;
+ to_be_applied_axi_vote.uncompressed_bw;
top_priv->applied_axi_vote.compressed_bw =
- top_priv->
to_be_applied_axi_vote.compressed_bw;
} else {
CAM_ERR(CAM_ISP, "BW request failed, rc=%d", rc);
}
- top_priv->counter_to_update_axi_vote = 0;
}
return rc;
@@ -706,11 +696,12 @@
}
vfe_top->top_priv = top_priv;
top_priv->hw_clk_rate = 0;
- top_priv->to_be_applied_axi_vote.compressed_bw = 0;
- top_priv->to_be_applied_axi_vote.uncompressed_bw = 0;
top_priv->applied_axi_vote.compressed_bw = 0;
top_priv->applied_axi_vote.uncompressed_bw = 0;
- top_priv->counter_to_update_axi_vote = 0;
+ memset(top_priv->last_vote, 0x0, sizeof(struct cam_axi_vote) *
+ (CAM_VFE_TOP_VER2_MUX_MAX *
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES));
+ top_priv->last_counter = 0;
for (i = 0, j = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
top_priv->mux_rsrc[i].res_type = CAM_ISP_RESOURCE_VFE_IN;
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
index f172a79..97d076a 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
@@ -770,13 +770,6 @@
} else {
CAM_ERR(CAM_JPEG, "process_cmd null ");
}
- rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd(
- hw_mgr->devices[dev_type][0]->hw_priv,
- CAM_JPEG_CMD_SET_IRQ_CB,
- &irq_cb, sizeof(irq_cb));
- if (rc)
- CAM_ERR(CAM_JPEG,
- "CMD_SET_IRQ_CB failed %d", rc);
if (hw_mgr->devices[dev_type][0]->hw_ops.stop) {
rc = hw_mgr->devices[dev_type][0]->hw_ops.stop(
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c
index 898997a..a60661e 100644
--- a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c
@@ -879,6 +879,7 @@
if (args->num_in_map_entries == 0 || args->num_out_map_entries == 0) {
CAM_ERR(CAM_LRME, "Error in port number in %d, out %d",
args->num_in_map_entries, args->num_out_map_entries);
+ rc = -EINVAL;
goto error;
}
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index aaba481..4602d6c 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -163,6 +163,34 @@
}
/**
+ * __cam_req_mgr_validate_inject_delay()
+ *
+ * @brief : Check if any pd device is introducing inject delay
+ * @tbl : cam_req_mgr_req_tbl
+ * @curr_idx : slot idx
+ *
+ * @return : 0 for success, negative for failure
+ */
+static int __cam_req_mgr_validate_inject_delay(
+ struct cam_req_mgr_req_tbl *tbl,
+ int32_t curr_idx)
+{
+ struct cam_req_mgr_tbl_slot *slot = NULL;
+
+ while (tbl) {
+ slot = &tbl->slot[curr_idx];
+ if (slot->inject_delay > 0) {
+ slot->inject_delay--;
+ return -EAGAIN;
+ }
+ __cam_req_mgr_dec_idx(&curr_idx, tbl->pd_delta,
+ tbl->num_slots);
+ tbl = tbl->next;
+ }
+ return 0;
+}
+
+/**
* __cam_req_mgr_traverse()
*
* @brief : Traverse through pd tables, it will internally cover all linked
@@ -183,6 +211,7 @@
int32_t curr_idx = traverse_data->idx;
struct cam_req_mgr_req_tbl *tbl;
struct cam_req_mgr_apply *apply_data;
+ struct cam_req_mgr_tbl_slot *slot = NULL;
if (!traverse_data->tbl || !traverse_data->apply_data) {
CAM_ERR(CAM_CRM, "NULL pointer %pK %pK",
@@ -193,20 +222,24 @@
tbl = traverse_data->tbl;
apply_data = traverse_data->apply_data;
+ slot = &tbl->slot[curr_idx];
CAM_DBG(CAM_CRM,
"Enter pd %d idx %d state %d skip %d status %d skip_idx %d",
tbl->pd, curr_idx, tbl->slot[curr_idx].state,
tbl->skip_traverse, traverse_data->in_q->slot[curr_idx].status,
traverse_data->in_q->slot[curr_idx].skip_idx);
- if ((tbl->inject_delay > 0) &&
- (traverse_data->self_link == true)) {
- CAM_DBG(CAM_CRM, "Injecting Delay of one frame");
- apply_data[tbl->pd].req_id = -1;
- tbl->inject_delay--;
- /* This pd table is not ready to proceed with asked idx */
- SET_FAILURE_BIT(traverse_data->result, tbl->pd);
- return -EAGAIN;
+ if ((traverse_data->self_link == true) &&
+ (!traverse_data->inject_delay_chk)) {
+ rc = __cam_req_mgr_validate_inject_delay(tbl, curr_idx);
+ if (rc) {
+ CAM_DBG(CAM_CRM, "Injecting Delay of one frame");
+ apply_data[tbl->pd].req_id = -1;
+ /* This pd tbl not ready to proceed with asked idx */
+ SET_FAILURE_BIT(traverse_data->result, tbl->pd);
+ return -EAGAIN;
+ }
+ traverse_data->inject_delay_chk = true;
}
/* Check if req is ready or in skip mode or pd tbl is in skip mode */
@@ -230,7 +263,7 @@
traverse_data->in_q, curr_idx);
apply_data[tbl->pd].idx = curr_idx;
- CAM_DBG(CAM_CRM, "req_id: %d with pd of %d",
+ CAM_DBG(CAM_CRM, "req_id: %lld with pd of %d",
apply_data[tbl->pd].req_id,
apply_data[tbl->pd].pd);
/*
@@ -248,6 +281,11 @@
}
} else {
/* This pd table is not ready to proceed with asked idx */
+ CAM_INFO(CAM_CRM,
+ "Skip Frame: req: %lld not ready pd: %d open_req count: %d",
+ CRM_GET_REQ_ID(traverse_data->in_q, curr_idx),
+ tbl->pd,
+ traverse_data->open_req_cnt);
SET_FAILURE_BIT(traverse_data->result, tbl->pd);
return -EAGAIN;
}
@@ -450,6 +488,9 @@
rc = dev->ops->apply_req(&apply_req);
if (rc < 0)
break;
+
+ if (pd == link->max_delay)
+ link->open_req_cnt--;
}
}
}
@@ -460,6 +501,7 @@
for (; i >= 0; i--) {
dev = &link->l_dev[i];
evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_ERR;
+ evt_data.dev_hdl = dev->dev_hdl;
evt_data.link_hdl = link->link_hdl;
evt_data.req_id = apply_req.request_id;
evt_data.u.error = CRM_KMD_ERR_BUBBLE;
@@ -509,6 +551,8 @@
traverse_data.result = 0;
traverse_data.validate_only = validate_only;
traverse_data.self_link = self_link;
+ traverse_data.inject_delay_chk = false;
+ traverse_data.open_req_cnt = link->open_req_cnt;
/*
* Traverse through all pd tables, if result is success,
* apply the settings
@@ -1435,6 +1479,8 @@
if (idx < 0) {
CAM_ERR(CAM_CRM, "req_id %lld not found in input queue",
flush_info->req_id);
+ mutex_unlock(&link->req.lock);
+ return -EINVAL;
} else {
CAM_DBG(CAM_CRM, "req_id %lld found at idx %d",
flush_info->req_id, idx);
@@ -1513,6 +1559,7 @@
slot->sync_mode = sched_req->sync_mode;
slot->skip_idx = 0;
slot->recover = sched_req->bubble_enable;
+ link->open_req_cnt++;
__cam_req_mgr_inc_idx(&in_q->wr_idx, 1, in_q->num_slots);
mutex_unlock(&link->req.lock);
@@ -1582,10 +1629,13 @@
goto end;
}
- if (add_req->skip_before_applying > tbl->inject_delay)
- tbl->inject_delay = add_req->skip_before_applying;
-
slot = &tbl->slot[idx];
+ if (add_req->skip_before_applying > slot->inject_delay) {
+ slot->inject_delay = add_req->skip_before_applying;
+ CAM_DBG(CAM_CRM, "Req_id %llu injecting delay %u",
+ add_req->req_id, add_req->skip_before_applying);
+ }
+
if (slot->state != CRM_REQ_STATE_PENDING &&
slot->state != CRM_REQ_STATE_EMPTY) {
CAM_WARN(CAM_CRM, "Unexpected state %d for slot %d map %x",
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index e15b3b0..025c16a 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -126,14 +126,16 @@
/**
* struct cam_req_mgr_traverse
- * @idx : slot index
- * @result : contains which all tables were able to apply successfully
- * @tbl : pointer of pipeline delay based request table
- * @apply_data : pointer which various tables will update during traverse
- * @in_q : input request queue pointer
- * @validate_only : Whether to validate only and/or update settings
- * @self_link : To indicate whether the check is for the given link or the
- * other sync link
+ * @idx : slot index
+ * @result : contains which all tables were able to apply successfully
+ * @tbl : pointer of pipeline delay based request table
+ * @apply_data : pointer which various tables will update during traverse
+ * @in_q : input request queue pointer
+ * @validate_only : Whether to validate only and/or update settings
+ * @self_link : To indicate whether the check is for the given link or
+ * the other sync link
+ * @inject_delay_chk : if inject delay has been validated for all pd devices
+ * @open_req_cnt : Count of open requests yet to be serviced in the kernel.
*/
struct cam_req_mgr_traverse {
int32_t idx;
@@ -143,6 +145,8 @@
struct cam_req_mgr_req_queue *in_q;
bool validate_only;
bool self_link;
+ bool inject_delay_chk;
+ int32_t open_req_cnt;
};
/**
@@ -164,11 +168,13 @@
* @idx : slot index
* @req_ready_map : mask tracking which all devices have request ready
* @state : state machine for life cycle of a slot
+ * @inject_delay : insert extra bubbling for flash type of use cases
*/
struct cam_req_mgr_tbl_slot {
int32_t idx;
uint32_t req_ready_map;
enum crm_req_state state;
+ uint32_t inject_delay;
};
/**
@@ -183,7 +189,6 @@
* @pd_delta : differnce between this table's pipeline delay and next
* @num_slots : number of request slots present in the table
* @slot : array of slots tracking requests availability at devices
- * @inject_delay : insert extra bubbling for flash type of use cases
*/
struct cam_req_mgr_req_tbl {
int32_t id;
@@ -195,7 +200,6 @@
int32_t pd_delta;
int32_t num_slots;
struct cam_req_mgr_tbl_slot slot[MAX_REQ_SLOTS];
- uint32_t inject_delay;
};
/**
@@ -301,6 +305,8 @@
* @sync_link_sof_skip : flag determines if a pkt is not available for a given
* frame in a particular link skip corresponding
* frame in sync link as well.
+ * @open_req_cnt : Counter to keep track of open requests that are yet
+ * to be serviced in the kernel.
*
*/
struct cam_req_mgr_core_link {
@@ -324,6 +330,7 @@
int64_t sync_self_ref;
bool frame_skip_flag;
bool sync_link_sof_skip;
+ int32_t open_req_cnt;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
index 966b573..066efd6 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
@@ -59,11 +59,11 @@
(struct cam_req_mgr_core_workq *)task->parent;
unsigned long flags = 0;
- WORKQ_ACQUIRE_LOCK(workq, flags);
list_del_init(&task->entry);
task->cancel = 0;
task->process_cb = NULL;
task->priv = NULL;
+ WORKQ_ACQUIRE_LOCK(workq, flags);
list_add_tail(&task->entry,
&workq->task.empty_head);
atomic_add(1, &workq->task.free_cnt);
@@ -127,28 +127,6 @@
}
}
-void crm_workq_clear_q(struct cam_req_mgr_core_workq *workq)
-{
- int32_t i = CRM_TASK_PRIORITY_0;
- struct crm_workq_task *task, *task_save;
-
- CAM_DBG(CAM_CRM, "pending_cnt %d",
- atomic_read(&workq->task.pending_cnt));
-
- while (i < CRM_TASK_PRIORITY_MAX) {
- if (!list_empty(&workq->task.process_head[i])) {
- list_for_each_entry_safe(task, task_save,
- &workq->task.process_head[i], entry) {
- cam_req_mgr_workq_put_task(task);
- CAM_WARN(CAM_CRM, "flush task %pK, %d, cnt %d",
- task, i, atomic_read(
- &workq->task.free_cnt));
- }
- }
- i++;
- }
-}
-
int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task,
void *priv, int32_t prio)
{
@@ -167,10 +145,6 @@
rc = -EINVAL;
goto end;
}
- if (!workq->job) {
- rc = -EINVAL;
- goto end;
- }
if (task->cancel == 1) {
cam_req_mgr_workq_put_task(task);
@@ -184,16 +158,21 @@
? prio : CRM_TASK_PRIORITY_0;
WORKQ_ACQUIRE_LOCK(workq, flags);
+ if (!workq->job) {
+ rc = -EINVAL;
+ WORKQ_RELEASE_LOCK(workq, flags);
+ goto end;
+ }
+
list_add_tail(&task->entry,
&workq->task.process_head[task->priority]);
- WORKQ_RELEASE_LOCK(workq, flags);
atomic_add(1, &workq->task.pending_cnt);
CAM_DBG(CAM_CRM, "enq task %pK pending_cnt %d",
task, atomic_read(&workq->task.pending_cnt));
queue_work(workq->job, &workq->work);
-
+ WORKQ_RELEASE_LOCK(workq, flags);
end:
return rc;
}
@@ -252,8 +231,7 @@
task = &crm_workq->task.pool[i];
task->parent = (void *)crm_workq;
/* Put all tasks in free pool */
- list_add_tail(&task->entry,
- &crm_workq->task.process_head[CRM_TASK_PRIORITY_0]);
+ INIT_LIST_HEAD(&task->entry);
cam_req_mgr_workq_put_task(task);
}
*workq = crm_workq;
@@ -266,13 +244,18 @@
void cam_req_mgr_workq_destroy(struct cam_req_mgr_core_workq **crm_workq)
{
+ unsigned long flags = 0;
+ struct workqueue_struct *job;
CAM_DBG(CAM_CRM, "destroy workque %pK", crm_workq);
if (*crm_workq) {
- crm_workq_clear_q(*crm_workq);
+ WORKQ_ACQUIRE_LOCK(*crm_workq, flags);
if ((*crm_workq)->job) {
- destroy_workqueue((*crm_workq)->job);
+ job = (*crm_workq)->job;
(*crm_workq)->job = NULL;
- }
+ WORKQ_RELEASE_LOCK(*crm_workq, flags);
+ destroy_workqueue(job);
+ } else
+ WORKQ_RELEASE_LOCK(*crm_workq, flags);
kfree((*crm_workq)->task.pool);
kfree(*crm_workq);
*crm_workq = NULL;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index 56cb49a..a34d70c 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -603,7 +603,11 @@
void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl)
{
- int rc;
+ int rc = 0;
+ struct cam_actuator_soc_private *soc_private =
+ (struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+ struct cam_sensor_power_ctrl_t *power_info =
+ &soc_private->power_info;
if (a_ctrl->cam_act_state == CAM_ACTUATOR_INIT)
return;
@@ -612,6 +616,7 @@
rc = cam_actuator_power_down(a_ctrl);
if (rc < 0)
CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
+ a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
}
if (a_ctrl->cam_act_state >= CAM_ACTUATOR_ACQUIRE) {
@@ -622,6 +627,12 @@
a_ctrl->bridge_intf.link_hdl = -1;
a_ctrl->bridge_intf.session_hdl = -1;
}
+
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
+ power_info->power_setting = NULL;
+ power_info->power_down_setting = NULL;
+
a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
}
@@ -636,6 +647,12 @@
return -EINVAL;
}
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_ACTUATOR, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+
CAM_DBG(CAM_ACTUATOR, "Opcode to Actuator: %d", cmd->op_code);
mutex_lock(&(a_ctrl->actuator_mutex));
@@ -769,6 +786,7 @@
rc = cam_actuator_i2c_pkt_parse(a_ctrl, arg);
if (rc < 0) {
CAM_ERR(CAM_ACTUATOR, "Failed in actuator Parsing");
+ goto release_mutex;
}
if (a_ctrl->setting_apply_state ==
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
index fe69fcb..b47f4f3 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
@@ -205,8 +205,6 @@
CAM_INFO(CAM_CCI, "****CCI MASTER %d Registers ****",
master);
for (i = 0; i < DEBUG_MASTER_REG_COUNT; i++) {
- if (i == 6)
- continue;
reg_offset = DEBUG_MASTER_REG_START + master*0x100 + i * 4;
read_val = cam_io_r_mb(base + reg_offset);
CAM_INFO(CAM_CCI, "offset = 0x%X value = 0x%X",
@@ -868,6 +866,180 @@
return rc;
}
+static int32_t cam_cci_burst_read(struct v4l2_subdev *sd,
+ struct cam_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ uint32_t val = 0, i = 0;
+ unsigned long rem_jiffies;
+ int32_t read_words = 0, exp_words = 0;
+ int32_t index = 0, first_byte = 0, total_read_words = 0;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_1;
+ struct cci_device *cci_dev = NULL;
+ struct cam_cci_read_cfg *read_cfg = NULL;
+ struct cam_hw_soc_info *soc_info = NULL;
+ void __iomem *base = NULL;
+
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+
+ if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX
+ || c_ctrl->cci_info->cci_i2c_master < 0) {
+ CAM_ERR(CAM_CCI, "Invalid I2C master addr");
+ return -EINVAL;
+ }
+
+ soc_info = &cci_dev->soc_info;
+ base = soc_info->reg_map[0].mem_base;
+ mutex_lock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+
+ /*
+ * Todo: If there is a change in frequency of operation
+ * Wait for previos transaction to complete
+ */
+
+ /* Set the I2C Frequency */
+ rc = cam_cci_set_clk_param(cci_dev, c_ctrl);
+ if (rc < 0) {
+ CAM_ERR(CAM_CCI, "cam_cci_set_clk_param failed rc = %d", rc);
+ goto rel_mutex;
+ }
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_read call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = cam_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ CAM_ERR(CAM_CCI, "Initial validataion failed rc %d", rc);
+ goto rel_mutex;
+ }
+
+ if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+ CAM_ERR(CAM_CCI, "More than max retries");
+ goto rel_mutex;
+ }
+
+ if (read_cfg->data == NULL) {
+ CAM_ERR(CAM_CCI, "Data ptr is NULL");
+ goto rel_mutex;
+ }
+
+ if (read_cfg->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+ CAM_ERR(CAM_CCI, "failed : Invalid addr type: %u",
+ read_cfg->addr_type);
+ rc = -EINVAL;
+ goto rel_mutex;
+ }
+
+ CAM_DBG(CAM_CCI, "set param sid 0x%x retries %d id_map %d",
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CAM_DBG(CAM_CCI, "failed rc: %d", rc);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CAM_DBG(CAM_CCI, "failed rc: %d", rc);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4);
+ for (i = 0; i < read_cfg->addr_type; i++) {
+ val |= ((read_cfg->addr >> (i << 3)) & 0xFF) <<
+ ((read_cfg->addr_type - i) << 3);
+ }
+
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CAM_DBG(CAM_CCI, "failed rc: %d", rc);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4);
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CAM_DBG(CAM_CCI, "failed rc: %d", rc);
+ goto rel_mutex;
+ }
+
+ val = CCI_I2C_UNLOCK_CMD;
+ rc = cam_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CAM_DBG(CAM_CCI, "failed rc: %d", rc);
+ goto rel_mutex;
+ }
+
+ val = cam_io_r_mb(base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+ CAM_DBG(CAM_CCI, "cur word cnt 0x%x", val);
+ cam_io_w_mb(val, base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR
+ + master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ cam_io_w_mb(val, base + CCI_QUEUE_START_ADDR);
+ exp_words = ((read_cfg->num_byte / 4) + 1);
+
+ while (exp_words != total_read_words) {
+ rem_jiffies = wait_for_completion_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (!rem_jiffies) {
+ rc = -ETIMEDOUT;
+ val = cam_io_r_mb(base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR +
+ master * 0x100);
+ CAM_ERR(CAM_CCI,
+ "wait_for_completion_timeout rc = %d FIFO buf_lvl:0x%x",
+ rc, val);
+#ifdef DUMP_CCI_REGISTERS
+ cam_cci_dump_registers(cci_dev, master, queue);
+#endif
+ cam_cci_flush_queue(cci_dev, master);
+ goto rel_mutex;
+ }
+
+ read_words = cam_io_r_mb(base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
+ total_read_words += read_words;
+ do {
+ val = cam_io_r_mb(base +
+ CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
+ for (i = 0; (i < 4) &&
+ (index < read_cfg->num_byte); i++) {
+ CAM_DBG(CAM_CCI, "i:%d index:%d", i, index);
+ if (!first_byte) {
+ CAM_DBG(CAM_CCI, "sid 0x%x",
+ val & 0xFF);
+ first_byte++;
+ } else {
+ read_cfg->data[index] =
+ (val >> (i * 8)) & 0xFF;
+ CAM_DBG(CAM_CCI, "data[%d] 0x%x", index,
+ read_cfg->data[index]);
+ index++;
+ }
+ }
+ } while (--read_words > 0);
+ }
+
+rel_mutex:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex_q[queue]);
+ return rc;
+}
+
static int32_t cam_cci_read(struct v4l2_subdev *sd,
struct cam_cci_ctrl *c_ctrl)
{
@@ -1004,7 +1176,11 @@
#endif
if (rc == 0)
rc = -ETIMEDOUT;
- CAM_ERR(CAM_CCI, "wait_for_completion_timeout rc = %d", rc);
+ val = cam_io_r_mb(base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
+ CAM_ERR(CAM_CCI,
+ "wait_for_completion_timeout rc = %d FIFO buf_lvl: 0x%x",
+ rc, val);
cam_cci_flush_queue(cci_dev, master);
goto rel_mutex;
} else {
@@ -1224,19 +1400,26 @@
read_bytes = read_cfg->num_byte;
do {
- if (read_bytes > CCI_READ_MAX)
- read_cfg->num_byte = CCI_READ_MAX;
+ if (read_bytes > CCI_I2C_MAX_BYTE_COUNT)
+ read_cfg->num_byte = CCI_I2C_MAX_BYTE_COUNT;
else
read_cfg->num_byte = read_bytes;
- rc = cam_cci_read(sd, c_ctrl);
- if (rc < 0) {
- CAM_ERR(CAM_CCI, "failed rc %d", rc);
+
+ if (read_cfg->num_byte > CCI_READ_MAX)
+ rc = cam_cci_burst_read(sd, c_ctrl);
+ else
+ rc = cam_cci_read(sd, c_ctrl);
+
+ if (!rc) {
+ CAM_ERR(CAM_CCI, "failed to read rc:%d", rc);
goto ERROR;
}
- if (read_bytes > CCI_READ_MAX) {
- read_cfg->addr += CCI_READ_MAX;
- read_cfg->data += CCI_READ_MAX;
- read_bytes -= CCI_READ_MAX;
+
+ if (read_bytes > CCI_I2C_MAX_BYTE_COUNT) {
+ read_cfg->addr += (CCI_I2C_MAX_BYTE_COUNT /
+ read_cfg->data_type);
+ read_cfg->data += CCI_I2C_MAX_BYTE_COUNT;
+ read_bytes -= CCI_I2C_MAX_BYTE_COUNT;
} else {
read_bytes = 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
index fb37526..c8ca85d 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
@@ -16,7 +16,6 @@
#include "cam_cci_core.h"
#define CCI_MAX_DELAY 1000000
-#define CCI_TIMEOUT msecs_to_jiffies(500)
static struct v4l2_subdev *g_cci_subdev;
@@ -59,18 +58,23 @@
irqreturn_t cam_cci_irq(int irq_num, void *data)
{
- uint32_t irq;
+ uint32_t irq_status0 = 0;
+ uint32_t irq_status1 = 0;
struct cci_device *cci_dev = data;
struct cam_hw_soc_info *soc_info =
&cci_dev->soc_info;
void __iomem *base = soc_info->reg_map[0].mem_base;
unsigned long flags;
+ bool burst_read_assert = false;
- irq = cam_io_r_mb(base + CCI_IRQ_STATUS_0_ADDR);
- cam_io_w_mb(irq, base + CCI_IRQ_CLEAR_0_ADDR);
+ irq_status0 = cam_io_r_mb(base + CCI_IRQ_STATUS_0_ADDR);
+ irq_status1 = cam_io_r_mb(base + CCI_IRQ_STATUS_1_ADDR);
+ cam_io_w_mb(irq_status0, base + CCI_IRQ_CLEAR_0_ADDR);
+ cam_io_w_mb(irq_status1, base + CCI_IRQ_CLEAR_1_ADDR);
cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
- if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) {
+ CAM_DBG(CAM_CCI, "irq0:%x irq1:%x", irq_status0, irq_status1);
+ if (irq_status0 & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) {
if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) {
cci_dev->cci_master_info[MASTER_0].reset_pending =
FALSE;
@@ -84,11 +88,24 @@
&cci_dev->cci_master_info[MASTER_1].reset_complete);
}
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) {
+
+ if ((irq_status0 & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) &&
+ (irq_status1 & CCI_IRQ_STATUS_1_I2C_M0_RD_THRESHOLD)) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ burst_read_assert = true;
+ }
+ if ((irq_status0 & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) &&
+ (!burst_read_assert)) {
cci_dev->cci_master_info[MASTER_0].status = 0;
complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
+ if ((irq_status1 & CCI_IRQ_STATUS_1_I2C_M0_RD_THRESHOLD) &&
+ (!burst_read_assert)) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ }
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) {
struct cam_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_0];
@@ -105,7 +122,7 @@
&cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_0],
flags);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
struct cam_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_0];
@@ -122,11 +139,23 @@
&cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_1],
flags);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
+ if ((irq_status0 & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) &&
+ (irq_status1 & CCI_IRQ_STATUS_1_I2C_M1_RD_THRESHOLD)) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ burst_read_assert = true;
+ }
+ if ((irq_status0 & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) &&
+ (!burst_read_assert)) {
cci_dev->cci_master_info[MASTER_1].status = 0;
complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
+ if ((irq_status1 & CCI_IRQ_STATUS_1_I2C_M1_RD_THRESHOLD) &&
+ (!burst_read_assert)) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) {
struct cam_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_1];
@@ -143,7 +172,7 @@
&cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_0],
flags);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
struct cam_cci_master_info *cci_master_info;
cci_master_info = &cci_dev->cci_master_info[MASTER_1];
@@ -160,27 +189,27 @@
&cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_1],
flags);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
cam_io_w_mb(CCI_M0_RESET_RMSK,
base + CCI_RESET_CMD_ADDR);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
cam_io_w_mb(CCI_M1_RESET_RMSK,
base + CCI_RESET_CMD_ADDR);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
cam_io_w_mb(CCI_M0_HALT_REQ_RMSK,
base + CCI_HALT_REQ_ADDR);
- CAM_DBG(CAM_CCI, "MASTER_0 error 0x%x", irq);
+ CAM_DBG(CAM_CCI, "MASTER_0 error 0x%x", irq_status0);
}
- if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
+ if (irq_status0 & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
cam_io_w_mb(CCI_M1_HALT_REQ_RMSK,
base + CCI_HALT_REQ_ADDR);
- CAM_DBG(CAM_CCI, "MASTER_1 error 0x%x", irq);
+ CAM_DBG(CAM_CCI, "MASTER_1 error 0x%x", irq_status0);
}
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
index d25964e..d48ffd1 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
@@ -45,7 +45,7 @@
#define CYCLES_PER_MICRO_SEC_DEFAULT 4915
#define CCI_MAX_DELAY 1000000
-#define CCI_TIMEOUT msecs_to_jiffies(500)
+#define CCI_TIMEOUT msecs_to_jiffies(1500)
#define NUM_MASTERS 2
#define NUM_QUEUES 2
@@ -65,19 +65,14 @@
#define MAX_LRME_V4l2_EVENTS 30
/* Max bytes that can be read per CCI read transaction */
-#define CCI_READ_MAX 12
+#define CCI_READ_MAX 256
#define CCI_I2C_READ_MAX_RETRIES 3
#define CCI_I2C_MAX_READ 8192
#define CCI_I2C_MAX_WRITE 8192
+#define CCI_I2C_MAX_BYTE_COUNT 65535
#define CAMX_CCI_DEV_NAME "cam-cci-driver"
-/* Max bytes that can be read per CCI read transaction */
-#define CCI_READ_MAX 12
-#define CCI_I2C_READ_MAX_RETRIES 3
-#define CCI_I2C_MAX_READ 8192
-#define CCI_I2C_MAX_WRITE 8192
-
#define PRIORITY_QUEUE (QUEUE_0)
#define SYNC_QUEUE (QUEUE_1)
@@ -125,6 +120,7 @@
uint16_t addr_type;
uint8_t *data;
uint16_t num_byte;
+ uint16_t data_type;
};
struct cam_cci_i2c_queue_info {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h
index c18593e..31c8e26 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_hwreg.h
@@ -43,27 +43,36 @@
#define CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x00000310
#define CCI_IRQ_MASK_0_ADDR 0x00000c04
#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7
+#define CCI_IRQ_MASK_1_ADDR 0x00000c10
+#define CCI_IRQ_MASK_1_RMSK 0x00110000
#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08
+#define CCI_IRQ_CLEAR_1_ADDR 0x00000c14
#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c
+#define CCI_IRQ_STATUS_1_ADDR 0x00000c18
#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000
#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000
#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000
#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK 0x100000
#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK 0x10000
#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000
+#define CCI_IRQ_STATUS_1_I2C_M1_RD_THRESHOLD 0x100000
#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100
#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10
#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6
#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000
#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1
+#define CCI_IRQ_STATUS_1_I2C_M0_RD_THRESHOLD 0x10000
+#define CCI_I2C_M0_RD_THRESHOLD_ADDR 0x00000120
+#define CCI_I2C_M1_RD_THRESHOLD_ADDR 0x00000220
+#define CCI_I2C_RD_THRESHOLD_VALUE 0x38
#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
#define DEBUG_TOP_REG_START 0x0
#define DEBUG_TOP_REG_COUNT 14
#define DEBUG_MASTER_REG_START 0x100
-#define DEBUG_MASTER_REG_COUNT 8
+#define DEBUG_MASTER_REG_COUNT 9
#define DEBUG_MASTER_QUEUE_REG_START 0x300
-#define DEBUG_MASTER_QUEUE_REG_COUNT 6
+#define DEBUG_MASTER_QUEUE_REG_COUNT 7
#define DEBUG_INTR_REG_START 0xC00
#define DEBUG_INTR_REG_COUNT 7
#endif /* _CAM_CCI_HWREG_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
index 295259d..e0b27ca 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
@@ -146,6 +146,10 @@
base + CCI_IRQ_MASK_0_ADDR);
cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
base + CCI_IRQ_CLEAR_0_ADDR);
+ cam_io_w_mb(CCI_IRQ_MASK_1_RMSK,
+ base + CCI_IRQ_MASK_1_ADDR);
+ cam_io_w_mb(CCI_IRQ_MASK_1_RMSK,
+ base + CCI_IRQ_CLEAR_1_ADDR);
cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
for (i = 0; i < MASTER_MAX; i++) {
@@ -157,6 +161,13 @@
flush_workqueue(cci_dev->write_wq[i]);
}
}
+
+ /* Set RD FIFO threshold for M0 & M1 */
+ cam_io_w_mb(CCI_I2C_RD_THRESHOLD_VALUE,
+ base + CCI_I2C_M0_RD_THRESHOLD_ADDR);
+ cam_io_w_mb(CCI_I2C_RD_THRESHOLD_VALUE,
+ base + CCI_I2C_M1_RD_THRESHOLD_ADDR);
+
cci_dev->cci_state = CCI_STATE_ENABLED;
return 0;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index e978a40..2688cd5 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -438,6 +438,12 @@
return -EINVAL;
}
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_CSIPHY, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+
CAM_DBG(CAM_CSIPHY, "Opcode received: %d", cmd->op_code);
mutex_lock(&csiphy_dev->mutex);
switch (cmd->op_code) {
@@ -608,6 +614,13 @@
if (csiphy_dev->acquire_count == 0)
csiphy_dev->csiphy_state = CAM_CSIPHY_INIT;
+
+ if (csiphy_dev->config_count == 0) {
+ CAM_DBG(CAM_CSIPHY, "reset csiphy_info");
+ csiphy_dev->csiphy_info.lane_mask = 0;
+ csiphy_dev->csiphy_info.lane_cnt = 0;
+ csiphy_dev->csiphy_info.combo_mode = 0;
+ }
}
break;
case CAM_CONFIG_DEV: {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
index 3f743fc..3245093 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
@@ -152,7 +152,7 @@
{0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
{0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0060, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
},
{
@@ -168,7 +168,7 @@
{0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0760, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
},
{
@@ -183,7 +183,7 @@
{0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
{0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0260, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
},
{
@@ -198,7 +198,7 @@
{0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
{0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0460, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
},
{
@@ -214,7 +214,7 @@
{0x060C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0638, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0660, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
{0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
},
};
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
index b0fbead..7f94f8d 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -109,6 +109,7 @@
rc = camera_io_dev_read_seq(&e_ctrl->io_master_info,
emap[j].mem.addr, memptr,
emap[j].mem.addr_type,
+ emap[j].mem.data_type,
emap[j].mem.valid_size);
if (rc) {
CAM_ERR(CAM_EEPROM, "read failed rc %d",
@@ -314,8 +315,8 @@
power_down:
cam_eeprom_power_down(e_ctrl);
data_mem_free:
- kfree(e_ctrl->cal_data.mapdata);
- kfree(e_ctrl->cal_data.map);
+ vfree(e_ctrl->cal_data.mapdata);
+ vfree(e_ctrl->cal_data.map);
e_ctrl->cal_data.num_data = 0;
e_ctrl->cal_data.num_map = 0;
e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
@@ -542,9 +543,9 @@
(struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
- e_ctrl->cal_data.map = kcalloc((MSM_EEPROM_MEMORY_MAP_MAX_SIZE *
- MSM_EEPROM_MAX_MEM_MAP_CNT),
- (sizeof(struct cam_eeprom_memory_map_t)), GFP_KERNEL);
+ e_ctrl->cal_data.map = vzalloc((MSM_EEPROM_MEMORY_MAP_MAX_SIZE *
+ MSM_EEPROM_MAX_MEM_MAP_CNT) *
+ (sizeof(struct cam_eeprom_memory_map_t)));
if (!e_ctrl->cal_data.map) {
rc = -ENOMEM;
CAM_ERR(CAM_EEPROM, "failed");
@@ -707,11 +708,6 @@
ioctl_ctrl = (struct cam_control *)arg;
- if (ioctl_ctrl->handle_type != CAM_HANDLE_USER_POINTER) {
- CAM_ERR(CAM_EEPROM, "Invalid Handle Type");
- return -EINVAL;
- }
-
if (copy_from_user(&dev_config, (void __user *) ioctl_ctrl->handle,
sizeof(dev_config)))
return -EFAULT;
@@ -742,8 +738,8 @@
return rc;
}
rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet);
- kfree(e_ctrl->cal_data.mapdata);
- kfree(e_ctrl->cal_data.map);
+ vfree(e_ctrl->cal_data.mapdata);
+ vfree(e_ctrl->cal_data.map);
e_ctrl->cal_data.num_data = 0;
e_ctrl->cal_data.num_map = 0;
CAM_DBG(CAM_EEPROM,
@@ -758,7 +754,7 @@
}
e_ctrl->cal_data.mapdata =
- kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL);
+ vzalloc(e_ctrl->cal_data.num_data);
if (!e_ctrl->cal_data.mapdata) {
rc = -ENOMEM;
CAM_ERR(CAM_EEPROM, "failed");
@@ -783,8 +779,12 @@
rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet);
rc = cam_eeprom_power_down(e_ctrl);
e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
- kfree(e_ctrl->cal_data.mapdata);
- kfree(e_ctrl->cal_data.map);
+ vfree(e_ctrl->cal_data.mapdata);
+ vfree(e_ctrl->cal_data.map);
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
+ power_info->power_setting = NULL;
+ power_info->power_down_setting = NULL;
e_ctrl->cal_data.num_data = 0;
e_ctrl->cal_data.num_map = 0;
break;
@@ -795,11 +795,13 @@
power_down:
cam_eeprom_power_down(e_ctrl);
memdata_free:
- kfree(e_ctrl->cal_data.mapdata);
+ vfree(e_ctrl->cal_data.mapdata);
error:
kfree(power_info->power_setting);
kfree(power_info->power_down_setting);
- kfree(e_ctrl->cal_data.map);
+ power_info->power_setting = NULL;
+ power_info->power_down_setting = NULL;
+ vfree(e_ctrl->cal_data.map);
e_ctrl->cal_data.num_data = 0;
e_ctrl->cal_data.num_map = 0;
e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
@@ -834,6 +836,8 @@
kfree(power_info->power_setting);
kfree(power_info->power_down_setting);
+ power_info->power_setting = NULL;
+ power_info->power_down_setting = NULL;
}
e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
@@ -852,8 +856,14 @@
struct cam_eeprom_query_cap_t eeprom_cap = {0};
struct cam_control *cmd = (struct cam_control *)arg;
- if (!e_ctrl) {
- CAM_ERR(CAM_EEPROM, "e_ctrl is NULL");
+ if (!e_ctrl || !cmd) {
+ CAM_ERR(CAM_EEPROM, "Invalid Arguments");
+ return -EINVAL;
+ }
+
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_EEPROM, "Invalid handle type: %d",
+ cmd->handle_type);
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
index 6120e02..f9411fc 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
@@ -29,6 +29,12 @@
return -EINVAL;
}
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_FLASH, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+
mutex_lock(&(fctrl->flash_mutex));
switch (cmd->op_code) {
case CAM_ACQUIRE_DEV: {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
index c792be4..dfcb9fc 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
@@ -636,7 +636,11 @@
void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl)
{
- int rc;
+ int rc = 0;
+ struct cam_ois_soc_private *soc_private =
+ (struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+ struct cam_sensor_power_ctrl_t *power_info =
+ &soc_private->power_info;
if (o_ctrl->cam_ois_state == CAM_OIS_INIT)
return;
@@ -645,6 +649,7 @@
rc = cam_ois_power_down(o_ctrl);
if (rc < 0)
CAM_ERR(CAM_OIS, "OIS Power down failed");
+ o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
}
if (o_ctrl->cam_ois_state >= CAM_OIS_ACQUIRE) {
@@ -656,6 +661,11 @@
o_ctrl->bridge_intf.session_hdl = -1;
}
+ kfree(power_info->power_setting);
+ kfree(power_info->power_down_setting);
+ power_info->power_setting = NULL;
+ power_info->power_down_setting = NULL;
+
o_ctrl->cam_ois_state = CAM_OIS_INIT;
}
@@ -672,8 +682,14 @@
struct cam_ois_query_cap_t ois_cap = {0};
struct cam_control *cmd = (struct cam_control *)arg;
- if (!o_ctrl) {
- CAM_ERR(CAM_OIS, "e_ctrl is NULL");
+ if (!o_ctrl || !arg) {
+ CAM_ERR(CAM_OIS, "Invalid arguments");
+ return -EINVAL;
+ }
+
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_OIS, "Invalid handle type: %d",
+ cmd->handle_type);
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index e9dd1ad..2133932 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -518,6 +518,8 @@
kfree(power_info->power_setting);
kfree(power_info->power_down_setting);
+ power_info->power_setting = NULL;
+ power_info->power_down_setting = NULL;
s_ctrl->streamon_count = 0;
s_ctrl->streamoff_count = 0;
@@ -568,6 +570,14 @@
return -EINVAL;
}
+ if (cmd->op_code != CAM_SENSOR_PROBE_CMD) {
+ if (cmd->handle_type != CAM_HANDLE_USER_POINTER) {
+ CAM_ERR(CAM_SENSOR, "Invalid handle type: %d",
+ cmd->handle_type);
+ return -EINVAL;
+ }
+ }
+
mutex_lock(&(s_ctrl->cam_sensor_mutex));
switch (cmd->op_code) {
case CAM_SENSOR_PROBE_CMD: {
@@ -576,24 +586,6 @@
"Already Sensor Probed in the slot");
break;
}
- /* Allocate memory for power up setting */
- pu = kzalloc(sizeof(struct cam_sensor_power_setting) *
- MAX_POWER_CONFIG, GFP_KERNEL);
- if (!pu) {
- rc = -ENOMEM;
- goto release_mutex;
- }
-
- pd = kzalloc(sizeof(struct cam_sensor_power_setting) *
- MAX_POWER_CONFIG, GFP_KERNEL);
- if (!pd) {
- kfree(pu);
- rc = -ENOMEM;
- goto release_mutex;
- }
-
- power_info->power_setting = pu;
- power_info->power_down_setting = pd;
if (cmd->handle_type ==
CAM_HANDLE_MEM_HANDLE) {
@@ -607,8 +599,12 @@
} else {
CAM_ERR(CAM_SENSOR, "Invalid Command Type: %d",
cmd->handle_type);
+ return -EINVAL;
}
+ pu = power_info->power_setting;
+ pd = power_info->power_down_setting;
+
/* Parse and fill vreg params for powerup settings */
rc = msm_camera_fill_vreg_params(
&s_ctrl->soc_info,
@@ -721,8 +717,9 @@
s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
CAM_INFO(CAM_SENSOR,
- "CAM_ACQUIRE_DEV Success, sensor_id:0x%x",
- s_ctrl->sensordata->slave_info.sensor_id);
+ "CAM_ACQUIRE_DEV Success, sensor_id:0x%x,sensor_slave_addr:0x%x",
+ s_ctrl->sensordata->slave_info.sensor_id,
+ s_ctrl->sensordata->slave_info.sensor_slave_addr);
}
break;
case CAM_RELEASE_DEV: {
@@ -761,8 +758,9 @@
s_ctrl->sensor_state = CAM_SENSOR_INIT;
CAM_INFO(CAM_SENSOR,
- "CAM_RELEASE_DEV Success, sensor_id:0x%x",
- s_ctrl->sensordata->slave_info.sensor_id);
+ "CAM_RELEASE_DEV Success, sensor_id:0x%x,sensor_slave_addr:0x%x",
+ s_ctrl->sensordata->slave_info.sensor_id,
+ s_ctrl->sensordata->slave_info.sensor_slave_addr);
s_ctrl->streamon_count = 0;
s_ctrl->streamoff_count = 0;
}
@@ -801,8 +799,9 @@
}
s_ctrl->sensor_state = CAM_SENSOR_START;
CAM_INFO(CAM_SENSOR,
- "CAM_START_DEV Success, sensor_id:0x%x",
- s_ctrl->sensordata->slave_info.sensor_id);
+ "CAM_START_DEV Success, sensor_id:0x%x,sensor_slave_addr:0x%x",
+ s_ctrl->sensordata->slave_info.sensor_id,
+ s_ctrl->sensordata->slave_info.sensor_slave_addr);
}
break;
case CAM_STOP_DEV: {
@@ -827,8 +826,9 @@
cam_sensor_release_resource(s_ctrl);
s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
CAM_INFO(CAM_SENSOR,
- "CAM_STOP_DEV Success, sensor_id:0x%x",
- s_ctrl->sensordata->slave_info.sensor_id);
+ "CAM_STOP_DEV Success, sensor_id:0x%x,sensor_slave_addr:0x%x",
+ s_ctrl->sensordata->slave_info.sensor_id,
+ s_ctrl->sensordata->slave_info.sensor_slave_addr);
}
break;
case CAM_CONFIG_DEV: {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
index 2c1f520..1ec7d9a 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -33,6 +33,7 @@
cci_ctrl.cci_info = cci_client;
cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = addr_type;
+ cci_ctrl.cfg.cci_i2c_read_cfg.data_type = data_type;
cci_ctrl.cfg.cci_i2c_read_cfg.data = buf;
cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = data_type;
rc = v4l2_subdev_call(cci_client->cci_subdev,
@@ -59,6 +60,7 @@
int32_t cam_camera_cci_i2c_read_seq(struct cam_sensor_cci_client *cci_client,
uint32_t addr, uint8_t *data,
enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type,
uint32_t num_byte)
{
int32_t rc = -EFAULT;
@@ -67,6 +69,7 @@
struct cam_cci_ctrl cci_ctrl;
if ((addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
+ || (data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
|| (num_byte > I2C_REG_DATA_MAX)) {
CAM_ERR(CAM_SENSOR, "addr_type %d num_byte %d", addr_type,
num_byte);
@@ -81,6 +84,7 @@
cci_ctrl.cci_info = cci_client;
cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = addr_type;
+ cci_ctrl.cfg.cci_i2c_read_cfg.data_type = data_type;
cci_ctrl.cfg.cci_i2c_read_cfg.data = buf;
cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = num_byte;
cci_ctrl.status = -EFAULT;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
index 7cddcf9..e79fffb 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,7 @@
* @addr: I2c address
* @data: I2C data
* @addr_type: I2c address type
+ * @data_type: I2c data type
* @num_byte: number of bytes
*
* This API handles CCI sequential read
@@ -53,6 +54,7 @@
int32_t cam_camera_cci_i2c_read_seq(struct cam_sensor_cci_client *client,
uint32_t addr, uint8_t *data,
enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type,
uint32_t num_byte);
/**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index 89aad4e..ed490fd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -69,11 +69,12 @@
int32_t camera_io_dev_read_seq(struct camera_io_master *io_master_info,
uint32_t addr, uint8_t *data,
- enum camera_sensor_i2c_type addr_type, int32_t num_bytes)
+ enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type, int32_t num_bytes)
{
if (io_master_info->master_type == CCI_MASTER) {
return cam_camera_cci_i2c_read_seq(io_master_info->cci_client,
- addr, data, addr_type, num_bytes);
+ addr, data, addr_type, data_type, num_bytes);
} else if (io_master_info->master_type == I2C_MASTER) {
return cam_qup_i2c_read_seq(io_master_info->client,
addr, data, addr_type, num_bytes);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
index ec5ed25..f1143c8 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -52,6 +52,7 @@
* @io_master_info: I2C/SPI master information
* @addr: I2C address
* @data: I2C data
+ * @addr_type: I2C addr type
* @data_type: I2C data type
* @num_bytes: number of bytes
*
@@ -60,6 +61,7 @@
int32_t camera_io_dev_read_seq(struct camera_io_master *io_master_info,
uint32_t addr, uint8_t *data,
enum camera_sensor_i2c_type addr_type,
+ enum camera_sensor_i2c_type data_type,
int32_t num_bytes);
/**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
index a399963..73a0cf7 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
@@ -860,8 +860,10 @@
return rc;
free_power_down_settings:
kfree(power_info->power_down_setting);
+ power_info->power_down_setting = NULL;
free_power_settings:
kfree(power_info->power_setting);
+ power_info->power_setting = NULL;
return rc;
}
@@ -1303,6 +1305,13 @@
for (index = 0; index < ctrl->power_setting_size; index++) {
CAM_DBG(CAM_SENSOR, "index: %d", index);
power_setting = &ctrl->power_setting[index];
+ if (!power_setting) {
+ CAM_ERR(CAM_SENSOR,
+ "Invalid power up settings for index %d",
+ index);
+ return -EINVAL;
+ }
+
CAM_DBG(CAM_SENSOR, "seq_type %d", power_setting->seq_type);
switch (power_setting->seq_type) {
@@ -1543,7 +1552,7 @@
if (ret)
CAM_ERR(CAM_SENSOR, "cannot set pin to suspend state");
cam_res_mgr_shared_pinctrl_select_state(false);
- pinctrl_put(ctrl->pinctrl_info.pinctrl);
+ devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
cam_res_mgr_shared_pinctrl_put();
}
@@ -1631,7 +1640,7 @@
{
int index = 0, ret = 0, num_vreg = 0, i;
struct cam_sensor_power_setting *pd = NULL;
- struct cam_sensor_power_setting *ps;
+ struct cam_sensor_power_setting *ps = NULL;
struct msm_camera_gpio_num_info *gpio_num_info = NULL;
CAM_DBG(CAM_SENSOR, "Enter");
@@ -1651,6 +1660,13 @@
for (index = 0; index < ctrl->power_down_setting_size; index++) {
CAM_DBG(CAM_SENSOR, "index %d", index);
pd = &ctrl->power_down_setting[index];
+ if (!pd) {
+ CAM_ERR(CAM_SENSOR,
+ "Invalid power down settings for index %d",
+ index);
+ return -EINVAL;
+ }
+
ps = NULL;
CAM_DBG(CAM_SENSOR, "type %d", pd->seq_type);
switch (pd->seq_type) {
@@ -1750,7 +1766,7 @@
CAM_ERR(CAM_SENSOR, "cannot set pin to suspend state");
cam_res_mgr_shared_pinctrl_select_state(false);
- pinctrl_put(ctrl->pinctrl_info.pinctrl);
+ devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
cam_res_mgr_shared_pinctrl_put();
}
diff --git a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
index ecfc566..5cd3008 100644
--- a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
@@ -3343,6 +3343,7 @@
rc = cam_populate_smmu_context_banks(dev, CAM_ARM_SMMU);
if (rc < 0) {
CAM_ERR(CAM_SMMU, "Error: populating context banks");
+ cam_smmu_release_cb(pdev);
return -ENOMEM;
}
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index cabc612..e5439d8 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -95,13 +95,22 @@
and also provides support for writing data in case of FLASH ROM.
Currently supports I2C, CCI and SPI protocol
+config MSM_ISP_V1
+ bool "QTI MSM Image Signal Processing interface support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Image Signal Processing interface module.
+ This module acts as a crossbar between CSID and VFE. Output
+ of any CID of CSID can be routed to of pix or raw
+ data interface in VFE.
+
config MSM_ISPIF
bool "QTI MSM Image Signal Processing interface support"
depends on MSMB_CAMERA
---help---
Enable support for Image Signal Processing interface module.
This module acts as a crossbar between CSID and VFE. Output
- of any CID of CSID can be routed to of of pix or raw
+ of any CID of CSID can be routed to of pix or raw
data interface in VFE.
config MSM_ISPIF_V1
diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c
index 22f4891..d32a0f2 100644
--- a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c
+++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c
@@ -20,8 +20,6 @@
#define EMPTY_QSEECOM_HANDLE NULL
#define QSEECOM_SBUFF_SIZE SZ_128K
-#define MSM_CAMERA_TZ_UTIL_VERBOSE
-
#define MSM_CAMERA_TZ_BOOT_PROTECTED (false)
/* Update version major number in case the HLOS-TA interface is changed*/
diff --git a/drivers/media/platform/msm/camera_v2/isp/Makefile b/drivers/media/platform/msm/camera_v2/isp/Makefile
index 621d81d..d36b1e2 100644
--- a/drivers/media/platform/msm/camera_v2/isp/Makefile
+++ b/drivers/media/platform/msm/camera_v2/isp/Makefile
@@ -1,5 +1,10 @@
ccflags-y += -Idrivers/media/platform/msm/camera_v2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ccflags-y += -Idrivers/media/platform/msm/camera_v2/common/
+ifeq ($(CONFIG_MSM_ISP_V1),y)
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp_32.o msm_buf_mgr.o msm_isp_util_32.o msm_isp_axi_util_32.o msm_isp_stats_util_32.o
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp32.o
+else
obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr.o msm_isp_util.o msm_isp_axi_util.o msm_isp_stats_util.o
obj-$(CONFIG_MSMB_CAMERA) += msm_isp48.o msm_isp47.o msm_isp46.o msm_isp44.o msm_isp40.o msm_isp.o
+endif
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index 691b492..6196a8c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -189,7 +189,9 @@
int i, rc = -1;
int ret;
struct msm_isp_buffer_mapped_info *mapped_info;
+#ifndef CONFIG_MSM_ISP_V1
uint32_t accu_length = 0;
+#endif
struct msm_isp_bufq *bufq = NULL;
bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
@@ -228,8 +230,12 @@
goto get_phy_err;
}
+#ifdef CONFIG_MSM_ISP_V1
+ mapped_info->paddr += qbuf_buf->planes[i].offset;
+#else
mapped_info->paddr += accu_length;
accu_length += qbuf_buf->planes[i].length;
+#endif
CDBG("%s: plane: %d addr:%pK\n",
__func__, i, (void *)mapped_info->paddr);
@@ -732,10 +738,17 @@
spin_lock_irqsave(&bufq->bufq_lock, flags);
buf_info->frame_id = frame_id;
+#ifdef CONFIG_MSM_ISP_V1
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED;
+ buf_info->tv = tv;
+ }
+#else
if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_NATIVE) {
buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED;
buf_info->tv = tv;
}
+#endif
spin_unlock_irqrestore(&bufq->bufq_lock, flags);
return 0;
}
@@ -1077,7 +1090,6 @@
}
}
-
/**
* msm_isp_buf_put_scratch() - Release scratch buffers
* @buf_mgr: The buffer structure for h/w
@@ -1220,7 +1232,6 @@
return rc;
}
-
static int msm_isp_init_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
const char *ctx_name)
{
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index 6da1360..a95917c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -12,21 +12,15 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-
#include "msm_isp32.h"
-#include "msm_isp_util.h"
-#include "msm_isp_axi_util.h"
-#include "msm_isp_stats_util.h"
-#include "msm_isp.h"
+#include "msm_isp_util_32.h"
+#include "msm_isp_axi_util_32.h"
+#include "msm_isp_stats_util_32.h"
+#include "msm_isp_32.h"
#include "msm.h"
#include "msm_camera_io_util.h"
-static const struct platform_device_id msm_vfe32_dev_id[] = {
- {"msm_vfe32", (kernel_ulong_t) &vfe32_hw_info},
- {}
-};
-
-#define VFE32_BURST_LEN 2
+#define VFE32_BURST_LEN 3
#define VFE32_UB_SIZE 1024
#define VFE32_UB_SIZE_32KB 2048
#define VFE32_EQUAL_SLICE_UB 194
@@ -36,7 +30,7 @@
#define VFE32_XBAR_BASE(idx) (0x40 + 0x4 * (idx / 4))
#define VFE32_XBAR_SHIFT(idx) ((idx % 4) * 8)
#define VFE32_PING_PONG_BASE(wm, ping_pong) \
- (VFE32_WM_BASE(wm) + 0x4 * (1 + ((~ping_pong) & 0x1)))
+ (VFE32_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1)))
static uint8_t stats_pingpong_offset_map[] = {
7, 8, 9, 10, 11, 12, 13};
@@ -60,16 +54,6 @@
{"csi_vfe_clk", -1},
};
-static uint32_t msm_vfe32_ub_reg_offset(struct vfe_device *vfe_dev, int idx)
-{
- return (VFE32_WM_BASE(idx) + 0x10);
-}
-
-static uint32_t msm_vfe32_get_ub_size(struct vfe_device *vfe_dev)
-{
- return MSM_ISP32_TOTAL_WM_UB;
-}
-
static int32_t msm_vfe32_init_qos_parms(struct vfe_device *vfe_dev,
struct msm_vfe_hw_init_parms *qos_parms,
struct msm_vfe_hw_init_parms *ds_parms)
@@ -284,8 +268,6 @@
pr_err("%s: vfe ioremap failed\n", __func__);
goto vfe_remap_failed;
}
- vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] =
- vfe_dev->vfe_base;
vfe_dev->vfe_vbif_base = ioremap(vfe_dev->vfe_vbif_mem->start,
resource_size(vfe_dev->vfe_vbif_mem));
@@ -330,14 +312,12 @@
static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev)
{
- msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1C);
- msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x20);
- disable_irq(vfe_dev->vfe_irq->start);
free_irq(vfe_dev->vfe_irq->start, vfe_dev);
tasklet_kill(&vfe_dev->vfe_tasklet);
- msm_isp_flush_tasklet(vfe_dev);
iounmap(vfe_dev->vfe_vbif_base);
vfe_dev->vfe_vbif_base = NULL;
+ iounmap(vfe_dev->vfe_base);
+ vfe_dev->vfe_base = NULL;
if (vfe_dev->vfe_clk_idx == 1)
msm_cam_clk_enable(&vfe_dev->pdev->dev,
msm_vfe32_1_clk_info, vfe_dev->vfe_clk,
@@ -346,9 +326,6 @@
msm_cam_clk_enable(&vfe_dev->pdev->dev,
msm_vfe32_2_clk_info, vfe_dev->vfe_clk,
ARRAY_SIZE(msm_vfe32_2_clk_info), 0);
- vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL;
- iounmap(vfe_dev->vfe_base);
- vfe_dev->vfe_base = NULL;
kfree(vfe_dev->vfe_clk);
regulator_disable(vfe_dev->fs_vfe);
msm_isp_deinit_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id);
@@ -369,7 +346,6 @@
ds_parms.entries = "ds-entries";
ds_parms.regs = "ds-regs";
ds_parms.settings = "ds-settings";
-
msm_vfe32_init_qos_parms(vfe_dev, &qos_parms, &ds_parms);
msm_vfe32_init_vbif_parms(vfe_dev, &vbif_parms);
@@ -381,6 +357,11 @@
msm_camera_io_w_mb(0x1CFFFFFF, vfe_dev->vfe_base + 0x20);
msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24);
msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x6FC);
+ msm_camera_io_w(0x10000000, vfe_dev->vfe_base + VFE32_RDI_BASE(1));
+ msm_camera_io_w(0x10000000, vfe_dev->vfe_base + VFE32_RDI_BASE(2));
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0));
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4));
}
@@ -396,13 +377,30 @@
static void msm_vfe32_process_reset_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1)
{
- if (irq_status1 & BIT(23))
+ if (irq_status1 & BIT(23)) {
+ if (vfe_dev->vfe_reset_timeout_processed == 1) {
+ pr_err("%s:vfe reset was processed.\n", __func__);
+ return;
+ }
complete(&vfe_dev->reset_complete);
+ }
}
static void msm_vfe32_process_halt_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1)
{
+ if (irq_status1 & (1 << 24))
+ msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x1D8);
+}
+
+static void msm_vfe32_process_epoch_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ if (!(irq_status0 & 0x18))
+ return;
+ if (irq_status0 & (1 << 3))
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
}
static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev,
@@ -416,10 +414,12 @@
ISP_DBG("%s: SOF IRQ\n", __func__);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- stream_count == 0) {
+ pix_stream_count == 0) {
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
- msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0, ts);
- msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev,
+ (1 << VFE_PIX_0));
+ msm_isp_update_framedrop_reg(vfe_dev, (1 << VFE_PIX_0));
}
}
}
@@ -485,7 +485,20 @@
static void msm_vfe32_get_overflow_mask(uint32_t *overflow_mask)
{
- *overflow_mask = 0x0;
+ *overflow_mask = 0x003FFF7E;
+}
+
+static void msm_vfe32_get_rdi_wm_mask(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask)
+{
+ *rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask;
+}
+
+static void msm_vfe32_get_irq_mask(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask)
+{
+ *irq0_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ *irq1_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20);
}
static void msm_vfe32_process_error_status(struct vfe_device *vfe_dev)
@@ -571,7 +584,7 @@
pr_err("%s: axi error\n", __func__);
}
-static void msm_vfe32_read_and_clear_irq_status(struct vfe_device *vfe_dev,
+static void msm_vfe32_read_irq_status(struct vfe_device *vfe_dev,
uint32_t *irq_status0, uint32_t *irq_status1)
{
*irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
@@ -589,122 +602,127 @@
msm_camera_io_r(vfe_dev->vfe_base + 0x7B4);
}
-static void msm_vfe32_read_irq_status(struct vfe_device *vfe_dev,
- uint32_t *irq_status0, uint32_t irq_status1)
-{
- *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
- *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x30);
-}
-
static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts)
{
- uint32_t rdi_status;
- enum msm_vfe_input_src i;
+ uint8_t input_src = 0x0;
if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000))
return;
if (irq_status0 & BIT(5)) {
- msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
- vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
- VFE_PIX_0);
- if (vfe_dev->axi_data.stream_update[VFE_PIX_0]) {
- rdi_status = msm_camera_io_r(vfe_dev->vfe_base +
- VFE32_XBAR_BASE(0));
- rdi_status |= msm_camera_io_r(vfe_dev->vfe_base +
- VFE32_XBAR_BASE(4));
-
- if ((rdi_status & BIT(7)) && (!(irq_status0 & 0x20)))
- return;
- }
- msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
- MSM_ISP_COMP_IRQ_REG_UPD);
+ msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE, VFE_PIX_0, ts);
+ input_src |= (1 << VFE_PIX_0);
+ }
+ if (irq_status1 & BIT(26)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_RAW_0, ts);
+ input_src |= (1 << VFE_RAW_0);
+ }
+ if (irq_status1 & BIT(27)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_RAW_1, ts);
+ input_src |= (1 << VFE_RAW_1);
+ }
+ if (irq_status1 & BIT(28)) {
+ msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_RAW_2, ts);
+ input_src |= (1 << VFE_RAW_2);
}
- for (i = VFE_RAW_0; i <= VFE_RAW_2; i++) {
- if (irq_status1 & BIT(26 + (i - VFE_RAW_0))) {
- msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
- msm_isp_axi_stream_update(vfe_dev, i, ts);
- msm_isp_update_framedrop_reg(vfe_dev, i);
-
- vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
- i);
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev, input_src);
+ if (atomic_read(&vfe_dev->stats_data.stats_update))
+ msm_isp_stats_stream_update(vfe_dev);
+ if (vfe_dev->axi_data.stream_update ||
+ atomic_read(&vfe_dev->stats_data.stats_update)) {
+ if (input_src & (1 << VFE_PIX_0)) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, (1 << VFE_PIX_0));
}
}
-
+ msm_isp_update_framedrop_reg(vfe_dev, input_src);
+ msm_isp_update_stats_framedrop_reg(vfe_dev);
msm_isp_update_error_frame_count(vfe_dev);
-
-}
-
-static void msm_vfe32_process_epoch_irq(struct vfe_device *vfe_dev,
- uint32_t irq_status0, uint32_t irq_status1,
- struct msm_isp_timestamp *ts)
-{
- /* Not supported */
-}
-
-static void msm_vfe32_reg_update(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src)
-{
- if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) {
- msm_camera_io_w_mb(0xF,
- vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0]
- + 0x260);
- msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260);
- } else if (!vfe_dev->is_split) {
- msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260);
+ if ((input_src & (1 << VFE_RAW_0)) ||
+ (input_src & (1 << VFE_RAW_1)) ||
+ (input_src & (1 << VFE_RAW_2))) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, input_src);
}
+
+ return;
+}
+
+static void msm_vfe32_reg_update(
+ struct vfe_device *vfe_dev, uint32_t input_src)
+{
+ msm_camera_io_w_mb(input_src, vfe_dev->vfe_base + 0x260);
}
static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev,
uint32_t first_start, uint32_t blocking)
{
- init_completion(&vfe_dev->reset_complete);
- msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
- return wait_for_completion_timeout(
- &vfe_dev->reset_complete, msecs_to_jiffies(50));
+ long rc = 0;
+ uint32_t irq_status1;
+
+ if (blocking) {
+ init_completion(&vfe_dev->reset_complete);
+ msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
+ vfe_dev->vfe_reset_timeout_processed = 0;
+ rc = wait_for_completion_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(500));
+ } else {
+ msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
+ }
+
+ if (blocking && rc <= 0) {
+ /*read ISP status register*/
+ irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x30);
+ pr_err("%s: handling vfe reset time out error. irq_status1 0x%x\n",
+ __func__, irq_status1);
+ if (irq_status1 & BIT(23)) {
+ pr_err("%s: vfe reset has done actually\n", __func__);
+ vfe_dev->vfe_reset_timeout_processed = 1;
+ return 1;
+ }
+ }
+ return rc;
}
static void msm_vfe32_axi_reload_wm(
- struct vfe_device *vfe_dev, void __iomem *vfe_base,
- uint32_t reload_mask)
+ struct vfe_device *vfe_dev, uint32_t reload_mask)
{
if (!vfe_dev->pdev->dev.of_node) {
/*vfe32 A-family: 8960*/
- msm_camera_io_w_mb(reload_mask, vfe_base + 0x38);
+ msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38);
} else {
/*vfe32 B-family: 8610*/
- msm_camera_io_w(0x0, vfe_base + 0x24);
- msm_camera_io_w(0x0, vfe_base + 0x28);
- msm_camera_io_w(0x0, vfe_base + 0x20);
- msm_camera_io_w_mb(0x1, vfe_base + 0x18);
- msm_camera_io_w(0x9AAAAAAA, vfe_base + 0x600);
- msm_camera_io_w(reload_mask, vfe_base + 0x38);
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w(0x1C800000, vfe_dev->vfe_base + 0x20);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18);
+ msm_camera_io_w(0x9AAAAAAA, vfe_dev->vfe_base + 0x600);
+ msm_camera_io_w(reload_mask, vfe_dev->vfe_base + 0x38);
}
}
-static void msm_vfe32_axi_enable_wm(void __iomem *vfe_base,
+static void msm_vfe32_axi_enable_wm(struct vfe_device *vfe_dev,
uint8_t wm_idx, uint8_t enable)
{
uint32_t val = msm_camera_io_r(
- vfe_base + VFE32_WM_BASE(wm_idx));
+ vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx));
if (enable)
val |= 0x1;
else
val &= ~0x1;
msm_camera_io_w_mb(val,
- vfe_base + VFE32_WM_BASE(wm_idx));
+ vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx));
}
static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index[vfe_idx];
+ stream_info->comp_mask_index;
uint32_t irq_mask;
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
@@ -721,9 +739,7 @@
static void msm_vfe32_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index[vfe_idx];
+ uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
uint32_t irq_mask;
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
@@ -739,10 +755,9 @@
struct msm_vfe_axi_stream *stream_info)
{
uint32_t irq_mask;
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- irq_mask |= BIT(stream_info->wm[vfe_idx][0] + 6);
+ irq_mask |= BIT(stream_info->wm[0] + 6);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
}
@@ -750,31 +765,40 @@
struct msm_vfe_axi_stream *stream_info)
{
uint32_t irq_mask;
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- irq_mask &= ~BIT(stream_info->wm[vfe_idx][0] + 6);
+ irq_mask &= ~BIT(stream_info->wm[0] + 6);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
}
static void msm_vfe32_cfg_framedrop(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
- uint32_t framedrop_period)
+ struct msm_vfe_axi_stream *stream_info)
{
- void __iomem *vfe_base = vfe_dev->vfe_base;
+ uint32_t framedrop_pattern = 0, framedrop_period = 0;
+
+ if (stream_info->runtime_init_frame_drop == 0) {
+ framedrop_pattern = stream_info->framedrop_pattern;
+ framedrop_period = stream_info->framedrop_period;
+ }
+
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->runtime_burst_frame_count == 0) {
+ framedrop_pattern = 0;
+ framedrop_period = 0;
+ }
if (stream_info->stream_src == PIX_ENCODER) {
- msm_camera_io_w(framedrop_period - 1, vfe_base + 0x504);
- msm_camera_io_w(framedrop_period - 1, vfe_base + 0x508);
- msm_camera_io_w(framedrop_pattern, vfe_base + 0x50C);
- msm_camera_io_w(framedrop_pattern, vfe_base + 0x510);
+ msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x504);
+ msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x508);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x50C);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x510);
} else if (stream_info->stream_src == PIX_VIEWFINDER) {
- msm_camera_io_w(framedrop_period - 1, vfe_base + 0x514);
- msm_camera_io_w(framedrop_period - 1, vfe_base + 0x518);
- msm_camera_io_w(framedrop_pattern, vfe_base + 0x51C);
- msm_camera_io_w(framedrop_pattern, vfe_base + 0x520);
+ msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x514);
+ msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x518);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x51C);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x520);
}
- msm_camera_io_w_mb(0x1, vfe_base + 0x260);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x260);
}
static void msm_vfe32_clear_framedrop(struct vfe_device *vfe_dev,
@@ -877,6 +901,7 @@
struct msm_vfe_pix_cfg *pix_cfg)
{
pr_err("%s: Fetch engine not supported\n", __func__);
+ return;
}
static void msm_vfe32_cfg_camif(struct vfe_device *vfe_dev,
@@ -924,6 +949,7 @@
pr_err("%s: Unsupported input mux %d\n",
__func__, pix_cfg->input_mux);
}
+ return;
}
static void msm_vfe32_update_camif_state(
@@ -938,19 +964,20 @@
if (update_state == ENABLE_CAMIF) {
val = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- val |= 0x1;
+ val |= 0x19;
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x1C);
-
+ msm_camera_io_w_mb(0xA, vfe_dev->vfe_base + 0x200);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x1E4);
bus_en =
((vfe_dev->axi_data.src_info[
VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
vfe_en =
((vfe_dev->axi_data.src_info[
- VFE_PIX_0].stream_count > 0) ? 1 : 0);
+ VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
msm_camera_io_w(val, vfe_dev->vfe_base + 0x1E4);
+ msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x1E0);
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1E0);
vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
} else if (update_state == DISABLE_CAMIF) {
@@ -990,17 +1017,16 @@
uint8_t plane_idx)
{
uint32_t val;
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
if (!stream_info->frame_based) {
/*WR_IMAGE_SIZE*/
val =
((msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[vfe_idx][plane_idx].
+ stream_info->plane_cfg[plane_idx].
output_width)+1)/2 - 1) << 16 |
- (stream_info->plane_cfg[vfe_idx][plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
@@ -1008,9 +1034,9 @@
val =
msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[vfe_idx][plane_idx].
+ stream_info->plane_cfg[plane_idx].
output_stride) << 16 |
- (stream_info->plane_cfg[vfe_idx][plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
} else {
@@ -1018,12 +1044,13 @@
val =
msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[vfe_idx][plane_idx].
+ stream_info->plane_cfg[plane_idx].
output_width) << 16 |
- (stream_info->plane_cfg[vfe_idx][plane_idx].
+ (stream_info->plane_cfg[plane_idx].
output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
}
+ return;
}
static void msm_vfe32_axi_clear_wm_reg(
@@ -1031,22 +1058,24 @@
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
uint32_t val = 0;
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+
+ /* FRAME BASED */
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base);
/*WR_IMAGE_SIZE*/
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
/*WR_BUFFER_CFG*/
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ return;
}
static void msm_vfe32_axi_cfg_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_info->plane_cfg[vfe_idx][plane_idx];
- uint8_t wm = stream_info->wm[vfe_idx][plane_idx];
+ &stream_info->plane_cfg[plane_idx];
+ uint8_t wm = stream_info->wm[plane_idx];
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
@@ -1093,14 +1122,14 @@
xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm));
xbar_reg_cfg |= (xbar_cfg << VFE32_XBAR_SHIFT(wm));
msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ return;
}
static void msm_vfe32_axi_clear_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- uint8_t wm = stream_info->wm[vfe_idx][plane_idx];
+ uint8_t wm = stream_info->wm[plane_idx];
uint32_t xbar_reg_cfg = 0;
xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
@@ -1108,21 +1137,95 @@
msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
}
-static void msm_vfe32_update_ping_pong_addr(void __iomem *vfe_base,
- uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr,
- int32_t buf_size)
+static void msm_vfe32_cfg_axi_ub_equal_default(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t total_image_size = 0;
+ uint32_t num_used_wms = 0;
+ uint32_t prop_size = 0;
+ uint32_t wm_ub_size;
+ uint64_t delta;
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i] > 0) {
+ num_used_wms++;
+ total_image_size += axi_data->wm_image_size[i];
+ }
+ }
+ prop_size = MSM_ISP32_TOTAL_WM_UB -
+ axi_data->hw_info->min_wm_ub * num_used_wms;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i]) {
+ delta =
+ (uint64_t)(axi_data->wm_image_size[i] *
+ prop_size);
+ do_div(delta, total_image_size);
+ wm_ub_size = axi_data->hw_info->min_wm_ub +
+ (uint32_t)delta;
+ msm_camera_io_w(ub_offset << 16 |
+ (wm_ub_size - 1), vfe_dev->vfe_base +
+ VFE32_WM_BASE(i) + 0xC);
+ ub_offset += wm_ub_size;
+ } else {
+ msm_camera_io_w(0,
+ vfe_dev->vfe_base + VFE32_WM_BASE(i) + 0xC);
+ }
+ }
+}
+
+static void msm_vfe32_cfg_axi_ub_equal_slicing(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ uint32_t final_ub_slice_size;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (ub_offset + VFE32_EQUAL_SLICE_UB > VFE32_AXI_SLICE_UB) {
+ final_ub_slice_size = VFE32_AXI_SLICE_UB - ub_offset;
+ msm_camera_io_w(ub_offset << 16 |
+ (final_ub_slice_size - 1), vfe_dev->vfe_base +
+ VFE32_WM_BASE(i) + 0xC);
+ ub_offset += final_ub_slice_size;
+ } else {
+ msm_camera_io_w(ub_offset << 16 |
+ (VFE32_EQUAL_SLICE_UB - 1), vfe_dev->vfe_base +
+ VFE32_WM_BASE(i) + 0xC);
+ ub_offset += VFE32_EQUAL_SLICE_UB;
+ }
+ }
+}
+
+static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ axi_data->wm_ub_cfg_policy = MSM_WM_UB_CFG_DEFAULT;
+ if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
+ msm_vfe32_cfg_axi_ub_equal_slicing(vfe_dev);
+ else
+ msm_vfe32_cfg_axi_ub_equal_default(vfe_dev);
+}
+
+static void msm_vfe32_update_ping_pong_addr(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, dma_addr_t paddr)
{
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- msm_camera_io_w(paddr32, vfe_base +
- VFE32_PING_PONG_BASE(wm_idx, pingpong_bit));
+ msm_camera_io_w(paddr32, vfe_dev->vfe_base +
+ VFE32_PING_PONG_BASE(wm_idx, pingpong_status));
}
static int msm_vfe32_axi_halt(struct vfe_device *vfe_dev, uint32_t blocking)
{
- uint32_t halt_mask;
uint32_t axi_busy_flag = true;
+ /* Keep only halt and restart mask */
+ msm_camera_io_w(0x01800000, vfe_dev->vfe_base + 0x20);
+ /*Clear IRQ Status */
+ msm_camera_io_w(0xFE7FFFFF, vfe_dev->vfe_base + 0x28);
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);
while (axi_busy_flag) {
if (msm_camera_io_r(
@@ -1130,10 +1233,27 @@
axi_busy_flag = false;
}
msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x1D8);
- halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20);
- halt_mask &= 0xFEFFFFFF;
- /* Disable AXI IRQ */
- msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x20);
+ return 0;
+}
+
+static int msm_vfe32_axi_restart(struct vfe_device *vfe_dev,
+ uint32_t blocking, uint32_t enable_camif)
+{
+ vfe_dev->hw_info->vfe_ops.core_ops.restore_irq_mask(vfe_dev);
+
+ /*Clear IRQ Status */
+ msm_camera_io_w(0xFE7FFFFF, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);
+ msm_camera_io_w_mb(0xA, vfe_dev->vfe_base + 0x200);
+ /* Start AXI */
+ msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x1D8);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, 0xF);
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+ if (enable_camif) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+ }
return 0;
}
@@ -1187,20 +1307,36 @@
}
static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev,
- uint32_t stats_mask, uint8_t comp_idx, uint8_t enable)
+ uint32_t stats_mask, uint8_t enable)
{
+ uint32_t i = 0;
+ atomic_t *stats_comp;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ stats_mask = stats_mask & 0x7F;
+
+ for (i = 0;
+ i < vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; i++) {
+ stats_comp = &stats_data->stats_comp_mask[i];
+ if (enable)
+ atomic_add(stats_mask, stats_comp);
+ else
+ atomic_sub(stats_mask, stats_comp);
+ ISP_DBG("%s: comp_mask: %x\n",
+ __func__, atomic_read(&stats_data->stats_comp_mask[i]));
+ }
+ return;
}
static void msm_vfe32_stats_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
- stream_info);
uint32_t irq_mask;
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- irq_mask |= BIT(STATS_IDX(stream_info->stream_handle[vfe_idx]) + 13);
+ irq_mask |= BIT(STATS_IDX(stream_info->stream_handle) + 13);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+ return;
}
static void msm_vfe32_stats_clear_wm_irq_mask(struct vfe_device *vfe_dev,
@@ -1211,18 +1347,21 @@
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
irq_mask &= ~(BIT(STATS_IDX(stream_info->stream_handle) + 13));
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+ return;
}
static void msm_vfe32_stats_cfg_wm_reg(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
/*Nothing to configure for VFE3.x*/
+ return;
}
static void msm_vfe32_stats_clear_wm_reg(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
/*Nothing to configure for VFE3.x*/
+ return;
}
static void msm_vfe32_stats_cfg_ub(struct vfe_device *vfe_dev)
@@ -1247,12 +1386,7 @@
msm_camera_io_w(ub_offset << 16 | (ub_size[i] - 1),
vfe_dev->vfe_base + VFE32_STATS_BASE(i) + 0x8);
}
-}
-
-static bool msm_vfe32_is_module_cfg_lock_needed(
- uint32_t reg_offset)
-{
- return false;
+ return;
}
static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev,
@@ -1294,15 +1428,13 @@
static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
- dma_addr_t paddr, uint32_t buf_sz)
+ dma_addr_t paddr)
{
- void __iomem *vfe_base = vfe_dev->vfe_base;
- int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
- stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- int stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
- msm_camera_io_w(paddr32, vfe_base +
+ int stats_idx = STATS_IDX(stream_info->stream_handle);
+
+ msm_camera_io_w(paddr32, vfe_dev->vfe_base +
VFE32_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
}
@@ -1361,28 +1493,6 @@
goto vfe_no_resource;
}
- if (!vfe_dev->pdev->dev.of_node)
- vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr");
- else
- vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe");
-
- if (!vfe_dev->iommu_ctx[0]) {
- pr_err("%s: no iommux ctx resource?\n", __func__);
- rc = -ENODEV;
- goto vfe_no_resource;
- }
-
- if (!vfe_dev->pdev->dev.of_node)
- vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc");
- else
- vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe");
-
- if (!vfe_dev->iommu_ctx[1]) {
- pr_err("%s: no iommux ctx resource?\n", __func__);
- rc = -ENODEV;
- goto vfe_no_resource;
- }
-
vfe_no_resource:
return rc;
}
@@ -1394,13 +1504,27 @@
*error_mask1 = 0x007FFFFF;
}
-struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = {
- .num_wm = 5,
+
+static void msm_vfe32_restore_irq_mask(struct vfe_device *vfe_dev)
+{
+ msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask0,
+ vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask1,
+ vfe_dev->vfe_base + 0x20);
+}
+
+static void msm_vfe32_get_halt_restart_mask(uint32_t *irq0_mask,
+ uint32_t *irq1_mask)
+{
+ *irq1_mask = 0x01800000;
+}
+
+static struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = {
+ .num_wm = 6,
.num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
.min_wm_ub = 64,
- .scratch_buf_range = SZ_32M,
};
static struct msm_vfe_stats_hardware_info msm_vfe32_stats_hw_info = {
@@ -1412,7 +1536,22 @@
1 << MSM_ISP_STATS_SKIN | 1 << MSM_ISP_STATS_BHIST,
.stats_ping_pong_offset = stats_pingpong_offset_map,
.num_stats_type = VFE32_NUM_STATS_TYPE,
- .num_stats_comp_mask = 0,
+ .num_stats_comp_mask = 1,
+};
+
+static struct v4l2_subdev_core_ops msm_vfe32_subdev_core_ops = {
+ .ioctl = msm_isp_ioctl,
+ .subscribe_event = msm_isp_subscribe_event,
+ .unsubscribe_event = msm_isp_unsubscribe_event,
+};
+
+static struct v4l2_subdev_ops msm_vfe32_subdev_ops = {
+ .core = &msm_vfe32_subdev_core_ops,
+};
+
+static struct v4l2_subdev_internal_ops msm_vfe32_internal_ops = {
+ .open = msm_isp_open_node,
+ .close = msm_isp_close_node,
};
struct msm_vfe_hardware_info vfe32_hw_info = {
@@ -1421,16 +1560,14 @@
.vfe_clk_idx = VFE32_CLK_IDX,
.vfe_ops = {
.irq_ops = {
- .read_and_clear_irq_status =
- msm_vfe32_read_and_clear_irq_status,
.read_irq_status = msm_vfe32_read_irq_status,
.process_camif_irq = msm_vfe32_process_camif_irq,
.process_reset_irq = msm_vfe32_process_reset_irq,
.process_halt_irq = msm_vfe32_process_halt_irq,
.process_reg_update = msm_vfe32_process_reg_update,
+ .process_epoch_irq = msm_vfe32_process_epoch_irq,
.process_axi_irq = msm_isp_process_axi_irq,
.process_stats_irq = msm_isp_process_stats_irq,
- .process_epoch_irq = msm_vfe32_process_epoch_irq,
},
.axi_ops = {
.reload_wm = msm_vfe32_axi_reload_wm,
@@ -1446,15 +1583,14 @@
.clear_wm_reg = msm_vfe32_axi_clear_wm_reg,
.cfg_wm_xbar_reg = msm_vfe32_axi_cfg_wm_xbar_reg,
.clear_wm_xbar_reg = msm_vfe32_axi_clear_wm_xbar_reg,
- .cfg_ub = msm_vfe47_cfg_axi_ub,
+ .cfg_ub = msm_vfe32_cfg_axi_ub,
.update_ping_pong_addr =
msm_vfe32_update_ping_pong_addr,
.get_comp_mask = msm_vfe32_get_comp_mask,
.get_wm_mask = msm_vfe32_get_wm_mask,
.get_pingpong_status = msm_vfe32_get_pingpong_status,
.halt = msm_vfe32_axi_halt,
- .ub_reg_offset = msm_vfe40_ub_reg_offset,
- .get_ub_size = msm_vfe40_get_ub_size,
+ .restart = msm_vfe32_axi_restart,
},
.core_ops = {
.reg_update = msm_vfe32_reg_update,
@@ -1469,13 +1605,13 @@
.release_hw = msm_vfe32_release_hardware,
.get_platform_data = msm_vfe32_get_platform_data,
.get_error_mask = msm_vfe32_get_error_mask,
- .process_error_status = msm_vfe32_process_error_status,
.get_overflow_mask = msm_vfe32_get_overflow_mask,
- .is_module_cfg_lock_needed =
- msm_vfe32_is_module_cfg_lock_needed,
- .ahb_clk_cfg = NULL,
- .set_bus_err_ign_mask = NULL,
- .get_bus_err_mask = NULL,
+ .get_rdi_wm_mask = msm_vfe32_get_rdi_wm_mask,
+ .get_irq_mask = msm_vfe32_get_irq_mask,
+ .restore_irq_mask = msm_vfe32_restore_irq_mask,
+ .get_halt_restart_mask =
+ msm_vfe32_get_halt_restart_mask,
+ .process_error_status = msm_vfe32_process_error_status,
},
.stats_ops = {
.get_stats_idx = msm_vfe32_get_stats_idx,
@@ -1493,47 +1629,12 @@
.get_wm_mask = msm_vfe32_stats_get_wm_mask,
.get_frame_id = msm_vfe32_stats_get_frame_id,
.get_pingpong_status = msm_vfe32_get_pingpong_status,
- .enable_stats_wm = NULL,
},
},
.dmi_reg_offset = 0x5A0,
.axi_hw_info = &msm_vfe32_axi_hw_info,
.stats_hw_info = &msm_vfe32_stats_hw_info,
+ .subdev_ops = &msm_vfe32_subdev_ops,
+ .subdev_internal_ops = &msm_vfe32_internal_ops,
};
EXPORT_SYMBOL(vfe32_hw_info);
-
-static const struct of_device_id msm_vfe32_dt_match[] = {
- {
- .compatible = "qcom,vfe32",
- .data = &vfe32_hw_info,
- },
- {}
-};
-
-MODULE_DEVICE_TABLE(of, msm_vfe32_dt_match);
-
-static struct platform_driver vfe32_driver = {
- .probe = vfe_hw_probe,
- .driver = {
- .name = "msm_vfe32",
- .owner = THIS_MODULE,
- .of_match_table = msm_vfe32_dt_match,
- },
- .id_table = msm_vfe32_dev_id,
-};
-
-static int __init msm_vfe32_init_module(void)
-{
- return platform_driver_register(&vfe32_driver);
-}
-
-static void __exit msm_vfe32_exit_module(void)
-{
- platform_driver_unregister(&vfe32_driver);
-}
-
-module_init(msm_vfe32_init_module);
-module_exit(msm_vfe32_exit_module);
-MODULE_DESCRIPTION("MSM VFE32 driver");
-MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 19b1aac..72ab3ba 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -889,8 +889,6 @@
msm_camera_io_w(temp | (framedrop_period - 1) << 2,
vfe_base + VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
}
-
- msm_camera_io_w_mb(0x1, vfe_base + 0x378);
}
static void msm_vfe40_clear_framedrop(struct vfe_device *vfe_dev,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.c
new file mode 100644
index 0000000..eada5fa
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.c
@@ -0,0 +1,557 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <linux/of_device.h>
+#include <linux/sched_clock.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+
+#include "msm_isp_32.h"
+#include "msm_isp_util_32.h"
+#include "msm_isp_axi_util_32.h"
+#include "msm_isp_stats_util_32.h"
+#include "msm_sd.h"
+#include "msm_isp32.h"
+
+static struct msm_sd_req_vb2_q vfe_vb2_ops;
+
+static const struct of_device_id msm_vfe_dt_match[] = {
+ {
+ .compatible = "qcom,vfe32",
+ .data = &vfe32_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe_dt_match);
+
+static const struct platform_device_id msm_vfe_dev_id[] = {
+ {"msm_vfe32", (kernel_ulong_t) &vfe32_hw_info},
+ {}
+};
+#define MAX_OVERFLOW_COUNTERS 29
+#define OVERFLOW_LENGTH 1024
+#define OVERFLOW_BUFFER_LENGTH 64
+static char stat_line[OVERFLOW_LENGTH];
+
+static struct msm_isp_buf_mgr vfe_buf_mgr;
+static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev,
+ struct msm_isp_bw_req_info *isp_req_hist);
+static char *stats_str[MAX_OVERFLOW_COUNTERS] = {
+ "imgmaster0_overflow_cnt",
+ "imgmaster1_overflow_cnt",
+ "imgmaster2_overflow_cnt",
+ "imgmaster3_overflow_cnt",
+ "imgmaster4_overflow_cnt",
+ "imgmaster5_overflow_cnt",
+ "imgmaster6_overflow_cnt",
+ "be_overflow_cnt",
+ "bg_overflow_cnt",
+ "bf_overflow_cnt",
+ "awb_overflow_cnt",
+ "rs_overflow_cnt",
+ "cs_overflow_cnt",
+ "ihist_overflow_cnt",
+ "skinbhist_overflow_cnt",
+ "bfscale_overflow_cnt",
+ "ISP_VFE0_client_info.active",
+ "ISP_VFE0_client_info.ab",
+ "ISP_VFE0_client_info.ib",
+ "ISP_VFE1_client_info.active",
+ "ISP_VFE1_client_info.ab",
+ "ISP_VFE1_client_info.ib",
+ "ISP_CPP_client_info.active",
+ "ISP_CPP_client_info.ab",
+ "ISP_CPP_client_info.ib",
+ "ISP_last_overflow.ab",
+ "ISP_last_overflow.ib",
+ "ISP_VFE_CLK_RATE",
+ "ISP_CPP_CLK_RATE",
+};
+
+#define MAX_DEPTH_BW_REQ_HISTORY 25
+#define MAX_BW_HISTORY_BUFF_LEN 6144
+#define MAX_BW_HISTORY_LINE_BUFF_LEN 512
+
+#define MAX_UB_INFO_BUFF_LEN 1024
+#define MAX_UB_INFO_LINE_BUFF_LEN 256
+
+static struct msm_isp_bw_req_info
+ msm_isp_bw_request_history[MAX_DEPTH_BW_REQ_HISTORY];
+static int msm_isp_bw_request_history_idx;
+static char bw_request_history_buff[MAX_BW_HISTORY_BUFF_LEN];
+static char ub_info_buffer[MAX_UB_INFO_BUFF_LEN];
+static spinlock_t req_history_lock;
+static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t vfe_debugfs_statistics_read(struct file *t_file,
+ char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ uint64_t *ptr;
+ char buffer[OVERFLOW_BUFFER_LENGTH] = {0};
+ struct vfe_device *vfe_dev = (struct vfe_device *)
+ t_file->private_data;
+ struct msm_isp_statistics *stats = vfe_dev->stats;
+
+ memset(stat_line, 0, sizeof(stat_line));
+ msm_isp_util_get_bandwidth_stats(vfe_dev, stats);
+ ptr = (uint64_t *)(stats);
+ for (i = 0; i < MAX_OVERFLOW_COUNTERS; i++) {
+ strlcat(stat_line, stats_str[i], sizeof(stat_line));
+ strlcat(stat_line, " ", sizeof(stat_line));
+ snprintf(buffer, sizeof(buffer), "%llu", ptr[i]);
+ strlcat(stat_line, buffer, sizeof(stat_line));
+ strlcat(stat_line, "\r\n", sizeof(stat_line));
+ }
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, stat_line, strlen(stat_line));
+}
+
+static ssize_t vfe_debugfs_statistics_write(struct file *t_file,
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct vfe_device *vfe_dev = (struct vfe_device *)
+ t_file->private_data;
+ struct msm_isp_statistics *stats = vfe_dev->stats;
+
+ memset(stats, 0, sizeof(struct msm_isp_statistics));
+
+ return sizeof(struct msm_isp_statistics);
+}
+
+static int bw_history_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t bw_history_read(struct file *t_file, char __user *t_char,
+ size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ char *out_buffer = bw_request_history_buff;
+ char line_buffer[MAX_BW_HISTORY_LINE_BUFF_LEN] = {0};
+ struct msm_isp_bw_req_info *isp_req_hist =
+ (struct msm_isp_bw_req_info *) t_file->private_data;
+
+ memset(out_buffer, 0, MAX_BW_HISTORY_BUFF_LEN);
+
+ snprintf(line_buffer, sizeof(line_buffer),
+ "Bus bandwidth request history in chronological order:\n");
+ strlcat(out_buffer, line_buffer, sizeof(bw_request_history_buff));
+
+ snprintf(line_buffer, sizeof(line_buffer),
+ "MSM_ISP_MIN_AB = %u, MSM_ISP_MIN_IB = %u\n\n",
+ MSM_ISP_MIN_AB, MSM_ISP_MIN_IB);
+ strlcat(out_buffer, line_buffer, sizeof(bw_request_history_buff));
+
+ for (i = 0; i < MAX_DEPTH_BW_REQ_HISTORY; i++) {
+ snprintf(line_buffer, sizeof(line_buffer),
+ "idx = %d, client = %u, timestamp = %llu, ab = %llu, ib = %llu\n"
+ "ISP0.active = %x, ISP0.ab = %llu, ISP0.ib = %llu\n"
+ "ISP1.active = %x, ISP1.ab = %llu, ISP1.ib = %llu\n"
+ "CPP.active = %x, CPP.ab = %llu, CPP.ib = %llu\n\n",
+ i, isp_req_hist[i].client, isp_req_hist[i].timestamp,
+ isp_req_hist[i].total_ab, isp_req_hist[i].total_ib,
+ isp_req_hist[i].client_info[0].active,
+ isp_req_hist[i].client_info[0].ab,
+ isp_req_hist[i].client_info[0].ib,
+ isp_req_hist[i].client_info[1].active,
+ isp_req_hist[i].client_info[1].ab,
+ isp_req_hist[i].client_info[1].ib,
+ isp_req_hist[i].client_info[2].active,
+ isp_req_hist[i].client_info[2].ab,
+ isp_req_hist[i].client_info[2].ib);
+ strlcat(out_buffer, line_buffer,
+ sizeof(bw_request_history_buff));
+ }
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, out_buffer, strlen(out_buffer));
+}
+
+static ssize_t bw_history_write(struct file *t_file,
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct msm_isp_bw_req_info *isp_req_hist =
+ (struct msm_isp_bw_req_info *) t_file->private_data;
+
+ memset(isp_req_hist, 0, sizeof(msm_isp_bw_request_history));
+ msm_isp_bw_request_history_idx = 0;
+ return sizeof(msm_isp_bw_request_history);
+}
+
+static int ub_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t ub_info_read(struct file *t_file, char __user *t_char,
+ size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ char *out_buffer = ub_info_buffer;
+ char line_buffer[MAX_UB_INFO_LINE_BUFF_LEN] = {0};
+ struct vfe_device *vfe_dev =
+ (struct vfe_device *) t_file->private_data;
+ struct msm_isp_ub_info *ub_info = vfe_dev->ub_info;
+
+ memset(out_buffer, 0, MAX_UB_INFO_LINE_BUFF_LEN);
+ snprintf(line_buffer, sizeof(line_buffer),
+ "wm_ub_policy_type = %d\n"
+ "num_wm = %d\n"
+ "wm_ub = %d\n",
+ ub_info->policy, ub_info->num_wm, ub_info->wm_ub);
+ strlcat(out_buffer, line_buffer,
+ sizeof(ub_info_buffer));
+ for (i = 0; i < ub_info->num_wm; i++) {
+ snprintf(line_buffer, sizeof(line_buffer),
+ "data[%d] = 0x%x, addr[%d] = 0x%llx\n",
+ i, ub_info->data[i], i, ub_info->addr[i]);
+ strlcat(out_buffer, line_buffer,
+ sizeof(ub_info_buffer));
+ }
+
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, out_buffer, strlen(out_buffer));
+}
+
+static ssize_t ub_info_write(struct file *t_file,
+ const char __user *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct vfe_device *vfe_dev =
+ (struct vfe_device *) t_file->private_data;
+ struct msm_isp_ub_info *ub_info = vfe_dev->ub_info;
+
+ memset(ub_info, 0, sizeof(struct msm_isp_ub_info));
+
+ return sizeof(struct msm_isp_ub_info);
+}
+
+static const struct file_operations vfe_debugfs_error = {
+ .open = vfe_debugfs_statistics_open,
+ .read = vfe_debugfs_statistics_read,
+ .write = vfe_debugfs_statistics_write,
+};
+
+static const struct file_operations bw_history_ops = {
+ .open = bw_history_open,
+ .read = bw_history_read,
+ .write = bw_history_write,
+};
+
+static const struct file_operations ub_info_ops = {
+ .open = ub_info_open,
+ .read = ub_info_read,
+ .write = ub_info_write,
+};
+
+static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev,
+ struct msm_isp_bw_req_info *isp_req_hist)
+{
+ struct dentry *debugfs_base;
+ char dirname[32] = {0};
+
+ snprintf(dirname, sizeof(dirname), "msm_isp%d", vfe_dev->pdev->id);
+ debugfs_base = debugfs_create_dir(dirname, NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+ if (!debugfs_create_file("stats", 0644, debugfs_base,
+ vfe_dev, &vfe_debugfs_error))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("bw_req_history", 0644,
+ debugfs_base, isp_req_hist, &bw_history_ops))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("ub_info", 0644,
+ debugfs_base, vfe_dev, &ub_info_ops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+void msm_isp_update_req_history(uint32_t client, uint64_t ab,
+ uint64_t ib,
+ struct msm_isp_bandwidth_info *client_info,
+ unsigned long long ts)
+{
+ int i;
+
+ spin_lock(&req_history_lock);
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].client =
+ client;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].timestamp =
+ ts;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ab =
+ ab;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ib =
+ ib;
+
+ for (i = 0; i < MAX_ISP_CLIENT; i++) {
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].
+ client_info[i].active = client_info[i].active;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].
+ client_info[i].ab = client_info[i].ab;
+ msm_isp_bw_request_history[msm_isp_bw_request_history_idx].
+ client_info[i].ib = client_info[i].ib;
+ }
+
+ msm_isp_bw_request_history_idx = (msm_isp_bw_request_history_idx + 1)
+ % MAX_DEPTH_BW_REQ_HISTORY;
+ spin_unlock(&req_history_lock);
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_isp_dqevent(struct file *file, struct v4l2_fh *vfh, void *arg)
+{
+ long rc;
+
+ if (is_compat_task()) {
+ struct msm_isp32_event_data32 *event_data32;
+ struct msm_isp32_event_data *event_data;
+ struct v4l2_event isp_event;
+ struct v4l2_event *isp_event_user;
+
+ memset(&isp_event, 0, sizeof(isp_event));
+ rc = v4l2_event_dequeue(vfh, &isp_event,
+ file->f_flags & O_NONBLOCK);
+ if (rc)
+ return rc;
+ event_data = (struct msm_isp32_event_data *)
+ isp_event.u.data;
+ isp_event_user = (struct v4l2_event *)arg;
+ memcpy(isp_event_user, &isp_event,
+ sizeof(*isp_event_user));
+ event_data32 = (struct msm_isp32_event_data32 *)
+ isp_event_user->u.data;
+ memset(event_data32, 0,
+ sizeof(struct msm_isp32_event_data32));
+ event_data32->timestamp.tv_sec =
+ event_data->timestamp.tv_sec;
+ event_data32->timestamp.tv_usec =
+ event_data->timestamp.tv_usec;
+ event_data32->mono_timestamp.tv_sec =
+ event_data->mono_timestamp.tv_sec;
+ event_data32->mono_timestamp.tv_usec =
+ event_data->mono_timestamp.tv_usec;
+ event_data32->input_intf = event_data->input_intf;
+ event_data32->frame_id = event_data->frame_id;
+ memcpy(&(event_data32->u), &(event_data->u),
+ sizeof(event_data32->u));
+ } else {
+ rc = v4l2_event_dequeue(vfh, arg,
+ file->f_flags & O_NONBLOCK);
+ }
+ return rc;
+}
+#else
+static long msm_isp_dqevent(struct file *file, struct v4l2_fh *vfh, void *arg)
+{
+ return v4l2_event_dequeue(vfh, arg,
+ file->f_flags & O_NONBLOCK);
+}
+#endif
+
+static long msm_isp_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+
+ switch (cmd) {
+ case VIDIOC_DQEVENT: {
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+ return msm_isp_dqevent(file, vfh, arg);
+ }
+ break;
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+}
+
+static long msm_isp_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_isp_subdev_do_ioctl);
+}
+
+static struct v4l2_file_operations msm_isp_v4l2_subdev_fops = {
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = msm_isp_subdev_fops_ioctl,
+#endif
+ .unlocked_ioctl = msm_isp_subdev_fops_ioctl
+};
+
+static int vfe_probe(struct platform_device *pdev)
+{
+ struct vfe_device *vfe_dev;
+ /*struct msm_cam_subdev_info sd_info;*/
+ const struct of_device_id *match_dev;
+ int rc = 0;
+
+ vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL);
+ if (!vfe_dev) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ vfe_dev->stats = kzalloc(sizeof(struct msm_isp_statistics), GFP_KERNEL);
+ if (!vfe_dev->stats) {
+ rc = -ENOMEM;
+ goto probe_fail1;
+ }
+
+ vfe_dev->ub_info = kzalloc(sizeof(struct msm_isp_ub_info), GFP_KERNEL);
+ if (!vfe_dev->ub_info) {
+ rc = -ENOMEM;
+ goto probe_fail2;
+ }
+ if (pdev->dev.of_node) {
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ match_dev = of_match_device(msm_vfe_dt_match, &pdev->dev);
+ if (!match_dev) {
+ pr_err("%s: No vfe hardware info\n", __func__);
+ rc = -EINVAL;
+ goto probe_fail3;
+ }
+ vfe_dev->hw_info =
+ (struct msm_vfe_hardware_info *) match_dev->data;
+ } else {
+ vfe_dev->hw_info = (struct msm_vfe_hardware_info *)
+ platform_get_device_id(pdev)->driver_data;
+ }
+
+ if (!vfe_dev->hw_info) {
+ pr_err("%s: No vfe hardware info\n", __func__);
+ rc = -EINVAL;
+ goto probe_fail3;
+ }
+ ISP_DBG("%s: device id = %d\n", __func__, pdev->id);
+
+ vfe_dev->pdev = pdev;
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.get_platform_data(vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to get platform resources\n", __func__);
+ rc = -ENOMEM;
+ goto probe_fail3;
+ }
+
+ INIT_LIST_HEAD(&vfe_dev->tasklet_q);
+ tasklet_init(&vfe_dev->vfe_tasklet,
+ msm_isp_do_tasklet, (unsigned long)vfe_dev);
+
+ v4l2_subdev_init(&vfe_dev->subdev.sd, vfe_dev->hw_info->subdev_ops);
+ vfe_dev->subdev.sd.internal_ops =
+ vfe_dev->hw_info->subdev_internal_ops;
+ snprintf(vfe_dev->subdev.sd.name,
+ ARRAY_SIZE(vfe_dev->subdev.sd.name),
+ "vfe");
+ vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+ v4l2_set_subdevdata(&vfe_dev->subdev.sd, vfe_dev);
+ platform_set_drvdata(pdev, &vfe_dev->subdev.sd);
+ mutex_init(&vfe_dev->realtime_mutex);
+ mutex_init(&vfe_dev->core_mutex);
+ spin_lock_init(&vfe_dev->tasklet_lock);
+ spin_lock_init(&vfe_dev->shared_data_lock);
+ spin_lock_init(&req_history_lock);
+ media_entity_pads_init(&vfe_dev->subdev.sd.entity, 0, NULL);
+ vfe_dev->subdev.sd.entity.function = MSM_CAMERA_SUBDEV_VFE;
+ vfe_dev->subdev.sd.entity.name = pdev->name;
+ vfe_dev->subdev.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x2;
+ rc = msm_sd_register(&vfe_dev->subdev);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto probe_fail3;
+ }
+
+ msm_isp_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
+ msm_isp_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
+ msm_isp_v4l2_subdev_fops.release = v4l2_subdev_fops.release;
+ msm_isp_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll;
+
+ vfe_dev->subdev.sd.devnode->fops = &msm_isp_v4l2_subdev_fops;
+
+ vfe_dev->buf_mgr = &vfe_buf_mgr;
+ v4l2_subdev_notify(&vfe_dev->subdev.sd,
+ MSM_SD_NOTIFY_REQ_CB, &vfe_vb2_ops);
+ rc = msm_isp_create_isp_buf_mgr(vfe_dev->buf_mgr,
+ &vfe_vb2_ops, &pdev->dev,
+ vfe_dev->hw_info->axi_hw_info->scratch_buf_range);
+ if (rc < 0) {
+ pr_err("%s: Unable to create buffer manager\n", __func__);
+ rc = -EINVAL;
+ goto probe_fail3;
+ }
+ msm_isp_enable_debugfs(vfe_dev, msm_isp_bw_request_history);
+
+ vfe_dev->buf_mgr->init_done = 1;
+ vfe_dev->vfe_open_cnt = 0;
+ return rc;
+
+probe_fail3:
+ kfree(vfe_dev->ub_info);
+probe_fail2:
+ kfree(vfe_dev->stats);
+probe_fail1:
+ kfree(vfe_dev);
+end:
+ return rc;
+}
+
+static struct platform_driver vfe_driver = {
+ .probe = vfe_probe,
+ .driver = {
+ .name = "msm_vfe",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe_dt_match,
+ },
+ .id_table = msm_vfe_dev_id,
+};
+
+static int __init msm_vfe_init_module(void)
+{
+ return platform_driver_register(&vfe_driver);
+}
+
+static void __exit msm_vfe_exit_module(void)
+{
+ platform_driver_unregister(&vfe_driver);
+}
+
+module_init(msm_vfe_init_module);
+module_exit(msm_vfe_exit_module);
+MODULE_DESCRIPTION("MSM VFE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.h
new file mode 100644
index 0000000..cbe92fa
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.h
@@ -0,0 +1,599 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_VFE_H__
+#define __MSM_VFE_H__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_isp.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+
+#include "msm_buf_mgr.h"
+
+#define VFE40_8974V1_VERSION 0x10000018
+#define VFE40_8974V2_VERSION 0x1001001A
+#define VFE40_8974V3_VERSION 0x1001001B
+#define VFE40_8x26_VERSION 0x20000013
+#define VFE40_8x26V2_VERSION 0x20010014
+#define VFE40_8916_VERSION 0x10030000
+#define VFE40_8939_VERSION 0x10040000
+#define VFE32_8909_VERSION 0x30600
+
+#define MAX_IOMMU_CTX 2
+#define MAX_NUM_WM 7
+#define MAX_NUM_RDI 3
+#define MAX_NUM_RDI_MASTER 3
+#define MAX_NUM_COMPOSITE_MASK 4
+#define MAX_NUM_STATS_COMP_MASK 2
+#define MAX_INIT_FRAME_DROP 31
+#define ISP_Q2 (1 << 2)
+#define ISP_Q10 (1 << 10)
+
+#define VFE_PING_FLAG 0xFFFFFFFF
+#define VFE_PONG_FLAG 0x0
+
+#define VFE_MAX_CFG_TIMEOUT 3000
+#define VFE_CLK_INFO_MAX 16
+#define STATS_COMP_BIT_MASK 0xFF0000
+
+#define MSM_ISP_MIN_AB 11000000
+#define MSM_ISP_MIN_IB 11000000
+
+struct vfe_device;
+struct msm_vfe_axi_stream;
+struct msm_vfe_stats_stream;
+
+struct vfe_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum msm_isp_pack_fmt {
+ QCOM,
+ MIPI,
+ DPCM6,
+ DPCM8,
+ PLAIN8,
+ PLAIN16,
+ MAX_ISP_PACK_FMT,
+};
+
+enum msm_isp_camif_update_state {
+ NO_UPDATE,
+ ENABLE_CAMIF,
+ DISABLE_CAMIF,
+ DISABLE_CAMIF_IMMEDIATELY
+};
+
+struct msm_isp_timestamp {
+ /*Monotonic clock for v4l2 buffer*/
+ struct timeval buf_time;
+ /*Monotonic clock for VT */
+ struct timeval vt_time;
+ /*Wall clock for userspace event*/
+ struct timeval event_time;
+};
+
+struct msm_vfe_irq_ops {
+ void (*read_irq_status)(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1);
+ void (*process_reg_update)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ void (*process_epoch_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ void (*process_reset_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_halt_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_camif_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ void (*process_axi_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+ void (*process_stats_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+};
+
+struct msm_vfe_axi_ops {
+ void (*reload_wm)(struct vfe_device *vfe_dev,
+ uint32_t reload_mask);
+ void (*enable_wm)(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable);
+ int32_t (*cfg_io_format)(struct vfe_device *vfe_dev,
+ enum msm_vfe_axi_stream_src stream_src,
+ uint32_t io_format);
+ void (*cfg_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*cfg_comp_mask)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_comp_mask)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*cfg_wm_irq_mask)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_wm_irq_mask)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+
+ void (*cfg_wm_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx);
+ void (*clear_wm_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+
+ void (*cfg_wm_xbar_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint8_t plane_idx);
+ void (*clear_wm_xbar_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+
+ void (*cfg_ub)(struct vfe_device *vfe_dev);
+
+ void (*update_ping_pong_addr)(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, dma_addr_t paddr);
+
+ uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_comp_mask)(uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_pingpong_status)(struct vfe_device *vfe_dev);
+ int (*halt)(struct vfe_device *vfe_dev, uint32_t blocking);
+ int (*restart)(struct vfe_device *vfe_dev, uint32_t blocking,
+ uint32_t enable_camif);
+ void (*update_cgc_override)(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t cgc_override);
+};
+
+struct msm_vfe_core_ops {
+ void (*reg_update)(struct vfe_device *vfe_dev, uint32_t input_src);
+ long (*reset_hw)(struct vfe_device *vfe_dev, uint32_t first_start,
+ uint32_t blocking_call);
+ int (*init_hw)(struct vfe_device *vfe_dev);
+ void (*init_hw_reg)(struct vfe_device *vfe_dev);
+ void (*clear_status_reg)(struct vfe_device *vfe_dev);
+ void (*release_hw)(struct vfe_device *vfe_dev);
+ void (*cfg_input_mux)(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+ int (*start_fetch_eng)(struct vfe_device *vfe_dev,
+ void *arg);
+ void (*update_camif_state)(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state);
+ void (*cfg_rdi_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_rdi_cfg *rdi_cfg,
+ enum msm_vfe_input_src input_src);
+ int (*get_platform_data)(struct vfe_device *vfe_dev);
+ void (*get_error_mask)(uint32_t *error_mask0, uint32_t *error_mask1);
+ void (*process_error_status)(struct vfe_device *vfe_dev);
+ void (*get_overflow_mask)(uint32_t *overflow_mask);
+ void (*get_irq_mask)(struct vfe_device *vfe_dev,
+ uint32_t *irq0_mask, uint32_t *irq1_mask);
+ void (*restore_irq_mask)(struct vfe_device *vfe_dev);
+ void (*get_halt_restart_mask)(uint32_t *irq0_mask,
+ uint32_t *irq1_mask);
+ void (*get_rdi_wm_mask)(struct vfe_device *vfe_dev,
+ uint32_t *rdi_wm_mask);
+};
+struct msm_vfe_stats_ops {
+ int (*get_stats_idx)(enum msm_isp_stats_type stats_type);
+ int (*check_streams)(struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_framedrop)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_comp_mask)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+ void (*cfg_wm_irq_mask)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_wm_irq_mask)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+
+ void (*cfg_wm_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_wm_reg)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+
+ void (*cfg_ub)(struct vfe_device *vfe_dev);
+
+ void (*enable_module)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+
+ void (*update_ping_pong_addr)(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ uint32_t pingpong_status, dma_addr_t paddr);
+
+ uint32_t (*get_frame_id)(struct vfe_device *vfe_dev);
+ uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_comp_mask)(uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_pingpong_status)(struct vfe_device *vfe_dev);
+
+ void (*update_cgc_override)(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+};
+
+struct msm_vfe_ops {
+ struct msm_vfe_irq_ops irq_ops;
+ struct msm_vfe_axi_ops axi_ops;
+ struct msm_vfe_core_ops core_ops;
+ struct msm_vfe_stats_ops stats_ops;
+};
+
+struct msm_vfe_hardware_info {
+ int num_iommu_ctx;
+ /* secure iommu ctx nums */
+ int num_iommu_secure_ctx;
+ int vfe_clk_idx;
+ struct msm_vfe_ops vfe_ops;
+ struct msm_vfe_axi_hardware_info *axi_hw_info;
+ struct msm_vfe_stats_hardware_info *stats_hw_info;
+ struct v4l2_subdev_internal_ops *subdev_internal_ops;
+ struct v4l2_subdev_ops *subdev_ops;
+ uint32_t dmi_reg_offset;
+};
+
+struct msm_vfe_axi_hardware_info {
+ uint8_t num_wm;
+ uint8_t num_rdi;
+ uint8_t num_rdi_master;
+ uint8_t num_comp_mask;
+ uint32_t min_wm_ub;
+ uint32_t scratch_buf_range;
+};
+
+enum msm_vfe_axi_state {
+ AVAILABLE,
+ INACTIVE,
+ ACTIVE,
+ PAUSED,
+ START_PENDING,
+ STOP_PENDING,
+ PAUSE_PENDING,
+ RESUME_PENDING,
+ STARTING,
+ STOPPING,
+ PAUSING,
+ RESUMING,
+};
+
+enum msm_vfe_axi_cfg_update_state {
+ NO_AXI_CFG_UPDATE,
+ APPLYING_UPDATE_RESUME,
+ UPDATE_REQUESTED,
+};
+
+#define VFE_NO_DROP 0xFFFFFFFF
+#define VFE_DROP_EVERY_2FRAME 0x55555555
+#define VFE_DROP_EVERY_4FRAME 0x11111111
+#define VFE_DROP_EVERY_8FRAME 0x01010101
+#define VFE_DROP_EVERY_16FRAME 0x00010001
+#define VFE_DROP_EVERY_32FRAME 0x00000001
+
+enum msm_vfe_axi_stream_type {
+ CONTINUOUS_STREAM,
+ BURST_STREAM,
+};
+
+struct msm_vfe_axi_stream {
+ uint32_t frame_id;
+ enum msm_vfe_axi_state state;
+ enum msm_vfe_axi_stream_src stream_src;
+ uint8_t num_planes;
+ uint8_t wm[MAX_PLANES_PER_STREAM];
+ uint32_t output_format;/*Planar/RAW/Misc*/
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
+ uint8_t comp_mask_index;
+ struct msm_isp_buffer *buf[2];
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t bufq_scratch_handle;
+ uint32_t controllable_output;
+ uint32_t stream_handle;
+ uint32_t request_frm_num;
+ uint8_t buf_divert;
+ enum msm_vfe_axi_stream_type stream_type;
+ uint32_t frame_based;
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern;
+ uint32_t framedrop_period;
+ uint32_t framedrop_pattern;
+ uint32_t num_burst_capture;/*number of frame to capture*/
+ uint32_t init_frame_drop;
+ uint32_t burst_frame_count;/*number of sof before burst stop*/
+ uint8_t framedrop_update;
+ spinlock_t lock;
+
+ /*Bandwidth calculation info*/
+ uint32_t max_width;
+ /*Based on format plane size in Q2. e.g NV12 = 1.5*/
+ uint32_t format_factor;
+ uint32_t bandwidth;
+
+ /*Run time update variables*/
+ uint32_t runtime_init_frame_drop;
+ uint32_t runtime_burst_frame_count;/*number of sof before burst stop*/
+ uint32_t runtime_num_burst_capture;
+ uint8_t runtime_framedrop_update;
+ uint8_t runtime_framedrop_update_burst;
+ uint32_t runtime_output_format;
+ enum msm_stream_memory_input_t memory_input;
+};
+
+struct msm_vfe_axi_composite_info {
+ uint32_t stream_handle;
+ uint32_t stream_composite_mask;
+};
+
+struct msm_vfe_src_info {
+ uint32_t frame_id;
+ uint8_t active;
+ uint8_t pix_stream_count;
+ uint8_t raw_stream_count;
+ enum msm_vfe_inputmux input_mux;
+ uint32_t width;
+ long pixel_clock;
+ uint32_t input_format;/*V4L2 pix format with bayer pattern*/
+ uint32_t last_updt_frm_id;
+};
+
+struct msm_vfe_fetch_engine_info {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t buf_idx;
+ uint8_t is_busy;
+};
+
+enum msm_wm_ub_cfg_type {
+ MSM_WM_UB_CFG_DEFAULT,
+ MSM_WM_UB_EQUAL_SLICING,
+ MSM_WM_UB_CFG_MAX_NUM
+};
+
+struct msm_vfe_axi_shared_data {
+ struct msm_vfe_axi_hardware_info *hw_info;
+ struct msm_vfe_axi_stream stream_info[VFE_AXI_SRC_MAX];
+ uint32_t free_wm[MAX_NUM_WM];
+ uint32_t wm_image_size[MAX_NUM_WM];
+ enum msm_wm_ub_cfg_type wm_ub_cfg_policy;
+ uint8_t num_used_wm;
+ uint8_t num_active_stream;
+ uint8_t num_rdi_stream;
+ uint8_t num_pix_stream;
+ uint32_t rdi_wm_mask;
+ struct msm_vfe_axi_composite_info
+ composite_info[MAX_NUM_COMPOSITE_MASK];
+ uint8_t num_used_composite_mask;
+ uint32_t stream_update;
+ atomic_t axi_cfg_update;
+ enum msm_isp_camif_update_state pipeline_update;
+ struct msm_vfe_src_info src_info[VFE_SRC_MAX];
+ uint16_t stream_handle_cnt;
+ uint32_t event_mask;
+};
+
+struct msm_vfe_stats_hardware_info {
+ uint32_t stats_capability_mask;
+ uint8_t *stats_ping_pong_offset;
+ uint8_t num_stats_type;
+ uint8_t num_stats_comp_mask;
+};
+
+enum msm_vfe_stats_state {
+ STATS_AVAILABLE,
+ STATS_INACTIVE,
+ STATS_ACTIVE,
+ STATS_START_PENDING,
+ STATS_STOP_PENDING,
+ STATS_STARTING,
+ STATS_STOPPING,
+};
+
+struct msm_vfe_stats_stream {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t stream_handle;
+ uint32_t composite_flag;
+ enum msm_isp_stats_type stats_type;
+ enum msm_vfe_stats_state state;
+ uint32_t framedrop_pattern;
+ uint32_t framedrop_period;
+ uint32_t irq_subsample_pattern;
+ uint32_t init_stats_frame_drop;
+
+ uint32_t buffer_offset;
+ struct msm_isp_buffer *buf[2];
+ uint32_t bufq_handle;
+};
+
+struct msm_vfe_stats_shared_data {
+ struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX];
+ uint8_t num_active_stream;
+ atomic_t stats_comp_mask[MAX_NUM_STATS_COMP_MASK];
+ uint32_t reg_mask;
+ uint16_t stream_handle_cnt;
+ atomic_t stats_update;
+};
+
+struct msm_vfe_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ struct msm_isp_timestamp ts;
+ uint8_t cmd_used;
+};
+
+#define MSM_VFE_TASKLETQ_SIZE 200
+
+enum msm_vfe_overflow_state {
+ NO_OVERFLOW,
+ OVERFLOW_DETECTED,
+ HALT_REQUESTED,
+ RESTART_REQUESTED,
+};
+
+struct msm_vfe_error_info {
+ atomic_t overflow_state;
+ uint32_t overflow_recover_irq_mask0;
+ uint32_t overflow_recover_irq_mask1;
+ uint32_t error_mask0;
+ uint32_t error_mask1;
+ uint32_t violation_status;
+ uint32_t camif_status;
+ uint32_t stream_framedrop_count[MAX_NUM_STREAM];
+ uint32_t stats_framedrop_count[MSM_ISP_STATS_MAX];
+ uint32_t info_dump_frame_count;
+ uint32_t error_count;
+};
+
+struct msm_isp_statistics {
+ int64_t imagemaster0_overflow;
+ int64_t imagemaster1_overflow;
+ int64_t imagemaster2_overflow;
+ int64_t imagemaster3_overflow;
+ int64_t imagemaster4_overflow;
+ int64_t imagemaster5_overflow;
+ int64_t imagemaster6_overflow;
+ int64_t be_overflow;
+ int64_t bg_overflow;
+ int64_t bf_overflow;
+ int64_t awb_overflow;
+ int64_t rs_overflow;
+ int64_t cs_overflow;
+ int64_t ihist_overflow;
+ int64_t skinbhist_overflow;
+ int64_t bfscale_overflow;
+
+ int64_t isp_vfe0_active;
+ int64_t isp_vfe0_ab;
+ int64_t isp_vfe0_ib;
+
+ int64_t isp_vfe1_active;
+ int64_t isp_vfe1_ab;
+ int64_t isp_vfe1_ib;
+
+ int64_t isp_cpp_active;
+ int64_t isp_cpp_ab;
+ int64_t isp_cpp_ib;
+
+ int64_t last_overflow_ab;
+ int64_t last_overflow_ib;
+
+ int64_t vfe_clk_rate;
+ int64_t cpp_clk_rate;
+};
+
+enum msm_isp_hw_client {
+ ISP_VFE0,
+ ISP_VFE1,
+ ISP_CPP,
+ MAX_ISP_CLIENT,
+};
+
+struct msm_isp_bandwidth_info {
+ uint32_t active;
+ uint64_t ab;
+ uint64_t ib;
+};
+
+struct msm_isp_bw_req_info {
+ uint32_t client;
+ unsigned long long timestamp;
+ uint64_t total_ab;
+ uint64_t total_ib;
+ struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT];
+};
+
+#define MSM_ISP_MAX_WM 7
+struct msm_isp_ub_info {
+ enum msm_wm_ub_cfg_type policy;
+ uint8_t num_wm;
+ uint32_t wm_ub;
+ uint32_t data[MSM_ISP_MAX_WM];
+ uint64_t addr[MSM_ISP_MAX_WM];
+};
+
+struct msm_vfe_hw_init_parms {
+ const char *entries;
+ const char *regs;
+ const char *settings;
+};
+
+struct vfe_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev subdev;
+ struct resource *vfe_irq;
+ struct resource *vfe_mem;
+ struct resource *vfe_vbif_mem;
+ struct resource *vfe_io;
+ struct resource *vfe_vbif_io;
+ void __iomem *vfe_base;
+ void __iomem *vfe_vbif_base;
+
+ struct device *iommu_ctx[MAX_IOMMU_CTX];
+ /*Add secure context banks*/
+ struct device *iommu_secure_ctx[MAX_IOMMU_CTX];
+
+ struct regulator *fs_vfe;
+ struct clk **vfe_clk;
+ uint32_t num_clk;
+
+ uint32_t bus_perf_client;
+
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct completion stream_config_complete;
+ struct completion stats_config_complete;
+ struct mutex realtime_mutex;
+ struct mutex core_mutex;
+
+ atomic_t irq_cnt;
+ uint8_t taskletq_idx;
+ spinlock_t tasklet_lock;
+ spinlock_t shared_data_lock;
+ struct list_head tasklet_q;
+ struct tasklet_struct vfe_tasklet;
+ struct msm_vfe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE];
+
+ uint32_t vfe_hw_version;
+ struct msm_vfe_hardware_info *hw_info;
+ struct msm_vfe_axi_shared_data axi_data;
+ struct msm_vfe_stats_shared_data stats_data;
+ struct msm_vfe_error_info error_info;
+ struct msm_isp_buf_mgr *buf_mgr;
+ int dump_reg;
+ int vfe_clk_idx;
+ uint32_t vfe_open_cnt;
+ uint8_t vt_enable;
+ uint8_t ignore_error;
+ struct msm_isp_statistics *stats;
+ struct msm_vfe_fetch_engine_info fetch_engine_info;
+ uint64_t msm_isp_last_overflow_ab;
+ uint64_t msm_isp_last_overflow_ib;
+ uint64_t msm_isp_vfe_clk_rate;
+ struct msm_isp_ub_info *ub_info;
+ uint32_t vfe_ub_policy;
+ uint32_t isp_sof_debug;
+ uint8_t reset_pending;
+ uint32_t bus_util_factor;
+ uint8_t vfe_reset_timeout_processed;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.c
new file mode 100644
index 0000000..55a10ca
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.c
@@ -0,0 +1,2095 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <asm/div64.h>
+#include "msm_isp_util_32.h"
+#include "msm_isp_axi_util_32.h"
+
+#define SRC_TO_INTF(src) \
+ ((src < RDI_INTF_0 || src == VFE_AXI_SRC_MAX) ? VFE_PIX_0 : \
+ (VFE_RAW_0 + src - RDI_INTF_0))
+
+#define HANDLE_TO_IDX(handle) (handle & 0xFF)
+
+int msm_isp_axi_create_stream(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ uint32_t i = stream_cfg_cmd->stream_src;
+
+ if (i >= VFE_AXI_SRC_MAX) {
+ pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__,
+ stream_cfg_cmd->stream_src);
+ return -EINVAL;
+ }
+
+ if ((axi_data->stream_handle_cnt << 8) == 0)
+ axi_data->stream_handle_cnt++;
+
+ stream_cfg_cmd->axi_stream_handle =
+ (++axi_data->stream_handle_cnt) << 8 | i;
+
+ memset(&axi_data->stream_info[i], 0,
+ sizeof(struct msm_vfe_axi_stream));
+ spin_lock_init(&axi_data->stream_info[i].lock);
+ axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id;
+ axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id;
+ axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert;
+ axi_data->stream_info[i].state = INACTIVE;
+ axi_data->stream_info[i].stream_handle =
+ stream_cfg_cmd->axi_stream_handle;
+ axi_data->stream_info[i].controllable_output =
+ stream_cfg_cmd->controllable_output;
+ if (stream_cfg_cmd->controllable_output)
+ stream_cfg_cmd->frame_skip_pattern = SKIP_ALL;
+ return 0;
+}
+
+void msm_isp_axi_destroy_stream(
+ struct msm_vfe_axi_shared_data *axi_data, int stream_idx)
+{
+ if (axi_data->stream_info[stream_idx].state != AVAILABLE) {
+ axi_data->stream_info[stream_idx].state = AVAILABLE;
+ axi_data->stream_info[stream_idx].stream_handle = 0;
+ } else {
+ pr_err("%s: stream does not exist\n", __func__);
+ }
+}
+
+int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int rc = -1, i;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+
+ if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) < MAX_NUM_STREAM) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
+ } else {
+ pr_err("%s: Invalid axi_stream_handle\n", __func__);
+ return rc;
+ }
+
+ if (!stream_info) {
+ pr_err("%s: Stream info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ stream_info->num_planes = 1;
+ stream_info->format_factor = ISP_Q2;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ stream_info->num_planes = 2;
+ stream_info->format_factor = 1.5 * ISP_Q2;
+ break;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__,
+ stream_cfg_cmd->output_format);
+ return rc;
+ }
+
+ if (axi_data->hw_info->num_wm - axi_data->num_used_wm <
+ stream_info->num_planes) {
+ pr_err("%s: No free write masters\n", __func__);
+ return rc;
+ }
+
+ if ((stream_info->num_planes > 1) &&
+ (axi_data->hw_info->num_comp_mask -
+ axi_data->num_used_composite_mask < 1)) {
+ pr_err("%s: No free composite mask\n", __func__);
+ return rc;
+ }
+
+ if (stream_cfg_cmd->init_frame_drop >= MAX_INIT_FRAME_DROP) {
+ pr_err("%s: Invalid skip pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_cfg_cmd->frame_skip_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid skip pattern\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ stream_info->plane_cfg[i] = stream_cfg_cmd->plane_cfg[i];
+ stream_info->max_width = max(stream_info->max_width,
+ stream_cfg_cmd->plane_cfg[i].output_width);
+ }
+
+ stream_info->output_format = stream_cfg_cmd->output_format;
+ stream_info->runtime_output_format = stream_info->output_format;
+ stream_info->stream_src = stream_cfg_cmd->stream_src;
+ stream_info->frame_based = stream_cfg_cmd->frame_base;
+ return 0;
+}
+
+static uint32_t msm_isp_axi_get_plane_size(
+ struct msm_vfe_axi_stream *stream_info, int plane_idx)
+{
+ uint32_t size = 0;
+ struct msm_vfe_axi_plane_cfg *plane_cfg = stream_info->plane_cfg;
+
+ switch (stream_info->output_format) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ if (plane_cfg[plane_idx].output_plane_format == Y_PLANE)
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ else
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ if (plane_cfg[plane_idx].output_plane_format == Y_PLANE)
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ else
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__,
+ stream_info->output_format);
+ break;
+ }
+ return size;
+}
+
+void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i, j;
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ for (j = 0; j < axi_data->hw_info->num_wm; j++) {
+ if (!axi_data->free_wm[j]) {
+ axi_data->free_wm[j] =
+ stream_info->stream_handle;
+ axi_data->wm_image_size[j] =
+ msm_isp_axi_get_plane_size(
+ stream_info, i);
+ axi_data->num_used_wm++;
+ break;
+ }
+ }
+ stream_info->wm[i] = j;
+ }
+}
+
+void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ axi_data->free_wm[stream_info->wm[i]] = 0;
+ axi_data->num_used_wm--;
+ }
+ if (stream_info->stream_src <= IDEAL_RAW)
+ axi_data->num_pix_stream++;
+ else if (stream_info->stream_src < VFE_AXI_SRC_MAX)
+ axi_data->num_rdi_stream++;
+}
+
+void msm_isp_axi_reserve_comp_mask(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ uint8_t comp_mask = 0;
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ comp_mask |= 1 << stream_info->wm[i];
+
+ for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
+ if (!axi_data->composite_info[i].stream_handle) {
+ axi_data->composite_info[i].stream_handle =
+ stream_info->stream_handle;
+ axi_data->composite_info[i].
+ stream_composite_mask = comp_mask;
+ axi_data->num_used_composite_mask++;
+ break;
+ }
+ }
+ stream_info->comp_mask_index = i;
+}
+
+static void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ axi_data->composite_info[stream_info->comp_mask_index].
+ stream_composite_mask = 0;
+ axi_data->composite_info[stream_info->comp_mask_index].
+ stream_handle = 0;
+ axi_data->num_used_composite_mask--;
+}
+
+static int msm_isp_axi_get_bufq_handles(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int rc = 0;
+
+ if (stream_info->stream_id & ISP_SCRATCH_BUF_BIT) {
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id & ~ISP_SCRATCH_BUF_BIT);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: Stream 0x%x has no valid buffer queue\n",
+ __func__, (unsigned int)stream_info->stream_id);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ stream_info->bufq_scratch_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_scratch_handle == 0) {
+ pr_err("%s: Stream 0x%x has no valid buffer queue\n",
+ __func__, (unsigned int)stream_info->stream_id);
+ rc = -EINVAL;
+ return rc;
+ }
+ } else {
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: Stream 0x%x has no valid buffer queue\n",
+ __func__, (unsigned int)stream_info->stream_id);
+ rc = -EINVAL;
+ return rc;
+ }
+ }
+ return rc;
+}
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int rc = 0, i;
+ unsigned long flags;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ enum msm_vfe_axi_state valid_state =
+ (stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ MAX_NUM_STREAM) {
+ return -EINVAL;
+ }
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state != valid_state) {
+ if ((stream_info->state == PAUSING ||
+ stream_info->state == PAUSED ||
+ stream_info->state == RESUME_PENDING ||
+ stream_info->state == RESUMING) &&
+ (stream_cfg_cmd->cmd == STOP_STREAM ||
+ stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) {
+ stream_info->state = ACTIVE;
+ } else {
+ pr_err("%s: Invalid stream state: %d\n",
+ __func__, stream_info->state);
+ spin_unlock_irqrestore(
+ &stream_info->lock, flags);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ rc = msm_isp_axi_get_bufq_handles(vfe_dev, stream_info);
+ if (rc)
+ break;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev,
+ uint8_t input_src)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->state != ACTIVE)
+ continue;
+
+ if (stream_info->runtime_framedrop_update) {
+ stream_info->runtime_init_frame_drop--;
+ if (stream_info->runtime_init_frame_drop == 0) {
+ stream_info->runtime_framedrop_update = 0;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ }
+ }
+ if (stream_info->stream_type == BURST_STREAM &&
+ ((1 << SRC_TO_INTF(stream_info->stream_src)) &
+ input_src)) {
+ if (stream_info->runtime_framedrop_update_burst) {
+ stream_info->runtime_framedrop_update_burst = 0;
+ stream_info->runtime_burst_frame_count =
+ stream_info->runtime_init_frame_drop +
+ (stream_info->runtime_num_burst_capture -
+ 1) *
+ (stream_info->framedrop_period + 1) + 1;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ } else {
+ stream_info->runtime_burst_frame_count--;
+ if (stream_info->
+ runtime_burst_frame_count == 0) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ }
+ }
+ }
+ }
+}
+
+void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ stream_info->runtime_init_frame_drop = stream_info->init_frame_drop;
+ stream_info->runtime_burst_frame_count =
+ stream_info->burst_frame_count;
+ stream_info->runtime_num_burst_capture =
+ stream_info->num_burst_capture;
+ stream_info->runtime_framedrop_update = stream_info->framedrop_update;
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(vfe_dev, stream_info);
+}
+
+void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
+ enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts)
+{
+ struct msm_isp32_event_data event_data;
+
+ memset(&event_data, 0, sizeof(event_data));
+ switch (event_type) {
+ case ISP_EVENT_SOF:
+ if ((frame_src == VFE_PIX_0) && (vfe_dev->isp_sof_debug < 5)) {
+ pr_err("%s: PIX0 frame id: %u\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ vfe_dev->isp_sof_debug++;
+ }
+ vfe_dev->axi_data.src_info[frame_src].frame_id++;
+ if (vfe_dev->axi_data.src_info[frame_src].frame_id == 0)
+ vfe_dev->axi_data.src_info[frame_src].frame_id = 1;
+ ISP_DBG("%s: frame_src %d frame id: %u\n", __func__,
+ frame_src,
+ vfe_dev->axi_data.src_info[frame_src].frame_id);
+ break;
+ case ISP_EVENT_REG_UPDATE:
+ vfe_dev->axi_data.src_info[frame_src].last_updt_frm_id = 0;
+ break;
+ default:
+ break;
+ }
+
+ event_data.input_intf = frame_src;
+ event_data.frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id;
+ event_data.timestamp = ts->event_time;
+ event_data.mono_timestamp = ts->buf_time;
+ msm_isp_send_event(vfe_dev, event_type | frame_src, &event_data);
+}
+
+void msm_isp_calculate_framedrop(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ uint32_t framedrop_period = 0;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+
+ if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) < MAX_NUM_STREAM) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
+ } else {
+ pr_err("%s: Invalid stream handle", __func__);
+ return;
+ }
+ if (!stream_info) {
+ pr_err("%s: Stream info is NULL\n", __func__);
+ return;
+ }
+
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_cfg_cmd->frame_skip_pattern);
+ stream_info->frame_skip_pattern =
+ stream_cfg_cmd->frame_skip_pattern;
+ if (stream_cfg_cmd->frame_skip_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+
+ if (stream_cfg_cmd->init_frame_drop < framedrop_period) {
+ stream_info->framedrop_pattern <<=
+ stream_cfg_cmd->init_frame_drop;
+ stream_info->init_frame_drop = 0;
+ stream_info->framedrop_update = 0;
+ } else {
+ stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
+ stream_info->framedrop_update = 1;
+ }
+
+ if (stream_cfg_cmd->burst_count > 0) {
+ stream_info->stream_type = BURST_STREAM;
+ stream_info->num_burst_capture =
+ stream_cfg_cmd->burst_count;
+ stream_info->burst_frame_count =
+ stream_cfg_cmd->init_frame_drop +
+ (stream_cfg_cmd->burst_count - 1) *
+ framedrop_period + 1;
+ } else {
+ stream_info->stream_type = CONTINUOUS_STREAM;
+ stream_info->burst_frame_count = 0;
+ stream_info->num_burst_capture = 0;
+ }
+}
+
+static void msm_isp_calculate_bandwidth(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int bpp = 0;
+
+ if (stream_info->stream_src < RDI_INTF_0) {
+ stream_info->bandwidth =
+ (axi_data->src_info[VFE_PIX_0].pixel_clock /
+ axi_data->src_info[VFE_PIX_0].width) *
+ stream_info->max_width;
+ stream_info->bandwidth = (unsigned long)stream_info->bandwidth *
+ stream_info->format_factor / ISP_Q2;
+ } else {
+ int rdi = SRC_TO_INTF(stream_info->stream_src);
+
+ bpp = msm_isp_get_bit_per_pixel(stream_info->output_format);
+ if (rdi < VFE_SRC_MAX)
+ stream_info->bandwidth =
+ (axi_data->src_info[rdi].pixel_clock / 8) * bpp;
+ else
+ pr_err("%s: Invalid rdi interface\n", __func__);
+ }
+}
+
+#ifdef CONFIG_MSM_AVTIMER
+void msm_isp_start_avtimer(void)
+{
+ avcs_core_open();
+ avcs_core_disable_power_collapse(1);
+}
+
+static inline void msm_isp_get_avtimer_ts(
+ struct msm_isp_timestamp *time_stamp)
+{
+ int rc = 0;
+ uint32_t avtimer_usec = 0;
+ uint64_t avtimer_tick = 0;
+
+ rc = avcs_core_query_timer(&avtimer_tick);
+ if (rc < 0) {
+ pr_err("%s: Error: Invalid AVTimer Tick, rc=%d\n",
+ __func__, rc);
+ /* In case of error return zero AVTimer Tick Value */
+ time_stamp->vt_time.tv_sec = 0;
+ time_stamp->vt_time.tv_usec = 0;
+ } else {
+ avtimer_usec = do_div(avtimer_tick, USEC_PER_SEC);
+ time_stamp->vt_time.tv_sec = (uint32_t)(avtimer_tick);
+ time_stamp->vt_time.tv_usec = avtimer_usec;
+ pr_debug("%s: AVTimer TS = %u:%u\n", __func__,
+ (uint32_t)(avtimer_tick), avtimer_usec);
+ }
+}
+#else
+void msm_isp_start_avtimer(void)
+{
+ pr_err("AV Timer is not supported\n");
+}
+
+inline void msm_isp_get_avtimer_ts(
+ struct msm_isp_timestamp *time_stamp)
+{
+ pr_err_ratelimited("%s: Error: AVTimer driver not available\n",
+ __func__);
+ time_stamp->vt_time.tv_sec = 0;
+ time_stamp->vt_time.tv_usec = 0;
+}
+#endif
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ uint32_t io_format = 0;
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_axi_stream *stream_info;
+
+ rc = msm_isp_axi_create_stream(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ if (rc) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ rc = msm_isp_validate_axi_request(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ if (rc) {
+ pr_err("%s: Request validation failed\n", __func__);
+ if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) <
+ MAX_NUM_STREAM)
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle));
+ return rc;
+ }
+ stream_info = &vfe_dev->axi_data.
+ stream_info[HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
+ if (!stream_info) {
+ pr_err("%s: can not find stream handle %x\n", __func__,
+ stream_cfg_cmd->axi_stream_handle);
+ return -EINVAL;
+ }
+
+ stream_info->memory_input = stream_cfg_cmd->memory_input;
+
+ msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_info);
+
+ if (stream_info->stream_src < RDI_INTF_0) {
+ io_format = vfe_dev->axi_data.src_info[VFE_PIX_0].input_format;
+ if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ if (stream_info->stream_src == CAMIF_RAW &&
+ io_format != stream_info->output_format)
+ pr_debug("%s: Overriding input format\n",
+ __func__);
+
+ io_format = stream_info->output_format;
+ }
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.cfg_io_format(
+ vfe_dev, stream_info->stream_src, io_format);
+ if (rc) {
+ pr_err("%s: cfg io format failed\n", __func__);
+ msm_isp_axi_free_wm(&vfe_dev->axi_data,
+ stream_info);
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ HANDLE_TO_IDX(
+ stream_cfg_cmd->axi_stream_handle));
+ return rc;
+ }
+ }
+
+ msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd);
+ if (stream_cfg_cmd->vt_enable && !vfe_dev->vt_enable) {
+ vfe_dev->vt_enable = stream_cfg_cmd->vt_enable;
+ msm_isp_start_avtimer();
+ }
+ if (stream_info->num_planes > 1) {
+ msm_isp_axi_reserve_comp_mask(
+ &vfe_dev->axi_data, stream_info);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_xbar_reg(vfe_dev, stream_info, i);
+ }
+ return rc;
+}
+
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
+
+
+ if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >=
+ MAX_NUM_STREAM) {
+ pr_err("%s: Invalid stream handle\n", __func__);
+ return -EINVAL;
+ }
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_release_cmd->stream_handle)];
+ if (stream_info->state == AVAILABLE) {
+ pr_err("%s: Stream already released\n", __func__);
+ return -EINVAL;
+ } else if (stream_info->state != INACTIVE) {
+ stream_cfg.cmd = STOP_STREAM;
+ stream_cfg.num_streams = 1;
+ stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle;
+ msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg);
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_reg(vfe_dev, stream_info, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_xbar_reg(vfe_dev, stream_info, i);
+ }
+
+ if (stream_info->num_planes > 1) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_comp_mask(vfe_dev, stream_info);
+ msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+ }
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info);
+ msm_isp_axi_free_wm(axi_data, stream_info);
+
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ HANDLE_TO_IDX(stream_release_cmd->stream_handle));
+
+ return rc;
+}
+
+static void msm_isp_axi_stream_enable_cfg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ if (stream_info->state == INACTIVE)
+ return;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ if (stream_info->state == START_PENDING ||
+ stream_info->state == RESUME_PENDING) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(vfe_dev, stream_info->wm[i], 1);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(vfe_dev, stream_info->wm[i], 0);
+ /* Issue a reg update for Raw Snapshot Case
+ * since we dont have reg update ack
+ */
+ if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, (1 << VFE_PIX_0));
+ }
+ }
+ }
+
+ if (stream_info->state == START_PENDING)
+ axi_data->num_active_stream++;
+ else if (stream_info->state == STOP_PENDING)
+ axi_data->num_active_stream--;
+}
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev, uint8_t input_src)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ if (axi_data->stream_info[i].state == START_PENDING ||
+ axi_data->stream_info[i].state ==
+ STOP_PENDING) {
+ if ((1 <<
+ SRC_TO_INTF(axi_data->stream_info[i].
+ stream_src)) &
+ input_src) {
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, &axi_data->stream_info[i]);
+ axi_data->stream_info[i].state =
+ axi_data->stream_info[i].state ==
+ START_PENDING ? STARTING : STOPPING;
+ }
+ } else if (axi_data->stream_info[i].state == STARTING ||
+ axi_data->stream_info[i].state == STOPPING) {
+ if ((1 <<
+ SRC_TO_INTF(axi_data->stream_info[i].
+ stream_src)) &
+ input_src) {
+ axi_data->stream_info[i].state =
+ axi_data->stream_info[i].state == STARTING ?
+ ACTIVE : INACTIVE;
+ vfe_dev->axi_data.stream_update--;
+ }
+ }
+ }
+
+ if (vfe_dev->axi_data.pipeline_update == DISABLE_CAMIF ||
+ (vfe_dev->axi_data.pipeline_update ==
+ DISABLE_CAMIF_IMMEDIATELY)) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ enable_module(vfe_dev, 0xFF, 0);
+ vfe_dev->axi_data.pipeline_update = NO_UPDATE;
+ }
+
+ if (vfe_dev->axi_data.stream_update == 0)
+ complete(&vfe_dev->stream_config_complete);
+}
+
+static void msm_isp_reload_ping_pong_offset(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i, j;
+ uint32_t flag;
+ struct msm_isp_buffer *buf;
+
+ for (i = 0; i < 2; i++) {
+ buf = stream_info->buf[i];
+ if (!buf)
+ continue;
+ flag = i ? VFE_PONG_FLAG : VFE_PING_FLAG;
+ for (j = 0; j < stream_info->num_planes; j++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->wm[j], flag,
+ buf->mapped_info[j].paddr +
+ stream_info->plane_cfg[j].plane_addr_offset);
+ }
+ }
+}
+
+void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev)
+{
+ int i, j;
+ uint32_t update_state;
+ unsigned long flags;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->stream_type == BURST_STREAM ||
+ stream_info->state == AVAILABLE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == PAUSING) {
+ /*AXI Stopped, apply update*/
+ stream_info->state = PAUSED;
+ msm_isp_reload_ping_pong_offset(vfe_dev, stream_info);
+ for (j = 0; j < stream_info->num_planes; j++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ /*Resume AXI*/
+ stream_info->state = RESUME_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, &axi_data->stream_info[i]);
+ stream_info->state = RESUMING;
+ } else if (stream_info->state == RESUMING) {
+ stream_info->runtime_output_format =
+ stream_info->output_format;
+ stream_info->state = ACTIVE;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+
+ update_state = atomic_dec_return(&axi_data->axi_cfg_update);
+}
+
+static void msm_isp_cfg_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ struct msm_isp_buffer *buf = stream_info->buf[0];
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->wm[i],
+ VFE_PONG_FLAG, buf->mapped_info[i].paddr +
+ stream_info->plane_cfg[i].plane_addr_offset);
+ stream_info->buf[1] = buf;
+}
+
+static void msm_isp_get_done_buf(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ struct msm_isp_buffer **done_buf)
+{
+ uint32_t pingpong_bit = 0, i;
+
+ pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
+ for (i = 0; i < stream_info->num_planes; i++) {
+ if (pingpong_bit !=
+ (~(pingpong_status >> stream_info->wm[i]) & 0x1)) {
+ pr_debug("%s: Write master ping pong mismatch. Status: 0x%x\n",
+ __func__, pingpong_status);
+ }
+ }
+
+ *done_buf = stream_info->buf[pingpong_bit];
+
+ if (stream_info->controllable_output) {
+ stream_info->buf[pingpong_bit] = NULL;
+ stream_info->request_frm_num--;
+ }
+}
+
+static int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ uint32_t pingpong_bit)
+{
+ int i, rc = -1;
+ struct msm_isp_buffer *buf = NULL;
+ uint32_t bufq_handle = 0, frame_id = 0;
+ uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
+
+ if (stream_idx >= MAX_NUM_STREAM) {
+ pr_err("%s: Invalid stream_idx", __func__);
+ return rc;
+ }
+
+ if (stream_info->controllable_output && !stream_info->request_frm_num)
+ return 0;
+
+ frame_id = vfe_dev->axi_data.
+ src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id;
+ if (frame_id && stream_info->frame_id &&
+ stream_info->frame_id == frame_id) {
+ /* This could happen if reg update ack is delayed */
+ pr_err("%s: Duplicate frame streamId:%d stream_fid:%d frame_id:%d\n",
+ __func__, stream_info->stream_id, stream_info->frame_id,
+ frame_id);
+ vfe_dev->error_info.stream_framedrop_count[stream_idx]++;
+ return rc;
+ }
+
+ bufq_handle = stream_info->bufq_handle;
+ if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX)
+ rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, bufq_handle,
+ MSM_ISP_INVALID_BUF_INDEX, &buf);
+ else {
+ pr_err("%s: Invalid stream index\n", __func__);
+ rc = -1;
+ }
+
+ if (rc < 0) {
+ vfe_dev->error_info.stream_framedrop_count[stream_idx]++;
+ return rc;
+ }
+
+ if (buf->num_planes != stream_info->num_planes) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ rc = -EINVAL;
+ goto buf_error;
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->wm[i],
+ pingpong_status, buf->mapped_info[i].paddr +
+ stream_info->plane_cfg[i].plane_addr_offset);
+
+ stream_info->buf[pingpong_bit] = buf;
+
+ return 0;
+buf_error:
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return rc;
+}
+
+static void msm_isp_process_done_buf(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf,
+ struct msm_isp_timestamp *ts)
+{
+ int rc;
+ struct msm_isp32_event_data buf_event;
+ struct timeval *time_stamp;
+ uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
+ uint32_t frame_id;
+ uint32_t buf_src;
+
+ memset(&buf_event, 0, sizeof(buf_event));
+
+ if (stream_idx >= MAX_NUM_STREAM) {
+ pr_err("%s: Invalid stream_idx", __func__);
+ return;
+ }
+
+ if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX)
+ frame_id = vfe_dev->axi_data.
+ src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id;
+ else {
+ pr_err("%s: Invalid stream index, put buf back to vb2 queue\n",
+ __func__);
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return;
+ }
+
+ if (buf && ts) {
+ if (vfe_dev->vt_enable) {
+ msm_isp_get_avtimer_ts(ts);
+ time_stamp = &ts->vt_time;
+ } else
+ time_stamp = &ts->buf_time;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf_src(vfe_dev->buf_mgr,
+ buf->bufq_handle, &buf_src);
+ if (stream_info->buf_divert && rc == 0 &&
+ buf_src != MSM_ISP_BUFFER_SRC_SCRATCH) {
+ rc = vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx,
+ time_stamp, frame_id);
+ /* Buf divert return value represent whether the buf
+ * can be diverted. A positive return value means
+ * other ISP hardware is still processing the frame.
+ */
+ if (rc == 0) {
+ buf_event.input_intf =
+ SRC_TO_INTF(stream_info->stream_src);
+ buf_event.frame_id = frame_id;
+ buf_event.timestamp = *time_stamp;
+ buf_event.u.buf_done.session_id =
+ stream_info->session_id;
+ buf_event.u.buf_done.stream_id =
+ stream_info->stream_id;
+ buf_event.u.buf_done.handle =
+ stream_info->bufq_handle;
+ buf_event.u.buf_done.buf_idx = buf->buf_idx;
+ buf_event.u.buf_done.output_format =
+ stream_info->runtime_output_format;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_BUF_DIVERT + stream_idx,
+ &buf_event);
+ }
+ } else {
+ buf_event.input_intf =
+ SRC_TO_INTF(stream_info->stream_src);
+ buf_event.frame_id = frame_id;
+ buf_event.timestamp = ts->buf_time;
+ buf_event.u.buf_done.session_id =
+ stream_info->session_id;
+ buf_event.u.buf_done.stream_id =
+ stream_info->stream_id;
+ buf_event.u.buf_done.output_format =
+ stream_info->runtime_output_format;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_BUF_DONE, &buf_event);
+ vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx,
+ time_stamp, frame_id,
+ stream_info->runtime_output_format);
+ }
+ }
+}
+
+static enum msm_isp_camif_update_state
+ msm_isp_get_camif_update_state(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint8_t pix_stream_cnt = 0, cur_pix_stream_cnt;
+
+ cur_pix_stream_cnt =
+ axi_data->src_info[VFE_PIX_0].pix_stream_count +
+ axi_data->src_info[VFE_PIX_0].raw_stream_count;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info =
+ &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ if (stream_info->stream_src < RDI_INTF_0)
+ pix_stream_cnt++;
+ }
+
+ if ((pix_stream_cnt) &&
+ (axi_data->src_info[VFE_PIX_0].input_mux != EXTERNAL_READ)) {
+
+ if (cur_pix_stream_cnt == 0 && pix_stream_cnt &&
+ stream_cfg_cmd->cmd == START_STREAM)
+ return ENABLE_CAMIF;
+ else if (cur_pix_stream_cnt &&
+ (cur_pix_stream_cnt - pix_stream_cnt) == 0 &&
+ stream_cfg_cmd->cmd == STOP_STREAM)
+ return DISABLE_CAMIF;
+ else if (cur_pix_stream_cnt &&
+ (cur_pix_stream_cnt - pix_stream_cnt) == 0 &&
+ stream_cfg_cmd->cmd == STOP_IMMEDIATELY)
+ return DISABLE_CAMIF_IMMEDIATELY;
+ }
+
+ return NO_UPDATE;
+}
+
+static void msm_isp_update_camif_output_count(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ MAX_NUM_STREAM) {
+ return;
+ }
+ stream_info =
+ &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ if (stream_info->stream_src >= RDI_INTF_0)
+ continue;
+ if (stream_info->stream_src == PIX_ENCODER ||
+ stream_info->stream_src == PIX_VIEWFINDER ||
+ stream_info->stream_src == PIX_VIDEO ||
+ stream_info->stream_src == IDEAL_RAW) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count--;
+ } else if (stream_info->stream_src == CAMIF_RAW) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count--;
+ }
+ }
+}
+
+
+static void msm_isp_update_rdi_output_count(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])
+ > MAX_NUM_STREAM)
+ return;
+ stream_info =
+ &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ if (stream_info->stream_src < RDI_INTF_0)
+ continue;
+ if (stream_info->stream_src == RDI_INTF_0) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_RAW_0].
+ raw_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_RAW_0].
+ raw_stream_count--;
+ } else if (stream_info->stream_src == RDI_INTF_1) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_RAW_1].
+ raw_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_RAW_1].
+ raw_stream_count--;
+ } else if (stream_info->stream_src == RDI_INTF_2) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_RAW_2].
+ raw_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_RAW_2].
+ raw_stream_count--;
+ }
+
+ }
+}
+
+static uint8_t msm_isp_get_curr_stream_cnt(
+ struct vfe_device *vfe_dev)
+{
+ uint8_t curr_stream_cnt = 0;
+
+ curr_stream_cnt = vfe_dev->axi_data.src_info[VFE_RAW_0].
+ raw_stream_count +
+ vfe_dev->axi_data.src_info[VFE_RAW_1].
+ raw_stream_count +
+ vfe_dev->axi_data.src_info[VFE_RAW_2].
+ raw_stream_count +
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count +
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count;
+ return curr_stream_cnt;
+}
+
+/*Factor in Q2 format*/
+#define ISP_DEFAULT_FORMAT_FACTOR 6
+#define ISP_BUS_UTILIZATION_FACTOR 1536 /* 1.5 in Q10 format */
+static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
+{
+ int i, rc = 0;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint64_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0;
+ uint32_t num_pix_streams = 0;
+ uint32_t num_rdi_streams = 0;
+ uint32_t total_streams = 0;
+ uint64_t total_bandwidth = 0;
+ uint32_t bus_util_factor = ISP_BUS_UTILIZATION_FACTOR;
+
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->state == ACTIVE ||
+ stream_info->state == START_PENDING) {
+ if (stream_info->stream_src < RDI_INTF_0) {
+ total_pix_bandwidth += stream_info->bandwidth;
+ num_pix_streams++;
+ } else {
+ total_rdi_bandwidth += stream_info->bandwidth;
+ num_rdi_streams++;
+ }
+ }
+ }
+ total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
+ total_streams = num_pix_streams + num_rdi_streams;
+ if (vfe_dev->bus_util_factor)
+ bus_util_factor = vfe_dev->bus_util_factor;
+ ISP_DBG("%s: bus_util_factor = %u\n", __func__, bus_util_factor);
+
+ if (total_streams == 1)
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ total_bandwidth,
+ (total_bandwidth * bus_util_factor / ISP_Q10));
+ else
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ (total_bandwidth + MSM_ISP_MIN_AB), (total_bandwidth *
+ bus_util_factor / ISP_Q10 + MSM_ISP_MIN_IB));
+ if (rc < 0)
+ pr_err("%s: update failed\n", __func__);
+
+ return rc;
+}
+
+static int msm_isp_axi_wait_for_cfg_done(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state camif_update)
+{
+ int rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
+ init_completion(&vfe_dev->stream_config_complete);
+ vfe_dev->axi_data.pipeline_update = camif_update;
+ spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
+ rc = wait_for_completion_timeout(
+ &vfe_dev->stream_config_complete,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+ if (rc == 0) {
+ pr_err("%s: wait timeout\n", __func__);
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+static int msm_isp_init_stream_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int rc = 0;
+ /*Set address for both PING & PONG register */
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PING_FLAG, 0);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n",
+ __func__);
+ return rc;
+ }
+
+ /* For burst stream of one capture, only one buffer
+ * is allocated. Duplicate ping buffer address to pong
+ * buffer to ensure hardware write to a valid address
+ */
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->runtime_num_burst_capture <= 1) {
+ msm_isp_cfg_pong_address(vfe_dev, stream_info);
+ } else {
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PONG_FLAG, 1);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n",
+ __func__);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static void msm_isp_deinit_stream_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ struct msm_isp_buffer *buf;
+
+ buf = stream_info->buf[i];
+ if (buf) {
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ }
+ }
+}
+
+static void msm_isp_get_stream_wm_mask(
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t *wm_reload_mask)
+{
+ int i;
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ *wm_reload_mask |= (1 << stream_info->wm[i]);
+}
+
+int msm_isp_axi_halt(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_halt_cmd *halt_cmd)
+{
+ int rc = 0;
+
+ if (halt_cmd->overflow_detected) {
+ /*Store current IRQ mask*/
+ if (vfe_dev->error_info.overflow_recover_irq_mask0 == 0) {
+ vfe_dev->hw_info->vfe_ops.core_ops.get_irq_mask(vfe_dev,
+ &vfe_dev->error_info.overflow_recover_irq_mask0,
+ &vfe_dev->error_info.overflow_recover_irq_mask1);
+ }
+ atomic_set(&vfe_dev->error_info.overflow_state,
+ OVERFLOW_DETECTED);
+ }
+
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+
+ if (halt_cmd->stop_camif) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
+ }
+
+ return rc;
+}
+
+int msm_isp_axi_reset(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_reset_cmd *reset_cmd)
+{
+ int rc = 0, i, j;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_timestamp timestamp;
+
+ if (!reset_cmd) {
+ pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd);
+ rc = -1;
+ return rc;
+ }
+
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
+ 0, reset_cmd->blocking);
+
+ for (i = 0, j = 0; j < axi_data->num_active_stream &&
+ i < MAX_NUM_STREAM; i++, j++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->stream_src >= VFE_AXI_SRC_MAX) {
+ rc = -1;
+ pr_err("%s invalid stream src = %d\n", __func__,
+ stream_info->stream_src);
+ break;
+ }
+ if (stream_info->state != ACTIVE) {
+ j--;
+ continue;
+ }
+
+ bufq = vfe_dev->buf_mgr->ops->get_bufq(vfe_dev->buf_mgr,
+ stream_info->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: bufq null %pK by handle %x\n", __func__,
+ bufq, stream_info->bufq_handle);
+ continue;
+ }
+
+ if (bufq->buf_type != ISP_SHARE_BUF) {
+ msm_isp_deinit_stream_ping_pong_reg(vfe_dev,
+ stream_info);
+ } else {
+ vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL,
+ ×tamp.buf_time,
+ reset_cmd->frame_id);
+ }
+ axi_data->src_info[SRC_TO_INTF(stream_info->stream_src)].
+ frame_id = reset_cmd->frame_id;
+ msm_isp_reset_burst_count_and_frame_drop(vfe_dev, stream_info);
+ }
+
+ if (rc < 0)
+ pr_err("%s Error! reset hw Timed out\n", __func__);
+
+ return rc;
+}
+
+int msm_isp_axi_restart(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_restart_cmd *restart_cmd)
+{
+ int rc = 0, i, j;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t wm_reload_mask = 0x0;
+
+ for (i = 0, j = 0; j < axi_data->num_active_stream &&
+ i < MAX_NUM_STREAM; i++, j++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->state != ACTIVE) {
+ j--;
+ continue;
+ }
+ msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
+ msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
+ }
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, wm_reload_mask);
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.restart(vfe_dev, 0,
+ restart_cmd->enable_camif);
+ if (rc < 0)
+ pr_err("%s Error restarting HW\n", __func__);
+
+ return rc;
+}
+
+static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
+ uint8_t cgc_override)
+{
+ int i = 0, j = 0;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ MAX_NUM_STREAM) {
+ return -EINVAL;
+ }
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ for (j = 0; j < stream_info->num_planes; j++) {
+ if (vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_cgc_override)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_cgc_override(vfe_dev,
+ stream_info->wm[j], cgc_override);
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
+ enum msm_isp_camif_update_state camif_update)
+{
+ int i, rc = 0;
+ uint8_t src_state, wait_for_complete = 0;
+ uint32_t wm_reload_mask = 0x0;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint8_t init_frm_drop = 0;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ MAX_NUM_STREAM) {
+ return -EINVAL;
+ }
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX)
+ src_state = axi_data->src_info[
+ SRC_TO_INTF(stream_info->stream_src)].active;
+ else {
+ pr_err("%s: invalid src info index\n", __func__);
+ return -EINVAL;
+ }
+
+ msm_isp_calculate_bandwidth(axi_data, stream_info);
+ msm_isp_reset_framedrop(vfe_dev, stream_info);
+ init_frm_drop = stream_info->init_frame_drop;
+ msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
+ rc = msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
+ if (rc < 0) {
+ pr_err("%s: No buffer for stream %d\n",
+ __func__,
+ HANDLE_TO_IDX(
+ stream_cfg_cmd->stream_handle[i]));
+ return rc;
+ }
+
+ stream_info->state = START_PENDING;
+ if (src_state) {
+ wait_for_complete = 1;
+ } else {
+ if (vfe_dev->dump_reg)
+ msm_camera_io_dump_2(vfe_dev->vfe_base, 0x900);
+
+ /*Configure AXI start bits to start immediately*/
+ msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info);
+ stream_info->state = ACTIVE;
+ }
+ if (SRC_TO_INTF(stream_info->stream_src) != VFE_PIX_0 &&
+ stream_info->stream_src < VFE_AXI_SRC_MAX) {
+ vfe_dev->axi_data.src_info[SRC_TO_INTF(
+ stream_info->stream_src)].frame_id =
+ init_frm_drop;
+ }
+ }
+ msm_isp_update_stream_bandwidth(vfe_dev);
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, wm_reload_mask);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, 0xF);
+ msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+ msm_isp_update_rdi_output_count(vfe_dev, stream_cfg_cmd);
+ if (camif_update == ENABLE_CAMIF) {
+ atomic_set(&vfe_dev->error_info.overflow_state,
+ NO_OVERFLOW);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 0;
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, camif_update);
+ }
+
+ if (wait_for_complete) {
+ vfe_dev->axi_data.stream_update = stream_cfg_cmd->num_streams;
+ rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
+ }
+
+ return rc;
+}
+
+static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
+ enum msm_isp_camif_update_state camif_update)
+{
+ int i, rc = 0;
+ uint8_t wait_for_complete_for_this_stream = 0, cur_stream_cnt = 0;
+ uint8_t wait_for_complete = 0;
+ struct msm_vfe_axi_stream *stream_info = NULL;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int ext_read =
+ axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ;
+
+ if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM ||
+ stream_cfg_cmd->num_streams == 0)
+ return -EINVAL;
+
+ msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+ msm_isp_update_rdi_output_count(vfe_dev, stream_cfg_cmd);
+ cur_stream_cnt = msm_isp_get_curr_stream_cnt(vfe_dev);
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
+ MAX_NUM_STREAM) {
+ return -EINVAL;
+ }
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ wait_for_complete_for_this_stream = 0;
+ stream_info->state = STOP_PENDING;
+ if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ /* We dont get reg update IRQ for raw snapshot
+ * so frame skip cant be ocnfigured
+ */
+ if ((camif_update != DISABLE_CAMIF_IMMEDIATELY) &&
+ (!ext_read))
+ wait_for_complete_for_this_stream = 1;
+ } else if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->runtime_num_burst_capture == 0) {
+ /* Configure AXI writemasters to stop immediately
+ * since for burst case, write masters already skip
+ * all frames.
+ */
+ if (stream_info->stream_src == RDI_INTF_0 ||
+ stream_info->stream_src == RDI_INTF_1 ||
+ stream_info->stream_src == RDI_INTF_2)
+ wait_for_complete_for_this_stream = 1;
+ } else {
+ if ((camif_update != DISABLE_CAMIF_IMMEDIATELY) &&
+ (!ext_read) &&
+ !(stream_info->stream_src == RDI_INTF_0 ||
+ stream_info->stream_src == RDI_INTF_1 ||
+ stream_info->stream_src == RDI_INTF_2))
+ wait_for_complete_for_this_stream = 1;
+ }
+ if (!wait_for_complete_for_this_stream) {
+ msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info);
+ stream_info->state = INACTIVE;
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ }
+ wait_for_complete |= wait_for_complete_for_this_stream;
+ }
+ if (wait_for_complete) {
+ vfe_dev->axi_data.stream_update = stream_cfg_cmd->num_streams;
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
+ if (rc < 0) {
+ pr_err("%s: wait for config done failed\n", __func__);
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(
+ stream_cfg_cmd->stream_handle[i])];
+ stream_info->state = STOP_PENDING;
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info);
+ stream_info->state = INACTIVE;
+ }
+ }
+ }
+ if (camif_update == DISABLE_CAMIF) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF);
+ } else if ((camif_update == DISABLE_CAMIF_IMMEDIATELY) ||
+ (ext_read)) {
+ /*during stop immediately, stop output then stop input*/
+ if (!ext_read)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF_IMMEDIATELY);
+ }
+ if (cur_stream_cnt == 0) {
+ vfe_dev->ignore_error = 1;
+ vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 1, 1);
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+ vfe_dev->ignore_error = 0;
+ }
+ msm_isp_update_stream_bandwidth(vfe_dev);
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ msm_isp_deinit_stream_ping_pong_reg(vfe_dev, stream_info);
+ }
+
+ return rc;
+}
+
+
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ enum msm_isp_camif_update_state camif_update;
+
+ rc = msm_isp_axi_check_stream_state(vfe_dev, stream_cfg_cmd);
+ if (rc < 0) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ return rc;
+ }
+
+ if (axi_data->num_active_stream == 0) {
+ /*Configure UB*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
+ }
+ camif_update = msm_isp_get_camif_update_state(vfe_dev, stream_cfg_cmd);
+
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1);
+
+ rc = msm_isp_start_axi_stream(
+ vfe_dev, stream_cfg_cmd, camif_update);
+ } else {
+ rc = msm_isp_stop_axi_stream(
+ vfe_dev, stream_cfg_cmd, camif_update);
+
+ msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 0);
+ }
+
+ if (rc < 0)
+ pr_err("%s: start/stop stream failed\n", __func__);
+ return rc;
+}
+
+static int msm_isp_request_frame(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t request_frm_num)
+{
+ struct msm_vfe32_axi_stream_request_cmd stream_cfg_cmd;
+ int rc = 0;
+ uint32_t pingpong_status, pingpong_bit, wm_reload_mask = 0x0;
+
+ if (!stream_info->controllable_output)
+ return 0;
+
+ if (!request_frm_num) {
+ pr_err("%s: Invalid frame request.\n", __func__);
+ return -EINVAL;
+ }
+
+ stream_info->request_frm_num += request_frm_num;
+
+ stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle;
+ stream_cfg_cmd.frame_skip_pattern = NO_SKIP;
+ stream_cfg_cmd.init_frame_drop = 0;
+ stream_cfg_cmd.burst_count = stream_info->request_frm_num;
+ msm_isp_calculate_framedrop(&vfe_dev->axi_data, &stream_cfg_cmd);
+ msm_isp_reset_framedrop(vfe_dev, stream_info);
+
+ if (stream_info->request_frm_num != request_frm_num) {
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
+ vfe_dev);
+ pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
+
+ if (!stream_info->buf[pingpong_bit]) {
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info,
+ pingpong_status, pingpong_bit);
+ if (rc) {
+ pr_err("%s:%d fail to set ping pong address\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ }
+
+ if (!stream_info->buf[!pingpong_bit] && request_frm_num > 1) {
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info,
+ ~pingpong_status, !pingpong_bit);
+ if (rc) {
+ pr_err("%s:%d fail to set ping pong address\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ }
+ } else {
+ if (!stream_info->buf[0]) {
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info,
+ VFE_PING_FLAG, 0);
+ if (rc) {
+ pr_err("%s:%d fail to set ping pong address\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ }
+
+ if (!stream_info->buf[1] && request_frm_num > 1) {
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info,
+ VFE_PONG_FLAG, 1);
+ if (rc) {
+ pr_err("%s:%d fail to set ping pong address\n",
+ __func__, __LINE__);
+ return rc;
+ }
+ }
+
+ msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
+ wm_reload_mask);
+ }
+
+ return rc;
+}
+
+int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i, j;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
+ struct msm_vfe_axi_stream_cfg_update_info *update_info;
+ uint32_t frame_id;
+ struct msm_isp_timestamp timestamp;
+
+ if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG &&
+ atomic_read(&axi_data->axi_cfg_update)) {
+ pr_err("%s: AXI stream config updating\n", __func__);
+ return -EBUSY;
+ }
+
+ /*num_stream is uint32 and update_info[] bound by MAX_NUM_STREAM*/
+ if (update_cmd->num_streams > MAX_NUM_STREAM)
+ return -EINVAL;
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ /*check array reference bounds*/
+ if (HANDLE_TO_IDX(update_info->stream_handle) >=
+ MAX_NUM_STREAM) {
+ return -EINVAL;
+ }
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(update_info->stream_handle)];
+ if (stream_info->state != ACTIVE &&
+ stream_info->state != INACTIVE) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(update_info->stream_handle)];
+
+ switch (update_cmd->update_type) {
+ case ENABLE_STREAM_BUF_DIVERT:
+ stream_info->buf_divert = 1;
+ break;
+ case DISABLE_STREAM_BUF_DIVERT:
+ msm_isp_get_timestamp(×tamp, vfe_dev);
+ stream_info->buf_divert = 0;
+ frame_id = vfe_dev->axi_data.
+ src_info[SRC_TO_INTF(
+ stream_info->stream_src)].
+ frame_id;
+ vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_DIVERTED,
+ ×tamp.buf_time, frame_id);
+ break;
+ case UPDATE_STREAM_FRAMEDROP_PATTERN: {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(
+ update_info->skip_pattern);
+ if (update_info->skip_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+ if (stream_info->stream_type == BURST_STREAM) {
+ stream_info->runtime_framedrop_update_burst = 1;
+ } else {
+ stream_info->runtime_init_frame_drop = 0;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ }
+ break;
+ }
+ case UPDATE_STREAM_AXI_CONFIG: {
+ for (j = 0; j < stream_info->num_planes; j++) {
+ stream_info->plane_cfg[j] =
+ update_info->plane_cfg[j];
+ }
+ stream_info->output_format = update_info->output_format;
+ if (stream_info->state == ACTIVE) {
+ stream_info->state = PAUSE_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info);
+ stream_info->state = PAUSING;
+ atomic_set(&axi_data->axi_cfg_update,
+ UPDATE_REQUESTED);
+ } else {
+ for (j = 0; j < stream_info->num_planes; j++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ stream_info->runtime_output_format =
+ stream_info->output_format;
+ }
+ break;
+ }
+ case UPDATE_STREAM_REQUEST_FRAMES: {
+ rc = msm_isp_request_frame(vfe_dev, stream_info,
+ update_info->frame_id);
+ if (rc)
+ pr_err("%s failed to request frame!\n",
+ __func__);
+ break;
+ }
+ default:
+ pr_err("%s: Invalid update type\n", __func__);
+ return -EINVAL;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ int i, rc = 0;
+ struct msm_isp_buffer *done_buf = NULL;
+ uint32_t comp_mask = 0, wm_mask = 0;
+ uint32_t pingpong_status, stream_idx;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_composite_info *comp_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t pingpong_bit = 0, frame_id = 0;
+
+ comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ wm_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(comp_mask || wm_mask))
+ return;
+
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+ for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
+ rc = 0;
+ comp_info = &axi_data->composite_info[i];
+ if (comp_mask & (1 << i)) {
+ stream_idx = HANDLE_TO_IDX(comp_info->stream_handle);
+ if ((!comp_info->stream_handle) ||
+ (stream_idx >= MAX_NUM_STREAM)) {
+ pr_err("%s: Invalid handle for composite irq\n",
+ __func__);
+ } else {
+ stream_idx =
+ HANDLE_TO_IDX(comp_info->stream_handle);
+ stream_info =
+ &axi_data->stream_info[stream_idx];
+
+ pingpong_bit = (~(pingpong_status >>
+ stream_info->wm[0]) & 0x1);
+
+ if (stream_info->stream_type == BURST_STREAM)
+ stream_info->
+ runtime_num_burst_capture--;
+
+ msm_isp_get_done_buf(vfe_dev, stream_info,
+ pingpong_status, &done_buf);
+ if (stream_info->stream_type ==
+ CONTINUOUS_STREAM ||
+ stream_info->
+ runtime_num_burst_capture > 1) {
+ rc = msm_isp_cfg_ping_pong_address(
+ vfe_dev, stream_info,
+ pingpong_status,
+ pingpong_bit);
+ }
+ frame_id = vfe_dev->axi_data.
+ src_info[SRC_TO_INTF(
+ stream_info->stream_src)].
+ frame_id;
+ stream_info->frame_id = frame_id;
+ ISP_DBG("%s: stream id:%d frame id:%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->frame_id);
+ if (done_buf && !rc)
+ msm_isp_process_done_buf(vfe_dev,
+ stream_info, done_buf, ts);
+ }
+ }
+ wm_mask &= ~(comp_info->stream_composite_mask);
+ }
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (wm_mask & (1 << i)) {
+ stream_idx = HANDLE_TO_IDX(axi_data->free_wm[i]);
+ if ((!axi_data->free_wm[i]) ||
+ (stream_idx >= MAX_NUM_STREAM)) {
+ pr_err("%s: Invalid handle for wm irq\n",
+ __func__);
+ continue;
+ }
+ stream_info = &axi_data->stream_info[stream_idx];
+
+ pingpong_bit = (~(pingpong_status >>
+ stream_info->wm[0]) & 0x1);
+
+ if (stream_info->stream_type == BURST_STREAM)
+ stream_info->runtime_num_burst_capture--;
+
+ msm_isp_get_done_buf(vfe_dev, stream_info,
+ pingpong_status, &done_buf);
+ if (stream_info->stream_type == CONTINUOUS_STREAM ||
+ stream_info->runtime_num_burst_capture > 1) {
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status,
+ pingpong_bit);
+ }
+ stream_info->frame_id = frame_id;
+ ISP_DBG("%s: stream id:%d frame id:%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->frame_id);
+ if (done_buf && !rc)
+ msm_isp_process_done_buf(vfe_dev,
+ stream_info, done_buf, ts);
+ }
+ }
+}
+int msm_isp_user_buf_done(struct vfe_device *vfe_dev,
+ struct msm_isp32_event_data *buf_cmd)
+{
+ int rc = 0;
+ struct msm_isp32_event_data buf_event;
+
+ memset(&buf_event, 0, sizeof(buf_event));
+ buf_event.input_intf = buf_cmd->input_intf;
+ buf_event.frame_id = buf_cmd->frame_id;
+ buf_event.timestamp = buf_cmd->timestamp;
+ buf_event.u.buf_done.session_id =
+ buf_cmd->u.buf_done.session_id;
+ buf_event.u.buf_done.stream_id =
+ buf_cmd->u.buf_done.stream_id;
+ buf_event.u.buf_done.output_format =
+ buf_cmd->u.buf_done.output_format;
+ buf_event.u.buf_done.buf_idx =
+ buf_cmd->u.buf_done.buf_idx;
+ buf_event.u.buf_done.handle =
+ buf_cmd->u.buf_done.handle;
+
+ vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ buf_event.u.buf_done.handle,
+ buf_event.u.buf_done.buf_idx,
+ &buf_event.timestamp, buf_event.frame_id,
+ buf_event.u.buf_done.output_format);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.h
new file mode 100644
index 0000000..5d89a84
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_ISP_AXI_UTIL_H__
+#define __MSM_ISP_AXI_UTIL_H__
+
+#include "msm_isp_32.h"
+
+int msm_isp_axi_create_stream(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_destroy_stream(
+ struct msm_vfe_axi_shared_data *axi_data, int stream_idx);
+
+int msm_isp_validate_axi_request(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_wm(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info);
+
+void msm_isp_axi_reserve_comp_mask(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info);
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
+
+void msm_isp_calculate_framedrop(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd);
+void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+
+void msm_isp_start_avtimer(void);
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev);
+int msm_isp_axi_halt(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_halt_cmd *halt_cmd);
+int msm_isp_axi_reset(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_reset_cmd *reset_cmd);
+int msm_isp_axi_restart(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_restart_cmd *restart_cmd);
+int msm_isp_user_buf_done(struct vfe_device *vfe_dev,
+ struct msm_isp32_event_data *buf_cmd);
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
+ uint8_t input_src);
+
+void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev,
+ uint8_t input_src);
+void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
+ enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts);
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info);
+#endif /* __MSM_ISP_AXI_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.c
new file mode 100644
index 0000000..8273298
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.c
@@ -0,0 +1,709 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/io.h>
+#include <linux/atomic.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_isp.h>
+#include "msm_isp_util_32.h"
+#include "msm_isp_stats_util_32.h"
+
+static int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
+ struct msm_isp_buffer **done_buf)
+{
+ int rc = -1;
+ struct msm_isp_buffer *buf;
+ uint32_t pingpong_bit = 0;
+ uint32_t bufq_handle = stream_info->bufq_handle;
+ uint32_t stats_pingpong_offset;
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle);
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type ||
+ stats_idx >= MSM_ISP_STATS_MAX) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stats_pingpong_offset =
+ vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
+ stats_idx];
+
+ pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+ rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
+ vfe_dev->pdev->id, bufq_handle,
+ MSM_ISP_INVALID_BUF_INDEX, &buf);
+ if (rc < 0) {
+ vfe_dev->error_info.stats_framedrop_count[stats_idx]++;
+ return rc;
+ }
+
+ if (buf->num_planes != 1) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ rc = -EINVAL;
+ goto buf_error;
+ }
+
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
+ vfe_dev, stream_info,
+ pingpong_status, buf->mapped_info[0].paddr +
+ stream_info->buffer_offset);
+
+ if (stream_info->buf[pingpong_bit] && done_buf)
+ *done_buf = stream_info->buf[pingpong_bit];
+
+ stream_info->buf[pingpong_bit] = buf;
+ return 0;
+buf_error:
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return rc;
+}
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts)
+{
+ int i, j, rc;
+ struct msm_isp32_event_data buf_event;
+ struct msm_isp_stats_event *stats_event = &buf_event.u.stats;
+ struct msm_isp_buffer *done_buf;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ uint32_t pingpong_status;
+ uint32_t comp_stats_type_mask = 0, atomic_stats_mask = 0;
+ uint32_t stats_comp_mask = 0, stats_irq_mask = 0;
+ uint32_t num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+ stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ stats_irq_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(stats_comp_mask || stats_irq_mask))
+ return;
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+
+ /*
+ * If any of composite mask is set, clear irq bits from mask,
+ * they will be restored by comp mask
+ */
+ if (stats_comp_mask) {
+ for (j = 0; j < num_stats_comp_mask; j++) {
+ stats_irq_mask &= ~atomic_read(
+ &vfe_dev->stats_data.stats_comp_mask[j]);
+ }
+ }
+
+ for (j = 0; j < num_stats_comp_mask; j++) {
+ atomic_stats_mask = atomic_read(
+ &vfe_dev->stats_data.stats_comp_mask[j]);
+ if (!stats_comp_mask) {
+ stats_irq_mask &= ~atomic_stats_mask;
+ } else {
+ /* restore irq bits from composite mask */
+ if (stats_comp_mask & (1 << j))
+ stats_irq_mask |= atomic_stats_mask;
+ }
+ /* if no irq bits set from this composite mask continue*/
+ if (!stats_irq_mask)
+ continue;
+ memset(&buf_event, 0, sizeof(struct msm_isp32_event_data));
+ buf_event.timestamp = ts->event_time;
+ buf_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ buf_event.input_intf = VFE_PIX_0;
+ pingpong_status = vfe_dev->hw_info->
+ vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type;
+ i++) {
+ if (!(stats_irq_mask & (1 << i)))
+ continue;
+
+ stats_irq_mask &= ~(1 << i);
+ stream_info = &vfe_dev->stats_data.stream_info[i];
+ done_buf = NULL;
+ msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, &done_buf);
+ if (done_buf) {
+ rc = vfe_dev->buf_mgr->ops->buf_divert(
+ vfe_dev->buf_mgr, done_buf->bufq_handle,
+ done_buf->buf_idx, &ts->buf_time,
+ vfe_dev->axi_data.
+ src_info[VFE_PIX_0].frame_id);
+ if (rc != 0)
+ continue;
+
+ stats_event->stats_buf_idxs
+ [stream_info->stats_type] =
+ done_buf->buf_idx;
+ if (!stream_info->composite_flag) {
+ stats_event->stats_mask =
+ 1 << stream_info->stats_type;
+ ISP_DBG("%s: stats frameid: 0x%x %d\n",
+ __func__, buf_event.frame_id,
+ stream_info->stats_type);
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_STATS_NOTIFY +
+ stream_info->stats_type,
+ &buf_event);
+ } else {
+ comp_stats_type_mask |=
+ 1 << stream_info->stats_type;
+ }
+ }
+ }
+
+ if (comp_stats_type_mask) {
+ ISP_DBG("%s: comp_stats frameid: 0x%x, 0x%x\n",
+ __func__, buf_event.frame_id,
+ comp_stats_type_mask);
+ stats_event->stats_mask = comp_stats_type_mask;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_COMP_STATS_NOTIFY, &buf_event);
+ comp_stats_type_mask = 0;
+ }
+ }
+}
+
+int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ uint32_t stats_idx;
+
+ if (!(vfe_dev->hw_info->stats_hw_info->stats_capability_mask &
+ (1 << stream_req_cmd->stats_type))) {
+ pr_err("%s: Stats type not supported\n", __func__);
+ return rc;
+ }
+
+ stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_stats_idx(stream_req_cmd->stats_type);
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[stats_idx];
+ if (stream_info->state != STATS_AVAILABLE) {
+ pr_err("%s: Stats already requested\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->framedrop_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid framedrop pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_req_cmd->irq_subsample_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid irq subsample pattern\n", __func__);
+ return rc;
+ }
+
+ stream_info->session_id = stream_req_cmd->session_id;
+ stream_info->stream_id = stream_req_cmd->stream_id;
+ stream_info->composite_flag = stream_req_cmd->composite_flag;
+ stream_info->stats_type = stream_req_cmd->stats_type;
+ stream_info->buffer_offset = stream_req_cmd->buffer_offset;
+ stream_info->framedrop_pattern = stream_req_cmd->framedrop_pattern;
+ stream_info->init_stats_frame_drop = stream_req_cmd->init_frame_drop;
+ stream_info->irq_subsample_pattern =
+ stream_req_cmd->irq_subsample_pattern;
+ stream_info->state = STATS_INACTIVE;
+
+ if ((vfe_dev->stats_data.stream_handle_cnt << 8) == 0)
+ vfe_dev->stats_data.stream_handle_cnt++;
+
+ stream_req_cmd->stream_handle =
+ (++vfe_dev->stats_data.stream_handle_cnt) << 8 | stats_idx;
+
+ stream_info->stream_handle = stream_req_cmd->stream_handle;
+ return 0;
+}
+
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ uint32_t framedrop_period;
+ uint32_t stats_idx;
+
+ rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd);
+ if (rc < 0) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ stats_idx = STATS_IDX(stream_req_cmd->stream_handle);
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[stats_idx];
+
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
+
+ if (stream_req_cmd->framedrop_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+
+ if (!stream_info->composite_flag)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+
+ if (stream_info->init_stats_frame_drop == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev,
+ stream_info);
+
+ return rc;
+}
+
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = -1;
+ struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ int stats_idx = STATS_IDX(stream_release_cmd->stream_handle);
+ struct msm_vfe_stats_stream *stream_info = NULL;
+
+ if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, stats_idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[stats_idx];
+ if (stream_info->state == STATS_AVAILABLE) {
+ pr_err("%s: stream already release\n", __func__);
+ return rc;
+ } else if (stream_info->state != STATS_INACTIVE) {
+ stream_cfg_cmd.enable = 0;
+ stream_cfg_cmd.num_streams = 1;
+ stream_cfg_cmd.stream_handle[0] =
+ stream_release_cmd->stream_handle;
+ rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+ }
+
+ if (!stream_info->composite_flag)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+
+ vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info);
+ memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream));
+ return 0;
+}
+
+static int msm_isp_init_stats_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int rc = 0;
+
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: no buf configured for stream: 0x%x\n",
+ __func__, stream_info->stream_handle);
+ return -EINVAL;
+ }
+
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PING_FLAG, NULL);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n", __func__);
+ return rc;
+ }
+ rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PONG_FLAG, NULL);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n", __func__);
+ return rc;
+ }
+ return rc;
+}
+
+static void msm_isp_deinit_stats_ping_pong_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int i;
+ struct msm_isp_buffer *buf;
+
+ for (i = 0; i < 2; i++) {
+ buf = stream_info->buf[i];
+ if (buf)
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ }
+}
+
+void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ stream_info = &stats_data->stream_info[i];
+ if (stream_info->state != STATS_ACTIVE)
+ continue;
+
+ if (stream_info->init_stats_frame_drop) {
+ stream_info->init_stats_frame_drop--;
+ if (stream_info->init_stats_frame_drop == 0) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(
+ vfe_dev, stream_info);
+ }
+ }
+ }
+}
+
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t stats_mask = 0, comp_stats_mask = 0;
+ uint32_t enable = 0;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ if (stats_data->stream_info[i].state == STATS_START_PENDING ||
+ stats_data->stream_info[i].state ==
+ STATS_STOP_PENDING) {
+ stats_mask |= i;
+ enable = stats_data->stream_info[i].state ==
+ STATS_START_PENDING ? 1 : 0;
+ stats_data->stream_info[i].state =
+ stats_data->stream_info[i].state ==
+ STATS_START_PENDING ?
+ STATS_STARTING : STATS_STOPPING;
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, BIT(i), enable);
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, BIT(i), enable);
+ } else if (stats_data->stream_info[i].state == STATS_STARTING ||
+ stats_data->stream_info[i].state == STATS_STOPPING) {
+ if (stats_data->stream_info[i].composite_flag)
+ comp_stats_mask |= i;
+ stats_data->stream_info[i].state =
+ stats_data->stream_info[i].state ==
+ STATS_STARTING ? STATS_ACTIVE : STATS_INACTIVE;
+ }
+ }
+ atomic_sub(1, &stats_data->stats_update);
+ if (!atomic_read(&stats_data->stats_update))
+ complete(&vfe_dev->stats_config_complete);
+}
+
+static int msm_isp_stats_wait_for_cfg_done(struct vfe_device *vfe_dev)
+{
+ int rc;
+
+ init_completion(&vfe_dev->stats_config_complete);
+ atomic_set(&vfe_dev->stats_data.stats_update, 2);
+ rc = wait_for_completion_timeout(
+ &vfe_dev->stats_config_complete,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+ if (rc == 0) {
+ pr_err("%s: wait timeout\n", __func__);
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ uint32_t stats_mask = 0, idx;
+
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+ stats_mask |= 1 << idx;
+ }
+
+ if (vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ }
+ return 0;
+}
+
+static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t stats_mask = 0, idx;
+ uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
+ num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+ rc = vfe_dev->hw_info->vfe_ops.stats_ops.check_streams(
+ stats_data->stream_info);
+ if (rc < 0)
+ return rc;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[idx];
+ if (stream_info->stream_handle !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received\n",
+ __func__, stream_cfg_cmd->stream_handle[i]);
+ continue;
+ }
+
+ if (stream_info->composite_flag > num_stats_comp_mask) {
+ pr_err("%s: comp grp %d exceed max %d\n",
+ __func__, stream_info->composite_flag,
+ num_stats_comp_mask);
+ return -EINVAL;
+ }
+ rc = msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info);
+ if (rc < 0) {
+ pr_err("%s: No buffer for stream%d type:%d stmID:0x%x\n",
+ __func__, idx, stream_info->stats_type,
+ stream_info->stream_id);
+ return rc;
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
+ stream_info->state = STATS_START_PENDING;
+ else
+ stream_info->state = STATS_ACTIVE;
+
+ stats_data->num_active_stream++;
+ stats_mask |= 1 << idx;
+
+ if (stream_info->composite_flag > 0)
+ comp_stats_mask[stream_info->composite_flag-1] |=
+ 1 << idx;
+
+ ISP_DBG("%s: stats_mask %x %x active streams %d\n",
+ __func__, comp_stats_mask[0],
+ comp_stats_mask[1],
+ stats_data->num_active_stream);
+
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ for (i = 0; i < num_stats_comp_mask; i++) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, comp_stats_mask[i], 1);
+ }
+ }
+ return rc;
+}
+
+static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t stats_mask = 0, idx;
+ uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[idx];
+ if (stream_info->stream_handle !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received\n",
+ __func__, stream_cfg_cmd->stream_handle[i]);
+ continue;
+ }
+
+ if (stream_info->composite_flag > num_stats_comp_mask) {
+ pr_err("%s: comp grp %d exceed max %d\n",
+ __func__, stream_info->composite_flag,
+ num_stats_comp_mask);
+ return -EINVAL;
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
+ stream_info->state = STATS_STOP_PENDING;
+ else
+ stream_info->state = STATS_INACTIVE;
+
+ stats_data->num_active_stream--;
+ stats_mask |= 1 << idx;
+
+ if (stream_info->composite_flag > 0)
+ comp_stats_mask[stream_info->composite_flag-1] |=
+ 1 << idx;
+
+ ISP_DBG("%s: stats_mask %x %x active streams %d\n",
+ __func__, comp_stats_mask[0],
+ comp_stats_mask[1],
+ stats_data->num_active_stream);
+ }
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ for (i = 0; i < num_stats_comp_mask; i++) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
+ vfe_dev, comp_stats_mask[i], 0);
+ }
+ }
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+ if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s Invalid stats index %d", __func__, idx);
+ return -EINVAL;
+ }
+
+ stream_info = &stats_data->stream_info[idx];
+ msm_isp_deinit_stats_ping_pong_reg(vfe_dev, stream_info);
+ }
+ return rc;
+}
+
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
+
+ if (vfe_dev->stats_data.num_active_stream == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
+
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
+ if (stream_cfg_cmd->enable) {
+ msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
+
+ rc = msm_isp_start_stats_stream(vfe_dev, stream_cfg_cmd);
+ } else {
+ rc = msm_isp_stop_stats_stream(vfe_dev, stream_cfg_cmd);
+
+ msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
+ }
+
+ return rc;
+}
+
+int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
+ struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
+
+ /*validate request*/
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ /*check array reference bounds*/
+ if (STATS_IDX(update_info->stream_handle)
+ > vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+ pr_err("%s: stats idx %d out of bound!", __func__,
+ STATS_IDX(update_info->stream_handle));
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info = &update_cmd->update_info[i];
+ stream_info = &stats_data->stream_info[
+ STATS_IDX(update_info->stream_handle)];
+ if (stream_info->stream_handle !=
+ update_info->stream_handle) {
+ pr_err("%s: stats stream handle %x %x mismatch!\n",
+ __func__, stream_info->stream_handle,
+ update_info->stream_handle);
+ continue;
+ }
+
+ switch (update_cmd->update_type) {
+ case UPDATE_STREAM_STATS_FRAMEDROP_PATTERN: {
+ uint32_t framedrop_period =
+ msm_isp_get_framedrop_period(
+ update_info->skip_pattern);
+ if (update_info->skip_pattern == SKIP_ALL)
+ stream_info->framedrop_pattern = 0x0;
+ else
+ stream_info->framedrop_pattern = 0x1;
+ stream_info->framedrop_period = framedrop_period - 1;
+ if (stream_info->init_stats_frame_drop == 0)
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(
+ vfe_dev, stream_info);
+ break;
+ }
+
+ default:
+ pr_err("%s: Invalid update type\n", __func__);
+ return -EINVAL;
+ }
+ }
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.h
new file mode 100644
index 0000000..11c0c28
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_ISP_STATS_UTIL_H__
+#define __MSM_ISP_STATS_UTIL_H__
+
+#include "msm_isp_32.h"
+#define STATS_IDX(idx) (idx & 0xFF)
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct msm_isp_timestamp *ts);
+int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd);
+void msm_isp_stats_stream_update(struct vfe_device *vfe_dev);
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev);
+#endif /* __MSM_ISP_STATS_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.c
new file mode 100644
index 0000000..f6ca41c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.c
@@ -0,0 +1,1939 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <linux/ratelimit.h>
+
+#include "msm.h"
+#include "msm_isp_util_32.h"
+#include "msm_isp_axi_util_32.h"
+#include "msm_isp_stats_util_32.h"
+#include "msm_camera_io_util.h"
+#include "cam_smmu_api.h"
+
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+
+#define MAX_ISP_V4l2_EVENTS 100
+#define MAX_ISP_REG_LIST 100
+static DEFINE_MUTEX(bandwidth_mgr_mutex);
+static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
+
+static uint64_t msm_isp_cpp_clk_rate;
+
+#define VFE40_8974V2_VERSION 0x1001001A
+static struct msm_bus_vectors msm_isp_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors msm_isp_ping_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = MSM_ISP_MIN_AB,
+ .ib = MSM_ISP_MIN_IB,
+ },
+};
+
+static struct msm_bus_vectors msm_isp_pong_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = MSM_ISP_MIN_AB,
+ .ib = MSM_ISP_MIN_IB,
+ },
+};
+
+static struct msm_bus_paths msm_isp_bus_client_config[] = {
+ {
+ ARRAY_SIZE(msm_isp_init_vectors),
+ msm_isp_init_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_isp_ping_vectors),
+ msm_isp_ping_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_isp_pong_vectors),
+ msm_isp_pong_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata msm_isp_bus_client_pdata = {
+ msm_isp_bus_client_config,
+ NULL,
+ ARRAY_SIZE(msm_isp_bus_client_config),
+ .name = "msm_camera_isp",
+ 0,
+};
+
+
+void msm_camera_io_dump_2(void __iomem *addr, int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 __iomem *p = (u32 __iomem *) addr;
+ u32 data;
+
+ pr_err("%s: %pK %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+#ifdef CONFIG_COMPAT
+ snprintf(p_str, 20, "%016lx: ", (unsigned long) p);
+ p_str += 18;
+#else
+ snprintf(p_str, 12, "%08lx: ", (unsigned long) p);
+ p_str += 10;
+#endif
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ pr_err("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ pr_err("%s\n", line_str);
+}
+
+void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format)
+{
+ int i;
+ char text[5];
+
+ text[4] = '\0';
+ for (i = 0; i < 4; i++) {
+ text[i] = (char)(((fourcc_format) >> (i * 8)) & 0xFF);
+ if ((text[i] < '0') || (text[i] > 'z')) {
+ pr_err("%s: Invalid output format %d (unprintable)\n",
+ origin, fourcc_format);
+ return;
+ }
+ }
+ pr_err("%s: Invalid output format %s\n",
+ origin, text);
+}
+
+int msm_isp_init_bandwidth_mgr(enum msm_isp_hw_client client)
+{
+ int rc = 0;
+
+ mutex_lock(&bandwidth_mgr_mutex);
+ isp_bandwidth_mgr.client_info[client].active = 1;
+ if (isp_bandwidth_mgr.use_count++) {
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return rc;
+ }
+ isp_bandwidth_mgr.bus_client =
+ msm_bus_scale_register_client(&msm_isp_bus_client_pdata);
+ if (!isp_bandwidth_mgr.bus_client) {
+ pr_err("%s: client register failed\n", __func__);
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return -EINVAL;
+ }
+
+ isp_bandwidth_mgr.bus_vector_active_idx = 1;
+ msm_bus_scale_client_update_request(
+ isp_bandwidth_mgr.bus_client,
+ isp_bandwidth_mgr.bus_vector_active_idx);
+
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return 0;
+}
+
+int msm_isp_update_bandwidth(enum msm_isp_hw_client client,
+ uint64_t ab, uint64_t ib)
+{
+ int i;
+ struct msm_bus_paths *path;
+
+ mutex_lock(&bandwidth_mgr_mutex);
+ if (!isp_bandwidth_mgr.use_count ||
+ !isp_bandwidth_mgr.bus_client) {
+ pr_err("%s:error bandwidth manager inactive use_cnt:%d bus_clnt:%d\n",
+ __func__, isp_bandwidth_mgr.use_count,
+ isp_bandwidth_mgr.bus_client);
+ return -EINVAL;
+ }
+
+ isp_bandwidth_mgr.client_info[client].ab = ab;
+ isp_bandwidth_mgr.client_info[client].ib = ib;
+ ALT_VECTOR_IDX(isp_bandwidth_mgr.bus_vector_active_idx);
+ path =
+ &(msm_isp_bus_client_pdata.usecase[
+ isp_bandwidth_mgr.bus_vector_active_idx]);
+ path->vectors[0].ab = 0;
+ path->vectors[0].ib = 0;
+ for (i = 0; i < MAX_ISP_CLIENT; i++) {
+ if (isp_bandwidth_mgr.client_info[i].active) {
+ path->vectors[0].ab +=
+ isp_bandwidth_mgr.client_info[i].ab;
+ path->vectors[0].ib +=
+ isp_bandwidth_mgr.client_info[i].ib;
+ }
+ }
+ ISP_DBG("%s: Total AB = %llu IB = %llu\n", __func__,
+ path->vectors[0].ab, path->vectors[0].ib);
+ msm_bus_scale_client_update_request(isp_bandwidth_mgr.bus_client,
+ isp_bandwidth_mgr.bus_vector_active_idx);
+ /* Insert into circular buffer */
+ msm_isp_update_req_history(isp_bandwidth_mgr.bus_client,
+ path->vectors[0].ab,
+ path->vectors[0].ib,
+ isp_bandwidth_mgr.client_info,
+ sched_clock());
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return 0;
+}
+
+void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client)
+{
+ if (client >= MAX_ISP_CLIENT) {
+ pr_err("invalid Client id %d", client);
+ return;
+ }
+ mutex_lock(&bandwidth_mgr_mutex);
+ memset(&isp_bandwidth_mgr.client_info[client], 0,
+ sizeof(struct msm_isp_bandwidth_info));
+ if (--isp_bandwidth_mgr.use_count) {
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return;
+ }
+
+ if (!isp_bandwidth_mgr.bus_client) {
+ pr_err("%s:%d error: bus client invalid\n", __func__, __LINE__);
+ mutex_unlock(&bandwidth_mgr_mutex);
+ return;
+ }
+
+ msm_bus_scale_client_update_request(
+ isp_bandwidth_mgr.bus_client, 0);
+ msm_bus_scale_unregister_client(isp_bandwidth_mgr.bus_client);
+ isp_bandwidth_mgr.bus_client = 0;
+ mutex_unlock(&bandwidth_mgr_mutex);
+}
+
+void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev,
+ struct msm_isp_statistics *stats)
+{
+ stats->isp_vfe0_active = isp_bandwidth_mgr.client_info[ISP_VFE0].active;
+ stats->isp_vfe0_ab = isp_bandwidth_mgr.client_info[ISP_VFE0].ab;
+ stats->isp_vfe0_ib = isp_bandwidth_mgr.client_info[ISP_VFE0].ib;
+
+ stats->isp_vfe1_active = isp_bandwidth_mgr.client_info[ISP_VFE1].active;
+ stats->isp_vfe1_ab = isp_bandwidth_mgr.client_info[ISP_VFE1].ab;
+ stats->isp_vfe1_ib = isp_bandwidth_mgr.client_info[ISP_VFE1].ib;
+
+ stats->isp_cpp_active = isp_bandwidth_mgr.client_info[ISP_CPP].active;
+ stats->isp_cpp_ab = isp_bandwidth_mgr.client_info[ISP_CPP].ab;
+ stats->isp_cpp_ib = isp_bandwidth_mgr.client_info[ISP_CPP].ib;
+ stats->last_overflow_ab = vfe_dev->msm_isp_last_overflow_ab;
+ stats->last_overflow_ib = vfe_dev->msm_isp_last_overflow_ib;
+ stats->vfe_clk_rate = vfe_dev->msm_isp_vfe_clk_rate;
+ stats->cpp_clk_rate = msm_isp_cpp_clk_rate;
+}
+
+void msm_isp_util_update_last_overflow_ab_ib(struct vfe_device *vfe_dev)
+{
+ struct msm_bus_paths *path;
+ path = &(msm_isp_bus_client_pdata.usecase[
+ isp_bandwidth_mgr.bus_vector_active_idx]);
+ vfe_dev->msm_isp_last_overflow_ab = path->vectors[0].ab;
+ vfe_dev->msm_isp_last_overflow_ib = path->vectors[0].ib;
+}
+
+void msm_isp_util_update_clk_rate(long clock_rate)
+{
+ msm_isp_cpp_clk_rate = clock_rate;
+}
+
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern)
+{
+ switch (frame_skip_pattern) {
+ case NO_SKIP:
+ case EVERY_2FRAME:
+ case EVERY_3FRAME:
+ case EVERY_4FRAME:
+ case EVERY_5FRAME:
+ case EVERY_6FRAME:
+ case EVERY_7FRAME:
+ case EVERY_8FRAME:
+ return frame_skip_pattern + 1;
+ case EVERY_16FRAME:
+ return 16;
+ case EVERY_32FRAME:
+ return 32;
+ case SKIP_ALL:
+ return 1;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
+int msm_isp_get_clk_info(struct vfe_device *vfe_dev,
+ struct platform_device *pdev, struct msm_cam_clk_info *vfe_clk_info)
+{
+ int i, count, rc;
+ uint32_t rates[VFE_CLK_INFO_MAX];
+
+ struct device_node *of_node;
+
+ of_node = pdev->dev.of_node;
+
+ count = of_property_count_strings(of_node, "clock-names");
+
+ ISP_DBG("count = %d\n", count);
+ if (count <= 0) {
+ pr_err("no clocks found in device tree, count=%d", count);
+ return 0;
+ }
+
+ if (count > VFE_CLK_INFO_MAX) {
+ pr_err("invalid count=%d, max is %d\n", count,
+ VFE_CLK_INFO_MAX);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node, "clock-names",
+ i, &(vfe_clk_info[i].clk_name));
+ ISP_DBG("clock-names[%d] = %s\n", i, vfe_clk_info[i].clk_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+ }
+ rc = of_property_read_u32_array(of_node, "qcom,clock-rates",
+ rates, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+ for (i = 0; i < count; i++) {
+ vfe_clk_info[i].clk_rate =
+ (rates[i] == 0) ? (long)-1 : rates[i];
+ ISP_DBG("clk_rate[%d] = %ld\n", i, vfe_clk_info[i].clk_rate);
+ }
+ vfe_dev->num_clk = count;
+ return 0;
+}
+
+void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp,
+ struct vfe_device *vfe_dev)
+{
+ struct timespec ts;
+
+ do_gettimeofday(&(time_stamp->event_time));
+ if (vfe_dev->vt_enable) {
+ msm_isp_get_avtimer_ts(time_stamp);
+ time_stamp->buf_time.tv_sec = time_stamp->vt_time.tv_sec;
+ time_stamp->buf_time.tv_usec = time_stamp->vt_time.tv_usec;
+ } else {
+ get_monotonic_boottime(&ts);
+ time_stamp->buf_time.tv_sec = ts.tv_sec;
+ time_stamp->buf_time.tv_usec = ts.tv_nsec/1000;
+ }
+}
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ rc = v4l2_event_subscribe(fh, sub, MAX_ISP_V4l2_EVENTS, NULL);
+ if (rc == 0) {
+ if (sub->type == V4L2_EVENT_ALL) {
+ int i;
+
+ vfe_dev->axi_data.event_mask = 0;
+ for (i = 0; i < ISP_EVENT_MAX; i++)
+ vfe_dev->axi_data.event_mask |= (1 << i);
+ } else {
+ int event_idx = sub->type - ISP_EVENT_BASE;
+
+ vfe_dev->axi_data.event_mask |= (1 << event_idx);
+ }
+ }
+ return rc;
+}
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ rc = v4l2_event_unsubscribe(fh, sub);
+ if (sub->type == V4L2_EVENT_ALL) {
+ vfe_dev->axi_data.event_mask = 0;
+ } else {
+ int event_idx = sub->type - ISP_EVENT_BASE;
+
+ vfe_dev->axi_data.event_mask &= ~(1 << event_idx);
+ }
+ return rc;
+}
+
+static int msm_isp_get_max_clk_rate(struct vfe_device *vfe_dev, long *rate)
+{
+ int clk_idx = 0;
+ unsigned long max_value = ~0;
+ long round_rate = 0;
+
+ if (!vfe_dev || !rate) {
+ pr_err("%s:%d failed: vfe_dev %pK rate %pK\n",
+ __func__, __LINE__, vfe_dev, rate);
+ return -EINVAL;
+ }
+
+ *rate = 0;
+ if (!vfe_dev->hw_info) {
+ pr_err("%s:%d failed: vfe_dev->hw_info %pK\n", __func__,
+ __LINE__, vfe_dev->hw_info);
+ return -EINVAL;
+ }
+
+ clk_idx = vfe_dev->hw_info->vfe_clk_idx;
+ if (clk_idx >= vfe_dev->num_clk) {
+ pr_err("%s:%d failed: clk_idx %d max array size %d\n",
+ __func__, __LINE__, clk_idx,
+ vfe_dev->num_clk);
+ return -EINVAL;
+ }
+
+ round_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], max_value);
+ if (round_rate < 0) {
+ pr_err("%s: Invalid vfe clock rate\n", __func__);
+ return -EINVAL;
+ }
+
+ *rate = round_rate;
+ return 0;
+}
+
+static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
+{
+ int rc = 0;
+ int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
+ long round_rate =
+ clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
+ if (round_rate < 0) {
+ pr_err("%s: Invalid vfe clock rate\n", __func__);
+ return round_rate;
+ }
+
+ rc = clk_set_rate(vfe_dev->vfe_clk[clk_idx], round_rate);
+ if (rc < 0) {
+ pr_err("%s: Vfe set rate error\n", __func__);
+ return rc;
+ }
+ *rate = round_rate;
+ vfe_dev->msm_isp_vfe_clk_rate = round_rate;
+ return 0;
+}
+
+void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev,
+ struct msm_vfe_fetch_engine_info *fetch_engine_info)
+{
+ struct msm_isp32_event_data fe_rd_done_event;
+
+ if (!fetch_engine_info->is_busy)
+ return;
+ memset(&fe_rd_done_event, 0, sizeof(struct msm_isp32_event_data));
+ fe_rd_done_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ fe_rd_done_event.u.buf_done.session_id = fetch_engine_info->session_id;
+ fe_rd_done_event.u.buf_done.stream_id = fetch_engine_info->stream_id;
+ fe_rd_done_event.u.buf_done.handle = fetch_engine_info->bufq_handle;
+ fe_rd_done_event.u.buf_done.buf_idx = fetch_engine_info->buf_idx;
+ ISP_DBG("%s:VFE%d ISP_EVENT_FE_READ_DONE buf_idx %d\n",
+ __func__, vfe_dev->pdev->id, fetch_engine_info->buf_idx);
+ fetch_engine_info->is_busy = 0;
+ msm_isp_send_event(vfe_dev, ISP_EVENT_FE_READ_DONE, &fe_rd_done_event);
+}
+
+static int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
+ struct msm_vfe_input_cfg *input_cfg)
+{
+ int rc = 0;
+
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
+ pr_err("%s: src %d path is active\n", __func__, VFE_PIX_0);
+ return -EINVAL;
+ }
+
+ vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock =
+ input_cfg->input_pix_clk;
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux =
+ input_cfg->d.pix_cfg.input_mux;
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_format =
+ input_cfg->d.pix_cfg.input_format;
+
+ rc = msm_isp_set_clk_rate(vfe_dev,
+ &vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock);
+ if (rc < 0) {
+ pr_err("%s: clock set rate failed\n", __func__);
+ return rc;
+ }
+
+ ISP_DBG("%s: input mux is %d CAMIF %d io_format 0x%x\n", __func__,
+ input_cfg->d.pix_cfg.input_mux, CAMIF,
+ input_cfg->d.pix_cfg.input_format);
+
+ if (input_cfg->d.pix_cfg.input_mux == CAMIF) {
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
+ } else if (input_cfg->d.pix_cfg.input_mux == EXTERNAL_READ) {
+ vfe_dev->axi_data.src_info[VFE_PIX_0].width =
+ input_cfg->d.pix_cfg.fetch_engine_cfg.buf_stride;
+ }
+ vfe_dev->hw_info->vfe_ops.core_ops.cfg_input_mux(
+ vfe_dev, &input_cfg->d.pix_cfg);
+ return rc;
+}
+
+static int msm_isp_cfg_rdi(struct vfe_device *vfe_dev,
+ struct msm_vfe_input_cfg *input_cfg)
+{
+ int rc = 0;
+
+ if (vfe_dev->axi_data.src_info[input_cfg->input_src].active) {
+ pr_err("%s: RAW%d path is active\n", __func__,
+ input_cfg->input_src - VFE_RAW_0);
+ return -EINVAL;
+ }
+
+ vfe_dev->axi_data.src_info[input_cfg->input_src].pixel_clock =
+ input_cfg->input_pix_clk;
+ vfe_dev->hw_info->vfe_ops.core_ops.cfg_rdi_reg(
+ vfe_dev, &input_cfg->d.rdi_cfg, input_cfg->input_src);
+ return rc;
+}
+
+int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_input_cfg *input_cfg = arg;
+
+ switch (input_cfg->input_src) {
+ case VFE_PIX_0:
+ rc = msm_isp_cfg_pix(vfe_dev, input_cfg);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ rc = msm_isp_cfg_rdi(vfe_dev, input_cfg);
+ break;
+ default:
+ pr_err("%s: Invalid input source\n", __func__);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int msm_isp_proc_cmd_list_unlocked(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ uint32_t count = 0;
+ struct msm_vfe_cfg_cmd_list *proc_cmd =
+ (struct msm_vfe_cfg_cmd_list *)arg;
+ struct msm_vfe_cfg_cmd_list cmd, cmd_next;
+ struct msm_vfe_cfg_cmd2 cfg_cmd;
+
+ if (!vfe_dev || !arg) {
+ pr_err("%s:%d failed: vfe_dev %pK arg %pK", __func__, __LINE__,
+ vfe_dev, arg);
+ return -EINVAL;
+ }
+
+ rc = msm_isp_proc_cmd(vfe_dev, &proc_cmd->cfg_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = *proc_cmd;
+
+ while (cmd.next) {
+ if (cmd.next_size != sizeof(struct msm_vfe_cfg_cmd_list)) {
+ pr_err("%s:%d failed: next size %u != expected %zu\n",
+ __func__, __LINE__, cmd.next_size,
+ sizeof(struct msm_vfe_cfg_cmd_list));
+ break;
+ }
+ if (++count >= MAX_ISP_REG_LIST) {
+ pr_err("%s:%d Error exceeding the max register count:%u\n",
+ __func__, __LINE__, count);
+ rc = -EFAULT;
+ break;
+ }
+ if (copy_from_user(&cmd_next, (void __user *)cmd.next,
+ sizeof(struct msm_vfe_cfg_cmd_list))) {
+ rc = -EFAULT;
+ continue;
+ }
+
+ cfg_cmd = cmd_next.cfg_cmd;
+
+ rc = msm_isp_proc_cmd(vfe_dev, &cfg_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = cmd_next;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_vfe_cfg_cmd2_32 {
+ uint16_t num_cfg;
+ uint16_t cmd_len;
+ compat_caddr_t cfg_data;
+ compat_caddr_t cfg_cmd;
+};
+
+struct msm_vfe_cfg_cmd_list_32 {
+ struct msm_vfe_cfg_cmd2_32 cfg_cmd;
+ compat_caddr_t next;
+ uint32_t next_size;
+};
+
+#define VIDIOC_MSM_VFE_REG_CFG_COMPAT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2_32)
+#define VIDIOC_MSM_VFE_REG_LIST_CFG_COMPAT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+14, struct msm_vfe_cfg_cmd_list_32)
+
+static void msm_isp_compat_to_proc_cmd(struct msm_vfe_cfg_cmd2 *proc_cmd,
+ struct msm_vfe_cfg_cmd2_32 *proc_cmd_ptr32)
+{
+ proc_cmd->num_cfg = proc_cmd_ptr32->num_cfg;
+ proc_cmd->cmd_len = proc_cmd_ptr32->cmd_len;
+ proc_cmd->cfg_data = compat_ptr(proc_cmd_ptr32->cfg_data);
+ proc_cmd->cfg_cmd = compat_ptr(proc_cmd_ptr32->cfg_cmd);
+}
+
+static int msm_isp_proc_cmd_list_compat(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ uint32_t count = 0;
+ struct msm_vfe_cfg_cmd_list_32 *proc_cmd =
+ (struct msm_vfe_cfg_cmd_list_32 *)arg;
+ struct msm_vfe_cfg_cmd_list_32 cmd, cmd_next;
+ struct msm_vfe_cfg_cmd2 current_cmd;
+
+ if (!vfe_dev || !arg) {
+ pr_err("%s:%d failed: vfe_dev %pK arg %pK", __func__, __LINE__,
+ vfe_dev, arg);
+ return -EINVAL;
+ }
+ msm_isp_compat_to_proc_cmd(¤t_cmd, &proc_cmd->cfg_cmd);
+ rc = msm_isp_proc_cmd(vfe_dev, ¤t_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = *proc_cmd;
+
+ while (compat_ptr(cmd.next) != NULL) {
+ if (cmd.next_size != sizeof(struct msm_vfe_cfg_cmd_list_32)) {
+ pr_err("%s:%d failed: next size %u != expected %zu\n",
+ __func__, __LINE__, cmd.next_size,
+ sizeof(struct msm_vfe_cfg_cmd_list));
+ break;
+ }
+ if (++count >= MAX_ISP_REG_LIST) {
+ pr_err("%s:%d Error exceeding the max register count:%u\n",
+ __func__, __LINE__, count);
+ rc = -EFAULT;
+ break;
+ }
+ if (copy_from_user(&cmd_next, compat_ptr(cmd.next),
+ sizeof(struct msm_vfe_cfg_cmd_list_32))) {
+ rc = -EFAULT;
+ continue;
+ }
+
+ msm_isp_compat_to_proc_cmd(¤t_cmd, &cmd_next.cfg_cmd);
+ rc = msm_isp_proc_cmd(vfe_dev, ¤t_cmd);
+ if (rc < 0)
+ pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc);
+
+ cmd = cmd_next;
+ }
+ return rc;
+}
+
+static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg)
+{
+ if (is_compat_task())
+ return msm_isp_proc_cmd_list_compat(vfe_dev, arg);
+ else
+ return msm_isp_proc_cmd_list_unlocked(vfe_dev, arg);
+}
+#else /* CONFIG_COMPAT */
+static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg)
+{
+ return msm_isp_proc_cmd_list_unlocked(vfe_dev, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+
+static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ long rc = 0;
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ if (!vfe_dev || !vfe_dev->vfe_base) {
+ pr_err("%s:%d failed: invalid params %pK\n",
+ __func__, __LINE__, vfe_dev);
+ if (vfe_dev)
+ pr_err("%s:%d failed %pK\n", __func__,
+ __LINE__, vfe_dev->vfe_base);
+ return -EINVAL;
+ }
+
+ /* use real time mutex for hard real-time ioctls such as
+ * buffer operations and register updates.
+ * Use core mutex for other ioctls that could take
+ * longer time to complete such as start/stop ISP streams
+ * which blocks until the hardware start/stop streaming
+ */
+ ISP_DBG("%s cmd: %d\n", __func__, _IOC_TYPE(cmd));
+ switch (cmd) {
+ case VIDIOC_MSM_VFE_REG_CFG: {
+ mutex_lock(&vfe_dev->realtime_mutex);
+ rc = msm_isp_proc_cmd(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ case VIDIOC_MSM_VFE_REG_LIST_CFG: {
+ mutex_lock(&vfe_dev->realtime_mutex);
+ rc = msm_isp_proc_cmd_list(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_BUF:
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF:
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ }
+ case VIDIOC_MSM_ISP32_REQUEST_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_request_axi_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_release_axi_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_cfg_axi_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AXI_HALT:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_axi_halt(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AXI_RESET:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_axi_reset(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_AXI_RESTART:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_axi_restart(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_INPUT_CFG:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_cfg_input(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_FETCH_ENG_START:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.
+ start_fetch_eng(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_REG_UPDATE_CMD:
+ if (arg) {
+ enum msm_vfe_input_src frame_src =
+ *((enum msm_vfe_input_src *)arg);
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, (1 << frame_src));
+ vfe_dev->axi_data.src_info[frame_src].last_updt_frm_id =
+ vfe_dev->axi_data.src_info[frame_src].frame_id;
+ }
+ break;
+ case VIDIOC_MSM_ISP_SET_SRC_STATE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_set_src_state(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_request_stats_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_release_stats_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_cfg_stats_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_UPDATE_STATS_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_update_stats_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_UPDATE_STREAM:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_update_axi_stream(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case VIDIOC_MSM_ISP_SMMU_ATTACH:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_smmu_attach(vfe_dev->buf_mgr, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case MSM_SD_NOTIFY_FREEZE:
+ vfe_dev->isp_sof_debug = 0;
+ break;
+ case VIDIOC_MSM_ISP_BUF_DONE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_user_buf_done(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
+ case MSM_SD_SHUTDOWN:
+ while (vfe_dev->vfe_open_cnt != 0)
+ msm_isp_close_node(sd, NULL);
+ break;
+
+ default:
+ pr_err_ratelimited("%s: Invalid ISP command\n", __func__);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+
+#ifdef CONFIG_COMPAT
+static long msm_isp_ioctl_compat(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ long rc = 0;
+
+ if (!vfe_dev || !vfe_dev->vfe_base) {
+ pr_err("%s:%d failed: invalid params %pK\n",
+ __func__, __LINE__, vfe_dev);
+ if (vfe_dev)
+ pr_err("%s:%d failed %pK\n", __func__,
+ __LINE__, vfe_dev->vfe_base);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case VIDIOC_MSM_VFE_REG_CFG_COMPAT: {
+ struct msm_vfe_cfg_cmd2 proc_cmd;
+
+ mutex_lock(&vfe_dev->realtime_mutex);
+ msm_isp_compat_to_proc_cmd(&proc_cmd,
+ (struct msm_vfe_cfg_cmd2_32 *) arg);
+ rc = msm_isp_proc_cmd(vfe_dev, &proc_cmd);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ case VIDIOC_MSM_VFE_REG_LIST_CFG_COMPAT: {
+ mutex_lock(&vfe_dev->realtime_mutex);
+ rc = msm_isp_proc_cmd_list(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ break;
+ }
+ default:
+ return msm_isp_ioctl_unlocked(sd, cmd, arg);
+ }
+
+ return rc;
+}
+
+long msm_isp_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ return msm_isp_ioctl_compat(sd, cmd, arg);
+}
+#else /* CONFIG_COMPAT */
+long msm_isp_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ return msm_isp_ioctl_unlocked(sd, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
+ struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd,
+ uint32_t *cfg_data, uint32_t cmd_len)
+{
+ if (!vfe_dev || !reg_cfg_cmd) {
+ pr_err("%s:%d failed: vfe_dev %pK reg_cfg_cmd %pK\n", __func__,
+ __LINE__, vfe_dev, reg_cfg_cmd);
+ return -EINVAL;
+ }
+ if ((reg_cfg_cmd->cmd_type != VFE_CFG_MASK) &&
+ (!cfg_data || !cmd_len)) {
+ pr_err("%s:%d failed: cmd type %d cfg_data %pK cmd_len %d\n",
+ __func__, __LINE__, reg_cfg_cmd->cmd_type, cfg_data,
+ cmd_len);
+ return -EINVAL;
+ }
+
+ /* Validate input parameters */
+ switch (reg_cfg_cmd->cmd_type) {
+ case VFE_WRITE:
+ case VFE_READ:
+ case VFE_WRITE_MB: {
+ if ((reg_cfg_cmd->u.rw_info.reg_offset >
+ (UINT_MAX - reg_cfg_cmd->u.rw_info.len)) ||
+ ((reg_cfg_cmd->u.rw_info.reg_offset +
+ reg_cfg_cmd->u.rw_info.len) >
+ resource_size(vfe_dev->vfe_mem)) ||
+ (reg_cfg_cmd->u.rw_info.reg_offset & 0x3)) {
+ pr_err("%s:%d reg_offset %d len %d res %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.rw_info.reg_offset,
+ reg_cfg_cmd->u.rw_info.len,
+ (uint32_t)resource_size(vfe_dev->vfe_mem));
+ return -EINVAL;
+ }
+
+ if ((reg_cfg_cmd->u.rw_info.cmd_data_offset >
+ (UINT_MAX - reg_cfg_cmd->u.rw_info.len)) ||
+ ((reg_cfg_cmd->u.rw_info.cmd_data_offset +
+ reg_cfg_cmd->u.rw_info.len) > cmd_len)) {
+ pr_err("%s:%d cmd_data_offset %d len %d cmd_len %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.rw_info.cmd_data_offset,
+ reg_cfg_cmd->u.rw_info.len, cmd_len);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VFE_WRITE_DMI_16BIT:
+ case VFE_WRITE_DMI_32BIT:
+ case VFE_WRITE_DMI_64BIT:
+ case VFE_READ_DMI_16BIT:
+ case VFE_READ_DMI_32BIT:
+ case VFE_READ_DMI_64BIT: {
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT ||
+ reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
+ if ((reg_cfg_cmd->u.dmi_info.hi_tbl_offset <=
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset) ||
+ (reg_cfg_cmd->u.dmi_info.hi_tbl_offset -
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset !=
+ (sizeof(uint32_t)))) {
+ pr_err("%s:%d hi %d lo %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset,
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset);
+ return -EINVAL;
+ }
+ if (reg_cfg_cmd->u.dmi_info.len <= sizeof(uint32_t)) {
+ pr_err("%s:%d len %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.len);
+ return -EINVAL;
+ }
+ if (((UINT_MAX -
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset) <
+ (reg_cfg_cmd->u.dmi_info.len -
+ sizeof(uint32_t))) ||
+ ((reg_cfg_cmd->u.dmi_info.hi_tbl_offset +
+ reg_cfg_cmd->u.dmi_info.len -
+ sizeof(uint32_t)) > cmd_len)) {
+ pr_err("%s:%d hi_tbl_offset %d len %d cmd %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset,
+ reg_cfg_cmd->u.dmi_info.len, cmd_len);
+ return -EINVAL;
+ }
+ }
+ if ((reg_cfg_cmd->u.dmi_info.lo_tbl_offset >
+ (UINT_MAX - reg_cfg_cmd->u.dmi_info.len)) ||
+ ((reg_cfg_cmd->u.dmi_info.lo_tbl_offset +
+ reg_cfg_cmd->u.dmi_info.len) > cmd_len)) {
+ pr_err("%s:%d lo_tbl_offset %d len %d cmd_len %d\n",
+ __func__, __LINE__,
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset,
+ reg_cfg_cmd->u.dmi_info.len, cmd_len);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ switch (reg_cfg_cmd->cmd_type) {
+ case VFE_WRITE: {
+ msm_camera_io_memcpy(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.rw_info.reg_offset,
+ cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
+ reg_cfg_cmd->u.rw_info.len);
+ break;
+ }
+ case VFE_WRITE_MB: {
+ msm_camera_io_memcpy_mb(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.rw_info.reg_offset,
+ cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4,
+ reg_cfg_cmd->u.rw_info.len);
+ break;
+ }
+ case VFE_CFG_MASK: {
+ uint32_t temp;
+
+ if ((UINT_MAX - sizeof(temp) <
+ reg_cfg_cmd->u.mask_info.reg_offset) ||
+ (resource_size(vfe_dev->vfe_mem) <
+ reg_cfg_cmd->u.mask_info.reg_offset +
+ sizeof(temp)) ||
+ (reg_cfg_cmd->u.mask_info.reg_offset & 0x3)) {
+ pr_err("%s: VFE_CFG_MASK: Invalid length\n", __func__);
+ return -EINVAL;
+ }
+ temp = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.mask_info.reg_offset);
+
+ temp &= ~reg_cfg_cmd->u.mask_info.mask;
+ temp |= reg_cfg_cmd->u.mask_info.val;
+ msm_camera_io_w(temp, vfe_dev->vfe_base +
+ reg_cfg_cmd->u.mask_info.reg_offset);
+ break;
+ }
+ case VFE_WRITE_DMI_16BIT:
+ case VFE_WRITE_DMI_32BIT:
+ case VFE_WRITE_DMI_64BIT: {
+ int i;
+ uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL;
+ uint32_t hi_val, lo_val, lo_val1;
+
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT) {
+ hi_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4;
+ }
+ lo_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4;
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT)
+ reg_cfg_cmd->u.dmi_info.len =
+ reg_cfg_cmd->u.dmi_info.len / 2;
+ for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) {
+ lo_val = *lo_tbl_ptr++;
+ if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_16BIT) {
+ lo_val1 = lo_val & 0x0000FFFF;
+ lo_val = (lo_val & 0xFFFF0000)>>16;
+ msm_camera_io_w(lo_val1, vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+ } else if (reg_cfg_cmd->cmd_type ==
+ VFE_WRITE_DMI_64BIT) {
+ lo_tbl_ptr++;
+ hi_val = *hi_tbl_ptr;
+ hi_tbl_ptr = hi_tbl_ptr + 2;
+ msm_camera_io_w(hi_val, vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset);
+ }
+ msm_camera_io_w(lo_val, vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+ }
+ break;
+ }
+ case VFE_READ_DMI_16BIT:
+ case VFE_READ_DMI_32BIT:
+ case VFE_READ_DMI_64BIT: {
+ int i;
+ uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL;
+ uint32_t hi_val, lo_val, lo_val1;
+
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
+ hi_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4;
+ }
+
+ lo_tbl_ptr = cfg_data +
+ reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4;
+
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT)
+ reg_cfg_cmd->u.dmi_info.len =
+ reg_cfg_cmd->u.dmi_info.len / 2;
+
+ for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) {
+ lo_val = msm_camera_io_r(vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_16BIT) {
+ lo_val1 = msm_camera_io_r(vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset + 0x4);
+ lo_val |= lo_val1 << 16;
+ }
+ *lo_tbl_ptr++ = lo_val;
+ if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) {
+ hi_val = msm_camera_io_r(vfe_dev->vfe_base +
+ vfe_dev->hw_info->dmi_reg_offset);
+ *hi_tbl_ptr = hi_val;
+ hi_tbl_ptr += 2;
+ lo_tbl_ptr++;
+ }
+ }
+ break;
+ }
+ case VFE_HW_UPDATE_LOCK: {
+ uint32_t update_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].last_updt_frm_id;
+ if (update_id) {
+ ISP_DBG("%s hw_update_lock fail cur_id %u,last_id %u\n",
+ __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
+ update_id);
+ return -EINVAL;
+ }
+ break;
+ }
+ case VFE_HW_UPDATE_UNLOCK: {
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id
+ != *cfg_data) {
+ ISP_DBG("hw_updt over frm bound,strt_id %u end_id %d\n",
+ *cfg_data,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ }
+ vfe_dev->axi_data.src_info[VFE_PIX_0].last_updt_frm_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ break;
+ }
+ case VFE_READ: {
+ int i;
+ uint32_t *data_ptr = cfg_data +
+ reg_cfg_cmd->u.rw_info.cmd_data_offset/4;
+ for (i = 0; i < reg_cfg_cmd->u.rw_info.len/4; i++) {
+ if ((data_ptr < cfg_data) ||
+ (UINT_MAX / sizeof(*data_ptr) <
+ (data_ptr - cfg_data)) ||
+ (sizeof(*data_ptr) * (data_ptr - cfg_data) >=
+ cmd_len))
+ return -EINVAL;
+ *data_ptr++ = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->u.rw_info.reg_offset);
+ reg_cfg_cmd->u.rw_info.reg_offset += 4;
+ }
+ break;
+ }
+ case GET_MAX_CLK_RATE: {
+ int rc = 0;
+ unsigned long rate;
+
+ if (cmd_len != sizeof(__u32)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(__u32));
+ return -EINVAL;
+ }
+ rc = msm_isp_get_max_clk_rate(vfe_dev, &rate);
+ if (rc < 0) {
+ pr_err("%s:%d failed: rc %d\n", __func__, __LINE__, rc);
+ return -EINVAL;
+ }
+
+ *(__u32 *)cfg_data = (__u32)rate;
+
+ break;
+ }
+ case GET_ISP_ID: {
+ uint32_t *isp_id = NULL;
+
+ if (cmd_len < sizeof(uint32_t)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(uint32_t));
+ return -EINVAL;
+ }
+
+ isp_id = (uint32_t *)cfg_data;
+ *isp_id = vfe_dev->pdev->id;
+ break;
+ }
+ case SET_WM_UB_SIZE:
+ break;
+ case SET_UB_POLICY: {
+
+ if (cmd_len < sizeof(vfe_dev->vfe_ub_policy)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(vfe_dev->vfe_ub_policy));
+ return -EINVAL;
+ }
+ vfe_dev->vfe_ub_policy = *cfg_data;
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_cfg_cmd2 *proc_cmd = arg;
+ struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd;
+ uint32_t *cfg_data = NULL;
+
+ if (!proc_cmd->num_cfg) {
+ pr_err("%s: Passed num_cfg as 0\n", __func__);
+ return -EINVAL;
+ }
+
+ reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)*
+ proc_cmd->num_cfg, GFP_KERNEL);
+ if (!reg_cfg_cmd) {
+ rc = -ENOMEM;
+ goto reg_cfg_failed;
+ }
+
+ if (copy_from_user(reg_cfg_cmd,
+ (void __user *)(proc_cmd->cfg_cmd),
+ sizeof(struct msm_vfe_reg_cfg_cmd) * proc_cmd->num_cfg)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+ if (proc_cmd->cmd_len > 0 &&
+ proc_cmd->cmd_len < UINT16_MAX) {
+ cfg_data = kzalloc(proc_cmd->cmd_len, GFP_KERNEL);
+ if (!cfg_data) {
+ pr_err("%s: cfg_data alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto cfg_data_failed;
+ }
+
+ if (copy_from_user(cfg_data,
+ (void __user *)(proc_cmd->cfg_data),
+ proc_cmd->cmd_len)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+ }
+
+ for (i = 0; i < proc_cmd->num_cfg; i++)
+ rc = msm_isp_send_hw_cmd(vfe_dev, ®_cfg_cmd[i],
+ cfg_data, proc_cmd->cmd_len);
+
+ if (copy_to_user(proc_cmd->cfg_data,
+ cfg_data, proc_cmd->cmd_len)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+copy_cmd_failed:
+ kfree(cfg_data);
+cfg_data_failed:
+ kfree(reg_cfg_cmd);
+reg_cfg_failed:
+ return rc;
+}
+
+int msm_isp_send_event(struct vfe_device *vfe_dev,
+ uint32_t event_type,
+ struct msm_isp32_event_data *event_data)
+{
+ struct v4l2_event isp_event;
+
+ memset(&isp_event, 0, sizeof(struct v4l2_event));
+ isp_event.id = 0;
+ isp_event.type = event_type;
+
+ memcpy(&isp_event.u.data[0], event_data,
+ sizeof(struct msm_isp32_event_data));
+ v4l2_event_queue(vfe_dev->subdev.sd.devnode, &isp_event);
+ return 0;
+}
+
+#define CAL_WORD(width, M, N) ((width * M + N - 1) / N)
+
+int msm_isp_cal_word_per_line(uint32_t output_format,
+ uint32_t pixel_per_line)
+{
+ int val = -1;
+
+ switch (output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ val = CAL_WORD(pixel_per_line, 5, 32);
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ val = CAL_WORD(pixel_per_line, 3, 16);
+ break;
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ val = CAL_WORD(pixel_per_line, 7, 32);
+ break;
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ val = CAL_WORD(pixel_per_line, 1, 6);
+ break;
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ val = CAL_WORD(pixel_per_line, 1, 5);
+ break;
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ val = CAL_WORD(pixel_per_line, 1, 4);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ val = CAL_WORD(pixel_per_line, 2, 8);
+ break;
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ val = CAL_WORD(pixel_per_line, 1, 4);
+ break;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__, output_format);
+ break;
+ }
+ return val;
+}
+
+enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format)
+{
+ switch (output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ return MIPI;
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ return QCOM;
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ return PLAIN16;
+ default:
+ msm_isp_print_fourcc_error(__func__, output_format);
+ break;
+ }
+ return -EINVAL;
+}
+
+int msm_isp_get_bit_per_pixel(uint32_t output_format)
+{
+ switch (output_format) {
+ case V4L2_PIX_FMT_Y4:
+ return 4;
+ case V4L2_PIX_FMT_Y6:
+ return 6;
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_QBGGR8:
+ case V4L2_PIX_FMT_QGBRG8:
+ case V4L2_PIX_FMT_QGRBG8:
+ case V4L2_PIX_FMT_QRGGB8:
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_META:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV14:
+ case V4L2_PIX_FMT_NV41:
+ case V4L2_PIX_FMT_YVU410:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YYUV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUV411P:
+ case V4L2_PIX_FMT_Y41P:
+ case V4L2_PIX_FMT_YUV444:
+ case V4L2_PIX_FMT_YUV555:
+ case V4L2_PIX_FMT_YUV565:
+ case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_YUV410:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_PAL8:
+ case V4L2_PIX_FMT_UV8:
+ case MSM_V4L2_PIX_FMT_META:
+ return 8;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_QBGGR10:
+ case V4L2_PIX_FMT_QGBRG10:
+ case V4L2_PIX_FMT_QGRBG10:
+ case V4L2_PIX_FMT_QRGGB10:
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y10BPACK:
+ case V4L2_PIX_FMT_P16BGGR10:
+ case V4L2_PIX_FMT_P16GBRG10:
+ case V4L2_PIX_FMT_P16GRBG10:
+ case V4L2_PIX_FMT_P16RGGB10:
+ return 10;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_QBGGR12:
+ case V4L2_PIX_FMT_QGBRG12:
+ case V4L2_PIX_FMT_QGRBG12:
+ case V4L2_PIX_FMT_QRGGB12:
+ case V4L2_PIX_FMT_Y12:
+ return 12;
+ case V4L2_PIX_FMT_SBGGR14:
+ case V4L2_PIX_FMT_SGBRG14:
+ case V4L2_PIX_FMT_SGRBG14:
+ case V4L2_PIX_FMT_SRGGB14:
+ case V4L2_PIX_FMT_QBGGR14:
+ case V4L2_PIX_FMT_QGBRG14:
+ case V4L2_PIX_FMT_QGRBG14:
+ case V4L2_PIX_FMT_QRGGB14:
+ return 14;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_Y16:
+ return 16;
+ /*TD: Add more image format*/
+ default:
+ msm_isp_print_fourcc_error(__func__, output_format);
+ pr_err("%s: Invalid output format %x\n",
+ __func__, output_format);
+ return -EINVAL;
+ }
+}
+
+void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
+
+ error_info->info_dump_frame_count++;
+}
+
+void msm_isp_process_error_info(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint8_t num_stats_type =
+ vfe_dev->hw_info->stats_hw_info->num_stats_type;
+ struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
+ static DEFINE_RATELIMIT_STATE(rs,
+ DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+ static DEFINE_RATELIMIT_STATE(rs_stats,
+ DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+
+ if (error_info->error_count == 1 ||
+ !(error_info->info_dump_frame_count % 100)) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ process_error_status(vfe_dev);
+ error_info->error_mask0 = 0;
+ error_info->error_mask1 = 0;
+ error_info->camif_status = 0;
+ error_info->violation_status = 0;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ if (error_info->stream_framedrop_count[i] != 0 &&
+ __ratelimit(&rs)) {
+ pr_err("%s: Stream[%d]: dropped %d frames\n",
+ __func__, i,
+ error_info->stream_framedrop_count[i]);
+ error_info->stream_framedrop_count[i] = 0;
+ }
+ }
+ for (i = 0; i < num_stats_type; i++) {
+ if (error_info->stats_framedrop_count[i] != 0 &&
+ __ratelimit(&rs_stats)) {
+ pr_err("%s: Stats stream[%d]: dropped %d frames\n",
+ __func__, i,
+ error_info->stats_framedrop_count[i]);
+ error_info->stats_framedrop_count[i] = 0;
+ }
+ }
+ }
+}
+
+static inline void msm_isp_update_error_info(struct vfe_device *vfe_dev,
+ uint32_t error_mask0, uint32_t error_mask1)
+{
+ vfe_dev->error_info.error_mask0 |= error_mask0;
+ vfe_dev->error_info.error_mask1 |= error_mask1;
+ vfe_dev->error_info.error_count++;
+}
+
+static void msm_isp_process_overflow_irq(
+ struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1)
+{
+ uint32_t overflow_mask;
+
+ /* if there are no active streams - do not start recovery */
+ if (!vfe_dev->axi_data.num_active_stream)
+ return;
+
+ /*Mask out all other irqs if recovery is started*/
+ if (atomic_read(&vfe_dev->error_info.overflow_state) != NO_OVERFLOW) {
+ uint32_t halt_restart_mask0, halt_restart_mask1;
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_halt_restart_mask(&halt_restart_mask0,
+ &halt_restart_mask1);
+ *irq_status0 &= halt_restart_mask0;
+ *irq_status1 &= halt_restart_mask1;
+
+ return;
+ }
+
+ /*Check if any overflow bit is set*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_overflow_mask(&overflow_mask);
+ overflow_mask &= *irq_status1;
+
+ if (overflow_mask) {
+ struct msm_isp32_event_data error_event;
+
+ if (vfe_dev->reset_pending == 1) {
+ pr_err("%s:%d failed: overflow %x during reset\n",
+ __func__, __LINE__, overflow_mask);
+ /* Clear overflow bits since reset is pending */
+ *irq_status1 &= ~overflow_mask;
+ return;
+ }
+
+ ISP_DBG("%s: Bus overflow detected: 0x%x, start recovery!\n",
+ __func__, overflow_mask);
+ atomic_set(&vfe_dev->error_info.overflow_state,
+ OVERFLOW_DETECTED);
+ /*Store current IRQ mask*/
+ vfe_dev->hw_info->vfe_ops.core_ops.get_irq_mask(vfe_dev,
+ &vfe_dev->error_info.overflow_recover_irq_mask0,
+ &vfe_dev->error_info.overflow_recover_irq_mask1);
+
+ /*Halt the hardware & Clear all other IRQ mask*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 0);
+
+ /*Stop CAMIF Immediately*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
+
+ /*Update overflow state*/
+ *irq_status0 = 0;
+ *irq_status1 = 0;
+
+ memset(&error_event, 0, sizeof(error_event));
+ error_event.frame_id =
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+ error_event.u.error_info.error_mask = 1 << ISP_WM_BUS_OVERFLOW;
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_WM_BUS_OVERFLOW, &error_event);
+ }
+}
+
+void msm_isp_reset_burst_count_and_frame_drop(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t framedrop_period = 0;
+
+ if (stream_info->state != ACTIVE ||
+ stream_info->stream_type != BURST_STREAM) {
+ return;
+ }
+ if (stream_info->num_burst_capture != 0) {
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_info->frame_skip_pattern);
+ stream_info->burst_frame_count =
+ stream_info->init_frame_drop +
+ (stream_info->num_burst_capture - 1) *
+ framedrop_period + 1;
+ msm_isp_reset_framedrop(vfe_dev, stream_info);
+ }
+}
+
+irqreturn_t msm_isp_process_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ uint32_t irq_status0, irq_status1;
+ uint32_t error_mask0, error_mask1;
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.
+ read_irq_status(vfe_dev, &irq_status0, &irq_status1);
+
+ if ((irq_status0 == 0) && (irq_status1 == 0)) {
+ pr_err_ratelimited("%s:VFE%d irq_status0 & 1 are both 0\n",
+ __func__, vfe_dev->pdev->id);
+ return IRQ_HANDLED;
+ }
+
+ msm_isp_process_overflow_irq(vfe_dev,
+ &irq_status0, &irq_status1);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ get_error_mask(&error_mask0, &error_mask1);
+ error_mask0 &= irq_status0;
+ error_mask1 &= irq_status1;
+ irq_status0 &= ~error_mask0;
+ irq_status1 &= ~error_mask1;
+ if (!vfe_dev->ignore_error &&
+ ((error_mask0 != 0) || (error_mask1 != 0)))
+ msm_isp_update_error_info(vfe_dev, error_mask0, error_mask1);
+
+ if ((irq_status0 == 0) && (irq_status1 == 0) &&
+ (!(((error_mask0 != 0) || (error_mask1 != 0)) &&
+ vfe_dev->error_info.error_count == 1))) {
+ ISP_DBG("%s: error_mask0/1 & error_count are set!\n", __func__);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
+ queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ pr_err_ratelimited("%s: Tasklet queue overflow: %d\n",
+ __func__, vfe_dev->pdev->id);
+ list_del(&queue_cmd->list);
+ } else {
+ atomic_add(1, &vfe_dev->irq_cnt);
+ }
+ queue_cmd->vfeInterruptStatus0 = irq_status0;
+ queue_cmd->vfeInterruptStatus1 = irq_status1;
+ msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev);
+ queue_cmd->cmd_used = 1;
+ vfe_dev->taskletq_idx =
+ (vfe_dev->taskletq_idx + 1) % MSM_VFE_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &vfe_dev->tasklet_q);
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ tasklet_schedule(&vfe_dev->vfe_tasklet);
+ return IRQ_HANDLED;
+}
+
+void msm_isp_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ struct msm_vfe_irq_ops *irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct msm_isp_timestamp ts;
+ uint32_t irq_status0, irq_status1;
+
+ while (atomic_read(&vfe_dev->irq_cnt)) {
+ spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
+ queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
+ struct msm_vfe_tasklet_queue_cmd, list);
+
+ if (!queue_cmd) {
+ atomic_set(&vfe_dev->irq_cnt, 0);
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ return;
+ }
+ atomic_sub(1, &vfe_dev->irq_cnt);
+ list_del(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+ irq_status0 = queue_cmd->vfeInterruptStatus0;
+ irq_status1 = queue_cmd->vfeInterruptStatus1;
+ ts = queue_cmd->ts;
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ ISP_DBG("%s: status0: 0x%x status1: 0x%x\n",
+ __func__, irq_status0, irq_status1);
+ irq_ops->process_reset_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_halt_irq(vfe_dev,
+ irq_status0, irq_status1);
+ if (atomic_read(&vfe_dev->error_info.overflow_state)
+ != NO_OVERFLOW) {
+ pr_err("%s: Recovery in processing, Ignore IRQs!!!\n",
+ __func__);
+ continue;
+ }
+ msm_isp_process_error_info(vfe_dev);
+ irq_ops->process_camif_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_axi_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_stats_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_reg_update(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ irq_ops->process_epoch_irq(vfe_dev,
+ irq_status0, irq_status1, &ts);
+ }
+}
+
+int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg)
+{
+ struct msm_vfe_axi_src_state *src_state = arg;
+
+ if (src_state->input_src >= VFE_SRC_MAX)
+ return -EINVAL;
+ vfe_dev->axi_data.src_info[src_state->input_src].active =
+ src_state->src_active;
+ vfe_dev->axi_data.src_info[src_state->input_src].frame_id =
+ src_state->src_frame_id;
+ return 0;
+}
+
+static void msm_vfe_iommu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova, int flags, void *token)
+{
+ struct vfe_device *vfe_dev = NULL;
+
+ if (token) {
+ vfe_dev = (struct vfe_device *)token;
+ if (!vfe_dev->buf_mgr || !vfe_dev->buf_mgr->ops) {
+ pr_err("%s:%d] buf_mgr %pK\n", __func__,
+ __LINE__, vfe_dev->buf_mgr);
+ goto end;
+ }
+ if (!vfe_dev->buf_mgr->pagefault_debug_disable) {
+ pr_err("%s:%d] vfe_dev %pK id %d\n", __func__,
+ __LINE__, vfe_dev, vfe_dev->pdev->id);
+ vfe_dev->buf_mgr->ops->buf_mgr_debug(vfe_dev->buf_mgr,
+ iova);
+ }
+ } else {
+ ISP_DBG("%s:%d] no token received: %pK\n",
+ __func__, __LINE__, token);
+ goto end;
+ }
+end:
+ return;
+}
+
+int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ long rc = 0;
+
+ ISP_DBG("%s\n", __func__);
+
+ mutex_lock(&vfe_dev->realtime_mutex);
+ mutex_lock(&vfe_dev->core_mutex);
+
+ if (vfe_dev->vfe_open_cnt++ && vfe_dev->vfe_base) {
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+ }
+
+ if (vfe_dev->vfe_base) {
+ pr_err("%s:%d invalid params cnt %d base %pK\n", __func__,
+ __LINE__, vfe_dev->vfe_open_cnt, vfe_dev->vfe_base);
+ vfe_dev->vfe_base = NULL;
+ }
+
+ vfe_dev->reset_pending = 0;
+ vfe_dev->isp_sof_debug = 0;
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
+ pr_err("%s: init hardware failed\n", __func__);
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return -EBUSY;
+ }
+
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.clear_status_reg(vfe_dev);
+
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 1, 1);
+ if (rc <= 0) {
+ pr_err("%s: reset timeout\n", __func__);
+ vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return -EINVAL;
+ }
+ vfe_dev->vfe_hw_version = msm_camera_io_r(vfe_dev->vfe_base);
+ ISP_DBG("%s: HW Version: 0x%x\n", __func__, vfe_dev->vfe_hw_version);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+
+ vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp");
+
+ memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data));
+ memset(&vfe_dev->stats_data, 0,
+ sizeof(struct msm_vfe_stats_shared_data));
+ memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
+ memset(&vfe_dev->fetch_engine_info, 0,
+ sizeof(vfe_dev->fetch_engine_info));
+ vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info;
+ vfe_dev->taskletq_idx = 0;
+ vfe_dev->vt_enable = 0;
+ vfe_dev->bus_util_factor = 0;
+ rc = of_property_read_u32(vfe_dev->pdev->dev.of_node,
+ "bus-util-factor", &vfe_dev->bus_util_factor);
+ if (rc < 0)
+ ISP_DBG("%s: Use default bus utilization factor\n", __func__);
+
+ cam_smmu_reg_client_page_fault_handler(
+ vfe_dev->buf_mgr->iommu_hdl,
+ msm_vfe_iommu_fault_handler,
+ NULL,
+ vfe_dev);
+
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+}
+
+#ifdef CONFIG_MSM_AVTIMER
+static void msm_isp_end_avtimer(void)
+{
+ avcs_core_disable_power_collapse(0);
+}
+#else
+static void msm_isp_end_avtimer(void)
+{
+ pr_err("AV Timer is not supported\n");
+}
+#endif
+
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ long rc = 0;
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ ISP_DBG("%s E\n", __func__);
+ mutex_lock(&vfe_dev->realtime_mutex);
+ mutex_lock(&vfe_dev->core_mutex);
+
+ if (!vfe_dev->vfe_open_cnt) {
+ pr_err("%s invalid state open cnt %d\n", __func__,
+ vfe_dev->vfe_open_cnt);
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return -EINVAL;
+ }
+
+ if (--vfe_dev->vfe_open_cnt) {
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+ }
+
+ /* Unregister page fault handler */
+ cam_smmu_reg_client_page_fault_handler(
+ vfe_dev->buf_mgr->iommu_hdl,
+ NULL, NULL, vfe_dev);
+
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+ if (rc < 0)
+ pr_err("%s: halt timeout rc=%ld\n", __func__, rc);
+
+ vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
+ vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+ if (vfe_dev->vt_enable) {
+ msm_isp_end_avtimer();
+ vfe_dev->vt_enable = 0;
+ }
+ mutex_unlock(&vfe_dev->core_mutex);
+ mutex_unlock(&vfe_dev->realtime_mutex);
+ return 0;
+}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.h
new file mode 100644
index 0000000..f2268c3
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MSM_ISP_UTIL_H__
+#define __MSM_ISP_UTIL_H__
+
+#include "msm_isp_32.h"
+#include <soc/qcom/camera2.h>
+
+/* #define CONFIG_MSM_ISP_DBG 1 */
+
+#ifdef CONFIG_MSM_ISP_DBG
+#define ISP_DBG(fmt, args...) printk(fmt, ##args)
+#else
+#define ISP_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define ALT_VECTOR_IDX(x) {x = 3 - x; }
+
+struct msm_isp_bandwidth_mgr {
+ uint32_t bus_client;
+ uint32_t bus_vector_active_idx;
+ uint32_t use_count;
+ struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT];
+};
+
+uint32_t msm_isp_get_framedrop_period(
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern);
+void msm_isp_reset_burst_count_and_frame_drop(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info);
+
+int msm_isp_init_bandwidth_mgr(enum msm_isp_hw_client client);
+int msm_isp_update_bandwidth(enum msm_isp_hw_client client,
+ uint64_t ab, uint64_t ib);
+void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev,
+ struct msm_isp_statistics *stats);
+void msm_isp_util_update_last_overflow_ab_ib(struct vfe_device *vfe_dev);
+void msm_isp_util_update_clk_rate(long clock_rate);
+void msm_isp_update_req_history(uint32_t client, uint64_t ab,
+ uint64_t ib,
+ struct msm_isp_bandwidth_info *client_info,
+ unsigned long long ts);
+void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client);
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_send_event(struct vfe_device *vfe_dev,
+ uint32_t type, struct msm_isp32_event_data *event_data);
+int msm_isp_cal_word_per_line(uint32_t output_format,
+ uint32_t pixel_per_line);
+int msm_isp_get_bit_per_pixel(uint32_t output_format);
+enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format);
+irqreturn_t msm_isp_process_irq(int irq_num, void *data);
+int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_do_tasklet(unsigned long data);
+void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev);
+void msm_isp_process_error_info(struct vfe_device *vfe_dev);
+int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+long msm_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+int msm_isp_get_clk_info(struct vfe_device *vfe_dev,
+ struct platform_device *pdev, struct msm_cam_clk_info *vfe_clk_info);
+void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev,
+ struct msm_vfe_fetch_engine_info *fetch_engine_info);
+void msm_camera_io_dump_2(void __iomem *addr, int size);
+void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format);
+void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp,
+ struct vfe_device *vfe_dev);
+void msm_isp_get_avtimer_ts(struct msm_isp_timestamp *time_stamp);
+int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg);
+#endif /* __MSM_ISP_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/ispif/Makefile b/drivers/media/platform/msm/camera_v2/ispif/Makefile
index 236ec73..d56332d 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/Makefile
+++ b/drivers/media/platform/msm/camera_v2/ispif/Makefile
@@ -1,4 +1,8 @@
ccflags-y += -Idrivers/media/platform/msm/camera_v2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+ifeq ($(CONFIG_MSM_ISP_V1),y)
+obj-$(CONFIG_MSM_CSID) += msm_ispif_32.o
+else
obj-$(CONFIG_MSM_CSID) += msm_ispif.o
+endif
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.c
new file mode 100644
index 0000000..e9b2a1d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.c
@@ -0,0 +1,1581 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/iopoll.h>
+#include <linux/compat.h>
+#include <media/msmb_isp.h>
+
+#include "msm_ispif_32.h"
+#include "msm.h"
+#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+
+#ifdef CONFIG_MSM_ISPIF_V1
+#include "msm_ispif_hwreg_v1.h"
+#else
+#include "msm_ispif_hwreg_v2.h"
+#endif
+
+#define V4L2_IDENT_ISPIF 50001
+#define MSM_ISPIF_DRV_NAME "msm_ispif"
+
+#define ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY 0x00
+#define ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY 0x01
+#define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02
+
+#define ISPIF_TIMEOUT_SLEEP_US 1000
+#define ISPIF_TIMEOUT_ALL_US 1000000
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
+{
+ if (!ispif->enb_dump_reg)
+ return;
+
+ if (!ispif->base) {
+ pr_err("%s: null pointer for the ispif base\n", __func__);
+ return;
+ }
+
+ msm_camera_io_dump(ispif->base, 0x250, 1);
+}
+
+
+static inline int msm_ispif_is_intf_valid(uint32_t csid_version,
+ uint8_t intf_type)
+{
+ return (((csid_version <= CSID_VERSION_V22
+ && intf_type != VFE0) ||
+ (intf_type >= VFE_MAX))
+ ? false : true);
+}
+
+static struct msm_cam_clk_info ispif_8626_reset_clk_info[] = {
+ {"ispif_ahb_clk", NO_SET_RATE},
+ {"csi0_src_clk", NO_SET_RATE},
+ {"csi0_clk", NO_SET_RATE},
+ {"csi0_pix_clk", NO_SET_RATE},
+ {"csi0_rdi_clk", NO_SET_RATE},
+ {"csi1_src_clk", NO_SET_RATE},
+ {"csi1_clk", NO_SET_RATE},
+ {"csi1_pix_clk", NO_SET_RATE},
+ {"csi1_rdi_clk", NO_SET_RATE},
+ {"camss_vfe_vfe0_clk", NO_SET_RATE},
+ {"camss_csi_vfe0_clk", NO_SET_RATE},
+};
+
+static struct msm_cam_clk_info ispif_8974_ahb_clk_info[ISPIF_CLK_INFO_MAX];
+
+static struct msm_cam_clk_info ispif_8974_reset_clk_info[] = {
+ {"csi0_src_clk", INIT_RATE},
+ {"csi0_clk", NO_SET_RATE},
+ {"csi0_pix_clk", NO_SET_RATE},
+ {"csi0_rdi_clk", NO_SET_RATE},
+ {"csi1_src_clk", INIT_RATE},
+ {"csi1_clk", NO_SET_RATE},
+ {"csi1_pix_clk", NO_SET_RATE},
+ {"csi1_rdi_clk", NO_SET_RATE},
+ {"csi2_src_clk", INIT_RATE},
+ {"csi2_clk", NO_SET_RATE},
+ {"csi2_pix_clk", NO_SET_RATE},
+ {"csi2_rdi_clk", NO_SET_RATE},
+ {"csi3_src_clk", INIT_RATE},
+ {"csi3_clk", NO_SET_RATE},
+ {"csi3_pix_clk", NO_SET_RATE},
+ {"csi3_rdi_clk", NO_SET_RATE},
+ {"vfe0_clk_src", INIT_RATE},
+ {"camss_vfe_vfe0_clk", NO_SET_RATE},
+ {"camss_csi_vfe0_clk", NO_SET_RATE},
+ {"vfe1_clk_src", INIT_RATE},
+ {"camss_vfe_vfe1_clk", NO_SET_RATE},
+ {"camss_csi_vfe1_clk", NO_SET_RATE},
+};
+
+static int msm_ispif_reset_hw(struct ispif_device *ispif)
+{
+ int rc = 0;
+ long timeout = 0;
+ struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)];
+ struct clk *reset_clk1[ARRAY_SIZE(ispif_8626_reset_clk_info)];
+
+ ispif->clk_idx = 0;
+
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 1);
+ if (rc < 0) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ } else {
+ /* This is set when device is 8x26 */
+ ispif->clk_idx = 2;
+ }
+ } else {
+ /* This is set when device is 8974 */
+ ispif->clk_idx = 1;
+ }
+
+ init_completion(&ispif->reset_complete[VFE0]);
+ if (ispif->hw_num_isps > 1)
+ init_completion(&ispif->reset_complete[VFE1]);
+
+ /* initiate reset of ISPIF */
+ msm_camera_io_w(ISPIF_RST_CMD_MASK,
+ ispif->base + ISPIF_RST_CMD_ADDR);
+ if (ispif->hw_num_isps > 1)
+ msm_camera_io_w(ISPIF_RST_CMD_1_MASK,
+ ispif->base + ISPIF_RST_CMD_1_ADDR);
+
+ timeout = wait_for_completion_timeout(
+ &ispif->reset_complete[VFE0], msecs_to_jiffies(500));
+ CDBG("%s: VFE0 done\n", __func__);
+
+ if (timeout <= 0) {
+ pr_err("%s: VFE0 reset wait timeout\n", __func__);
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ if (rc < 0) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 0);
+ if (rc < 0)
+ pr_err("%s: VFE0 reset wait timeout\n",
+ __func__);
+ }
+ return -ETIMEDOUT;
+ }
+
+ if (ispif->hw_num_isps > 1) {
+ timeout = wait_for_completion_timeout(
+ &ispif->reset_complete[VFE1],
+ msecs_to_jiffies(500));
+ CDBG("%s: VFE1 done\n", __func__);
+ if (timeout <= 0) {
+ pr_err("%s: VFE1 reset wait timeout\n", __func__);
+ msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if (ispif->clk_idx == 1) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ if (rc < 0) {
+ pr_err("%s: cannot disable clock, error = %d",
+ __func__, rc);
+ }
+ }
+
+ if (ispif->clk_idx == 2) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 0);
+ if (rc < 0) {
+ pr_err("%s: cannot disable clock, error = %d",
+ __func__, rc);
+ }
+ }
+
+ return rc;
+}
+
+static int msm_ispif_get_ahb_clk_info(struct ispif_device *ispif_dev,
+ struct platform_device *pdev,
+ struct msm_cam_clk_info *ahb_clk_info)
+{
+ uint32_t num_ahb_clk = 0;
+ int i, count, rc;
+ uint32_t rates[ISPIF_CLK_INFO_MAX];
+
+ struct device_node *of_node;
+
+ of_node = pdev->dev.of_node;
+
+ count = of_property_count_strings(of_node, "clock-names");
+
+ CDBG("count = %d\n", count);
+ if (count <= 0) {
+ pr_err("no clocks found in device tree, count=%d", count);
+ return 0;
+ }
+
+ if (count > ISPIF_CLK_INFO_MAX) {
+ pr_err("invalid count=%d, max is %d\n", count,
+ ISPIF_CLK_INFO_MAX);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,clock-rates",
+ rates, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node, "clock-names",
+ i, &(ahb_clk_info[num_ahb_clk].clk_name));
+ CDBG("clock-names[%d] = %s\n",
+ i, ahb_clk_info[i].clk_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return rc;
+ }
+ if (strnstr(ahb_clk_info[num_ahb_clk].clk_name, "ahb",
+ sizeof(ahb_clk_info[num_ahb_clk].clk_name))) {
+ ahb_clk_info[num_ahb_clk].clk_rate =
+ (rates[i] == 0) ? (long)-1 : rates[i];
+ CDBG("clk_rate[%d] = %ld\n", i,
+ ahb_clk_info[i].clk_rate);
+ num_ahb_clk++;
+ }
+ }
+ ispif_dev->num_ahb_clk = num_ahb_clk;
+ return 0;
+}
+
+static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable)
+{
+ int rc = 0;
+
+ if (ispif->csid_version < CSID_VERSION_V30) {
+ /* Older ISPIF versiond don't need ahb clokc */
+ return 0;
+ }
+
+ rc = msm_ispif_get_ahb_clk_info(ispif, ispif->pdev,
+ ispif_8974_ahb_clk_info);
+ if (rc < 0) {
+ pr_err("%s: msm_isp_get_clk_info() failed", __func__);
+ return -EFAULT;
+ }
+
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_ahb_clk_info, ispif->ahb_clk,
+ ispif->num_ahb_clk, enable);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ }
+
+ return rc;
+}
+
+static int msm_ispif_reset(struct ispif_device *ispif)
+{
+ int rc = 0;
+ int i;
+
+ if (WARN_ON(!ispif))
+ return -EINVAL;
+
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+ for (i = 0; i < ispif->vfe_info.num_vfe; i++) {
+
+ msm_camera_io_w(1 << PIX0_LINE_BUF_EN_BIT,
+ ispif->base + ISPIF_VFE_m_CTRL_0(i));
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(i));
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(i));
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(i));
+ msm_camera_io_w(0xFFFFFFFF, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_0(i));
+ msm_camera_io_w(0xFFFFFFFF, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_1(i));
+ msm_camera_io_w(0xFFFFFFFF, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_2(i));
+
+ msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_INPUT_SEL(i));
+
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
+ msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
+ pr_debug("%s: base %pK", __func__, ispif->base);
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 1));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 0));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 1));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 2));
+
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CROP(i, 0));
+ msm_camera_io_w(0, ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_CROP(i, 1));
+ }
+
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ return rc;
+}
+
+static void msm_ispif_sel_csid_core(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ uint32_t data;
+
+ if (WARN_ON(!ispif))
+ return;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+
+ data = msm_camera_io_r(ispif->base + ISPIF_VFE_m_INPUT_SEL(vfe_intf));
+ switch (intftype) {
+ case PIX0:
+ data &= ~(BIT(1) | BIT(0));
+ data |= (uint32_t)csid;
+ break;
+ case RDI0:
+ data &= ~(BIT(5) | BIT(4));
+ data |= (uint32_t)(csid << 4);
+ break;
+ case PIX1:
+ data &= ~(BIT(9) | BIT(8));
+ data |= (uint32_t)(csid << 8);
+ break;
+ case RDI1:
+ data &= ~(BIT(13) | BIT(12));
+ data |= (uint32_t)(csid << 12);
+ break;
+ case RDI2:
+ data &= ~(BIT(21) | BIT(20));
+ data |= (uint32_t)(csid << 20);
+ break;
+ }
+
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_VFE_m_INPUT_SEL(vfe_intf));
+}
+
+static void msm_ispif_enable_crop(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf, uint16_t start_pixel,
+ uint16_t end_pixel)
+{
+ uint32_t data;
+
+ if (WARN_ON(!ispif))
+ return;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+
+ data = msm_camera_io_r(ispif->base + ISPIF_VFE_m_CTRL_0(vfe_intf));
+ data |= (1 << (intftype + 7));
+ if (intftype == PIX0)
+ data |= 1 << PIX0_LINE_BUF_EN_BIT;
+ msm_camera_io_w(data,
+ ispif->base + ISPIF_VFE_m_CTRL_0(vfe_intf));
+
+ if (intftype == PIX0)
+ msm_camera_io_w_mb(start_pixel | (end_pixel << 16),
+ ispif->base + ISPIF_VFE_m_PIX_INTF_n_CROP(vfe_intf, 0));
+ else if (intftype == PIX1)
+ msm_camera_io_w_mb(start_pixel | (end_pixel << 16),
+ ispif->base + ISPIF_VFE_m_PIX_INTF_n_CROP(vfe_intf, 1));
+ else {
+ pr_err("%s: invalid intftype=%d\n", __func__, intftype);
+ WARN_ON(1);
+ return;
+ }
+}
+
+static void msm_ispif_enable_intf_cids(struct ispif_device *ispif,
+ uint8_t intftype, uint16_t cid_mask, uint8_t vfe_intf, uint8_t enable)
+{
+ uint32_t intf_addr, data;
+
+ if (WARN_ON((!ispif)))
+ return;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return;
+ }
+
+ switch (intftype) {
+ case PIX0:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe_intf, 0);
+ break;
+ case RDI0:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 0);
+ break;
+ case PIX1:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe_intf, 1);
+ break;
+ case RDI1:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 1);
+ break;
+ case RDI2:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 2);
+ break;
+ default:
+ pr_err("%s: invalid intftype=%d\n", __func__, intftype);
+ WARN_ON(1);
+ return;
+ }
+
+ data = msm_camera_io_r(ispif->base + intf_addr);
+ if (enable)
+ data |= (uint32_t)cid_mask;
+ else
+ data &= ~((uint32_t)cid_mask);
+ msm_camera_io_w_mb(data, ispif->base + intf_addr);
+}
+
+static int msm_ispif_validate_intf_status(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf)
+{
+ int rc = 0;
+ uint32_t data = 0;
+
+ if (WARN_ON((!ispif)))
+ return -ENODEV;
+
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0));
+ break;
+ case RDI0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0));
+ break;
+ case PIX1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1));
+ break;
+ case RDI1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1));
+ break;
+ case RDI2:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2));
+ break;
+ }
+ if ((data & 0xf) != 0xf)
+ rc = -EBUSY;
+ return rc;
+}
+
+static void msm_ispif_select_clk_mux(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ uint32_t data = 0;
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->clk_mux_base);
+ data &= ~(0xf << (vfe_intf * 8));
+ data |= (csid << (vfe_intf * 8));
+ msm_camera_io_w(data, ispif->clk_mux_base);
+ break;
+
+ case RDI0:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (vfe_intf * 12));
+ data |= (csid << (vfe_intf * 12));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+
+ case PIX1:
+ data = msm_camera_io_r(ispif->clk_mux_base);
+ data &= ~(0xf0 << (vfe_intf * 8));
+ data |= (csid << (4 + (vfe_intf * 8)));
+ msm_camera_io_w(data, ispif->clk_mux_base);
+ break;
+
+ case RDI1:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (4 + (vfe_intf * 12)));
+ data |= (csid << (4 + (vfe_intf * 12)));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+
+ case RDI2:
+ data = msm_camera_io_r(ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ data &= ~(0xf << (8 + (vfe_intf * 12)));
+ data |= (csid << (8 + (vfe_intf * 12)));
+ msm_camera_io_w(data, ispif->clk_mux_base +
+ ISPIF_RDI_CLK_MUX_SEL_ADDR);
+ break;
+ }
+ CDBG("%s intftype %d data %x\n", __func__, intftype, data);
+ /* ensure clk mux is enabled */
+ mb();
+}
+
+static uint16_t msm_ispif_get_cids_mask_from_cfg(
+ struct msm_ispif_params_entry *entry)
+{
+ int i;
+ uint16_t cids_mask = 0;
+
+ if (WARN_ON(!entry)) {
+ pr_err("%s: invalid entry", __func__);
+ return cids_mask;
+ }
+
+ for (i = 0; i < entry->num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++)
+ cids_mask |= (1 << entry->cids[i]);
+
+ return cids_mask;
+}
+
+static int msm_ispif_config(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0, i = 0;
+ uint16_t cid_mask;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (vfe_intf >= VFE_MAX) {
+ pr_err("%s: %d invalid i %d vfe_intf %d\n", __func__,
+ __LINE__, i, vfe_intf);
+ return -EINVAL;
+ }
+ if (!msm_ispif_is_intf_valid(ispif->csid_version,
+ vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_w(0x0, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_0(vfe_intf));
+ msm_camera_io_w(0x0, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_1(vfe_intf));
+ msm_camera_io_w_mb(0x0, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_2(vfe_intf));
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+
+ vfe_intf = params->entries[i].vfe_intf;
+
+ CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__,
+ intftype, vfe_intf, params->entries[i].csid);
+
+ if ((intftype >= INTF_MAX) ||
+ (vfe_intf >= ispif->vfe_info.num_vfe) ||
+ (ispif->csid_version <= CSID_VERSION_V22 &&
+ (vfe_intf > VFE0))) {
+ pr_err("%s: VFEID %d and CSID version %d mismatch\n",
+ __func__, vfe_intf, ispif->csid_version);
+ return -EINVAL;
+ }
+
+ if (ispif->csid_version >= CSID_VERSION_V30)
+ msm_ispif_select_clk_mux(ispif, intftype,
+ params->entries[i].csid, vfe_intf);
+
+ rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
+ if (rc) {
+ pr_err("%s:validate_intf_status failed, rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ msm_ispif_sel_csid_core(ispif, intftype,
+ params->entries[i].csid, vfe_intf);
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ msm_ispif_enable_intf_cids(ispif, intftype,
+ cid_mask, vfe_intf, 1);
+ if (params->entries[i].crop_enable)
+ msm_ispif_enable_crop(ispif, intftype, vfe_intf,
+ params->entries[i].crop_start_pixel,
+ params->entries[i].crop_end_pixel);
+ }
+
+ for (vfe_intf = 0; vfe_intf < 2; vfe_intf++) {
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_0(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_0(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_1(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_1(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_MASK_2(vfe_intf));
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_VFE_m_IRQ_CLEAR_2(vfe_intf));
+ }
+
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ return rc;
+}
+
+static int msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits,
+ struct msm_ispif_param_data *params)
+{
+ uint8_t vc;
+ int i, k;
+ enum msm_ispif_intftype intf_type;
+ enum msm_ispif_cid cid;
+ enum msm_ispif_vfe_intf vfe_intf;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ return -EINVAL;
+ }
+ if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY) {
+ pr_err("%s: out of range of cid_num %d\n",
+ __func__, params->entries[i].num_cids);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intf_type = params->entries[i].intftype;
+ vfe_intf = params->entries[i].vfe_intf;
+ for (k = 0; k < params->entries[i].num_cids; k++) {
+ cid = params->entries[i].cids[k];
+ vc = cid / 4;
+ if (intf_type == RDI2) {
+ /* zero out two bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 &=
+ ~(0x3 << (vc * 2 + 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 |=
+ (cmd_bits << (vc * 2 + 8));
+ } else {
+ /* zero 2 bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd &=
+ ~(0x3 << (vc * 2 + intf_type * 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
+ (cmd_bits << (vc * 2 + intf_type * 8));
+ }
+ }
+ /* cmd for PIX0, PIX1, RDI0, RDI1 */
+ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd != 0xFFFFFFFF)
+ msm_camera_io_w_mb(
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe_intf));
+
+ /* cmd for RDI2 */
+ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd1 != 0xFFFFFFFF)
+ msm_camera_io_w_mb(
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1,
+ ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe_intf));
+ }
+ return 0;
+}
+
+static int msm_ispif_stop_immediately(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_DISABLE_IMMEDIATELY, params);
+
+ /* after stop the interface we need to unmask the CID enable bits */
+ for (i = 0; i < params->num; i++) {
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, params->entries[i].vfe_intf, 0);
+ }
+
+ return rc;
+}
+
+static int msm_ispif_start_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
+
+ return rc;
+}
+
+static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0, i;
+ long timeout = 0;
+ uint16_t cid_mask;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t vfe_mask = 0;
+ uint32_t intf_addr;
+ struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)];
+ struct clk *reset_clk1[ARRAY_SIZE(ispif_8626_reset_clk_info)];
+
+ ispif->clk_idx = 0;
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ vfe_intf = params->entries[i].vfe_intf;
+ if (vfe_intf >= VFE_MAX) {
+ pr_err("%s: %d invalid i %d vfe_intf %d\n", __func__,
+ __LINE__, i, vfe_intf);
+ return -EINVAL;
+ }
+ vfe_mask |= (1 << vfe_intf);
+ }
+
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 1);
+ if (rc < 0) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ } else {
+ /* This is set when device is 8x26 */
+ ispif->clk_idx = 2;
+ }
+ } else {
+ /* This is set when device is 8974 */
+ ispif->clk_idx = 1;
+ }
+
+ if (vfe_mask & (1 << VFE0)) {
+ init_completion(&ispif->reset_complete[VFE0]);
+ pr_err("%s Init completion VFE0\n", __func__);
+ /* initiate reset of ISPIF */
+ msm_camera_io_w(0x00001FF9,
+ ispif->base + ISPIF_RST_CMD_ADDR);
+ }
+ if (ispif->hw_num_isps > 1 && (vfe_mask & (1 << VFE1))) {
+ init_completion(&ispif->reset_complete[VFE1]);
+ pr_err("%s Init completion VFE1\n", __func__);
+ msm_camera_io_w(0x00001FF9,
+ ispif->base + ISPIF_RST_CMD_1_ADDR);
+ }
+
+ if (vfe_mask & (1 << VFE0)) {
+ timeout = wait_for_completion_timeout(
+ &ispif->reset_complete[VFE0], msecs_to_jiffies(500));
+ if (timeout <= 0) {
+ pr_err("%s: VFE0 reset wait timeout\n", __func__);
+ rc = -ETIMEDOUT;
+ goto disable_clk;
+ }
+ }
+
+ if (ispif->hw_num_isps > 1 && (vfe_mask & (1 << VFE1))) {
+ timeout = wait_for_completion_timeout(
+ &ispif->reset_complete[VFE1],
+ msecs_to_jiffies(500));
+ if (timeout <= 0) {
+ pr_err("%s: VFE1 reset wait timeout\n", __func__);
+ rc = -ETIMEDOUT;
+ goto disable_clk;
+ }
+ }
+
+ pr_info("%s: ISPIF reset hw done", __func__);
+
+ if (ispif->clk_idx == 1) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ if (rc < 0) {
+ pr_err("%s: cannot disable clock, error = %d",
+ __func__, rc);
+ goto end;
+ }
+ }
+
+ if (ispif->clk_idx == 2) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 0);
+ if (rc < 0) {
+ pr_err("%s: cannot disable clock, error = %d",
+ __func__, rc);
+ goto end;
+ }
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+ vfe_intf = params->entries[i].vfe_intf;
+
+ switch (params->entries[0].intftype) {
+ case PIX0:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case RDI0:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case PIX1:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI1:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI2:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2);
+ break;
+ default:
+ pr_err("%s: invalid intftype=%d\n", __func__,
+ params->entries[i].intftype);
+ rc = -EPERM;
+ goto end;
+ }
+
+ msm_ispif_intf_cmd(ispif,
+ ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
+ }
+
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+
+ vfe_intf = params->entries[i].vfe_intf;
+
+
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+
+ msm_ispif_enable_intf_cids(ispif, intftype,
+ cid_mask, vfe_intf, 1);
+ }
+
+end:
+ return rc;
+disable_clk:
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ if (rc < 0) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 0);
+ if (rc < 0)
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+ uint32_t intf_addr;
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t stop_flag = 0;
+
+ if (WARN_ON(!ispif) || WARN_ON(!params))
+ return -EINVAL;
+
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+
+ if (params->num > MAX_PARAM_ENTRIES) {
+ pr_err("%s: invalid param entries %d\n", __func__,
+ params->num);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ for (i = 0; i < params->num; i++) {
+ if (!msm_ispif_is_intf_valid(ispif->csid_version,
+ params->entries[i].vfe_intf)) {
+ pr_err("%s: invalid interface type\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+ msm_ispif_intf_cmd(ispif,
+ ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY, params);
+
+ for (i = 0; i < params->num; i++) {
+ cid_mask =
+ msm_ispif_get_cids_mask_from_cfg(¶ms->entries[i]);
+ vfe_intf = params->entries[i].vfe_intf;
+
+ switch (params->entries[i].intftype) {
+ case PIX0:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case RDI0:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0);
+ break;
+ case PIX1:
+ intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI1:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1);
+ break;
+ case RDI2:
+ intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2);
+ break;
+ default:
+ pr_err("%s: invalid intftype=%d\n", __func__,
+ params->entries[i].intftype);
+ rc = -EPERM;
+ goto end;
+ }
+
+ rc = readl_poll_timeout(ispif->base + intf_addr, stop_flag,
+ (stop_flag & 0xF) == 0xF,
+ ISPIF_TIMEOUT_SLEEP_US,
+ ISPIF_TIMEOUT_ALL_US);
+ if (rc < 0)
+ goto end;
+
+ /* disable CIDs in CID_MASK register */
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, vfe_intf, 0);
+ }
+
+end:
+ return rc;
+}
+
+static void ispif_process_irq(struct ispif_device *ispif,
+ struct ispif_irq_status *out, enum msm_ispif_vfe_intf vfe_id)
+{
+ if (WARN_ON(!ispif) || WARN_ON(!out))
+ return;
+
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
+ if (ispif->ispif_sof_debug < 5)
+ pr_err("%s: PIX0 frame id: %u\n", __func__,
+ ispif->sof_count[vfe_id].sof_cnt[PIX0]);
+ ispif->sof_count[vfe_id].sof_cnt[PIX0]++;
+ ispif->ispif_sof_debug++;
+ }
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_RDI0_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI0]++;
+ }
+ if (out[vfe_id].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_RDI1_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI1]++;
+ }
+ if (out[vfe_id].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI2]++;
+ }
+}
+
+static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
+ void *data)
+{
+ struct ispif_device *ispif = (struct ispif_device *)data;
+
+ if (WARN_ON(!ispif) || WARN_ON(!out))
+ return;
+
+ out[VFE0].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_0(VFE0));
+ msm_camera_io_w(out[VFE0].ispifIrqStatus0,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(VFE0));
+
+ out[VFE0].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_1(VFE0));
+ msm_camera_io_w(out[VFE0].ispifIrqStatus1,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(VFE0));
+
+ out[VFE0].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_2(VFE0));
+ msm_camera_io_w_mb(out[VFE0].ispifIrqStatus2,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(VFE0));
+
+ if (ispif->vfe_info.num_vfe > 1) {
+ out[VFE1].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_0(VFE1));
+ msm_camera_io_w(out[VFE1].ispifIrqStatus0,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(VFE1));
+
+ out[VFE1].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_1(VFE1));
+ msm_camera_io_w(out[VFE1].ispifIrqStatus1,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(VFE1));
+
+ out[VFE1].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_VFE_m_IRQ_STATUS_2(VFE1));
+ msm_camera_io_w_mb(out[VFE1].ispifIrqStatus2,
+ ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(VFE1));
+ }
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+
+ if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
+ if (out[VFE0].ispifIrqStatus0 & RESET_DONE_IRQ)
+ complete(&ispif->reset_complete[VFE0]);
+
+ if (out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ)
+ pr_err("%s: VFE0 pix0 overflow.\n", __func__);
+
+ if (out[VFE0].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ)
+ pr_err("%s: VFE0 rdi0 overflow.\n", __func__);
+
+ if (out[VFE0].ispifIrqStatus1 & RAW_INTF_1_OVERFLOW_IRQ)
+ pr_err("%s: VFE0 rdi1 overflow.\n", __func__);
+
+ if (out[VFE0].ispifIrqStatus2 & RAW_INTF_2_OVERFLOW_IRQ)
+ pr_err("%s: VFE0 rdi2 overflow.\n", __func__);
+
+ ispif_process_irq(ispif, out, VFE0);
+ }
+ if (ispif->hw_num_isps > 1) {
+ if (out[VFE1].ispifIrqStatus0 & RESET_DONE_IRQ)
+ complete(&ispif->reset_complete[VFE1]);
+
+ if (out[VFE1].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ)
+ pr_err("%s: VFE1 pix0 overflow.\n", __func__);
+
+ if (out[VFE1].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ)
+ pr_err("%s: VFE1 rdi0 overflow.\n", __func__);
+
+ if (out[VFE1].ispifIrqStatus1 & RAW_INTF_1_OVERFLOW_IRQ)
+ pr_err("%s: VFE1 rdi1 overflow.\n", __func__);
+
+ if (out[VFE1].ispifIrqStatus2 & RAW_INTF_2_OVERFLOW_IRQ)
+ pr_err("%s: VFE1 rdi2 overflow.\n", __func__);
+
+ ispif_process_irq(ispif, out, VFE1);
+ }
+}
+
+static irqreturn_t msm_io_ispif_irq(int irq_num, void *data)
+{
+ struct ispif_irq_status irq[VFE_MAX];
+
+ msm_ispif_read_irq_status(irq, data);
+ return IRQ_HANDLED;
+}
+
+static int msm_ispif_set_vfe_info(struct ispif_device *ispif,
+ struct msm_ispif_vfe_info *vfe_info)
+{
+ if (!vfe_info || (vfe_info->num_vfe <= 0) ||
+ ((uint32_t)(vfe_info->num_vfe) > ispif->hw_num_isps)) {
+ pr_err("Invalid VFE info: %pK %d\n", vfe_info,
+ (vfe_info ? vfe_info->num_vfe:0));
+ return -EINVAL;
+ }
+
+ memcpy(&ispif->vfe_info, vfe_info, sizeof(struct msm_ispif_vfe_info));
+
+ return 0;
+}
+
+static int msm_ispif_init(struct ispif_device *ispif,
+ uint32_t csid_version)
+{
+ int rc = 0;
+
+ if (WARN_ON(!ispif)) {
+ pr_err("%s: invalid ispif params", __func__);
+ return -EINVAL;
+ }
+
+ if (ispif->ispif_state == ISPIF_POWER_UP) {
+ pr_err("%s: ispif already initted state = %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EPERM;
+ return rc;
+ }
+
+ /* can we set to zero? */
+ ispif->applied_intf_cmd[VFE0].intf_cmd = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE0].intf_cmd1 = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE1].intf_cmd = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE1].intf_cmd1 = 0xFFFFFFFF;
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+
+ ispif->csid_version = csid_version;
+
+ if (ispif->csid_version >= CSID_VERSION_V30) {
+ if (!ispif->clk_mux_mem || !ispif->clk_mux_io) {
+ pr_err("%s csi clk mux mem %pK io %pK\n", __func__,
+ ispif->clk_mux_mem, ispif->clk_mux_io);
+ rc = -ENOMEM;
+ return rc;
+ }
+ ispif->clk_mux_base = ioremap(ispif->clk_mux_mem->start,
+ resource_size(ispif->clk_mux_mem));
+ if (!ispif->clk_mux_base) {
+ pr_err("%s: clk_mux_mem ioremap failed\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ }
+
+ ispif->base = ioremap(ispif->mem->start,
+ resource_size(ispif->mem));
+ if (!ispif->base) {
+ rc = -ENOMEM;
+ pr_err("%s: nomem\n", __func__);
+ goto end;
+ }
+ rc = request_irq(ispif->irq->start, msm_io_ispif_irq,
+ IRQF_TRIGGER_RISING, "ispif", ispif);
+ if (rc) {
+ pr_err("%s: request_irq error = %d\n", __func__, rc);
+ goto error_irq;
+ }
+
+ rc = msm_ispif_clk_ahb_enable(ispif, 1);
+ if (rc) {
+ pr_err("%s: ahb_clk enable failed", __func__);
+ goto error_ahb;
+ }
+
+ msm_ispif_reset_hw(ispif);
+
+ rc = msm_ispif_reset(ispif);
+ if (rc == 0) {
+ ispif->ispif_state = ISPIF_POWER_UP;
+ CDBG("%s: power up done\n", __func__);
+ goto end;
+ }
+
+error_ahb:
+ free_irq(ispif->irq->start, ispif);
+error_irq:
+ iounmap(ispif->base);
+
+end:
+ return rc;
+}
+
+static void msm_ispif_release(struct ispif_device *ispif)
+{
+ if (WARN_ON(!ispif)) {
+ pr_err("%s: invalid ispif params", __func__);
+ return;
+ }
+
+ if (!ispif->base) {
+ pr_err("%s: ispif base is NULL\n", __func__);
+ return;
+ }
+
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ return;
+ }
+
+ /* make sure no streaming going on */
+ msm_ispif_reset(ispif);
+
+ msm_ispif_clk_ahb_enable(ispif, 0);
+
+ free_irq(ispif->irq->start, ispif);
+
+ iounmap(ispif->base);
+
+ iounmap(ispif->clk_mux_base);
+
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+}
+
+static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
+{
+ long rc = 0;
+ struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg;
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+
+ if (WARN_ON(!sd) || WARN_ON(!pcdata))
+ return -EINVAL;
+
+ mutex_lock(&ispif->mutex);
+ switch (pcdata->cfg_type) {
+ case ISPIF_ENABLE_REG_DUMP:
+ ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */
+ break;
+ case ISPIF_INIT:
+ rc = msm_ispif_init(ispif, pcdata->csid_version);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_CFG:
+ rc = msm_ispif_config(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_START_FRAME_BOUNDARY:
+ rc = msm_ispif_start_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_RESTART_FRAME_BOUNDARY:
+ rc = msm_ispif_restart_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+
+ case ISPIF_STOP_FRAME_BOUNDARY:
+ rc = msm_ispif_stop_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_IMMEDIATELY:
+ rc = msm_ispif_stop_immediately(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_RELEASE:
+ msm_ispif_release(ispif);
+ break;
+ case ISPIF_SET_VFE_INFO:
+ rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info);
+ break;
+ default:
+ pr_err("%s: invalid cfg_type\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+static struct v4l2_file_operations msm_ispif_v4l2_subdev_fops;
+
+static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_MSM_ISPIF_CFG:
+ return msm_ispif_cmd(sd, arg);
+ case MSM_SD_NOTIFY_FREEZE: {
+ ispif->ispif_sof_debug = 0;
+ return 0;
+ }
+ case MSM_SD_SHUTDOWN: {
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+ if (ispif && ispif->base) {
+ mutex_lock(&ispif->mutex);
+ msm_ispif_release(ispif);
+ mutex_unlock(&ispif->mutex);
+ }
+ return 0;
+ }
+ default:
+ pr_err_ratelimited("%s: invalid cmd 0x%x received\n",
+ __func__, cmd);
+ return -ENOIOCTLCMD;
+ }
+}
+
+static long msm_ispif_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return msm_ispif_subdev_ioctl(sd, cmd, arg);
+}
+
+static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ispif_subdev_do_ioctl);
+}
+
+static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&ispif->mutex);
+ /* mem remap is done in init when the clock is on */
+ ispif->open_cnt++;
+ mutex_unlock(&ispif->mutex);
+ return 0;
+}
+
+static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+
+ if (!ispif) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ispif->mutex);
+ if (ispif->open_cnt == 0) {
+ pr_err("%s: Invalid close\n", __func__);
+ rc = -ENODEV;
+ goto end;
+ }
+ ispif->open_cnt--;
+ if (ispif->open_cnt == 0)
+ msm_ispif_release(ispif);
+end:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = {
+ /* .g_chip_ident = &msm_ispif_subdev_g_chip_ident, */
+ .ioctl = &msm_ispif_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_ispif_subdev_ops = {
+ .core = &msm_ispif_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_ispif_internal_ops = {
+ .open = ispif_open_node,
+ .close = ispif_close_node,
+};
+
+static int ispif_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct ispif_device *ispif;
+
+ ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
+ if (!ispif)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ rc = of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,num-isps", &ispif->hw_num_isps);
+ if (rc)
+ /* backward compatibility */
+ ispif->hw_num_isps = 1;
+ /* not an error condition */
+ rc = 0;
+ }
+
+ mutex_init(&ispif->mutex);
+ ispif->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "ispif");
+ if (!ispif->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+ ispif->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "ispif");
+ if (!ispif->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+ ispif->io = request_mem_region(ispif->mem->start,
+ resource_size(ispif->mem), pdev->name);
+ if (!ispif->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto error;
+ }
+ ispif->clk_mux_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csi_clk_mux");
+ if (ispif->clk_mux_mem) {
+ ispif->clk_mux_io = request_mem_region(
+ ispif->clk_mux_mem->start,
+ resource_size(ispif->clk_mux_mem),
+ ispif->clk_mux_mem->name);
+ if (!ispif->clk_mux_io)
+ pr_err("%s: no valid csi_mux region\n", __func__);
+ }
+
+ v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops);
+ ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops;
+ ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ snprintf(ispif->msm_sd.sd.name,
+ ARRAY_SIZE(ispif->msm_sd.sd.name), MSM_ISPIF_DRV_NAME);
+ v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif);
+
+ platform_set_drvdata(pdev, &ispif->msm_sd.sd);
+
+ media_entity_pads_init(&ispif->msm_sd.sd.entity, 0, NULL);
+ ispif->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_ISPIF;
+ ispif->msm_sd.sd.entity.name = pdev->name;
+ ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1;
+ rc = msm_sd_register(&ispif->msm_sd);
+ if (rc) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto error;
+ }
+ msm_ispif_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
+ msm_ispif_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
+ msm_ispif_v4l2_subdev_fops.unlocked_ioctl = msm_ispif_subdev_fops_ioctl;
+ msm_ispif_v4l2_subdev_fops.release = v4l2_subdev_fops.release;
+ msm_ispif_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll;
+#ifdef CONFIG_COMPAT
+ msm_ispif_v4l2_subdev_fops.compat_ioctl32 = msm_ispif_subdev_fops_ioctl;
+#endif
+ ispif->msm_sd.sd.devnode->fops = &msm_ispif_v4l2_subdev_fops;
+ ispif->pdev = pdev;
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+ ispif->open_cnt = 0;
+ return 0;
+
+error:
+ mutex_destroy(&ispif->mutex);
+ kfree(ispif);
+ return rc;
+}
+
+static const struct of_device_id msm_ispif_dt_match[] = {
+ {.compatible = "qcom,ispif"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_ispif_dt_match);
+
+static struct platform_driver ispif_driver = {
+ .probe = ispif_probe,
+ .driver = {
+ .name = MSM_ISPIF_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ispif_dt_match,
+ },
+};
+
+static int __init msm_ispif_init_module(void)
+{
+ return platform_driver_register(&ispif_driver);
+}
+
+static void __exit msm_ispif_exit_module(void)
+{
+ platform_driver_unregister(&ispif_driver);
+}
+
+module_init(msm_ispif_init_module);
+module_exit(msm_ispif_exit_module);
+MODULE_DESCRIPTION("MSM ISP Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.h
new file mode 100644
index 0000000..6217fba
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_ISPIF_H
+#define MSM_ISPIF_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_ispif.h>
+#include "msm_sd.h"
+
+#define ISPIF_CLK_INFO_MAX 24
+
+struct ispif_irq_status {
+ uint32_t ispifIrqStatus0;
+ uint32_t ispifIrqStatus1;
+ uint32_t ispifIrqStatus2;
+};
+
+enum msm_ispif_state_t {
+ ISPIF_POWER_UP,
+ ISPIF_POWER_DOWN,
+};
+struct ispif_sof_count {
+ uint32_t sof_cnt[INTF_MAX];
+};
+
+struct ispif_intf_cmd {
+ uint32_t intf_cmd;
+ uint32_t intf_cmd1;
+};
+
+struct ispif_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct resource *mem;
+ struct resource *clk_mux_mem;
+ struct resource *irq;
+ struct resource *io;
+ struct resource *clk_mux_io;
+ void __iomem *base;
+ void __iomem *clk_mux_base;
+ struct mutex mutex;
+ uint8_t start_ack_pending;
+ uint32_t csid_version;
+ int enb_dump_reg;
+ uint32_t open_cnt;
+ struct ispif_sof_count sof_count[VFE_MAX];
+ struct ispif_intf_cmd applied_intf_cmd[VFE_MAX];
+ enum msm_ispif_state_t ispif_state;
+ struct msm_ispif_vfe_info vfe_info;
+ struct clk *ahb_clk[ISPIF_CLK_INFO_MAX];
+ struct completion reset_complete[VFE_MAX];
+ uint32_t hw_num_isps;
+ uint32_t num_ahb_clk;
+ uint32_t clk_idx;
+ uint32_t ispif_sof_debug;
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index 625a0db..c045eda 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -2077,14 +2077,8 @@
static int __init msm_actuator_init_module(void)
{
- int32_t rc = 0;
-
CDBG("Enter\n");
- rc = platform_driver_register(&msm_actuator_platform_driver);
- if (!rc)
- return rc;
-
- CDBG("%s:%d rc %d\n", __func__, __LINE__, rc);
+ platform_driver_register(&msm_actuator_platform_driver);
return i2c_add_driver(&msm_actuator_i2c_driver);
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_3_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_3_hwreg.h
new file mode 100644
index 0000000..ac0a989
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_4_3_hwreg.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_CSID_3_4_3_HWREG_H
+#define MSM_CSID_3_4_3_HWREG_H
+
+#include <sensor/csid/msm_csid.h>
+
+static uint8_t csid_lane_assign_v3_4_3[PHY_LANE_MAX] = {0, 4, 1, 2, 3};
+static struct csid_reg_parms_t csid_v3_4_3 = {
+ /* MIPI CSID registers */
+ 0x0,
+ 0x4,
+ 0x8,
+ 0xC,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x1C,
+ 0x20,
+ 0x60,
+ 0x64,
+ 0x68,
+ 0x6C,
+ 0x70,
+ 0x74,
+ 0x78,
+ 0x7C,
+ 0x80,
+ 0x84,
+ 0x88,
+ 0x8C,
+ 0x90,
+ 0x94,
+ 0x98,
+ 0xA0,
+ 0xA4,
+ 0xAC,
+ 0xB0,
+ 0xB4,
+ 11,
+ 0x7FFF,
+ 0x4,
+ 17,
+ 0x30040003,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ 0x7f010800,
+ 20,
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index ba32526..9d3184e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -26,6 +26,7 @@
#include "include/msm_csid_3_5_hwreg.h"
#include "include/msm_csid_3_4_1_hwreg.h"
#include "include/msm_csid_3_4_2_hwreg.h"
+#include "include/msm_csid_3_4_3_hwreg.h"
#include "include/msm_csid_3_6_0_hwreg.h"
#include "include/msm_csid_3_5_1_hwreg.h"
#include "cam_hw_ops.h"
@@ -42,6 +43,7 @@
#define CSID_VERSION_V34 0x30040000
#define CSID_VERSION_V34_1 0x30040001
#define CSID_VERSION_V34_2 0x30040002
+#define CSID_VERSION_V34_3 0x30040003
#define CSID_VERSION_V36 0x30060000
#define CSID_VERSION_V37 0x30070000
#define CSID_VERSION_V35 0x30050000
@@ -229,7 +231,9 @@
static int msm_csid_reset(struct csid_device *csid_dev)
{
int32_t rc = 0;
+ uint32_t irq = 0, irq_bitshift;
+ irq_bitshift = csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift;
msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all,
csid_dev->base +
csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr);
@@ -238,8 +242,23 @@
if (rc < 0) {
pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n",
rc);
+ } else if (rc == 0) {
+ irq = msm_camera_io_r(csid_dev->base +
+ csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+ pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ if (irq & (0x1 << irq_bitshift)) {
+ rc = 1;
+ CDBG("%s succeeded", __func__);
+ } else {
+ rc = 0;
+ pr_err("%s reset csid_irq_status failed = 0x%x\n",
+ __func__, irq);
+ }
if (rc == 0)
rc = -ETIMEDOUT;
+ } else {
+ CDBG("%s succeeded", __func__);
}
return rc;
}
@@ -306,8 +325,7 @@
if (!msm_csid_find_max_clk_rate(csid_dev))
pr_err("msm_csid_find_max_clk_rate failed\n");
- clk_rate = (csid_params->csi_clk > 0) ?
- (csid_params->csi_clk) : csid_dev->csid_max_clk;
+ clk_rate = csid_dev->csid_max_clk;
clk_rate = msm_camera_clk_set_rate(&csid_dev->pdev->dev,
csid_dev->csid_clk[csid_dev->csid_clk_index], clk_rate);
@@ -1163,6 +1181,12 @@
new_csid_dev->ctrl_reg->csid_lane_assign =
csid_lane_assign_v3_4_2;
} else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
+ "qcom,csid-v3.4.3")) {
+ new_csid_dev->ctrl_reg->csid_reg = csid_v3_4_3;
+ new_csid_dev->hw_dts_version = CSID_VERSION_V34_3;
+ new_csid_dev->ctrl_reg->csid_lane_assign =
+ csid_lane_assign_v3_4_3;
+ } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node,
"qcom,csid-v3.6.0")) {
new_csid_dev->ctrl_reg->csid_reg = csid_v3_6_0;
new_csid_dev->hw_dts_version = CSID_VERSION_V36;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index 58a4390..0cfddb3 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/module.h>
#include <linux/irqreturn.h>
+#include <asm/div64.h>
#include "msm_csiphy.h"
#include "msm_sd.h"
#include "include/msm_csiphy_2_0_hwreg.h"
@@ -28,7 +29,6 @@
#include "include/msm_csiphy_5_0_hwreg.h"
#include "include/msm_csiphy_5_0_1_hwreg.h"
#include "include/msm_csiphy_10_0_0_hwreg.h"
-
#include "cam_hw_ops.h"
#define DBG_CSIPHY 0
@@ -179,12 +179,13 @@
enum snps_csiphy_mode mode, int num_lanes)
{
+ uint64_t local_data_rate = 0;
uint32_t offset;
uint32_t value, i, diff, diff_i;
void __iomem *csiphybase;
csiphybase = csiphy_dev->base;
-
+ local_data_rate = csiphy_params->data_rate;
if (mode == TWO_LANE_PHY_A)
offset = 0x0;
else if (mode == TWO_LANE_PHY_B)
@@ -192,13 +193,14 @@
else
return -EINVAL;
+ do_div(local_data_rate, num_lanes * MBPS);
diff = abs(snps_v100_freq_values[0].default_bit_rate -
- ((csiphy_params->data_rate / num_lanes) / MBPS));
+ local_data_rate);
/* ToDo: Can be optimized to a O(1) search */
for (i = 1; i < sizeof(snps_v100_freq_values)/
sizeof(snps_v100_freq_values[0]); i++) {
diff_i = abs(snps_v100_freq_values[i].default_bit_rate -
- ((csiphy_params->data_rate / num_lanes) / MBPS));
+ local_data_rate);
if (diff_i > diff) {
i--;
break;
@@ -208,13 +210,15 @@
if (i == (sizeof(snps_v100_freq_values)/
sizeof(snps_v100_freq_values[0]))) {
- if (((csiphy_params->data_rate / num_lanes) / MBPS) >
+ if (local_data_rate >
snps_v100_freq_values[--i].default_bit_rate) {
pr_err("unsupported data rate\n");
return -EINVAL;
}
}
+ csiphy_dev->snps_programmed_data_rate = csiphy_params->data_rate;
+
if (mode == TWO_LANE_PHY_A) {
msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_snps_reg.
mipi_csiphy_sys_ctrl.data,
@@ -290,10 +294,18 @@
void __iomem *csiphybase;
enum snps_csiphy_mode mode = INVALID_MODE;
uint32_t value, num_tries, num_lanes, offset;
+ uint32_t clk_mux_reg = 0;
csiphybase = csiphy_dev->base;
+ if (csiphy_dev->clk_mux_base != NULL)
+ clk_mux_reg = msm_camera_io_r(csiphy_dev->clk_mux_base);
+ else {
+ pr_err("%s: invalid clk_mux_base\n", __func__);
+ return -EINVAL;
+ }
+
/* lane mask usage
- * BIT LANE
+ * BIT LANE
* 0(LSB) PHY A data 0
* 1 PHY A data 1
* 2 PHY B data 0
@@ -311,45 +323,63 @@
mode = AGGREGATE_MODE;
num_lanes = 4;
if (csiphy_dev->snps_state != NOT_CONFIGURED) {
- pr_err("%s: invalid request\n", __func__);
- return -EINVAL;
+ if (csiphy_dev->snps_programmed_data_rate !=
+ csiphy_params->data_rate)
+ pr_err("reconfiguring snps phy");
+ else
+ return 0;
}
csiphy_dev->snps_state = CONFIGURED_AGGREGATE_MODE;
+ clk_mux_reg &= ~0xff;
+ clk_mux_reg |= csiphy_params->csid_core << 4;
+ clk_mux_reg |= (uint32_t)csiphy_params->csid_core;
} else if (lane_mask == LANE_MASK_PHY_A) { /* PHY A */
/* 2 lane config */
num_lanes = 2;
- if (csiphy_dev->snps_state != NOT_CONFIGURED) {
- mode = TWO_LANE_PHY_A;
+ mode = TWO_LANE_PHY_A;
+ if (csiphy_dev->snps_state == NOT_CONFIGURED) {
csiphy_dev->snps_state = CONFIGURED_TWO_LANE_PHY_A;
} else if (csiphy_dev->snps_state ==
CONFIGURED_TWO_LANE_PHY_B) {
/* 2 lane + 2 lane config */
- mode = TWO_LANE_PHY_A;
csiphy_dev->snps_state = CONFIGURED_COMBO_MODE;
} else {
- pr_err("%s: invalid request\n", __func__);
- return -EINVAL;
+ if (csiphy_dev->snps_programmed_data_rate !=
+ csiphy_params->data_rate)
+ pr_err("reconfiguring snps phy");
+ else
+ return 0;
}
+ clk_mux_reg &= ~0xf;
+ clk_mux_reg |= (uint32_t)csiphy_params->csid_core;
} else if (lane_mask == LANE_MASK_PHY_B) { /* PHY B */
/* 2 lane config */
num_lanes = 2;
- if (csiphy_dev->snps_state != NOT_CONFIGURED) {
- mode = TWO_LANE_PHY_B;
+ mode = TWO_LANE_PHY_B;
+ if (csiphy_dev->snps_state == NOT_CONFIGURED) {
csiphy_dev->snps_state = CONFIGURED_TWO_LANE_PHY_B;
} else if (csiphy_dev->snps_state ==
CONFIGURED_TWO_LANE_PHY_A) {
/* 2 lane + 2 lane config */
- mode = TWO_LANE_PHY_B;
csiphy_dev->snps_state = CONFIGURED_COMBO_MODE;
} else {
- pr_err("%s: invalid request\n", __func__);
- return -EINVAL;
+ if (csiphy_dev->snps_programmed_data_rate !=
+ csiphy_params->data_rate)
+ pr_err("reconfiguring snps phy");
+ else
+ return 0;
}
+ clk_mux_reg &= ~0xf0;
+ clk_mux_reg |= csiphy_params->csid_core << 4;
} else { /* None of available configurations */
pr_err("%s: invalid configuration requested\n", __func__);
return -EINVAL;
}
+ msm_camera_io_w(clk_mux_reg, csiphy_dev->clk_mux_base);
+ /* ensure write is done */
+ mb();
+
if (mode == AGGREGATE_MODE || mode == TWO_LANE_PHY_A) {
ret = msm_csiphy_snps_2_lane_config(csiphy_dev,
csiphy_params, TWO_LANE_PHY_A, num_lanes);
@@ -1217,9 +1247,7 @@
return rc;
}
- clk_rate = (csiphy_params->csiphy_clk > 0)
- ? csiphy_params->csiphy_clk :
- csiphy_dev->csiphy_max_clk;
+ clk_rate = csiphy_dev->csiphy_max_clk;
clk_rate = msm_camera_clk_set_rate(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_clk[csiphy_dev->csiphy_clk_index],
clk_rate);
@@ -1233,7 +1261,7 @@
ratio = csiphy_dev->csiphy_max_clk/clk_rate;
csiphy_params->settle_cnt = csiphy_params->settle_cnt/ratio;
}
- CDBG("%s csiphy_params, mask = 0x%x cnt = %d, data rate = %lu\n",
+ CDBG("%s csiphy_params, mask = 0x%x cnt = %d, data rate = %llu\n",
__func__,
csiphy_params->lane_mask,
csiphy_params->lane_cnt, csiphy_params->data_rate);
@@ -1638,6 +1666,7 @@
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
csiphy_dev->snps_state = NOT_CONFIGURED;
+ csiphy_dev->snps_programmed_data_rate = 0;
return 0;
csiphy_enable_clk_fail:
@@ -1744,6 +1773,7 @@
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
csiphy_dev->snps_state = NOT_CONFIGURED;
+ csiphy_dev->snps_programmed_data_rate = 0;
return 0;
csiphy_enable_clk_fail:
@@ -1893,6 +1923,7 @@
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
csiphy_dev->snps_state = NOT_CONFIGURED;
+ csiphy_dev->snps_programmed_data_rate = 0;
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
@@ -2025,6 +2056,7 @@
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
csiphy_dev->snps_state = NOT_CONFIGURED;
+ csiphy_dev->snps_programmed_data_rate = 0;
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
@@ -2057,7 +2089,10 @@
rc = -EFAULT;
break;
}
- csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE;
+ if (csiphy_dev->csiphy_sof_debug == SOF_DEBUG_ENABLE) {
+ csiphy_dev->csiphy_sof_debug = SOF_DEBUG_DISABLE;
+ rc = msm_camera_enable_irq(csiphy_dev->irq, false);
+ }
rc = msm_csiphy_lane_config(csiphy_dev, &csiphy_params);
break;
case CSIPHY_RELEASE:
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
index 79baf3c..41d2034 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
@@ -250,6 +250,7 @@
uint8_t is_snps_phy;
enum snps_csiphy_state snps_state;
uint8_t num_clk_irq_registers;
+ uint64_t snps_programmed_data_rate;
};
#define VIDIOC_MSM_CSIPHY_RELEASE \
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
index df22d84..3590e15 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
@@ -20,7 +20,6 @@
#include "msm_sensor.h"
#undef CDBG
-#define MSM_CAMERA_TZ_I2C_VERBOSE
#ifdef CONFIG_MSM_SEC_CCI_DEBUG
#define TZ_I2C_FN_RETURN(ret, i2c_fn, ...) \
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index 726c7c1..7832181 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-#define SENSOR_DRIVER_I2C "i2c_camera"
+#define SENSOR_DRIVER_I2C "camera"
/* Header file declaration */
#include "msm_sensor.h"
#include "msm_sd.h"
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index f3814e0..ce50dcd 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -3385,7 +3385,7 @@
*/
int sde_rotator_session_open(struct sde_rot_mgr *mgr,
struct sde_rot_file_private **pprivate, int session_id,
- struct sde_rot_queue *queue)
+ struct sde_rot_queue_v1 *queue)
{
int ret;
struct sde_rot_file_private *private;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 8421873..f6fdb90 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -242,6 +242,12 @@
struct sde_rot_hw_resource *hw;
};
+struct sde_rot_queue_v1 {
+ struct kthread_worker *rot_kw;
+ struct task_struct *rot_thread;
+ struct sde_rot_timeline *timeline;
+ struct sde_rot_hw_resource *hw;
+};
/*
* struct sde_rot_entry_container - rotation request
* @list: list of active requests managed by rotator manager
@@ -294,7 +300,7 @@
struct kthread_work commit_work;
struct kthread_work done_work;
struct sde_rot_queue *commitq;
- struct sde_rot_queue *fenceq;
+ struct sde_rot_queue_v1 *fenceq;
struct sde_rot_queue *doneq;
struct sde_rot_entry_container *request;
@@ -351,7 +357,7 @@
struct list_head req_list;
struct list_head perf_list;
struct sde_rot_mgr *mgr;
- struct sde_rot_queue *fenceq;
+ struct sde_rot_queue_v1 *fenceq;
};
/*
@@ -586,7 +592,7 @@
*/
int sde_rotator_session_open(struct sde_rot_mgr *mgr,
struct sde_rot_file_private **pprivate, int session_id,
- struct sde_rot_queue *queue);
+ struct sde_rot_queue_v1 *queue);
/*
* sde_rotator_session_close - close the given rotator per file session
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index a46194f..48ef46c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -942,6 +942,7 @@
ctx->rotate = 0;
ctx->secure = 0;
ctx->abort_pending = 0;
+ ctx->kthread_id = -1;
ctx->format_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ctx->format_cap.fmt.pix.pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2;
ctx->format_cap.fmt.pix.width = 640;
@@ -980,6 +981,7 @@
ctx, sde_rotator_queue_init);
if (IS_ERR_OR_NULL(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
+ ctx->fh.m2m_ctx = NULL;
goto error_m2m_init;
}
}
@@ -999,18 +1001,22 @@
goto error_create_sysfs;
}
- snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id,
- ctx->session_id);
- kthread_init_worker(&ctx->work_queue.rot_kw);
- ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn,
- &ctx->work_queue.rot_kw, name);
- if (IS_ERR(ctx->work_queue.rot_thread)) {
- SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n");
- ret = -EPERM;
- ctx->work_queue.rot_thread = NULL;
- goto error_alloc_workqueue;
+ for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) {
+ if (rot_dev->kthread_free[i]) {
+ rot_dev->kthread_free[i] = false;
+ ctx->kthread_id = i;
+ ctx->work_queue.rot_kw = &rot_dev->rot_kw[i];
+ ctx->work_queue.rot_thread = rot_dev->rot_thread[i];
+ break;
+ }
}
- SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name);
+
+ if (ctx->kthread_id < 0) {
+ SDEDEV_ERR(ctx->rot_dev->dev,
+ "fail to acquire the kthread\n");
+ ret = -EINVAL;
+ goto error_alloc_kthread;
+ }
snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id,
ctx->session_id);
@@ -1069,13 +1075,17 @@
error_open_session:
sde_rot_mgr_unlock(rot_dev->mgr);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- kthread_flush_worker(&ctx->work_queue.rot_kw);
- kthread_stop(ctx->work_queue.rot_thread);
-error_alloc_workqueue:
+ rot_dev->kthread_free[ctx->kthread_id] = true;
+ ctx->kthread_id = -1;
+error_alloc_kthread:
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
error_create_sysfs:
kobject_put(&ctx->kobj);
error_kobj_init:
+ if (ctx->file) {
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ ctx->fh.m2m_ctx = NULL;
+ }
error_m2m_init:
if (ctx->file) {
v4l2_fh_del(&ctx->fh);
@@ -1084,6 +1094,7 @@
mutex_unlock(&rot_dev->lock);
error_lock:
kfree(ctx);
+ ctx = NULL;
return ERR_PTR(ret);
}
@@ -1096,10 +1107,18 @@
static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx,
struct file *file)
{
- struct sde_rotator_device *rot_dev = ctx->rot_dev;
- u32 session_id = ctx->session_id;
+ struct sde_rotator_device *rot_dev;
+ u32 session_id;
struct list_head *curr, *next;
+ if (!ctx) {
+ SDEROT_DBG("ctx is NULL\n");
+ return -EINVAL;
+ }
+
+ rot_dev = ctx->rot_dev;
+ session_id = ctx->session_id;
+
ATRACE_END(ctx->kobj.name);
SDEDEV_DBG(rot_dev->dev, "release s:%d\n", session_id);
@@ -1113,10 +1132,12 @@
if (ctx->file) {
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
SDEDEV_DBG(rot_dev->dev, "release streams s:%d\n", session_id);
- v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
+ if (ctx->fh.m2m_ctx) {
+ v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
V4L2_BUF_TYPE_VIDEO_OUTPUT);
- v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
+ v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ }
}
mutex_unlock(&rot_dev->lock);
SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n", session_id);
@@ -1146,14 +1167,19 @@
mutex_lock(&rot_dev->lock);
SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id);
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
- kthread_flush_worker(&ctx->work_queue.rot_kw);
- kthread_stop(ctx->work_queue.rot_thread);
+ if (ctx->kthread_id >= 0 && ctx->work_queue.rot_kw) {
+ kthread_flush_worker(ctx->work_queue.rot_kw);
+ rot_dev->kthread_free[ctx->kthread_id] = true;
+ }
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
kobject_put(&ctx->kobj);
if (ctx->file) {
- v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
+ if (ctx->fh.m2m_ctx)
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ if (ctx->fh.vdev) {
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ }
}
kfree(ctx->vbinfo_out);
kfree(ctx->vbinfo_cap);
@@ -1834,7 +1860,7 @@
} else {
SDEROT_ERR("invalid stats timestamp\n");
}
- req->retire_kw = &ctx->work_queue.rot_kw;
+ req->retire_kw = ctx->work_queue.rot_kw;
req->retire_work = &request->retire_work;
trace_rot_entry_fence(
@@ -3187,7 +3213,7 @@
goto error_init_request;
}
- req->retire_kw = &ctx->work_queue.rot_kw;
+ req->retire_kw = ctx->work_queue.rot_kw;
req->retire_work = &request->retire_work;
ret = sde_rotator_handle_request_common(
@@ -3485,7 +3511,7 @@
list_del_init(&request->list);
list_add_tail(&request->list, &ctx->pending_list);
spin_unlock(&ctx->list_lock);
- kthread_queue_work(&ctx->work_queue.rot_kw,
+ kthread_queue_work(ctx->work_queue.rot_kw,
&request->submit_work);
}
} else if (request && !atomic_read(&request->req->pending_count)) {
@@ -3539,7 +3565,8 @@
{
struct sde_rotator_device *rot_dev;
struct video_device *vdev;
- int ret;
+ int ret, i;
+ char name[32];
SDEDEV_DBG(&pdev->dev, "SDE v4l2 rotator probed\n");
@@ -3622,9 +3649,29 @@
rot_dev->debugfs_root = sde_rotator_create_debugfs(rot_dev);
+ for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) {
+ snprintf(name, sizeof(name), "rot_fenceq_%d_%d",
+ rot_dev->dev->id, i);
+ kthread_init_worker(&rot_dev->rot_kw[i]);
+ rot_dev->rot_thread[i] = kthread_run(kthread_worker_fn,
+ &rot_dev->rot_kw[i], name);
+ if (IS_ERR(rot_dev->rot_thread[i])) {
+ SDEDEV_ERR(rot_dev->dev,
+ "fail allocate kthread i:%d\n", i);
+ ret = -EPERM;
+ goto error_kthread_create;
+ }
+ rot_dev->kthread_free[i] = true;
+ }
+
SDEDEV_INFO(&pdev->dev, "SDE v4l2 rotator probe success\n");
return 0;
+error_kthread_create:
+ for (i--; i >= 0; i--)
+ kthread_stop(rot_dev->rot_thread[i]);
+ sde_rotator_destroy_debugfs(rot_dev->debugfs_root);
+ video_unregister_device(rot_dev->vdev);
error_video_register:
video_device_release(vdev);
error_alloc_video_device:
@@ -3647,6 +3694,7 @@
static int sde_rotator_remove(struct platform_device *pdev)
{
struct sde_rotator_device *rot_dev;
+ int i;
rot_dev = platform_get_drvdata(pdev);
if (rot_dev == NULL) {
@@ -3655,6 +3703,8 @@
}
sde_rotator_pm_qos_remove(rot_dev->mdata);
+ for (i = MAX_ROT_OPEN_SESSION - 1; i >= 0; i--)
+ kthread_stop(rot_dev->rot_thread[i]);
sde_rotator_destroy_debugfs(rot_dev->debugfs_root);
video_unregister_device(rot_dev->vdev);
video_device_release(rot_dev->vdev);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index ab27043..f818016 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,8 @@
/* maximum number of outstanding requests per ctx session */
#define SDE_ROTATOR_REQUEST_MAX 2
+#define MAX_ROT_OPEN_SESSION 16
+
struct sde_rotator_device;
struct sde_rotator_ctx;
@@ -140,6 +142,7 @@
* @retired_list: list of retired/free request
* @requests: static allocation of free requests
* @rotcfg: current core rotation configuration
+ * @kthread_id: thread_id used for fence management
*/
struct sde_rotator_ctx {
struct kobject kobj;
@@ -164,7 +167,7 @@
struct sde_rotator_vbinfo *vbinfo_cap;
struct sde_rotator_vbinfo *vbinfo_out;
wait_queue_head_t wait_queue;
- struct sde_rot_queue work_queue;
+ struct sde_rot_queue_v1 work_queue;
struct sde_rot_file_private *private;
struct llcc_slice_desc *slice;
u32 commit_sequence_id;
@@ -174,6 +177,8 @@
struct list_head retired_list;
struct sde_rotator_request requests[SDE_ROTATOR_REQUEST_MAX];
struct sde_rotation_config rotcfg;
+
+ int kthread_id;
};
/*
@@ -212,6 +217,9 @@
* @open_timeout: maximum wait time for ctx open in msec
* @open_wq: wait queue for ctx open
* @excl_ctx: Pointer to exclusive ctx
+ * @rot_kw: rotator thread work
+ * @rot_thread: rotator threads
+ * @kthread_free: check if thread is available or not
*/
struct sde_rotator_device {
struct mutex lock;
@@ -237,6 +245,10 @@
u32 open_timeout;
wait_queue_head_t open_wq;
struct sde_rotator_ctx *excl_ctx;
+
+ struct kthread_worker rot_kw[MAX_ROT_OPEN_SESSION];
+ struct task_struct *rot_thread[MAX_ROT_OPEN_SESSION];
+ bool kthread_free[MAX_ROT_OPEN_SESSION];
};
static inline
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index e3a5cc7..9ef9d05 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1239,6 +1239,51 @@
struct hfi_h264_entropy_control);
break;
}
+ case HAL_CONFIG_HEIC_FRAME_QUALITY:
+ {
+ struct hfi_heic_frame_quality *hfi;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY;
+ hfi =
+ (struct hfi_heic_frame_quality *) &pkt->rg_property_data[1];
+ hfi->frame_quality =
+ ((struct hal_heic_frame_quality *)pdata)->frame_quality;
+ pkt->size += sizeof(u32) +
+ sizeof(struct hfi_heic_frame_quality);
+ break;
+ }
+ case HAL_CONFIG_HEIC_GRID_ENABLE:
+ {
+ struct hfi_heic_grid_enable *hfi;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_HEIC_GRID_ENABLE;
+ hfi = (struct hfi_heic_grid_enable *) &pkt->rg_property_data[1];
+ hfi->grid_enable =
+ ((struct hal_heic_grid_enable *)pdata)->grid_enable;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_heic_grid_enable);
+ break;
+ }
+ case HAL_CONFIG_HEIC_FRAME_CROP_INFO:
+ {
+ struct hfi_frame_crop *hfi_crop_info;
+ struct hal_frame_crop *hal_crop_info =
+ (struct hal_frame_crop *) pdata;
+
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_HEIC_FRAME_CROP_INFO;
+ hfi_crop_info =
+ (struct hfi_frame_crop *) &pkt->rg_property_data[1];
+
+ hfi_crop_info->left = hal_crop_info->left;
+ hfi_crop_info->top = hal_crop_info->top;
+ hfi_crop_info->width = hal_crop_info->width;
+ hfi_crop_info->height = hal_crop_info->height;
+
+ pkt->size += sizeof(u32) + sizeof(struct hfi_frame_crop);
+ break;
+ }
case HAL_PARAM_VENC_RATE_CONTROL:
{
u32 *rc;
@@ -1266,6 +1311,9 @@
case HAL_RATE_CONTROL_MBR_VFR:
pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_VFR;
break;
+ case HAL_RATE_CONTROL_CQ:
+ pkt->rg_property_data[1] = HFI_RATE_CONTROL_CQ;
+ break;
default:
dprintk(VIDC_ERR,
"Invalid Rate control setting: %pK\n",
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 97f5ca2..0cdcbd6 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -599,6 +599,9 @@
case HFI_CAPABILITY_UBWC_CR_STATS:
hal_cap = HAL_CAPABILITY_UBWC_CR_STATS;
break;
+ case HFI_CAPABILITY_IMG_GRID_DIMENSION:
+ hal_cap = HAL_CAPABILITY_IMG_GRID_DIMENSION;
+ break;
default:
dprintk(VIDC_DBG, "%s: unknown capablity %#x\n",
__func__, capability_type);
@@ -723,6 +726,9 @@
case HFI_CAPABILITY_UBWC_CR_STATS:
out = &capability->ubwc_cr_stats;
break;
+ case HFI_CAPABILITY_IMG_GRID_DIMENSION:
+ out = &capability->img_grid_dimension;
+ break;
default:
dprintk(VIDC_DBG, "%s: unknown capablity %#x\n",
__func__, in->capability_type);
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index be24f8d..e6a4ed30 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -24,6 +24,14 @@
#define BIT_RATE_STEP 1
#define DEFAULT_FRAME_RATE 15
#define OPERATING_FRAME_RATE_STEP (1 << 16)
+#define MIN_FRAME_QUALITY 0
+#define MAX_FRAME_QUALITY 100
+#define DEFAULT_FRAME_QUALITY 80
+#define FRAME_QUALITY_STEP 1
+#define MIN_GRID_DIMENSION 512
+#define MAX_GRID_DIMENSION 8192
+#define DEFAULT_GRID_DIMENSION 512
+#define GRID_DIMENSION_STEP 256
#define MAX_SLICE_BYTE_SIZE ((MAX_BIT_RATE)>>3)
#define MIN_SLICE_BYTE_SIZE 512
#define MAX_SLICE_MB_SIZE ((4096 * 2304) >> 8)
@@ -50,6 +58,7 @@
"CBR CFR",
"MBR CFR",
"MBR VFR",
+ "CQ",
NULL
};
@@ -358,7 +367,7 @@
.name = "Video Framerate and Bitrate Control",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
- .maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR,
+ .maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ,
.default_value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
.step = 0,
.menu_skip_mask = ~(
@@ -368,11 +377,35 @@
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR) |
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR) |
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR) |
- (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR)
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR) |
+ (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ)
),
.qmenu = mpeg_video_rate_control,
},
{
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY,
+ .name = "Frame quality",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MIN_FRAME_QUALITY,
+ .maximum = MAX_FRAME_QUALITY,
+ .default_value = DEFAULT_FRAME_QUALITY,
+ .step = FRAME_QUALITY_STEP,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION,
+ .name = "Image grid",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = MIN_GRID_DIMENSION,
+ .maximum = MAX_GRID_DIMENSION,
+ .default_value = DEFAULT_GRID_DIMENSION,
+ .step = GRID_DIMENSION_STEP,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_BITRATE,
.name = "Bit Rate",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -705,7 +738,7 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)|
(1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP)|
- (1UL << V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP)
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO)
),
.qmenu = mpeg_video_vidc_extradata,
},
@@ -1383,6 +1416,8 @@
enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT;
u32 color_primaries, custom_matrix;
struct hal_nal_stream_format_select stream_format;
+ struct hal_heic_frame_quality frame_quality;
+ struct hal_heic_grid_enable grid_enable;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -1483,6 +1518,13 @@
rc = -ENOTSUPP;
break;
}
+ if ((ctrl->val ==
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ) &&
+ inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) {
+ dprintk(VIDC_ERR, "CQ supported only for HEVC\n");
+ rc = -ENOTSUPP;
+ break;
+ }
property_id = HAL_PARAM_VENC_RATE_CONTROL;
property_val = ctrl->val;
pdata = &property_val;
@@ -1497,6 +1539,31 @@
inst->clk_data.bitrate = ctrl->val;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY:
+ {
+ property_id = HAL_CONFIG_HEIC_FRAME_QUALITY;
+ frame_quality.frame_quality = ctrl->val;
+ pdata = &frame_quality;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION:
+ {
+ property_id = HAL_CONFIG_HEIC_GRID_ENABLE;
+ if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) {
+ dprintk(VIDC_ERR, "Grid is supported only for HEVC\n");
+ rc = -ENOTSUPP;
+ break;
+ }
+ if (ctrl->val == 0) {
+ dprintk(VIDC_ERR, "Dimension 0 is not supported\n");
+ rc = -ENOTSUPP;
+ break;
+ }
+ grid_enable.grid_enable = ctrl->val;
+ inst->img_grid_dimension = ctrl->val;
+ pdata = &grid_enable;
+ break;
+ }
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
{
struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL(
@@ -2172,6 +2239,7 @@
enable.enable = 0;
pdata = &enable;
inst->clk_data.low_latency_mode = (bool) enable.enable;
+ msm_dcvs_try_enable(inst);
break;
}
case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8:
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f077e3b..651d0a2 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -901,6 +901,51 @@
return rc;
}
+static int msm_vidc_create_tile_info_table(struct msm_vidc_inst *inst)
+{
+ int i = 0, j = 0;
+ u32 width = 0, height = 0;
+ u32 trows = 0, tcols = 0;
+
+ /* Don't create table for non-HEIC formats*/
+ if (inst->img_grid_dimension <= 0 ||
+ inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC)
+ return 0;
+
+ width = inst->prop.width[OUTPUT_PORT];
+ height = inst->prop.height[OUTPUT_PORT];
+ tcols = (width + inst->img_grid_dimension - 1) /
+ inst->img_grid_dimension;
+ trows = (height + inst->img_grid_dimension - 1) /
+ inst->img_grid_dimension;
+ inst->tinfo.count = trows * tcols;
+ if (inst->tinfo.count > MAX_HEIC_TILES_COUNT) {
+ dprintk(VIDC_ERR,
+ "Tiles count (%d) exceeds maximum\n",
+ inst->tinfo.count);
+ return -ENOTSUPP;
+ }
+
+ dprintk(VIDC_DBG,
+ "Grid dimension %d width %d height %d row %d col %d\n",
+ inst->img_grid_dimension, width, height,
+ trows, tcols);
+
+ for (j = 0; j < trows; ++j) {
+ for (i = 0; i < tcols; ++i) {
+ inst->tinfo.tile_rects[j*tcols+i].left =
+ (i * inst->img_grid_dimension);
+ inst->tinfo.tile_rects[j*tcols+i].top =
+ (j * inst->img_grid_dimension);
+ inst->tinfo.tile_rects[j*tcols+i].width =
+ inst->img_grid_dimension;
+ inst->tinfo.tile_rects[j*tcols+i].height =
+ inst->img_grid_dimension;
+ }
+ }
+ return 0;
+}
+
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
@@ -909,6 +954,13 @@
hdev = inst->core->device;
+ /* Create tile info table */
+ rc = msm_vidc_create_tile_info_table(inst);
+ if (rc) {
+ dprintk(VIDC_ERR, "Tile info table was not generated\n");
+ goto fail_start;
+ }
+
/* Check if current session is under HW capability */
rc = msm_vidc_check_session_supported(inst);
if (rc) {
@@ -1480,7 +1532,9 @@
V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE,
inst->profile);
break;
-
+ case V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION:
+ ctrl->val = inst->img_grid_dimension;
+ break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
ctrl->val = msm_comm_hal_to_v4l2(
V4L2_CID_MPEG_VIDEO_H264_LEVEL,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index 4a0aa58..a665978 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -114,14 +114,18 @@
{
struct recon_buf *binfo, *nextb;
struct vidc_input_cr_data *temp, *next;
- u32 min_cf = 0, max_cf = 0;
- u32 min_input_cr = 0, max_input_cr = 0, min_cr = 0, max_cr = 0;
+ u32 max_cr = 0, max_cf = 0, max_input_cr = 0;
+ u32 min_cr = MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO;
+ u32 min_input_cr = MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO;
+ u32 min_cf = MSM_VIDC_MAX_UBWC_COMPLEXITY_FACTOR;
mutex_lock(&inst->reconbufs.lock);
list_for_each_entry_safe(binfo, nextb, &inst->reconbufs.list, list) {
- min_cr = min(min_cr, binfo->CR);
+ if (binfo->CR)
+ min_cr = min(min_cr, binfo->CR);
+ if (binfo->CF)
+ min_cf = min(min_cf, binfo->CF);
max_cr = max(max_cr, binfo->CR);
- min_cf = min(min_cf, binfo->CF);
max_cf = max(max_cf, binfo->CF);
}
mutex_unlock(&inst->reconbufs.lock);
@@ -605,6 +609,7 @@
struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
u64 rate = 0;
struct clock_data *dcvs = NULL;
+ u32 operating_rate, vsp_factor_num = 10, vsp_factor_den = 7;
core = inst->core;
dcvs = &inst->clk_data;
@@ -627,8 +632,19 @@
vsp_cycles = mbs_per_second * inst->clk_data.entry->vsp_cycles;
- /* 10 / 7 is overhead factor */
- vsp_cycles += (inst->clk_data.bitrate * 10) / 7;
+ operating_rate = inst->clk_data.operating_rate >> 16;
+ if (operating_rate > inst->prop.fps && inst->prop.fps) {
+ vsp_factor_num *= operating_rate;
+ vsp_factor_den *= inst->prop.fps;
+ }
+ //adjust factor for 2 core case, due to workload is not
+ //equally distributed on 2 cores, use 0.65 instead of 0.5
+ if (inst->clk_data.core_id == VIDC_CORE_ID_3) {
+ vsp_factor_num = vsp_factor_num * 13 / 10;
+ vsp_factor_den *= 2;
+ }
+ vsp_cycles += ((u64)inst->clk_data.bitrate * vsp_factor_num) /
+ vsp_factor_den;
} else if (inst->session_type == MSM_VIDC_DECODER) {
vpp_cycles = mbs_per_second * inst->clk_data.entry->vpp_cycles;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7d9bbed..d77ea21 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1564,6 +1564,7 @@
print_cap("max_video_cores", &inst->capability.max_video_cores);
print_cap("max_work_modes", &inst->capability.max_work_modes);
print_cap("ubwc_cr_stats", &inst->capability.ubwc_cr_stats);
+ print_cap("image_grid_dimension", &inst->capability.img_grid_dimension);
dprintk(VIDC_DBG, "profile count : %u",
inst->capability.profile_level.profile_count);
@@ -2034,6 +2035,7 @@
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct v4l2_event flush_event = {0};
+ struct recon_buf *binfo;
u32 *ptr = NULL;
enum hal_flush flush_type;
int rc;
@@ -2088,6 +2090,13 @@
goto exit;
}
+ mutex_lock(&inst->reconbufs.lock);
+ list_for_each_entry(binfo, &inst->reconbufs.list, list) {
+ binfo->CR = 0;
+ binfo->CF = 0;
+ }
+ mutex_unlock(&inst->reconbufs.lock);
+
dprintk(VIDC_DBG,
"Notify flush complete, flush_type: %x\n", flush_type);
v4l2_event_queue_fh(&inst->event_handler, &flush_event);
@@ -2352,6 +2361,17 @@
return 0;
}
+static bool is_heic_encode_session(struct msm_vidc_inst *inst)
+{
+ if (inst->session_type == MSM_VIDC_ENCODER &&
+ (get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) ==
+ HAL_VIDEO_CODEC_HEVC) &&
+ (inst->img_grid_dimension > 0))
+ return true;
+ else
+ return false;
+}
+
static bool is_eos_buffer(struct msm_vidc_inst *inst, u32 device_addr)
{
struct eos_buf *temp, *next;
@@ -2397,6 +2417,12 @@
}
empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
+ if (is_heic_encode_session(inst) &&
+ (empty_buf_done->input_tag < inst->tinfo.count - 1)) {
+ dprintk(VIDC_DBG, "Wait for last tile. Current tile no: %d\n",
+ empty_buf_done->input_tag);
+ return;
+ }
/* If this is internal EOS buffer, handle it in driver */
if (is_eos_buffer(inst, empty_buf_done->packet_buffer)) {
dprintk(VIDC_DBG, "Received EOS buffer 0x%x\n",
@@ -3136,10 +3162,11 @@
static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
{
struct msm_vidc_inst *temp;
+ int op_rate = 0;
dprintk(VIDC_ERR, "Running instances:\n");
- dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%4s\n",
- "type", "w", "h", "fps", "prop");
+ dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%6s|%4s\n",
+ "type", "w", "h", "fps", "opr", "prop");
mutex_lock(&core->lock);
list_for_each_entry(temp, &core->instances, list) {
@@ -3153,13 +3180,21 @@
if (msm_comm_turbo_session(temp))
strlcat(properties, "T", sizeof(properties));
- dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4s\n",
+ if (is_realtime_session(temp))
+ strlcat(properties, "R", sizeof(properties));
+
+ if (temp->clk_data.operating_rate)
+ op_rate = temp->clk_data.operating_rate >> 16;
+ else
+ op_rate = temp->prop.fps;
+
+ dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%6d|%4s\n",
temp->session_type,
max(temp->prop.width[CAPTURE_PORT],
temp->prop.width[OUTPUT_PORT]),
max(temp->prop.height[CAPTURE_PORT],
temp->prop.height[OUTPUT_PORT]),
- temp->prop.fps, properties);
+ temp->prop.fps, op_rate, properties);
}
}
mutex_unlock(&core->lock);
@@ -4148,6 +4183,58 @@
}
}
+static int msm_comm_qbuf_heic_tiles(struct msm_vidc_inst *inst,
+ struct vidc_frame_data *frame_data)
+{
+ int rc = 0, tindex;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (inst->tinfo.count > MAX_HEIC_TILES_COUNT) {
+ dprintk(VIDC_ERR, "%s tiles count exceeds maximum\n", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
+
+ for (tindex = 0; tindex < inst->tinfo.count; ++tindex) {
+ dprintk(VIDC_DBG,
+ "%s tile# %d Crop: left %u top %u width %u height %u\n",
+ __func__, tindex,
+ inst->tinfo.tile_rects[tindex].left,
+ inst->tinfo.tile_rects[tindex].top,
+ inst->tinfo.tile_rects[tindex].width,
+ inst->tinfo.tile_rects[tindex].height);
+
+ rc = call_hfi_op(hdev, session_set_property,
+ (void *)inst->session,
+ HAL_CONFIG_HEIC_FRAME_CROP_INFO,
+ &inst->tinfo.tile_rects[tindex]);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to set HEIC crop info %d\n", rc);
+ goto err_bad_input;
+ }
+
+ frame_data->input_tag = tindex;
+
+ rc = call_hfi_op(hdev, session_etb,
+ (void *)inst->session, frame_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to issue etb: %d\n", rc);
+ goto err_bad_input;
+ }
+ }
+
+err_bad_input:
+ return rc;
+}
+
static int msm_comm_qbuf_rbr(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
@@ -4355,12 +4442,24 @@
for (c = 0; c < etbs.count; ++c) {
struct vidc_frame_data *frame_data = &etbs.data[c];
- rc = call_hfi_op(hdev, session_etb, inst->session,
- frame_data);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to issue etb: %d\n",
+ if (is_heic_encode_session(inst)) {
+ rc = msm_comm_qbuf_heic_tiles(inst, frame_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to send tiles: %d\n",
rc);
- goto err_bad_input;
+ goto err_bad_input;
+ }
+ } else {
+
+ rc = call_hfi_op(hdev, session_etb,
+ inst->session, frame_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to issue etb: %d\n",
+ rc);
+ goto err_bad_input;
+ }
}
log_frame(inst, frame_data,
@@ -5421,6 +5520,11 @@
u32 input_height, input_width, output_height, output_width;
u32 rotation;
+ if (is_heic_encode_session(inst)) {
+ dprintk(VIDC_DBG, "Skip downscale check for HEIC\n");
+ return 0;
+ }
+
input_height = inst->prop.height[OUTPUT_PORT];
input_width = inst->prop.width[OUTPUT_PORT];
output_height = inst->prop.height[CAPTURE_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 60cdd18..e107609 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -51,6 +51,7 @@
#define MAX_NUM_CAPTURE_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME
#define MAX_SUPPORTED_INSTANCES 16
+#define MAX_HEIC_TILES_COUNT 256
/* Maintains the number of FTB's between each FBD over a window */
#define DCVS_FTB_WINDOW 16
@@ -264,6 +265,11 @@
u32 height;
};
+struct tile_info {
+ u32 count;
+ struct session_crop tile_rects[MAX_HEIC_TILES_COUNT];
+};
+
struct session_prop {
u32 width[MAX_PORT_NUM];
u32 height[MAX_PORT_NUM];
@@ -419,6 +425,8 @@
u32 profile;
u32 level;
u32 entropy_mode;
+ u32 img_grid_dimension;
+ struct tile_info tinfo;
struct msm_vidc_codec_data *codec_data;
struct hal_hdr10_pq_sei hdr10_sei_params;
};
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 530fe3a..1dc6723 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -25,6 +25,7 @@
enum clock_properties {
CLOCK_PROP_HAS_SCALING = 1 << 0,
CLOCK_PROP_HAS_MEM_RETENTION = 1 << 1,
+ CLOCK_PROP_DISABLE_MEMCORE_ONLY = 1 << 2,
};
#define PERF_GOV "performance"
@@ -666,6 +667,11 @@
else
vc->has_mem_retention = false;
+ if (clock_props[c] & CLOCK_PROP_DISABLE_MEMCORE_ONLY)
+ vc->disable_memcore_only = true;
+ else
+ vc->disable_memcore_only = false;
+
dprintk(VIDC_DBG, "Found clock %s: scale-able = %s\n", vc->name,
vc->count ? "yes" : "no");
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 23e33fe..06d8fa5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -90,6 +90,7 @@
u32 count;
bool has_scaling;
bool has_mem_retention;
+ bool disable_memcore_only;
};
struct clock_set {
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 7e7ed47..79ed798 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -60,6 +60,9 @@
/* Poll interval in uS */
#define POLL_INTERVAL_US 50
+#define VENUS_AXI_HALT_ACK_TIMEOUT_US 500000
+
+
enum tzbsp_video_state {
TZBSP_VIDEO_STATE_SUSPEND = 0,
TZBSP_VIDEO_STATE_RESUME = 1,
@@ -1094,6 +1097,8 @@
int rc = 0;
venus_hfi_for_each_clock(device, cl) {
+ if (cl->disable_memcore_only)
+ continue;
if (cl->has_scaling) {/* has_scaling */
device->clk_freq = freq;
rc = clk_set_rate(cl->clk, freq);
@@ -2850,12 +2855,52 @@
mutex_unlock(&device->lock);
}
+static int __halt_axi(struct venus_hfi_device *device)
+{
+ u32 reg;
+ int rc = 0;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "Invalid input\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Driver needs to make sure that clocks are enabled to read Venus AXI
+ * registers. If not skip AXI HALT.
+ */
+ if (!device->power_enabled) {
+ dprintk(VIDC_WARN,
+ "Clocks are OFF, skipping AXI HALT\n");
+ return -EINVAL;
+ }
+
+ /* Halt AXI and AXI IMEM VBIF Access */
+ reg = __read_register(device, VENUS_WRAPPER_AXI_HALT);
+ reg |= BRIC_AXI_HALT;
+ __write_register(device, VENUS_WRAPPER_AXI_HALT, reg);
+
+ /* Request for AXI bus port halt */
+ rc = readl_poll_timeout(device->hal_data->register_base
+ + VENUS_WRAPPER_AXI_HALT_STATUS,
+ reg, reg & BRIC_AXI_HALT_ACK,
+ POLL_INTERVAL_US,
+ VENUS_AXI_HALT_ACK_TIMEOUT_US);
+ if (rc)
+ dprintk(VIDC_WARN, "AXI bus port halt timeout\n");
+
+ return rc;
+}
+
static void __process_sys_error(struct venus_hfi_device *device)
{
struct hfi_sfr_struct *vsfr = NULL;
__set_state(device, VENUS_STATE_DEINIT);
+ if (__halt_axi(device))
+ dprintk(VIDC_WARN, "Failed to halt AXI after SYS_ERROR\n");
+
vsfr = (struct hfi_sfr_struct *)device->sfr.align_virtual_addr;
if (vsfr) {
void *p = memchr(vsfr->rg_data, '\0', vsfr->bufSize);
@@ -3319,6 +3364,8 @@
}
venus_hfi_for_each_clock_reverse(device, cl) {
+ if (cl->disable_memcore_only)
+ continue;
dprintk(VIDC_DBG, "Clock: %s disable and unprepare\n",
cl->name);
rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
@@ -3348,6 +3395,11 @@
}
venus_hfi_for_each_clock(device, cl) {
+ /* MEM CORE is ON by default. Unset it for unused clocks*/
+ if (cl->disable_memcore_only) {
+ clk_set_flags(cl->clk, CLKFLAG_NORETAIN_MEM);
+ continue;
+ }
/*
* For the clocks we control, set the rate prior to preparing
* them. Since we don't really have a load at this point, scale
@@ -3385,6 +3437,8 @@
fail_clk_enable:
venus_hfi_for_each_clock_reverse_continue(device, cl, c) {
+ if (cl->disable_memcore_only)
+ continue;
dprintk(VIDC_ERR, "Clock: %s disable and unprepare\n",
cl->name);
clk_disable_unprepare(cl->clk);
@@ -3478,7 +3532,6 @@
devfreq_suspend_device(bus->devfreq);
}
- device->bus_vote = DEFAULT_BUS_VOTE;
return 0;
err_add_dev:
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index e899af3..0cfe693 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -298,6 +298,7 @@
#define HFI_RATE_CONTROL_CBR_CFR (HFI_OX_BASE + 0x5)
#define HFI_RATE_CONTROL_MBR_CFR (HFI_OX_BASE + 0x6)
#define HFI_RATE_CONTROL_MBR_VFR (HFI_OX_BASE + 0x7)
+#define HFI_RATE_CONTROL_CQ (HFI_OX_BASE + 0x8)
struct hfi_uncompressed_plane_actual_constraints_info {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 9290c48..f69b98e 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -229,6 +229,9 @@
HAL_PARAM_VIDEO_WORK_MODE,
HAL_PARAM_SECURE,
HAL_PARAM_VENC_HDR10_PQ_SEI,
+ HAL_CONFIG_HEIC_FRAME_CROP_INFO,
+ HAL_CONFIG_HEIC_FRAME_QUALITY,
+ HAL_CONFIG_HEIC_GRID_ENABLE,
};
enum hal_domain {
@@ -604,6 +607,7 @@
HAL_RATE_CONTROL_CBR_CFR,
HAL_RATE_CONTROL_MBR_CFR,
HAL_RATE_CONTROL_MBR_VFR,
+ HAL_RATE_CONTROL_CQ,
HAL_UNUSED_RC = 0x10000000,
};
@@ -651,6 +655,14 @@
u32 idr_period;
};
+struct hal_heic_frame_quality {
+ u32 frame_quality;
+};
+
+struct hal_heic_grid_enable {
+ u32 grid_enable;
+};
+
enum hal_rotate {
HAL_ROTATE_NONE,
HAL_ROTATE_90,
@@ -795,6 +807,7 @@
HAL_CAPABILITY_MAX_VIDEOCORES,
HAL_CAPABILITY_MAX_WORKMODES,
HAL_CAPABILITY_UBWC_CR_STATS,
+ HAL_CAPABILITY_IMG_GRID_DIMENSION,
HAL_UNUSED_CAPABILITY = 0x10000000,
};
@@ -834,6 +847,13 @@
u32 aspect_height;
};
+struct hal_frame_crop {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
struct hal_codec_supported {
u32 decoder_codec_supported;
u32 encoder_codec_supported;
@@ -1232,6 +1252,7 @@
struct hal_capability_supported max_video_cores;
struct hal_capability_supported max_work_modes;
struct hal_capability_supported ubwc_cr_stats;
+ struct hal_capability_supported img_grid_dimension;
struct hal_profile_level_supported profile_level;
struct hal_uncompressed_format_supported uncomp_format;
struct hal_interlace_format_supported HAL_format;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 7615736..dd94a88 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -356,6 +356,12 @@
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x010)
#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x012)
+#define HFI_PROPERTY_CONFIG_HEIC_FRAME_CROP_INFO \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x013)
+#define HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x014)
+#define HFI_PROPERTY_CONFIG_HEIC_GRID_ENABLE \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x015)
#define HFI_PROPERTY_PARAM_VPE_COMMON_START \
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000)
@@ -380,6 +386,14 @@
u32 colour_space;
};
+struct hfi_frame_crop {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+ u32 reserved[3];
+};
+
#define HFI_CAPABILITY_FRAME_WIDTH (HFI_COMMON_BASE + 0x1)
#define HFI_CAPABILITY_FRAME_HEIGHT (HFI_COMMON_BASE + 0x2)
#define HFI_CAPABILITY_MBS_PER_FRAME (HFI_COMMON_BASE + 0x3)
@@ -414,6 +428,8 @@
#define HFI_CAPABILITY_MAX_VIDEOCORES (HFI_COMMON_BASE + 0X2B)
#define HFI_CAPABILITY_MAX_WORKMODES (HFI_COMMON_BASE + 0X2C)
#define HFI_CAPABILITY_UBWC_CR_STATS (HFI_COMMON_BASE + 0X2D)
+#define HFI_CAPABILITY_CQ_QUALITY_LEVEL (HFI_COMMON_BASE + 0X32)
+#define HFI_CAPABILITY_IMG_GRID_DIMENSION (HFI_COMMON_BASE + 0X33)
struct hfi_capability_supported {
u32 capability_type;
@@ -474,6 +490,15 @@
u32 frame_rate;
};
+struct hfi_heic_frame_quality {
+ u32 frame_quality;
+ u32 reserved[3];
+};
+
+struct hfi_heic_grid_enable {
+ u32 grid_enable;
+};
+
#define HFI_INTRA_REFRESH_NONE (HFI_COMMON_BASE + 0x1)
#define HFI_INTRA_REFRESH_CYCLIC (HFI_COMMON_BASE + 0x2)
#define HFI_INTRA_REFRESH_RANDOM (HFI_COMMON_BASE + 0x5)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_io.h b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
index 3433483..3ddd633 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_io.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -148,6 +148,12 @@
#define VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0)
#define VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US 500000
+#define VENUS_WRAPPER_AXI_HALT 0x000E2008
+#define VENUS_WRAPPER_AXI_HALT_STATUS 0x000E200C
+
+#define BRIC_AXI_HALT BIT(16)
+#define BRIC_AXI_HALT_ACK BIT(16)
+
#define VIDC_VENUS0_WRAPPER_VBIF_REQ_PRIORITY \
(VIDC_WRAPPER_BASE_OFFS + 0x20)
#define VIDC_VENUS0_WRAPPER_VBIF_PRIORITY_LEVEL \
diff --git a/drivers/media/platform/msm/vidc_3x/hfi_packetization.c b/drivers/media/platform/msm/vidc_3x/hfi_packetization.c
index b15baaa..1de5bd1 100644
--- a/drivers/media/platform/msm/vidc_3x/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc_3x/hfi_packetization.c
@@ -658,9 +658,12 @@
case HAL_EXTRADATA_STREAM_USERDATA:
ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA;
break;
- case HAL_EXTRADATA_FRAME_QP:
+ case HAL_EXTRADATA_DEC_FRAME_QP:
ret = HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA;
break;
+ case HAL_EXTRADATA_ENC_FRAME_QP:
+ ret = HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA;
+ break;
case HAL_EXTRADATA_FRAME_BITS_INFO:
ret = HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA;
break;
diff --git a/drivers/media/platform/msm/vidc_3x/msm_venc.c b/drivers/media/platform/msm/vidc_3x/msm_venc.c
index f9760cf..ef6e360 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_venc.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_venc.c
@@ -823,7 +823,7 @@
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -846,7 +846,8 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)|
(1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO)
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO) |
+ (1ULL << V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP)
),
.qmenu = mpeg_video_vidc_extradata,
},
@@ -1564,6 +1565,7 @@
case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
case V4L2_MPEG_VIDC_EXTRADATA_LTR:
+ case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP:
case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
inst->fmts[CAPTURE_PORT].num_planes = 2;
default:
@@ -2278,6 +2280,21 @@
default:
goto unknown_value;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE:
+ switch (value) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE:
+ return HAL_INTRA_REFRESH_NONE;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC:
+ return HAL_INTRA_REFRESH_CYCLIC;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM:
+ return HAL_INTRA_REFRESH_RANDOM;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_ADAPTIVE:
+ return HAL_INTRA_REFRESH_ADAPTIVE;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE:
+ return HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE;
+ default:
+ goto unknown_value;
+ }
}
unknown_value:
@@ -3138,8 +3155,10 @@
}
property_id = HAL_PARAM_VENC_INTRA_REFRESH;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ctrl->val);
- intra_refresh.mode = ctrl->val;
intra_refresh.air_mbs = air_mbs->val;
intra_refresh.air_ref = air_ref->val;
intra_refresh.cir_mbs = cir_mbs->val;
@@ -3158,7 +3177,9 @@
property_id = HAL_PARAM_VENC_INTRA_REFRESH;
intra_refresh.air_mbs = ctrl->val;
- intra_refresh.mode = ir_mode->val;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ir_mode->val);
intra_refresh.air_ref = air_ref->val;
intra_refresh.cir_mbs = cir_mbs->val;
@@ -3177,7 +3198,9 @@
intra_refresh.air_ref = ctrl->val;
intra_refresh.air_mbs = air_mbs->val;
- intra_refresh.mode = ir_mode->val;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ir_mode->val);
intra_refresh.cir_mbs = cir_mbs->val;
pdata = &intra_refresh;
@@ -3196,7 +3219,9 @@
intra_refresh.cir_mbs = ctrl->val;
intra_refresh.air_mbs = air_mbs->val;
intra_refresh.air_ref = air_ref->val;
- intra_refresh.mode = ir_mode->val;
+ intra_refresh.mode = venc_v4l2_to_hal(
+ V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+ ir_mode->val);
pdata = &intra_refresh;
break;
diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
index 4dcb3b1..502a5c7 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c
@@ -77,9 +77,11 @@
"Extradata output crop",
"Extradata display colour SEI",
"Extradata light level SEI",
+ "Extradata PQ Info",
"Extradata display VUI",
"Extradata vpx color space",
- "Extradata PQ Info",
+ "Extradata UBWC CR stats info",
+ "Extradata enc frame QP"
};
struct getprop_buf {
@@ -2474,7 +2476,6 @@
}
hdev = inst->core->device;
abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE);
- init_completion(&inst->completions[abort_completion]);
rc = call_hfi_op(hdev, session_abort, (void *)inst->session);
if (rc) {
@@ -2632,8 +2633,6 @@
__func__);
}
- init_completion(&core->completions
- [SYS_MSG_INDEX(HAL_SYS_INIT_DONE)]);
rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to init core, id = %d\n",
@@ -2737,8 +2736,6 @@
dprintk(VIDC_ERR, "Invalid session\n");
return -EINVAL;
}
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_INIT_DONE)]);
rc = call_hfi_op(hdev, session_init, hdev->hfi_device_data,
inst, get_hal_domain(inst->session_type),
@@ -2877,8 +2874,6 @@
inst, inst->state);
goto exit;
}
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_START_DONE)]);
rc = call_hfi_op(hdev, session_start, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
@@ -2908,8 +2903,6 @@
goto exit;
}
dprintk(VIDC_DBG, "Send Stop to hal\n");
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_STOP_DONE)]);
rc = call_hfi_op(hdev, session_stop, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "Failed to send stop\n");
@@ -2939,8 +2932,6 @@
}
dprintk(VIDC_DBG,
"Send release res to hal\n");
- init_completion(&inst->completions[
- SESSION_MSG_INDEX(HAL_SESSION_RELEASE_RESOURCE_DONE)]);
rc = call_hfi_op(hdev, session_release_res, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
@@ -2971,8 +2962,6 @@
}
dprintk(VIDC_DBG,
"Send session close to hal\n");
- init_completion(
- &inst->completions[SESSION_MSG_INDEX(HAL_SESSION_END_DONE)]);
rc = call_hfi_op(hdev, session_end, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
@@ -4021,8 +4010,6 @@
}
mutex_unlock(&inst->sync_lock);
- init_completion(&inst->completions[
- SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)]);
switch (ptype) {
case HAL_PARAM_PROFILE_LEVEL_CURRENT:
case HAL_CONFIG_VDEC_ENTROPY:
@@ -4248,8 +4235,6 @@
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = true;
- init_completion(&inst->completions[SESSION_MSG_INDEX
- (HAL_SESSION_RELEASE_BUFFER_DONE)]);
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc) {
@@ -4321,9 +4306,6 @@
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID) {
buffer_info.response_required = true;
- init_completion(
- &inst->completions[SESSION_MSG_INDEX
- (HAL_SESSION_RELEASE_BUFFER_DONE)]);
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc) {
@@ -4747,7 +4729,10 @@
ret = HAL_EXTRADATA_STREAM_USERDATA;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP:
- ret = HAL_EXTRADATA_FRAME_QP;
+ ret = HAL_EXTRADATA_DEC_FRAME_QP;
+ break;
+ case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP:
+ ret = HAL_EXTRADATA_ENC_FRAME_QP;
break;
case V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO:
ret = HAL_EXTRADATA_FRAME_BITS_INFO;
diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h
index 93368f6..c7eb5f1 100644
--- a/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h
@@ -316,7 +316,7 @@
s32 maximum;
s32 default_value;
u32 step;
- u32 menu_skip_mask;
+ u64 menu_skip_mask;
u32 flags;
const char * const *qmenu;
};
diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h
index 1a25a58..875db09 100644
--- a/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h
@@ -112,7 +112,8 @@
HAL_EXTRADATA_ASPECT_RATIO,
HAL_EXTRADATA_MPEG2_SEQDISP,
HAL_EXTRADATA_STREAM_USERDATA,
- HAL_EXTRADATA_FRAME_QP,
+ HAL_EXTRADATA_DEC_FRAME_QP,
+ HAL_EXTRADATA_ENC_FRAME_QP,
HAL_EXTRADATA_FRAME_BITS_INFO,
HAL_EXTRADATA_INPUT_CROP,
HAL_EXTRADATA_DIGITAL_ZOOM,
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index c12209c..390d708 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -2169,6 +2169,12 @@
pxa_dma_stop_channels(pcdev);
pxa_camera_destroy_formats(pcdev);
+
+ if (pcdev->mclk_clk) {
+ v4l2_clk_unregister(pcdev->mclk_clk);
+ pcdev->mclk_clk = NULL;
+ }
+
video_unregister_device(&pcdev->vdev);
pcdev->sensor = NULL;
@@ -2495,7 +2501,13 @@
dma_release_channel(pcdev->dma_chans[1]);
dma_release_channel(pcdev->dma_chans[2]);
- v4l2_clk_unregister(pcdev->mclk_clk);
+ v4l2_async_notifier_unregister(&pcdev->notifier);
+
+ if (pcdev->mclk_clk) {
+ v4l2_clk_unregister(pcdev->mclk_clk);
+ pcdev->mclk_clk = NULL;
+ }
+
v4l2_device_unregister(&pcdev->v4l2_dev);
dev_info(&pdev->dev, "PXA Camera driver unloaded\n");
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 0413a86..5c9db09 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -1256,16 +1256,17 @@
{
const struct s3c_camif_variant *variant = camif->variant;
const struct vp_pix_limits *pix_lim;
- int i = ARRAY_SIZE(camif_mbus_formats);
+ unsigned int i;
/* FIXME: constraints against codec or preview path ? */
pix_lim = &variant->vp_pix_limits[VP_CODEC];
- while (i-- >= 0)
+ for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++)
if (camif_mbus_formats[i] == mf->code)
break;
- mf->code = camif_mbus_formats[i];
+ if (i == ARRAY_SIZE(camif_mbus_formats))
+ mf->code = camif_mbus_formats[0];
if (pad == CAMIF_SD_PAD_SINK) {
v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH,
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index 30c148b..06e2cfd 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -83,7 +83,7 @@
static void channel_swdemux_tsklet(unsigned long data)
{
struct channel_info *channel = (struct channel_info *)data;
- struct c8sectpfei *fei = channel->fei;
+ struct c8sectpfei *fei;
unsigned long wp, rp;
int pos, num_packets, n, size;
u8 *buf;
@@ -91,6 +91,8 @@
if (unlikely(!channel || !channel->irec))
return;
+ fei = channel->fei;
+
wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0));
rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0));
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index aceb38d..b1c3725 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -1167,6 +1167,7 @@
v4l2_ctrl_activate(dev->radio_rx_rds_ta, dev->radio_rx_rds_controls);
v4l2_ctrl_activate(dev->radio_rx_rds_tp, dev->radio_rx_rds_controls);
v4l2_ctrl_activate(dev->radio_rx_rds_ms, dev->radio_rx_rds_controls);
+ dev->radio_rx_dev.device_caps = dev->radio_rx_caps;
break;
case V4L2_CID_RDS_RECEPTION:
dev->radio_rx_rds_enabled = ctrl->val;
@@ -1241,6 +1242,7 @@
dev->radio_tx_caps &= ~V4L2_CAP_READWRITE;
if (!dev->radio_tx_rds_controls)
dev->radio_tx_caps |= V4L2_CAP_READWRITE;
+ dev->radio_tx_dev.device_caps = dev->radio_tx_caps;
break;
case V4L2_CID_RDS_TX_PTY:
if (dev->radio_rx_rds_controls)
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index cd209dc..8e2aa3f 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -596,6 +596,7 @@
pipe->bru = &vsp1->bru->entity;
pipe->lif = &vsp1->lif->entity;
pipe->output = vsp1->wpf[0];
+ pipe->output->pipe = pipe;
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 57c713a..4ac1ff4 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -509,7 +509,13 @@
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
- vsp1_pipelines_suspend(vsp1);
+ /*
+ * When used as part of a display pipeline, the VSP is stopped and
+ * restarted explicitly by the DU.
+ */
+ if (!vsp1->drm)
+ vsp1_pipelines_suspend(vsp1);
+
pm_runtime_force_suspend(vsp1->dev);
return 0;
@@ -520,7 +526,13 @@
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
pm_runtime_force_resume(vsp1->dev);
- vsp1_pipelines_resume(vsp1);
+
+ /*
+ * When used as part of a display pipeline, the VSP is stopped and
+ * restarted explicitly by the DU.
+ */
+ if (!vsp1->drm)
+ vsp1_pipelines_resume(vsp1);
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index d351b9c..743aa0f 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -792,6 +792,7 @@
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ bool start_pipeline = false;
unsigned long flags;
int ret;
@@ -802,11 +803,23 @@
mutex_unlock(&pipe->lock);
return ret;
}
+
+ start_pipeline = true;
}
pipe->stream_count++;
mutex_unlock(&pipe->lock);
+ /*
+ * vsp1_pipeline_ready() is not sufficient to establish that all streams
+ * are prepared and the pipeline is configured, as multiple streams
+ * can race through streamon with buffers already queued; Therefore we
+ * don't even attempt to start the pipeline until the last stream has
+ * called through here.
+ */
+ if (!start_pipeline)
+ return 0;
+
spin_lock_irqsave(&pipe->irqlock, flags);
if (vsp1_pipeline_ready(pipe))
vsp1_video_pipeline_run(pipe);
diff --git a/drivers/media/radio/radio-iris-transport.c b/drivers/media/radio/radio-iris-transport.c
index 98afd19..b03cb27 100644
--- a/drivers/media/radio/radio-iris-transport.c
+++ b/drivers/media/radio/radio-iris-transport.c
@@ -40,7 +40,7 @@
DEFINE_MUTEX(fm_smd_enable);
static int fmsmd_set;
static bool chan_opened;
-static int hcismd_fm_set_enable(const char *val, struct kernel_param *kp);
+static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp);
module_param_call(fmsmd_set, hcismd_fm_set_enable, NULL, &fmsmd_set, 0644);
static struct work_struct *reset_worker;
static void radio_hci_smd_deregister(void);
@@ -247,7 +247,7 @@
chan_opened = false;
}
-static int hcismd_fm_set_enable(const char *val, struct kernel_param *kp)
+static int hcismd_fm_set_enable(const char *val, const struct kernel_param *kp)
{
int ret = 0;
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index db525cd..d9f88a4 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1381,8 +1381,13 @@
goto rc_dev_fail;
/* wire up inbound data handler */
- usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
- mceusb_dev_recv, ir, ep_in->bInterval);
+ if (usb_endpoint_xfer_int(ep_in))
+ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+ mceusb_dev_recv, ir, ep_in->bInterval);
+ else
+ usb_fill_bulk_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+ mceusb_dev_recv, ir);
+
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c
index 9caea83..d793c63 100644
--- a/drivers/media/usb/cpia2/cpia2_v4l.c
+++ b/drivers/media/usb/cpia2/cpia2_v4l.c
@@ -812,7 +812,7 @@
struct camera_data *cam = video_drvdata(file);
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- buf->index > cam->num_frames)
+ buf->index >= cam->num_frames)
return -EINVAL;
buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
@@ -863,7 +863,7 @@
if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
buf->memory != V4L2_MEMORY_MMAP ||
- buf->index > cam->num_frames)
+ buf->index >= cam->num_frames)
return -EINVAL;
DBG("QBUF #%d\n", buf->index);
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index d148463..6bf48a7 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -189,7 +189,7 @@
USB 2.0 spec says bulk packet size is always 512 bytes
*/
#define EM28XX_BULK_PACKET_MULTIPLIER 384
-#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384
+#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 94
#define EM28XX_INTERLACED_DEFAULT 1
diff --git a/drivers/media/usb/stkwebcam/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c
index e546b01..2dcc8d0 100644
--- a/drivers/media/usb/stkwebcam/stk-sensor.c
+++ b/drivers/media/usb/stkwebcam/stk-sensor.c
@@ -228,7 +228,7 @@
static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val)
{
int i = 0;
- int tmpval = 0;
+ u8 tmpval = 0;
if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg))
return 1;
@@ -253,7 +253,7 @@
static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val)
{
int i = 0;
- int tmpval = 0;
+ u8 tmpval = 0;
if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg))
return 1;
@@ -274,7 +274,7 @@
if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval))
return 1;
- *val = (u8) tmpval;
+ *val = tmpval;
return 0;
}
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 22a9aae..1c48f2f 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -144,7 +144,7 @@
return 0;
}
-int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value)
+int stk_camera_read_reg(struct stk_camera *dev, u16 index, u8 *value)
{
struct usb_device *udev = dev->udev;
unsigned char *buf;
@@ -163,7 +163,7 @@
sizeof(u8),
500);
if (ret >= 0)
- memcpy(value, buf, sizeof(u8));
+ *value = *buf;
kfree(buf);
return ret;
@@ -171,9 +171,10 @@
static int stk_start_stream(struct stk_camera *dev)
{
- int value;
+ u8 value;
int i, ret;
- int value_116, value_117;
+ u8 value_116, value_117;
+
if (!is_present(dev))
return -ENODEV;
@@ -213,7 +214,7 @@
static int stk_stop_stream(struct stk_camera *dev)
{
- int value;
+ u8 value;
int i;
if (is_present(dev)) {
stk_camera_read_reg(dev, 0x0100, &value);
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h
index 9bbfa3d..92bb48e 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.h
+++ b/drivers/media/usb/stkwebcam/stk-webcam.h
@@ -129,7 +129,7 @@
#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev)
int stk_camera_write_reg(struct stk_camera *, u16, u8);
-int stk_camera_read_reg(struct stk_camera *, u16, int *);
+int stk_camera_read_reg(struct stk_camera *, u16, u8 *);
int stk_sensor_init(struct stk_camera *);
int stk_sensor_configure(struct stk_camera *);
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
index 0324633..e56a49a 100644
--- a/drivers/media/usb/usbtv/usbtv-core.c
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -109,6 +109,8 @@
return 0;
usbtv_audio_fail:
+ /* we must not free at this point */
+ usb_get_dev(usbtv->udev);
usbtv_video_free(usbtv);
usbtv_video_fail:
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index cde43b6..8412dfc 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -2184,7 +2184,7 @@
* Module parameters
*/
-static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
+static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp)
{
if (uvc_clock_param == CLOCK_MONOTONIC)
return sprintf(buffer, "CLOCK_MONOTONIC");
@@ -2192,7 +2192,7 @@
return sprintf(buffer, "CLOCK_REALTIME");
}
-static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
+static int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
{
if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
val += strlen("clock_");
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 9eaab98..d4e93f1 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -101,7 +101,7 @@
static int put_v4l2_window32(struct v4l2_window __user *kp,
struct v4l2_window32 __user *up)
{
- struct v4l2_clip __user *kclips = kp->clips;
+ struct v4l2_clip __user *kclips;
struct v4l2_clip32 __user *uclips;
compat_caddr_t p;
u32 clipcount;
@@ -116,6 +116,8 @@
if (!clipcount)
return 0;
+ if (get_user(kclips, &kp->clips))
+ return -EFAULT;
if (get_user(p, &up->clips))
return -EFAULT;
uclips = compat_ptr(p);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index d4ee140..9ae687b 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -2548,11 +2548,8 @@
unsigned int ioctl;
u32 flags;
const char * const name;
- union {
- u32 offset;
- int (*func)(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *p);
- } u;
+ int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *fh, void *p);
void (*debug)(const void *arg, bool write_only);
};
@@ -2560,25 +2557,21 @@
#define INFO_FL_PRIO (1 << 0)
/* This control can be valid if the filehandle passes a control handler. */
#define INFO_FL_CTRL (1 << 1)
-/* This is a standard ioctl, no need for special code */
-#define INFO_FL_STD (1 << 2)
/* This is ioctl has its own function */
-#define INFO_FL_FUNC (1 << 3)
+#define INFO_FL_FUNC (1 << 2)
/* Queuing ioctl */
-#define INFO_FL_QUEUE (1 << 4)
+#define INFO_FL_QUEUE (1 << 3)
/* Zero struct from after the field to the end */
#define INFO_FL_CLEAR(v4l2_struct, field) \
((offsetof(struct v4l2_struct, field) + \
sizeof(((struct v4l2_struct *)0)->field)) << 16)
#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
-#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
- [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags | INFO_FL_STD, \
- .name = #_ioctl, \
- .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
- .debug = _debug, \
+#define DEFINE_IOCTL_STD_FNC(_vidioc) \
+ static int __v4l_ ## _vidioc ## _fnc( \
+ const struct v4l2_ioctl_ops *ops, \
+ struct file *file, void *fh, void *p) { \
+ return ops->_vidioc(file, fh, p); \
}
#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
@@ -2586,10 +2579,44 @@
.ioctl = _ioctl, \
.flags = _flags | INFO_FL_FUNC, \
.name = #_ioctl, \
- .u.func = _func, \
+ .func = _func, \
.debug = _debug, \
}
+#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
+ IOCTL_INFO_FNC(_ioctl, __v4l_ ## _vidioc ## _fnc, _debug, _flags)
+
+DEFINE_IOCTL_STD_FNC(vidioc_g_fbuf)
+DEFINE_IOCTL_STD_FNC(vidioc_s_fbuf)
+DEFINE_IOCTL_STD_FNC(vidioc_expbuf)
+DEFINE_IOCTL_STD_FNC(vidioc_g_std)
+DEFINE_IOCTL_STD_FNC(vidioc_g_audio)
+DEFINE_IOCTL_STD_FNC(vidioc_s_audio)
+DEFINE_IOCTL_STD_FNC(vidioc_g_input)
+DEFINE_IOCTL_STD_FNC(vidioc_g_edid)
+DEFINE_IOCTL_STD_FNC(vidioc_s_edid)
+DEFINE_IOCTL_STD_FNC(vidioc_g_output)
+DEFINE_IOCTL_STD_FNC(vidioc_g_audout)
+DEFINE_IOCTL_STD_FNC(vidioc_s_audout)
+DEFINE_IOCTL_STD_FNC(vidioc_g_selection)
+DEFINE_IOCTL_STD_FNC(vidioc_s_selection)
+DEFINE_IOCTL_STD_FNC(vidioc_g_jpegcomp)
+DEFINE_IOCTL_STD_FNC(vidioc_s_jpegcomp)
+DEFINE_IOCTL_STD_FNC(vidioc_enumaudio)
+DEFINE_IOCTL_STD_FNC(vidioc_enumaudout)
+DEFINE_IOCTL_STD_FNC(vidioc_enum_framesizes)
+DEFINE_IOCTL_STD_FNC(vidioc_enum_frameintervals)
+DEFINE_IOCTL_STD_FNC(vidioc_g_enc_index)
+DEFINE_IOCTL_STD_FNC(vidioc_encoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_try_encoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_decoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_try_decoder_cmd)
+DEFINE_IOCTL_STD_FNC(vidioc_s_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_g_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_enum_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_query_dv_timings)
+DEFINE_IOCTL_STD_FNC(vidioc_dv_timings_cap)
+
static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
@@ -2774,14 +2801,8 @@
}
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
- if (info->flags & INFO_FL_STD) {
- typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
- const void *p = vfd->ioctl_ops;
- const vidioc_op *vidioc = p + info->u.offset;
-
- ret = (*vidioc)(file, fh, arg);
- } else if (info->flags & INFO_FL_FUNC) {
- ret = info->u.func(ops, file, fh, arg);
+ if (info->flags & INFO_FL_FUNC) {
+ ret = info->func(ops, file, fh, arg);
} else if (!ops->vidioc_default) {
ret = -ENOTTY;
} else {
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 1db0af6..b6189a4 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -185,12 +185,13 @@
dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
data, size, dma->nr_pages);
- err = get_user_pages(data & PAGE_MASK, dma->nr_pages,
+ err = get_user_pages_longterm(data & PAGE_MASK, dma->nr_pages,
flags, dma->pages, NULL);
if (err != dma->nr_pages) {
dma->nr_pages = (err >= 0) ? err : 0;
- dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages);
+ dprintk(1, "get_user_pages_longterm: err=%d [%d]\n", err,
+ dma->nr_pages);
return err < 0 ? err : -EINVAL;
}
return 0;
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 9ccf7f5..4299ce0 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -334,6 +334,10 @@
struct vb2_buffer *vb;
int ret;
+ /* Ensure that q->num_buffers+num_buffers is below VB2_MAX_FRAME */
+ num_buffers = min_t(unsigned int, num_buffers,
+ VB2_MAX_FRAME - q->num_buffers);
+
for (buffer = 0; buffer < num_buffers; ++buffer) {
/* Allocate videobuf buffer structures */
vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index ab3227b..760cbf2 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -104,7 +104,7 @@
if (nums[i-1] + 1 != nums[i])
goto fail_map;
buf->vaddr = (__force void *)
- ioremap_nocache(nums[0] << PAGE_SHIFT, size);
+ ioremap_nocache(__pfn_to_phys(nums[0]), size + offset);
} else {
buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1,
PAGE_KERNEL);
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 89c7ed1..1cdab6d 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -99,7 +99,7 @@
MODULE_PARM_DESC(mpt_channel_mapping, " Mapping id's to channels (default=0)");
static int mpt_debug_level;
-static int mpt_set_debug_level(const char *val, struct kernel_param *kp);
+static int mpt_set_debug_level(const char *val, const struct kernel_param *kp);
module_param_call(mpt_debug_level, mpt_set_debug_level, param_get_int,
&mpt_debug_level, 0600);
MODULE_PARM_DESC(mpt_debug_level,
@@ -242,7 +242,7 @@
pci_write_config_word(pdev, PCI_COMMAND, command_reg);
}
-static int mpt_set_debug_level(const char *val, struct kernel_param *kp)
+static int mpt_set_debug_level(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
MPT_ADAPTER *ioc;
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index 02b5f69..14cf6df 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -2698,6 +2698,8 @@
__FILE__, __LINE__, iocnum);
return -ENODEV;
}
+ if (karg.hdr.id >= MPT_MAX_FC_DEVICES)
+ return -EINVAL;
dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n",
ioc->name));
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 7ee1667..00dff9b 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -1994,6 +1994,7 @@
.cmd_per_lun = 7,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = mptscsih_host_attrs,
+ .no_write_same = 1,
};
static int mptsas_get_linkerrors(struct sas_phy *phy)
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index 8f8bacb..a6b5259 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -430,6 +430,20 @@
{
unsigned int addr;
int ret, slave;
+ struct device_node *np = palmas_dev->dev->of_node;
+
+ if (of_property_read_bool(np, "ti,palmas-override-powerhold")) {
+ addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
+ PALMAS_PRIMARY_SECONDARY_PAD2);
+ slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
+
+ ret = regmap_update_bits(palmas_dev->regmap[slave], addr,
+ PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK, 0);
+ if (ret)
+ dev_err(palmas_dev->dev,
+ "Unable to write PRIMARY_SECONDARY_PAD2 %d\n",
+ ret);
+ }
if (!palmas_dev)
return;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index e3f4c39..a233173 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -825,4 +825,5 @@
source "drivers/misc/genwqe/Kconfig"
source "drivers/misc/echo/Kconfig"
source "drivers/misc/cxl/Kconfig"
+source "drivers/misc/fpr_FingerprintCard/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fb07ce4..8e5d0f6 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -57,6 +57,7 @@
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o
+obj-$(CONFIG_FPR_FPC) += fpr_FingerprintCard/
obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o
@@ -69,6 +70,9 @@
lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o
+CFLAGS_lkdtm_rodata.o += $(DISABLE_LTO)
+KCOV_INSTRUMENT_lkdtm_rodata.o := n
+
OBJCOPYFLAGS :=
OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
--set-section-flags .text=alloc,readonly \
diff --git a/drivers/misc/cxl/flash.c b/drivers/misc/cxl/flash.c
index c63d61e..381a9a1 100644
--- a/drivers/misc/cxl/flash.c
+++ b/drivers/misc/cxl/flash.c
@@ -401,8 +401,10 @@
if (down_interruptible(&sem) != 0)
return -EPERM;
- if (!(adapter = get_cxl_adapter(adapter_num)))
- return -ENODEV;
+ if (!(adapter = get_cxl_adapter(adapter_num))) {
+ rc = -ENODEV;
+ goto err_unlock;
+ }
file->private_data = adapter;
continue_token = 0;
@@ -446,6 +448,8 @@
free_page((unsigned long) le);
err:
put_device(&adapter->dev);
+err_unlock:
+ up(&sem);
return rc;
}
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index cc91f7b..eb29113 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -148,7 +148,7 @@
for (i = 0; i < components; i++) {
edev->component[i].number = -1;
edev->component[i].slot = -1;
- edev->component[i].power_status = 1;
+ edev->component[i].power_status = -1;
}
mutex_lock(&container_list_lock);
@@ -600,6 +600,11 @@
if (edev->cb->get_power_status)
edev->cb->get_power_status(edev, ecomp);
+
+ /* If still uninitialized, the callback failed or does not exist. */
+ if (ecomp->power_status == -1)
+ return (edev->cb->get_power_status) ? -EIO : -ENOTTY;
+
return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off");
}
diff --git a/drivers/misc/fpr_FingerprintCard/Kconfig b/drivers/misc/fpr_FingerprintCard/Kconfig
new file mode 100644
index 0000000..c9599e6
--- /dev/null
+++ b/drivers/misc/fpr_FingerprintCard/Kconfig
@@ -0,0 +1,10 @@
+#
+# FingerprintCard fingerprint driver
+#
+menu "FingerprintCard fingerprint driver"
+config FPR_FPC
+ default n
+ tristate "FPC_BTP fingerprint sensor support"
+ depends on SPI_MASTER
+
+endmenu
diff --git a/drivers/misc/fpr_FingerprintCard/Makefile b/drivers/misc/fpr_FingerprintCard/Makefile
new file mode 100644
index 0000000..96681eb
--- /dev/null
+++ b/drivers/misc/fpr_FingerprintCard/Makefile
@@ -0,0 +1,5 @@
+# Makefile for FingerprintCard fingerprint driver
+
+fpc1020-objs := fpc1020_platform_tee.o
+obj-$(CONFIG_FPR_FPC) += fpc1020.o
+
diff --git a/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c b/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c
new file mode 100644
index 0000000..a6ed374
--- /dev/null
+++ b/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c
@@ -0,0 +1,683 @@
+/*
+ * FPC1020 Fingerprint sensor device driver
+ *
+ * This driver will control the platform resources that the FPC fingerprint
+ * sensor needs to operate. The major things are probing the sensor to check
+ * that it is actually connected and let the Kernel know this and with that also
+ * enabling and disabling of regulators, controlling GPIOs such as sensor reset
+ * line, sensor IRQ line.
+ *
+ * The driver will expose most of its available functionality in sysfs which
+ * enables dynamic control of these features from eg. a user space process.
+ *
+ * The sensor's IRQ events will be pushed to Kernel's event handling system and
+ * are exposed in the drivers event node.
+ *
+ * This driver will NOT send any commands to the sensor it only controls the
+ * electrical parts.
+ *
+ *
+ * Copyright (c) 2015 Fingerprint Cards AB <tech@fingerprints.com>
+ *
+ * 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/atomic.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+
+#define FPC_TTW_HOLD_TIME 1000
+#define RESET_LOW_SLEEP_MIN_US 5000
+#define RESET_LOW_SLEEP_MAX_US (RESET_LOW_SLEEP_MIN_US + 100)
+#define RESET_HIGH_SLEEP1_MIN_US 100
+#define RESET_HIGH_SLEEP1_MAX_US (RESET_HIGH_SLEEP1_MIN_US + 100)
+#define RESET_HIGH_SLEEP2_MIN_US 5000
+#define RESET_HIGH_SLEEP2_MAX_US (RESET_HIGH_SLEEP2_MIN_US + 100)
+#define PWR_ON_SLEEP_MIN_US 100
+#define PWR_ON_SLEEP_MAX_US (PWR_ON_SLEEP_MIN_US + 900)
+#define NUM_PARAMS_REG_ENABLE_SET 2
+
+#define RELEASE_WAKELOCK_W_V "release_wakelock_with_verification"
+#define RELEASE_WAKELOCK "release_wakelock"
+#define START_IRQS_RECEIVED_CNT "start_irqs_received_counter"
+
+static const char * const pctl_names[] = {
+ "fpc1020_reset_reset",
+ "fpc1020_reset_active",
+ "fpc1020_irq_active",
+};
+
+struct vreg_config {
+ char *name;
+ unsigned long vmin;
+ unsigned long vmax;
+ int ua_load;
+};
+
+static const struct vreg_config vreg_conf[] = {
+ { "vdd_ana", 1800000UL, 1800000UL, 6000, },
+ { "vcc_spi", 1800000UL, 1800000UL, 10, },
+ { "vdd_io", 1800000UL, 1800000UL, 6000, },
+};
+
+struct fpc1020_data {
+ struct device *dev;
+ struct pinctrl *fingerprint_pinctrl;
+ struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)];
+ struct regulator *vreg[ARRAY_SIZE(vreg_conf)];
+ struct wakeup_source ttw_wl;
+ struct mutex lock; /* To set/get exported values in sysfs */
+ int irq_gpio;
+ int rst_gpio;
+ int nbr_irqs_received;
+ int nbr_irqs_received_counter_start;
+ bool prepared;
+ atomic_t wakeup_enabled; /* Used both in ISR and non-ISR */
+};
+
+static int vreg_setup(struct fpc1020_data *fpc1020, const char *name,
+ bool enable)
+{
+ size_t i;
+ int rc;
+ struct regulator *vreg;
+ struct device *dev = fpc1020->dev;
+
+ for (i = 0; i < ARRAY_SIZE(vreg_conf); i++) {
+ const char *n = vreg_conf[i].name;
+
+ if (!memcmp(n, name, strlen(n)))
+ goto found;
+ }
+
+ dev_err(dev, "Regulator %s not found\n", name);
+
+ return -EINVAL;
+
+found:
+ vreg = fpc1020->vreg[i];
+ if (enable) {
+ if (!vreg) {
+ vreg = devm_regulator_get(dev, name);
+ if (IS_ERR_OR_NULL(vreg)) {
+ dev_err(dev, "Unable to get %s\n", name);
+ return PTR_ERR(vreg);
+ }
+ }
+
+ if (regulator_count_voltages(vreg) > 0) {
+ rc = regulator_set_voltage(vreg, vreg_conf[i].vmin,
+ vreg_conf[i].vmax);
+ if (rc)
+ dev_err(dev,
+ "Unable to set voltage on %s, %d\n",
+ name, rc);
+ }
+
+ rc = regulator_set_load(vreg, vreg_conf[i].ua_load);
+ if (rc < 0)
+ dev_err(dev, "Unable to set current on %s, %d\n",
+ name, rc);
+
+ rc = regulator_enable(vreg);
+ if (rc) {
+ dev_err(dev, "error enabling %s: %d\n", name, rc);
+ vreg = NULL;
+ }
+ fpc1020->vreg[i] = vreg;
+ } else {
+ if (vreg) {
+ if (regulator_is_enabled(vreg)) {
+ regulator_disable(vreg);
+ dev_dbg(dev, "disabled %s\n", name);
+ }
+ fpc1020->vreg[i] = NULL;
+ }
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/*
+ * sysfs node for controlling clocks.
+ *
+ * This is disabled in platform variant of this driver but kept for
+ * backwards compatibility. Only prints a debug print that it is
+ * disabled.
+ */
+static ssize_t clk_enable_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev_dbg(dev,
+ "clk_enable sysfs node not enabled in platform driver\n");
+
+ return count;
+}
+static DEVICE_ATTR(clk_enable, 0200, NULL, clk_enable_set);
+
+/*
+ * Will try to select the set of pins (GPIOS) defined in a pin control node of
+ * the device tree named @p name.
+ *
+ * The node can contain several eg. GPIOs that is controlled when selecting it.
+ * The node may activate or deactivate the pins it contains, the action is
+ * defined in the device tree node itself and not here. The states used
+ * internally is fetched at probe time.
+ *
+ * @see pctl_names
+ * @see fpc1020_probe
+ */
+static int select_pin_ctl(struct fpc1020_data *fpc1020, const char *name)
+{
+ size_t i;
+ int rc;
+ struct device *dev = fpc1020->dev;
+
+ for (i = 0; i < ARRAY_SIZE(pctl_names); i++) {
+ const char *n = pctl_names[i];
+
+ if (!memcmp(n, name, strlen(n))) {
+ rc = pinctrl_select_state(fpc1020->fingerprint_pinctrl,
+ fpc1020->pinctrl_state[i]);
+ if (rc)
+ dev_err(dev, "cannot select '%s'\n", name);
+ else
+ dev_dbg(dev, "Selected '%s'\n", name);
+ goto exit;
+ }
+ }
+
+ rc = -EINVAL;
+ dev_err(dev, "%s:'%s' not found\n", __func__, name);
+
+exit:
+ return rc;
+}
+
+static ssize_t pinctl_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ int rc;
+
+ mutex_lock(&fpc1020->lock);
+ rc = select_pin_ctl(fpc1020, buf);
+ mutex_unlock(&fpc1020->lock);
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(pinctl_set, 0200, NULL, pinctl_set);
+
+static ssize_t regulator_enable_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ char op;
+ char name[16];
+ int rc;
+ bool enable;
+
+ if (sscanf(buf, "%15[^,],%c", name, &op) != NUM_PARAMS_REG_ENABLE_SET)
+ return -EINVAL;
+ if (op == 'e')
+ enable = true;
+ else if (op == 'd')
+ enable = false;
+ else
+ return -EINVAL;
+
+ mutex_lock(&fpc1020->lock);
+ rc = vreg_setup(fpc1020, name, enable);
+ mutex_unlock(&fpc1020->lock);
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(regulator_enable, 0200, NULL, regulator_enable_set);
+
+static int hw_reset(struct fpc1020_data *fpc1020)
+{
+ int irq_gpio;
+ int rc;
+
+ irq_gpio = gpio_get_value(fpc1020->irq_gpio);
+
+ rc = select_pin_ctl(fpc1020, "fpc1020_reset_active");
+
+ if (rc)
+ goto exit;
+
+ usleep_range(RESET_HIGH_SLEEP1_MIN_US, RESET_HIGH_SLEEP1_MAX_US);
+
+ rc = select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+
+ if (rc)
+ goto exit;
+ usleep_range(RESET_LOW_SLEEP_MIN_US, RESET_LOW_SLEEP_MAX_US);
+
+ rc = select_pin_ctl(fpc1020, "fpc1020_reset_active");
+ if (rc)
+ goto exit;
+ usleep_range(RESET_HIGH_SLEEP2_MIN_US, RESET_HIGH_SLEEP2_MAX_US);
+
+ irq_gpio = gpio_get_value(fpc1020->irq_gpio);
+
+exit:
+ return rc;
+}
+
+static ssize_t hw_reset_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -EINVAL;
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+
+ if (!memcmp(buf, "reset", strlen("reset"))) {
+ mutex_lock(&fpc1020->lock);
+ rc = hw_reset(fpc1020);
+ mutex_unlock(&fpc1020->lock);
+ } else {
+ return rc;
+ }
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(hw_reset, 0200, NULL, hw_reset_set);
+
+/*
+ * Will setup GPIOs, and regulators to correctly initialize the touch sensor to
+ * be ready for work.
+ *
+ * In the correct order according to the sensor spec this function will
+ * enable/disable regulators, and reset line, all to set the sensor in a
+ * correct power on or off state "electrical" wise.
+ *
+ * @see device_prepare_set
+ * @note This function will not send any commands to the sensor it will only
+ * control it "electrically".
+ */
+static int device_prepare(struct fpc1020_data *fpc1020, bool enable)
+{
+ int rc = 0;
+
+ mutex_lock(&fpc1020->lock);
+ if (enable && !fpc1020->prepared) {
+ fpc1020->prepared = true;
+ select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+
+ rc = vreg_setup(fpc1020, "vcc_spi", true);
+ if (rc)
+ goto exit;
+
+ rc = vreg_setup(fpc1020, "vdd_io", true);
+ if (rc)
+ goto exit_1;
+
+ rc = vreg_setup(fpc1020, "vdd_ana", true);
+ if (rc)
+ goto exit_2;
+
+ usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US);
+
+ (void)select_pin_ctl(fpc1020, "fpc1020_reset_active");
+ } else if (!enable && fpc1020->prepared) {
+ rc = 0;
+ (void)select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+
+ usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US);
+
+ (void)vreg_setup(fpc1020, "vdd_ana", false);
+exit_2:
+ (void)vreg_setup(fpc1020, "vdd_io", false);
+exit_1:
+ (void)vreg_setup(fpc1020, "vcc_spi", false);
+exit:
+ fpc1020->prepared = false;
+ }
+
+ mutex_unlock(&fpc1020->lock);
+
+ return rc;
+}
+
+/*
+ * sysfs node to enable/disable (power up/power down) the touch sensor
+ *
+ * @see device_prepare
+ */
+static ssize_t device_prepare_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+
+ if (!memcmp(buf, "enable", strlen("enable")))
+ rc = device_prepare(fpc1020, true);
+ else if (!memcmp(buf, "disable", strlen("disable")))
+ rc = device_prepare(fpc1020, false);
+ else
+ return -EINVAL;
+
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(device_prepare, 0200, NULL, device_prepare_set);
+
+/**
+ * sysfs node for controlling whether the driver is allowed
+ * to wake up the platform on interrupt.
+ */
+static ssize_t wakeup_enable_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ ssize_t ret = count;
+
+ mutex_lock(&fpc1020->lock);
+ if (!memcmp(buf, "enable", strlen("enable")))
+ atomic_set(&fpc1020->wakeup_enabled, 1);
+ else if (!memcmp(buf, "disable", strlen("disable")))
+ atomic_set(&fpc1020->wakeup_enabled, 0);
+ else
+ ret = -EINVAL;
+ mutex_unlock(&fpc1020->lock);
+
+ return ret;
+}
+static DEVICE_ATTR(wakeup_enable, 0200, NULL, wakeup_enable_set);
+
+
+/*
+ * sysfs node for controlling the wakelock.
+ */
+static ssize_t handle_wakelock_cmd(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ ssize_t ret = count;
+
+ mutex_lock(&fpc1020->lock);
+ if (!memcmp(buf, RELEASE_WAKELOCK_W_V,
+ min(count, strlen(RELEASE_WAKELOCK_W_V)))) {
+ if (fpc1020->nbr_irqs_received_counter_start ==
+ fpc1020->nbr_irqs_received) {
+ __pm_relax(&fpc1020->ttw_wl);
+ } else {
+ dev_dbg(dev, "Ignore releasing of wakelock %d != %d",
+ fpc1020->nbr_irqs_received_counter_start,
+ fpc1020->nbr_irqs_received);
+ }
+ } else if (!memcmp(buf, RELEASE_WAKELOCK, min(count,
+ strlen(RELEASE_WAKELOCK)))) {
+ __pm_relax(&fpc1020->ttw_wl);
+ } else if (!memcmp(buf, START_IRQS_RECEIVED_CNT,
+ min(count, strlen(START_IRQS_RECEIVED_CNT)))) {
+ fpc1020->nbr_irqs_received_counter_start =
+ fpc1020->nbr_irqs_received;
+ } else
+ ret = -EINVAL;
+ mutex_unlock(&fpc1020->lock);
+
+ return ret;
+}
+static DEVICE_ATTR(handle_wakelock, 0200, NULL, handle_wakelock_cmd);
+
+/*
+ * sysf node to check the interrupt status of the sensor, the interrupt
+ * handler should perform sysf_notify to allow userland to poll the node.
+ */
+static ssize_t irq_get(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+ int irq = gpio_get_value(fpc1020->irq_gpio);
+
+ return scnprintf(buf, PAGE_SIZE, "%i\n", irq);
+}
+
+/*
+ * writing to the irq node will just drop a printk message
+ * and return success, used for latency measurement.
+ */
+static ssize_t irq_ack(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpc1020_data *fpc1020 = dev_get_drvdata(dev);
+
+ dev_dbg(fpc1020->dev, "%s\n", __func__);
+
+ return count;
+}
+static DEVICE_ATTR(irq, 0600 | 0200, irq_get, irq_ack);
+
+static struct attribute *attributes[] = {
+ &dev_attr_pinctl_set.attr,
+ &dev_attr_device_prepare.attr,
+ &dev_attr_regulator_enable.attr,
+ &dev_attr_hw_reset.attr,
+ &dev_attr_wakeup_enable.attr,
+ &dev_attr_handle_wakelock.attr,
+ &dev_attr_clk_enable.attr,
+ &dev_attr_irq.attr,
+ NULL
+};
+
+static const struct attribute_group attribute_group = {
+ .attrs = attributes,
+};
+
+static irqreturn_t fpc1020_irq_handler(int irq, void *handle)
+{
+ struct fpc1020_data *fpc1020 = handle;
+
+ pr_info("fpc1020 irq handler: %s\n", __func__);
+ mutex_lock(&fpc1020->lock);
+ if (atomic_read(&fpc1020->wakeup_enabled)) {
+ fpc1020->nbr_irqs_received++;
+ __pm_wakeup_event(&fpc1020->ttw_wl,
+ msecs_to_jiffies(FPC_TTW_HOLD_TIME));
+ }
+ mutex_unlock(&fpc1020->lock);
+
+ sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name);
+
+ return IRQ_HANDLED;
+}
+
+static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020,
+ const char *label, int *gpio)
+{
+ struct device *dev = fpc1020->dev;
+ struct device_node *np = dev->of_node;
+ int rc;
+
+ rc = of_get_named_gpio(np, label, 0);
+
+ if (rc < 0) {
+ dev_err(dev, "failed to get '%s'\n", label);
+ return rc;
+ }
+ *gpio = rc;
+
+ rc = devm_gpio_request(dev, *gpio, label);
+ if (rc) {
+ dev_err(dev, "failed to request gpio %d\n", *gpio);
+ return rc;
+ }
+ dev_dbg(dev, "%s %d\n", label, *gpio);
+
+ return 0;
+}
+
+static int fpc1020_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int rc = 0;
+ size_t i;
+ int irqf;
+ struct fpc1020_data *fpc1020 = devm_kzalloc(dev, sizeof(*fpc1020),
+ GFP_KERNEL);
+ if (!fpc1020) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+
+ fpc1020->dev = dev;
+ platform_set_drvdata(pdev, fpc1020);
+
+ rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_irq",
+ &fpc1020->irq_gpio);
+ if (rc)
+ goto exit;
+ rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_rst",
+ &fpc1020->rst_gpio);
+ if (rc)
+ goto exit;
+
+ fpc1020->fingerprint_pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(fpc1020->fingerprint_pinctrl)) {
+ if (PTR_ERR(fpc1020->fingerprint_pinctrl) == -EPROBE_DEFER) {
+ dev_info(dev, "pinctrl not ready\n");
+ rc = -EPROBE_DEFER;
+ goto exit;
+ }
+ dev_err(dev, "Target does not use pinctrl\n");
+ fpc1020->fingerprint_pinctrl = NULL;
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pctl_names); i++) {
+ const char *n = pctl_names[i];
+ struct pinctrl_state *state =
+ pinctrl_lookup_state(fpc1020->fingerprint_pinctrl, n);
+ if (IS_ERR(state)) {
+ dev_err(dev, "cannot find '%s'\n", n);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev_info(dev, "found pin control %s\n", n);
+ fpc1020->pinctrl_state[i] = state;
+ }
+
+ rc = select_pin_ctl(fpc1020, "fpc1020_reset_reset");
+ if (rc)
+ goto exit;
+ rc = select_pin_ctl(fpc1020, "fpc1020_irq_active");
+ if (rc)
+ goto exit;
+
+ atomic_set(&fpc1020->wakeup_enabled, 0);
+
+ irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT;
+ if (of_property_read_bool(dev->of_node, "fpc,enable-wakeup")) {
+ irqf |= IRQF_NO_SUSPEND;
+ device_init_wakeup(dev, 1);
+ }
+
+ mutex_init(&fpc1020->lock);
+ rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio),
+ NULL, fpc1020_irq_handler, irqf,
+ dev_name(dev), fpc1020);
+ if (rc) {
+ dev_err(dev, "could not request irq %d\n",
+ gpio_to_irq(fpc1020->irq_gpio));
+ goto exit;
+ }
+
+ dev_info(dev, "requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio));
+
+ /* Request that the interrupt should be wakeable */
+ enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio));
+
+ wakeup_source_init(&fpc1020->ttw_wl, "fpc_ttw_wl");
+
+ rc = sysfs_create_group(&dev->kobj, &attribute_group);
+ if (rc) {
+ dev_err(dev, "could not create sysfs\n");
+ goto exit;
+ }
+
+ if (of_property_read_bool(dev->of_node, "fpc,enable-on-boot")) {
+ dev_info(dev, "Enabling hardware\n");
+ (void)device_prepare(fpc1020, true);
+ }
+
+ rc = hw_reset(fpc1020);
+
+ dev_info(dev, "%s: ok\n", __func__);
+
+exit:
+ return rc;
+}
+
+static int fpc1020_remove(struct platform_device *pdev)
+{
+ struct fpc1020_data *fpc1020 = platform_get_drvdata(pdev);
+
+ sysfs_remove_group(&pdev->dev.kobj, &attribute_group);
+ mutex_destroy(&fpc1020->lock);
+ wakeup_source_trash(&fpc1020->ttw_wl);
+ (void)vreg_setup(fpc1020, "vdd_ana", false);
+ (void)vreg_setup(fpc1020, "vdd_io", false);
+ (void)vreg_setup(fpc1020, "vcc_spi", false);
+ dev_info(&pdev->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static const struct of_device_id fpc1020_of_match[] = {
+ { .compatible = "fpc,fpc1020", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fpc1020_of_match);
+
+static struct platform_driver fpc1020_driver = {
+ .driver = {
+ .name = "fpc1020",
+ .owner = THIS_MODULE,
+ .of_match_table = fpc1020_of_match,
+ },
+ .probe = fpc1020_probe,
+ .remove = fpc1020_remove,
+};
+
+static int __init fpc1020_init(void)
+{
+ int rc = platform_driver_register(&fpc1020_driver);
+
+ if (!rc)
+ pr_info("%s OK\n", __func__);
+ else
+ pr_err("%s %d\n", __func__, rc);
+
+ return rc;
+}
+
+static void __exit fpc1020_exit(void)
+{
+ pr_info("%s\n", __func__);
+ platform_driver_unregister(&fpc1020_driver);
+}
+
+module_init(fpc1020_init);
+module_exit(fpc1020_exit);
+
+
+MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 99635dd..2290845 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -1130,7 +1130,8 @@
ts.run_test(0, chr);
}
-static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp)
+static int param_set_kgdbts_var(const char *kmessage,
+ const struct kernel_param *kp)
{
int len = strlen(kmessage);
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 41f3186..60f5a8d 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -551,7 +551,6 @@
break;
default:
- dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd);
rets = -ENOIOCTLCMD;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index c0143db..07fb743 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -4061,8 +4061,11 @@
int retry = 0;
do {
- if (retry++)
+ if (retry++) {
+ mutex_unlock(&app_access_lock);
msleep(QSEECOM_TA_ION_ALLOCATE_DELAY);
+ mutex_lock(&app_access_lock);
+ }
ihandle = ion_alloc(qseecom.ion_clnt, fw_size,
SZ_4K, ION_HEAP(ION_QSECOM_TA_HEAP_ID), 0);
} while (IS_ERR_OR_NULL(ihandle) &&
@@ -4212,8 +4215,12 @@
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, cmd_buf, cmd_len,
&resp, sizeof(resp));
if (ret) {
- pr_err("scm_call to load failed : ret %d\n", ret);
- ret = -EIO;
+ pr_err("scm_call to load failed : ret %d, result %x\n",
+ ret, resp.result);
+ if (resp.result == QSEOS_RESULT_FAIL_APP_ALREADY_LOADED)
+ ret = -EEXIST;
+ else
+ ret = -EIO;
goto exit_disable_clk_vote;
}
@@ -4465,6 +4472,7 @@
}
mutex_lock(&app_access_lock);
+recheck:
app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
strlcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
ret = __qseecom_check_app_exists(app_ireq, &app_id);
@@ -4494,7 +4502,10 @@
pr_debug("%s: Loading app for the first time'\n",
qseecom.pdev->init_name);
ret = __qseecom_load_fw(data, app_name, &app_id);
- if (ret < 0)
+ if (ret == -EEXIST) {
+ pr_err("recheck if TA %s is loaded\n", app_name);
+ goto recheck;
+ } else if (ret < 0)
goto exit_ion_free;
}
data->client.app_id = app_id;
@@ -4510,6 +4521,7 @@
strlcpy(entry->app_name, app_name, MAX_APP_NAME_SIZE);
if (__qseecom_get_fw_size(app_name, &fw_size, &app_arch)) {
ret = -EIO;
+ kfree(entry);
goto exit_entry_free;
}
entry->app_arch = app_arch;
@@ -6978,19 +6990,6 @@
break;
}
- case QSEECOM_IOCTL_SET_ENCDEC_INFO: {
- struct qseecom_encdec_conf_t conf;
-
- ret = copy_from_user(&conf, argp, sizeof(conf));
- if (ret) {
- pr_err("copy_from_user failed\n");
- return -EFAULT;
- }
- ret = qcom_ice_set_fde_conf(conf.start_sector, conf.fs_size,
- conf.index, conf.mode);
- break;
- }
-
case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
if ((data->listener.id == 0) ||
(data->type != QSEECOM_LISTENER_SERVICE)) {
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
index 728c65a..a940f97 100644
--- a/drivers/misc/uid_sys_stats.c
+++ b/drivers/misc/uid_sys_stats.c
@@ -14,6 +14,7 @@
*/
#include <linux/atomic.h>
+#include <linux/cpufreq_times.h>
#include <linux/err.h>
#include <linux/hashtable.h>
#include <linux/init.h>
@@ -422,6 +423,10 @@
kstrtol(end_uid, 10, &uid_end) != 0) {
return -EINVAL;
}
+
+ /* Also remove uids from /proc/uid_time_in_state */
+ cpufreq_task_times_remove_uids(uid_start, uid_end);
+
rt_mutex_lock(&uid_lock);
for (; uid_start <= uid_end; uid_start++) {
diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c
index f84a427..f735ab4 100644
--- a/drivers/misc/vmw_vmci/vmci_queue_pair.c
+++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c
@@ -298,8 +298,11 @@
size_t pas_size;
size_t vas_size;
size_t queue_size = sizeof(*queue) + sizeof(*queue->kernel_if);
- const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
+ u64 num_pages;
+ if (size > SIZE_MAX - PAGE_SIZE)
+ return NULL;
+ num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
if (num_pages >
(SIZE_MAX - queue_size) /
(sizeof(*queue->kernel_if->u.g.pas) +
@@ -624,9 +627,12 @@
{
struct vmci_queue *queue;
size_t queue_page_size;
- const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
+ u64 num_pages;
const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if));
+ if (size > SIZE_MAX - PAGE_SIZE)
+ return NULL;
+ num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
if (num_pages > (SIZE_MAX - queue_size) /
sizeof(*queue->kernel_if->u.h.page))
return NULL;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 2405ae3..a5f273a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3630,7 +3630,7 @@
* or disable state so cannot receive any completion of
* other requests.
*/
- BUG_ON(test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state));
+ WARN_ON(test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state));
/* clear pending request */
BUG_ON(!test_and_clear_bit(cmdq_req->tag,
@@ -3664,7 +3664,7 @@
out:
mmc_cmdq_clk_scaling_stop_busy(host, true, is_dcmd);
- if (!test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state)) {
+ if (!(err || cmdq_req->resp_err)) {
mmc_host_clk_release(host);
wake_up(&ctx_info->wait);
mmc_put_card(host->card);
@@ -4099,6 +4099,13 @@
if (ret == -EBUSY || ret == -EAGAIN) {
mmc_blk_cmdq_requeue_rw_rq(mq, req);
mmc_put_card(host->card);
+ } else if (ret == -ENOMEM) {
+ /*
+ * Elaborate error handling is not needed for
+ * system errors. Let the higher layer decide
+ * on the next steps.
+ */
+ goto out;
}
}
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index aa8373d..66165d9 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -314,6 +314,8 @@
host->max_req_size / 512));
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
blk_queue_max_segments(mq->queue, host->max_segs);
+ if (host->inlinecrypt_support)
+ queue_flag_set_unlocked(QUEUE_FLAG_INLINECRYPT, mq->queue);
}
/**
@@ -483,6 +485,9 @@
success:
sema_init(&mq->thread_sem, 1);
+ if (host->inlinecrypt_support)
+ queue_flag_set_unlocked(QUEUE_FLAG_INLINECRYPT, mq->queue);
+
/* hook for pm qos legacy init */
if (card->host->ops->init)
card->host->ops->init(card->host);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1b961a9..60f8a6d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -3393,7 +3393,22 @@
if (host->bus_ops && !host->bus_dead && host->card) {
mmc_power_up(host, host->card->ocr);
BUG_ON(!host->bus_ops->resume);
- host->bus_ops->resume(host);
+ err = host->bus_ops->resume(host);
+ if (err) {
+ pr_err("%s: %s: resume failed: %d\n",
+ mmc_hostname(host), __func__, err);
+ /*
+ * If we have cd-gpio based detection mechanism and
+ * deferred resume is supported, we will not detect
+ * card removal event when system is suspended. So if
+ * resume fails after a system suspend/resume,
+ * schedule the work to detect card presence.
+ */
+ if (mmc_card_is_removable(host) &&
+ !(host->caps & MMC_CAP_NEEDS_POLL)) {
+ mmc_detect_change(host, 0);
+ }
+ }
if (mmc_card_cmdq(host->card)) {
err = mmc_cmdq_halt(host, false);
if (err)
@@ -4706,9 +4721,11 @@
int err = 0;
switch (mode) {
- case PM_HIBERNATION_PREPARE:
- case PM_SUSPEND_PREPARE:
case PM_RESTORE_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+ if (host->bus_ops && host->bus_ops->pre_hibernate)
+ host->bus_ops->pre_hibernate(host);
+ case PM_SUSPEND_PREPARE:
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
@@ -4723,6 +4740,14 @@
if (!err)
break;
+ if (!mmc_card_is_removable(host)) {
+ dev_warn(mmc_dev(host),
+ "pre_suspend failed for non-removable host: "
+ "%d\n", err);
+ /* Avoid removing non-removable hosts */
+ break;
+ }
+
/* Calling bus_ops->remove() with a claimed host can deadlock */
host->bus_ops->remove(host);
mmc_claim_host(host);
@@ -4732,9 +4757,11 @@
host->pm_flags = 0;
break;
- case PM_POST_SUSPEND:
- case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
+ case PM_POST_HIBERNATION:
+ if (host->bus_ops && host->bus_ops->post_hibernate)
+ host->bus_ops->post_hibernate(host);
+ case PM_POST_SUSPEND:
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ff4f84f..99165f7 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -3058,6 +3058,73 @@
return 0;
}
+static int mmc_pre_hibernate(struct mmc_host *host)
+{
+ int ret = 0;
+
+ mmc_get_card(host->card);
+ host->cached_caps2 = host->caps2;
+
+ /*
+ * Increase usage_count of card and host device till
+ * hibernation is over. This will ensure they will not runtime suspend.
+ */
+ pm_runtime_get_noresume(mmc_dev(host));
+ pm_runtime_get_noresume(&host->card->dev);
+
+ if (!mmc_can_scale_clk(host))
+ goto out;
+ /*
+ * Suspend clock scaling and mask host capability so that
+ * we will run in max frequency during:
+ * 1. Hibernation preparation and image creation
+ * 2. After finding hibernation image during reboot
+ * 3. Once hibernation image is loaded and till hibernation
+ * restore is complete.
+ */
+ if (host->clk_scaling.enable)
+ mmc_suspend_clk_scaling(host);
+ host->caps2 &= ~MMC_CAP2_CLK_SCALE;
+ host->clk_scaling.state = MMC_LOAD_HIGH;
+ ret = mmc_clk_update_freq(host, host->card->clk_scaling_highest,
+ host->clk_scaling.state);
+ if (ret)
+ pr_err("%s: %s: Setting clk frequency to max failed: %d\n",
+ mmc_hostname(host), __func__, ret);
+out:
+ mmc_host_clk_hold(host);
+ mmc_put_card(host->card);
+ return ret;
+}
+
+static int mmc_post_hibernate(struct mmc_host *host)
+{
+ int ret = 0;
+
+ mmc_get_card(host->card);
+ if (!(host->cached_caps2 & MMC_CAP2_CLK_SCALE))
+ goto enable_pm;
+ /* Enable the clock scaling and set the host capability */
+ host->caps2 |= MMC_CAP2_CLK_SCALE;
+ if (!host->clk_scaling.enable)
+ ret = mmc_resume_clk_scaling(host);
+ if (ret)
+ pr_err("%s: %s: Resuming clk scaling failed: %d\n",
+ mmc_hostname(host), __func__, ret);
+enable_pm:
+ /*
+ * Reduce usage count of card and host device so that they may
+ * runtime suspend.
+ */
+ pm_runtime_put_noidle(&host->card->dev);
+ pm_runtime_put_noidle(mmc_dev(host));
+
+ mmc_host_clk_release(host);
+
+ mmc_put_card(host->card);
+ return ret;
+}
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -3069,6 +3136,8 @@
.change_bus_speed = mmc_change_bus_speed,
.reset = mmc_reset,
.shutdown = mmc_shutdown,
+ .pre_hibernate = mmc_pre_hibernate,
+ .post_hibernate = mmc_post_hibernate
};
/*
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 965d1f0..245493e 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1329,6 +1329,8 @@
mmc_hostname(host), __func__, err);
mmc_card_set_removed(host->card);
mmc_detect_change(host, msecs_to_jiffies(200));
+ } else if (err) {
+ goto out;
}
mmc_card_clr_suspended(host->card);
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index f81f417..d382dbd 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -490,6 +490,7 @@
(sizeof(struct idmac_desc_64addr) *
(i + 1))) >> 32;
/* Initialize reserved and buffer size fields to "0" */
+ p->des0 = 0;
p->des1 = 0;
p->des2 = 0;
p->des3 = 0;
@@ -512,6 +513,7 @@
i++, p++) {
p->des3 = cpu_to_le32(host->sg_dma +
(sizeof(struct idmac_desc) * (i + 1)));
+ p->des0 = 0;
p->des1 = 0;
}
@@ -2878,8 +2880,8 @@
}
if (host->use_dma == TRANS_MODE_IDMAC)
- /* It is also recommended that we reset and reprogram idmac */
- dw_mci_idmac_reset(host);
+ /* It is also required that we reinit idmac */
+ dw_mci_idmac_init(host);
ret = true;
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 684087d..1752007 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -368,9 +368,9 @@
host->irq_mask &= ~irq;
else
host->irq_mask |= irq;
- spin_unlock_irqrestore(&host->lock, flags);
writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
+ spin_unlock_irqrestore(&host->lock, flags);
}
static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host,
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5f2f24a..a082aa3 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1762,8 +1762,8 @@
*/
if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
struct pinctrl *p = devm_pinctrl_get(host->dev);
- if (!p) {
- ret = -ENODEV;
+ if (IS_ERR(p)) {
+ ret = PTR_ERR(p);
goto err_free_irq;
}
if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT))) {
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index d91eb67..7d24213 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -5075,6 +5075,7 @@
goto vreg_deinit;
}
host->is_crypto_en = true;
+ msm_host->mmc->inlinecrypt_support = true;
/* Packed commands cannot be encrypted/decrypted using ICE */
msm_host->mmc->caps2 &= ~(MMC_CAP2_PACKED_WR |
MMC_CAP2_PACKED_WR_CONTROL);
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 3c27401..a51d636 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -432,6 +432,20 @@
if (esdhc->vendor_ver < VENDOR_V_23)
pre_div = 2;
+ /*
+ * Limit SD clock to 167MHz for ls1046a according to its datasheet
+ */
+ if (clock > 167000000 &&
+ of_find_compatible_node(NULL, NULL, "fsl,ls1046a-esdhc"))
+ clock = 167000000;
+
+ /*
+ * Limit SD clock to 125MHz for ls1012a according to its datasheet
+ */
+ if (clock > 125000000 &&
+ of_find_compatible_node(NULL, NULL, "fsl,ls1012a-esdhc"))
+ clock = 125000000;
+
/* Workaround to reduce the clock frequency for p1010 esdhc */
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
if (clock > 20000000)
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index b0b9ceb..cfb7947 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -492,6 +492,8 @@
slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
break;
case INTEL_MRFLD_SDIO:
+ /* Advertise 2.0v for compatibility with the SDIO card's OCR */
+ slot->host->ocr_mask = MMC_VDD_20_21 | MMC_VDD_165_195;
slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE |
MMC_CAP_POWER_OFF_CARD;
break;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b674b38..36aecd2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1575,6 +1575,13 @@
if (mode != MMC_POWER_OFF) {
switch (1 << vdd) {
case MMC_VDD_165_195:
+ /*
+ * Without a regulator, SDHCI does not support 2.0v
+ * so we only get here if the driver deliberately
+ * added the 2.0v range to ocr_avail. Map it to 1.8v
+ * for the purpose of turning on the power.
+ */
+ case MMC_VDD_20_21:
pwr = SDHCI_POWER_180;
break;
case MMC_VDD_29_30:
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 5e1b68c..e1b603c 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -45,6 +45,7 @@
#define I82802AB 0x00ad
#define I82802AC 0x00ac
#define PF38F4476 0x881c
+#define M28F00AP30 0x8963
/* STMicroelectronics chips */
#define M50LPW080 0x002F
#define M50FLW080A 0x0080
@@ -375,6 +376,17 @@
extp->MinorVersion = '1';
}
+static int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip)
+{
+ /*
+ * Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t
+ * Erase Supend for their small Erase Blocks(0x8000)
+ */
+ if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30)
+ return 1;
+ return 0;
+}
+
static inline struct cfi_pri_intelext *
read_pri_intelext(struct map_info *map, __u16 adr)
{
@@ -831,21 +843,30 @@
(mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
goto sleep;
+ /* Do not allow suspend iff read/write to EB address */
+ if ((adr & chip->in_progress_block_mask) ==
+ chip->in_progress_block_addr)
+ goto sleep;
+
+ /* do not suspend small EBs, buggy Micron Chips */
+ if (cfi_is_micron_28F00AP30(cfi, chip) &&
+ (chip->in_progress_block_mask == ~(0x8000-1)))
+ goto sleep;
/* Erase suspend */
- map_write(map, CMD(0xB0), adr);
+ map_write(map, CMD(0xB0), chip->in_progress_block_addr);
/* If the flash has finished erasing, then 'erase suspend'
* appears to make some (28F320) flash devices switch to
* 'read' mode. Make sure that we switch to 'read status'
* mode so we get the right data. --rmk
*/
- map_write(map, CMD(0x70), adr);
+ map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_ERASING;
chip->state = FL_ERASE_SUSPENDING;
chip->erase_suspended = 1;
for (;;) {
- status = map_read(map, adr);
+ status = map_read(map, chip->in_progress_block_addr);
if (map_word_andequal(map, status, status_OK, status_OK))
break;
@@ -1041,8 +1062,8 @@
sending the 0x70 (Read Status) command to an erasing
chip and expecting it to be ignored, that's what we
do. */
- map_write(map, CMD(0xd0), adr);
- map_write(map, CMD(0x70), adr);
+ map_write(map, CMD(0xd0), chip->in_progress_block_addr);
+ map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_READY;
chip->state = FL_ERASING;
break;
@@ -1933,6 +1954,8 @@
map_write(map, CMD(0xD0), adr);
chip->state = FL_ERASING;
chip->erase_suspended = 0;
+ chip->in_progress_block_addr = adr;
+ chip->in_progress_block_mask = ~(len - 1);
ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
adr, len,
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 9dca881..107c05b 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -812,9 +812,10 @@
(mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
goto sleep;
- /* We could check to see if we're trying to access the sector
- * that is currently being erased. However, no user will try
- * anything like that so we just wait for the timeout. */
+ /* Do not allow suspend iff read/write to EB address */
+ if ((adr & chip->in_progress_block_mask) ==
+ chip->in_progress_block_addr)
+ goto sleep;
/* Erase suspend */
/* It's harmless to issue the Erase-Suspend and Erase-Resume
@@ -2263,6 +2264,7 @@
chip->state = FL_ERASING;
chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
+ chip->in_progress_block_mask = ~(map->size - 1);
INVALIDATE_CACHE_UDELAY(map, chip,
adr, map->size,
@@ -2352,6 +2354,7 @@
chip->state = FL_ERASING;
chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
+ chip->in_progress_block_mask = ~(len - 1);
INVALIDATE_CACHE_UDELAY(map, chip,
adr, len,
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 7c0b27d..b479bd8 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -1889,6 +1889,8 @@
do {
uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), map, cfi);
mask = (1 << (cfi->device_type * 8)) - 1;
+ if (ofs >= map->size)
+ return 0;
result = map_read(map, base + ofs);
bank++;
} while ((result.x[0] & mask) == CFI_MFR_CONTINUATION);
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 7c887f1..62fd690 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -431,7 +431,7 @@
}
-static int block2mtd_setup(const char *val, struct kernel_param *kp)
+static int block2mtd_setup(const char *val, const struct kernel_param *kp)
{
#ifdef MODULE
return block2mtd_setup2(val);
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index 8b66e52..7287696 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -266,7 +266,7 @@
return ret;
}
-static int phram_param_call(const char *val, struct kernel_param *kp)
+static int phram_param_call(const char *val, const struct kernel_param *kp)
{
#ifdef MODULE
return phram_setup(val);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 2a47a3f..b4092ea 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -487,7 +487,7 @@
for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) {
u32 eccpos;
- ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
+ ret = mtd_ooblayout_ecc(mtd, section++, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
@@ -534,7 +534,7 @@
for (i = 0; i < ARRAY_SIZE(to->eccpos);) {
u32 eccpos;
- ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
+ ret = mtd_ooblayout_ecc(mtd, section++, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 1a4a790..ef9a6b2 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -1763,7 +1763,7 @@
err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
addr);
/* erased page bitflips corrected */
- if (err > 0)
+ if (err >= 0)
return err;
}
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index d1570f5..2f6b552 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -201,14 +201,9 @@
/* returns nonzero if entire page is blank */
static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
- u32 *eccstat, unsigned int bufnum)
+ u32 eccstat, unsigned int bufnum)
{
- u32 reg = eccstat[bufnum / 4];
- int errors;
-
- errors = (reg >> ((3 - bufnum % 4) * 8)) & 15;
-
- return errors;
+ return (eccstat >> ((3 - bufnum % 4) * 8)) & 15;
}
/*
@@ -221,7 +216,7 @@
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
- u32 eccstat[4];
+ u32 eccstat;
int i;
/* set the chip select for NAND Transaction */
@@ -256,19 +251,17 @@
if (nctrl->eccread) {
int errors;
int bufnum = nctrl->page & priv->bufnum_mask;
- int sector = bufnum * chip->ecc.steps;
- int sector_end = sector + chip->ecc.steps - 1;
+ int sector_start = bufnum * chip->ecc.steps;
+ int sector_end = sector_start + chip->ecc.steps - 1;
__be32 *eccstat_regs;
- if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
- eccstat_regs = ifc->ifc_nand.v2_nand_eccstat;
- else
- eccstat_regs = ifc->ifc_nand.v1_nand_eccstat;
+ eccstat_regs = ifc->ifc_nand.nand_eccstat;
+ eccstat = ifc_in32(&eccstat_regs[sector_start / 4]);
- for (i = sector / 4; i <= sector_end / 4; i++)
- eccstat[i] = ifc_in32(&eccstat_regs[i]);
+ for (i = sector_start; i <= sector_end; i++) {
+ if (i != sector_start && !(i % 4))
+ eccstat = ifc_in32(&eccstat_regs[i / 4]);
- for (i = sector; i <= sector_end; i++) {
errors = check_read_ecc(mtd, ctrl, eccstat, i);
if (errors == 15) {
@@ -656,6 +649,7 @@
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
u32 nand_fsr;
+ int status;
/* Use READ_STATUS command, but wait for the device to be ready */
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
@@ -670,12 +664,12 @@
fsl_ifc_run_command(mtd);
nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
-
+ status = nand_fsr >> 24;
/*
* The chip always seems to report that it is
* write-protected, even when it is not.
*/
- return nand_fsr | NAND_STATUS_WP;
+ return status | NAND_STATUS_WP;
}
static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
@@ -907,6 +901,13 @@
if (ctrl->version == FSL_IFC_VERSION_1_1_0)
fsl_ifc_sram_init(priv);
+ /*
+ * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older
+ * versions which had 8KB. Hence bufnum mask needs to be updated.
+ */
+ if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
+ priv->bufnum_mask = (priv->bufnum_mask * 2) + 1;
+
return 0;
}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 6c062b8..d9dab42 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -1059,9 +1059,6 @@
return ret;
}
- /* handle the block mark swapping */
- block_mark_swapping(this, payload_virt, auxiliary_virt);
-
/* Loop over status bytes, accumulating ECC status. */
status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
@@ -1150,6 +1147,9 @@
max_bitflips = max_t(unsigned int, max_bitflips, *status);
}
+ /* handle the block mark swapping */
+ block_mark_swapping(this, buf, auxiliary_virt);
+
if (oob_required) {
/*
* It's time to deliver the OOB bytes. See gpmi_ecc_read_oob()
@@ -2047,18 +2047,20 @@
ret = nand_boot_init(this);
if (ret)
- goto err_out;
+ goto err_nand_cleanup;
ret = chip->scan_bbt(mtd);
if (ret)
- goto err_out;
+ goto err_nand_cleanup;
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
- goto err_out;
+ goto err_nand_cleanup;
return 0;
+err_nand_cleanup:
+ nand_cleanup(chip);
err_out:
- gpmi_nand_exit(this);
+ gpmi_free_dma_buffer(this);
return ret;
}
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 21c0308..5fb4516 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -715,7 +715,8 @@
chip->cmd_ctrl(mtd, readcmd, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
}
- chip->cmd_ctrl(mtd, command, ctrl);
+ if (command != NAND_CMD_NONE)
+ chip->cmd_ctrl(mtd, command, ctrl);
/* Address cycle, when necessary */
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
@@ -744,6 +745,7 @@
*/
switch (command) {
+ case NAND_CMD_NONE:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
@@ -806,7 +808,9 @@
}
/* Command latch cycle */
- chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ if (command != NAND_CMD_NONE)
+ chip->cmd_ctrl(mtd, command,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
if (column != -1 || page_addr != -1) {
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
@@ -842,6 +846,7 @@
*/
switch (command) {
+ case NAND_CMD_NONE:
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
@@ -4780,6 +4785,11 @@
goto err_free;
}
ecc->total = ecc->steps * ecc->bytes;
+ if (ecc->total > mtd->oobsize) {
+ WARN(1, "Total number of ECC bytes exceeded oobsize\n");
+ ret = -EINVAL;
+ goto err_free;
+ }
/*
* The number of bytes available for a client to place data into
diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c
index 1cb3f77..766b2c3 100644
--- a/drivers/mtd/tests/oobtest.c
+++ b/drivers/mtd/tests/oobtest.c
@@ -193,6 +193,9 @@
ops.datbuf = NULL;
ops.oobbuf = readbuf;
err = mtd_read_oob(mtd, addr, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err || ops.oobretlen != use_len) {
pr_err("error: readoob failed at %#llx\n",
(long long)addr);
@@ -227,6 +230,9 @@
ops.datbuf = NULL;
ops.oobbuf = readbuf;
err = mtd_read_oob(mtd, addr, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err || ops.oobretlen != mtd->oobavail) {
pr_err("error: readoob failed at %#llx\n",
(long long)addr);
@@ -286,6 +292,9 @@
/* read entire block's OOB at one go */
err = mtd_read_oob(mtd, addr, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err || ops.oobretlen != len) {
pr_err("error: readoob failed at %#llx\n",
(long long)addr);
@@ -527,6 +536,9 @@
pr_info("attempting to start read past end of OOB\n");
pr_info("an error is expected...\n");
err = mtd_read_oob(mtd, addr0, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err) {
pr_info("error occurred as expected\n");
err = 0;
@@ -571,6 +583,9 @@
pr_info("attempting to read past end of device\n");
pr_info("an error is expected...\n");
err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err) {
pr_info("error occurred as expected\n");
err = 0;
@@ -615,6 +630,9 @@
pr_info("attempting to read past end of device\n");
pr_info("an error is expected...\n");
err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err) {
pr_info("error occurred as expected\n");
err = 0;
@@ -684,6 +702,9 @@
ops.datbuf = NULL;
ops.oobbuf = readbuf;
err = mtd_read_oob(mtd, addr, &ops);
+ if (mtd_is_bitflip(err))
+ err = 0;
+
if (err)
goto out;
if (memcmpshow(addr, readbuf, writebuf,
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
index 46913ef2..479a5f0 100644
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -244,7 +244,7 @@
* in any case.
*/
if (mode & FMODE_WRITE) {
- ret = -EPERM;
+ ret = -EROFS;
goto out_unlock;
}
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 85d54f3..68902b8 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -894,6 +894,17 @@
return -EINVAL;
}
+ /*
+ * Both UBI and UBIFS have been designed for SLC NAND and NOR flashes.
+ * MLC NAND is different and needs special care, otherwise UBI or UBIFS
+ * will die soon and you will lose all your data.
+ */
+ if (mtd->type == MTD_MLCNANDFLASH) {
+ pr_err("ubi: refuse attaching mtd%d - MLC NAND is not supported\n",
+ mtd->index);
+ return -EINVAL;
+ }
+
if (ubi_num == UBI_DEV_NUM_AUTO) {
/* Search for an empty slot in the @ubi_devices array */
for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++)
@@ -1389,7 +1400,7 @@
* This function returns zero in case of success and a negative error code in
* case of error.
*/
-static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
+static int __init ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
{
int i, len;
struct mtd_dev_param *p;
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index 4f0bd6b..69dd216 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -362,7 +362,6 @@
{
int i;
- flush_work(&ubi->fm_work);
return_unused_pool_pebs(ubi, &ubi->fm_pool);
return_unused_pool_pebs(ubi, &ubi->fm_wl_pool);
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index c1f5c29..b44c8d3 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -828,6 +828,24 @@
return ret;
}
+static struct ubi_ainf_peb *clone_aeb(struct ubi_attach_info *ai,
+ struct ubi_ainf_peb *old)
+{
+ struct ubi_ainf_peb *new;
+
+ new = ubi_alloc_aeb(ai, old->pnum, old->ec);
+ if (!new)
+ return NULL;
+
+ new->vol_id = old->vol_id;
+ new->sqnum = old->sqnum;
+ new->lnum = old->lnum;
+ new->scrub = old->scrub;
+ new->copy_flag = old->copy_flag;
+
+ return new;
+}
+
/**
* ubi_scan_fastmap - scan the fastmap.
* @ubi: UBI device object
@@ -847,7 +865,7 @@
struct ubi_vid_hdr *vh;
struct ubi_ec_hdr *ech;
struct ubi_fastmap_layout *fm;
- struct ubi_ainf_peb *tmp_aeb, *aeb;
+ struct ubi_ainf_peb *aeb;
int i, used_blocks, pnum, fm_anchor, ret = 0;
size_t fm_size;
__be32 crc, tmp_crc;
@@ -857,9 +875,16 @@
if (fm_anchor < 0)
return UBI_NO_FASTMAP;
- /* Move all (possible) fastmap blocks into our new attach structure. */
- list_for_each_entry_safe(aeb, tmp_aeb, &scan_ai->fastmap, u.list)
- list_move_tail(&aeb->u.list, &ai->fastmap);
+ /* Copy all (possible) fastmap blocks into our new attach structure. */
+ list_for_each_entry(aeb, &scan_ai->fastmap, u.list) {
+ struct ubi_ainf_peb *new;
+
+ new = clone_aeb(ai, aeb);
+ if (!new)
+ return -ENOMEM;
+
+ list_add(&new->u.list, &ai->fastmap);
+ }
down_write(&ubi->fm_protect);
memset(ubi->fm_buf, 0, ubi->fm_size);
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 7ac78c1..1bcb25b 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -265,6 +265,12 @@
vol->last_eb_bytes = vol->usable_leb_size;
}
+ /* Make volume "available" before it becomes accessible via sysfs */
+ spin_lock(&ubi->volumes_lock);
+ ubi->volumes[vol_id] = vol;
+ ubi->vol_count += 1;
+ spin_unlock(&ubi->volumes_lock);
+
/* Register character device for the volume */
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
vol->cdev.owner = THIS_MODULE;
@@ -304,11 +310,6 @@
if (err)
goto out_sysfs;
- spin_lock(&ubi->volumes_lock);
- ubi->volumes[vol_id] = vol;
- ubi->vol_count += 1;
- spin_unlock(&ubi->volumes_lock);
-
ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
self_check_volumes(ubi);
return err;
@@ -328,6 +329,10 @@
out_cdev:
cdev_del(&vol->cdev);
out_mapping:
+ spin_lock(&ubi->volumes_lock);
+ ubi->volumes[vol_id] = NULL;
+ ubi->vol_count -= 1;
+ spin_unlock(&ubi->volumes_lock);
if (do_free)
ubi_eba_destroy_table(eba_tbl);
out_acc:
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 551f0f8..91d8a48 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -450,7 +450,7 @@
{
int i;
- if (!client_info->slave)
+ if (!client_info->slave || !is_valid_ether_addr(client_info->mac_dst))
return;
for (i = 0; i < RLB_ARP_BURST_SIZE; i++) {
@@ -944,6 +944,10 @@
skb->priority = TC_PRIO_CONTROL;
skb->dev = slave->dev;
+ netdev_dbg(slave->bond->dev,
+ "Send learning packet: dev %s mac %pM vlan %d\n",
+ slave->dev->name, mac_addr, vid);
+
if (vid)
__vlan_hwaccel_put_tag(skb, vlan_proto, vid);
@@ -966,14 +970,13 @@
*/
rcu_read_lock();
netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
- if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) {
- if (strict_match &&
- ether_addr_equal_64bits(mac_addr,
- upper->dev_addr)) {
+ if (is_vlan_dev(upper) &&
+ bond->nest_level == vlan_get_encap_level(upper) - 1) {
+ if (upper->addr_assign_type == NET_ADDR_STOLEN) {
alb_send_lp_vid(slave, mac_addr,
vlan_dev_vlan_proto(upper),
vlan_dev_vlan_id(upper));
- } else if (!strict_match) {
+ } else {
alb_send_lp_vid(slave, upper->dev_addr,
vlan_dev_vlan_proto(upper),
vlan_dev_vlan_id(upper));
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 63d61c0..1a139d0 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -371,9 +371,10 @@
/* Get link speed and duplex from the slave's base driver
* using ethtool. If for some reason the call fails or the
* values are invalid, set speed and duplex to -1,
- * and return.
+ * and return. Return 1 if speed or duplex settings are
+ * UNKNOWN; 0 otherwise.
*/
-static void bond_update_speed_duplex(struct slave *slave)
+static int bond_update_speed_duplex(struct slave *slave)
{
struct net_device *slave_dev = slave->dev;
struct ethtool_link_ksettings ecmd;
@@ -383,24 +384,27 @@
slave->duplex = DUPLEX_UNKNOWN;
res = __ethtool_get_link_ksettings(slave_dev, &ecmd);
- if (res < 0)
- return;
-
- if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1))
- return;
-
+ if (res < 0) {
+ slave->link = BOND_LINK_DOWN;
+ return 1;
+ }
+ if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) {
+ slave->link = BOND_LINK_DOWN;
+ return 1;
+ }
switch (ecmd.base.duplex) {
case DUPLEX_FULL:
case DUPLEX_HALF:
break;
default:
- return;
+ slave->link = BOND_LINK_DOWN;
+ return 1;
}
slave->speed = ecmd.base.speed;
slave->duplex = ecmd.base.duplex;
- return;
+ return 0;
}
const char *bond_slave_link_status(s8 link)
@@ -1520,39 +1524,6 @@
goto err_close;
}
- /* If the mode uses primary, then the following is handled by
- * bond_change_active_slave().
- */
- if (!bond_uses_primary(bond)) {
- /* set promiscuity level to new slave */
- if (bond_dev->flags & IFF_PROMISC) {
- res = dev_set_promiscuity(slave_dev, 1);
- if (res)
- goto err_close;
- }
-
- /* set allmulti level to new slave */
- if (bond_dev->flags & IFF_ALLMULTI) {
- res = dev_set_allmulti(slave_dev, 1);
- if (res)
- goto err_close;
- }
-
- netif_addr_lock_bh(bond_dev);
-
- dev_mc_sync_multiple(slave_dev, bond_dev);
- dev_uc_sync_multiple(slave_dev, bond_dev);
-
- netif_addr_unlock_bh(bond_dev);
- }
-
- if (BOND_MODE(bond) == BOND_MODE_8023AD) {
- /* add lacpdu mc addr to mc list */
- u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
-
- dev_mc_add(slave_dev, lacpdu_multicast);
- }
-
res = vlan_vids_add_by_dev(slave_dev, bond_dev);
if (res) {
netdev_err(bond_dev, "Couldn't add bond vlan ids to %s\n",
@@ -1683,8 +1654,7 @@
} /* switch(bond_mode) */
#ifdef CONFIG_NET_POLL_CONTROLLER
- slave_dev->npinfo = bond->dev->npinfo;
- if (slave_dev->npinfo) {
+ if (bond->dev->npinfo) {
if (slave_enable_netpoll(new_slave)) {
netdev_info(bond_dev, "master_dev is using netpoll, but new slave device does not support netpoll\n");
res = -EBUSY;
@@ -1715,6 +1685,40 @@
goto err_upper_unlink;
}
+ /* If the mode uses primary, then the following is handled by
+ * bond_change_active_slave().
+ */
+ if (!bond_uses_primary(bond)) {
+ /* set promiscuity level to new slave */
+ if (bond_dev->flags & IFF_PROMISC) {
+ res = dev_set_promiscuity(slave_dev, 1);
+ if (res)
+ goto err_sysfs_del;
+ }
+
+ /* set allmulti level to new slave */
+ if (bond_dev->flags & IFF_ALLMULTI) {
+ res = dev_set_allmulti(slave_dev, 1);
+ if (res) {
+ if (bond_dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(slave_dev, -1);
+ goto err_sysfs_del;
+ }
+ }
+
+ netif_addr_lock_bh(bond_dev);
+ dev_mc_sync_multiple(slave_dev, bond_dev);
+ dev_uc_sync_multiple(slave_dev, bond_dev);
+ netif_addr_unlock_bh(bond_dev);
+
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ /* add lacpdu mc addr to mc list */
+ u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
+
+ dev_mc_add(slave_dev, lacpdu_multicast);
+ }
+ }
+
bond->slave_cnt++;
bond_compute_features(bond);
bond_set_carrier(bond);
@@ -1728,6 +1732,8 @@
if (bond_mode_uses_xmit_hash(bond))
bond_update_slave_arr(bond, NULL);
+ bond->nest_level = dev_get_nest_level(bond_dev);
+
netdev_info(bond_dev, "Enslaving %s as %s interface with %s link\n",
slave_dev->name,
bond_is_active_slave(new_slave) ? "an active" : "a backup",
@@ -1738,6 +1744,9 @@
return 0;
/* Undo stages on error */
+err_sysfs_del:
+ bond_sysfs_slave_del(new_slave);
+
err_upper_unlink:
bond_upper_dev_unlink(bond, new_slave);
@@ -1745,9 +1754,6 @@
netdev_rx_handler_unregister(slave_dev);
err_detach:
- if (!bond_uses_primary(bond))
- bond_hw_addr_flush(bond_dev, slave_dev);
-
vlan_vids_del_by_dev(slave_dev, bond_dev);
if (rcu_access_pointer(bond->primary_slave) == new_slave)
RCU_INIT_POINTER(bond->primary_slave, NULL);
@@ -2063,6 +2069,7 @@
(bond->params.downdelay - slave->delay) *
bond->params.miimon,
slave->dev->name);
+ commit++;
continue;
}
@@ -2100,7 +2107,7 @@
(bond->params.updelay - slave->delay) *
bond->params.miimon,
slave->dev->name);
-
+ commit++;
continue;
}
@@ -2600,11 +2607,13 @@
bond_for_each_slave_rcu(bond, slave, iter) {
unsigned long trans_start = dev_trans_start(slave->dev);
+ slave->new_link = BOND_LINK_NOCHANGE;
+
if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, trans_start, 1) &&
bond_time_in_interval(bond, slave->last_rx, 1)) {
- slave->link = BOND_LINK_UP;
+ slave->new_link = BOND_LINK_UP;
slave_state_changed = 1;
/* primary_slave has no meaning in round-robin
@@ -2631,7 +2640,7 @@
if (!bond_time_in_interval(bond, trans_start, 2) ||
!bond_time_in_interval(bond, slave->last_rx, 2)) {
- slave->link = BOND_LINK_DOWN;
+ slave->new_link = BOND_LINK_DOWN;
slave_state_changed = 1;
if (slave->link_failure_count < UINT_MAX)
@@ -2662,6 +2671,11 @@
if (!rtnl_trylock())
goto re_arm;
+ bond_for_each_slave(bond, slave, iter) {
+ if (slave->new_link != BOND_LINK_NOCHANGE)
+ slave->link = slave->new_link;
+ }
+
if (slave_state_changed) {
bond_slave_state_change(bond);
if (BOND_MODE(bond) == BOND_MODE_XOR)
@@ -3327,12 +3341,17 @@
for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) {
u64 nv = new[i];
u64 ov = old[i];
+ s64 delta = nv - ov;
/* detects if this particular field is 32bit only */
if (((nv | ov) >> 32) == 0)
- res[i] += (u32)nv - (u32)ov;
- else
- res[i] += nv - ov;
+ delta = (s64)(s32)((u32)nv - (u32)ov);
+
+ /* filter anomalies, some drivers reset their stats
+ * at down/up events.
+ */
+ if (delta > 0)
+ res[i] += delta;
}
}
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c
index 1e37313..6da69af 100644
--- a/drivers/net/can/cc770/cc770.c
+++ b/drivers/net/can/cc770/cc770.c
@@ -390,37 +390,23 @@
return 0;
}
-static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static void cc770_tx(struct net_device *dev, int mo)
{
struct cc770_priv *priv = netdev_priv(dev);
- struct net_device_stats *stats = &dev->stats;
- struct can_frame *cf = (struct can_frame *)skb->data;
- unsigned int mo = obj2msgobj(CC770_OBJ_TX);
+ struct can_frame *cf = (struct can_frame *)priv->tx_skb->data;
u8 dlc, rtr;
u32 id;
int i;
- if (can_dropped_invalid_skb(dev, skb))
- return NETDEV_TX_OK;
-
- if ((cc770_read_reg(priv,
- msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
- netdev_err(dev, "TX register is still occupied!\n");
- return NETDEV_TX_BUSY;
- }
-
- netif_stop_queue(dev);
-
dlc = cf->can_dlc;
id = cf->can_id;
- if (cf->can_id & CAN_RTR_FLAG)
- rtr = 0;
- else
- rtr = MSGCFG_DIR;
+ rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR;
+
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
cc770_write_reg(priv, msgobj[mo].ctrl1,
RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES);
- cc770_write_reg(priv, msgobj[mo].ctrl0,
- MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES);
+
if (id & CAN_EFF_FLAG) {
id &= CAN_EFF_MASK;
cc770_write_reg(priv, msgobj[mo].config,
@@ -439,22 +425,30 @@
for (i = 0; i < dlc; i++)
cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]);
- /* Store echo skb before starting the transfer */
- can_put_echo_skb(skb, dev, 0);
-
cc770_write_reg(priv, msgobj[mo].ctrl1,
- RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
-
- stats->tx_bytes += dlc;
-
-
- /*
- * HM: We had some cases of repeated IRQs so make sure the
- * INT is acknowledged I know it's already further up, but
- * doing again fixed the issue
- */
+ RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
cc770_write_reg(priv, msgobj[mo].ctrl0,
- MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
+ MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC);
+}
+
+static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+ unsigned int mo = obj2msgobj(CC770_OBJ_TX);
+
+ if (can_dropped_invalid_skb(dev, skb))
+ return NETDEV_TX_OK;
+
+ netif_stop_queue(dev);
+
+ if ((cc770_read_reg(priv,
+ msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
+ netdev_err(dev, "TX register is still occupied!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ priv->tx_skb = skb;
+ cc770_tx(dev, mo);
return NETDEV_TX_OK;
}
@@ -680,19 +674,46 @@
struct cc770_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
unsigned int mo = obj2msgobj(o);
+ struct can_frame *cf;
+ u8 ctrl1;
- /* Nothing more to send, switch off interrupts */
+ ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
+
cc770_write_reg(priv, msgobj[mo].ctrl0,
MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
- /*
- * We had some cases of repeated IRQ so make sure the
- * INT is acknowledged
- */
- cc770_write_reg(priv, msgobj[mo].ctrl0,
- MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ RMTPND_RES | TXRQST_RES | MSGLST_RES | NEWDAT_RES);
+ if (unlikely(!priv->tx_skb)) {
+ netdev_err(dev, "missing tx skb in tx interrupt\n");
+ return;
+ }
+
+ if (unlikely(ctrl1 & MSGLST_SET)) {
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ }
+
+ /* When the CC770 is sending an RTR message and it receives a regular
+ * message that matches the id of the RTR message, it will overwrite the
+ * outgoing message in the TX register. When this happens we must
+ * process the received message and try to transmit the outgoing skb
+ * again.
+ */
+ if (unlikely(ctrl1 & NEWDAT_SET)) {
+ cc770_rx(dev, mo, ctrl1);
+ cc770_tx(dev, mo);
+ return;
+ }
+
+ cf = (struct can_frame *)priv->tx_skb->data;
+ stats->tx_bytes += cf->can_dlc;
stats->tx_packets++;
+
+ can_put_echo_skb(priv->tx_skb, dev, 0);
can_get_echo_skb(dev, 0);
+ priv->tx_skb = NULL;
+
netif_wake_queue(dev);
}
@@ -804,6 +825,7 @@
priv->can.do_set_bittiming = cc770_set_bittiming;
priv->can.do_set_mode = cc770_set_mode;
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ priv->tx_skb = NULL;
memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags));
diff --git a/drivers/net/can/cc770/cc770.h b/drivers/net/can/cc770/cc770.h
index a1739db..95752e1 100644
--- a/drivers/net/can/cc770/cc770.h
+++ b/drivers/net/can/cc770/cc770.h
@@ -193,6 +193,8 @@
u8 cpu_interface; /* CPU interface register */
u8 clkout; /* Clock out register */
u8 bus_config; /* Bus conffiguration register */
+
+ struct sk_buff *tx_skb;
};
struct net_device *alloc_cc770dev(int sizeof_priv);
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 16f7cad..47f43bd 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -493,7 +493,7 @@
data = be32_to_cpup((__be32 *)&cf->data[0]);
flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[0]);
}
- if (cf->can_dlc > 3) {
+ if (cf->can_dlc > 4) {
data = be32_to_cpup((__be32 *)&cf->data[4]);
flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[1]);
}
diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c
index c06ef43..6c67640 100644
--- a/drivers/net/can/ifi_canfd/ifi_canfd.c
+++ b/drivers/net/can/ifi_canfd/ifi_canfd.c
@@ -30,6 +30,7 @@
#define IFI_CANFD_STCMD_ERROR_ACTIVE BIT(2)
#define IFI_CANFD_STCMD_ERROR_PASSIVE BIT(3)
#define IFI_CANFD_STCMD_BUSOFF BIT(4)
+#define IFI_CANFD_STCMD_ERROR_WARNING BIT(5)
#define IFI_CANFD_STCMD_BUSMONITOR BIT(16)
#define IFI_CANFD_STCMD_LOOPBACK BIT(18)
#define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24)
@@ -52,7 +53,10 @@
#define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13)
#define IFI_CANFD_INTERRUPT 0xc
+#define IFI_CANFD_INTERRUPT_ERROR_BUSOFF BIT(0)
#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1)
+#define IFI_CANFD_INTERRUPT_ERROR_STATE_CHG BIT(2)
+#define IFI_CANFD_INTERRUPT_ERROR_REC_TEC_INC BIT(3)
#define IFI_CANFD_INTERRUPT_ERROR_COUNTER BIT(10)
#define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16)
#define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22)
@@ -61,6 +65,10 @@
#define IFI_CANFD_INTERRUPT_SET_IRQ ((u32)BIT(31))
#define IFI_CANFD_IRQMASK 0x10
+#define IFI_CANFD_IRQMASK_ERROR_BUSOFF BIT(0)
+#define IFI_CANFD_IRQMASK_ERROR_WARNING BIT(1)
+#define IFI_CANFD_IRQMASK_ERROR_STATE_CHG BIT(2)
+#define IFI_CANFD_IRQMASK_ERROR_REC_TEC_INC BIT(3)
#define IFI_CANFD_IRQMASK_SET_ERR BIT(7)
#define IFI_CANFD_IRQMASK_SET_TS BIT(15)
#define IFI_CANFD_IRQMASK_TXFIFO_EMPTY BIT(16)
@@ -136,6 +144,8 @@
#define IFI_CANFD_SYSCLOCK 0x50
#define IFI_CANFD_VER 0x54
+#define IFI_CANFD_VER_REV_MASK 0xff
+#define IFI_CANFD_VER_REV_MIN_SUPPORTED 0x15
#define IFI_CANFD_IP_ID 0x58
#define IFI_CANFD_IP_ID_VALUE 0xD073CAFD
@@ -220,7 +230,10 @@
if (enable) {
enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY |
- IFI_CANFD_IRQMASK_RXFIFO_NEMPTY;
+ IFI_CANFD_IRQMASK_RXFIFO_NEMPTY |
+ IFI_CANFD_IRQMASK_ERROR_STATE_CHG |
+ IFI_CANFD_IRQMASK_ERROR_WARNING |
+ IFI_CANFD_IRQMASK_ERROR_BUSOFF;
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER;
}
@@ -361,12 +374,13 @@
return 1;
}
-static int ifi_canfd_handle_lec_err(struct net_device *ndev, const u32 errctr)
+static int ifi_canfd_handle_lec_err(struct net_device *ndev)
{
struct ifi_canfd_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats;
struct can_frame *cf;
struct sk_buff *skb;
+ u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST |
IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST |
IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST |
@@ -449,6 +463,11 @@
switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
+ /* error active state */
+ priv->can.can_stats.error_warning++;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ break;
+ case CAN_STATE_ERROR_WARNING:
/* error warning state */
priv->can.can_stats.error_warning++;
priv->can.state = CAN_STATE_ERROR_WARNING;
@@ -477,7 +496,7 @@
ifi_canfd_get_berr_counter(ndev, &bec);
switch (new_state) {
- case CAN_STATE_ERROR_ACTIVE:
+ case CAN_STATE_ERROR_WARNING:
/* error warning state */
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (bec.txerr > bec.rxerr) ?
@@ -510,22 +529,21 @@
return 1;
}
-static int ifi_canfd_handle_state_errors(struct net_device *ndev, u32 stcmd)
+static int ifi_canfd_handle_state_errors(struct net_device *ndev)
{
struct ifi_canfd_priv *priv = netdev_priv(ndev);
+ u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
int work_done = 0;
- u32 isr;
- /*
- * The ErrWarn condition is a little special, since the bit is
- * located in the INTERRUPT register instead of STCMD register.
- */
- isr = readl(priv->base + IFI_CANFD_INTERRUPT);
- if ((isr & IFI_CANFD_INTERRUPT_ERROR_WARNING) &&
+ if ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) &&
+ (priv->can.state != CAN_STATE_ERROR_ACTIVE)) {
+ netdev_dbg(ndev, "Error, entered active state\n");
+ work_done += ifi_canfd_handle_state_change(ndev,
+ CAN_STATE_ERROR_ACTIVE);
+ }
+
+ if ((stcmd & IFI_CANFD_STCMD_ERROR_WARNING) &&
(priv->can.state != CAN_STATE_ERROR_WARNING)) {
- /* Clear the interrupt */
- writel(IFI_CANFD_INTERRUPT_ERROR_WARNING,
- priv->base + IFI_CANFD_INTERRUPT);
netdev_dbg(ndev, "Error, entered warning state\n");
work_done += ifi_canfd_handle_state_change(ndev,
CAN_STATE_ERROR_WARNING);
@@ -552,18 +570,11 @@
{
struct net_device *ndev = napi->dev;
struct ifi_canfd_priv *priv = netdev_priv(ndev);
- const u32 stcmd_state_mask = IFI_CANFD_STCMD_ERROR_PASSIVE |
- IFI_CANFD_STCMD_BUSOFF;
+ u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD);
int work_done = 0;
- u32 stcmd = readl(priv->base + IFI_CANFD_STCMD);
- u32 rxstcmd = readl(priv->base + IFI_CANFD_RXSTCMD);
- u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR);
-
/* Handle bus state changes */
- if ((stcmd & stcmd_state_mask) ||
- ((stcmd & IFI_CANFD_STCMD_ERROR_ACTIVE) == 0))
- work_done += ifi_canfd_handle_state_errors(ndev, stcmd);
+ work_done += ifi_canfd_handle_state_errors(ndev);
/* Handle lost messages on RX */
if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW)
@@ -571,7 +582,7 @@
/* Handle lec errors on the bus */
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
- work_done += ifi_canfd_handle_lec_err(ndev, errctr);
+ work_done += ifi_canfd_handle_lec_err(ndev);
/* Handle normal messages on RX */
if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY))
@@ -592,12 +603,13 @@
struct net_device_stats *stats = &ndev->stats;
const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY |
IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER |
+ IFI_CANFD_INTERRUPT_ERROR_COUNTER |
+ IFI_CANFD_INTERRUPT_ERROR_STATE_CHG |
IFI_CANFD_INTERRUPT_ERROR_WARNING |
- IFI_CANFD_INTERRUPT_ERROR_COUNTER;
+ IFI_CANFD_INTERRUPT_ERROR_BUSOFF;
const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY |
IFI_CANFD_INTERRUPT_TXFIFO_REMOVE;
- const u32 clr_irq_mask = ~((u32)(IFI_CANFD_INTERRUPT_SET_IRQ |
- IFI_CANFD_INTERRUPT_ERROR_WARNING));
+ const u32 clr_irq_mask = ~((u32)IFI_CANFD_INTERRUPT_SET_IRQ);
u32 isr;
isr = readl(priv->base + IFI_CANFD_INTERRUPT);
@@ -933,7 +945,7 @@
struct resource *res;
void __iomem *addr;
int irq, ret;
- u32 id;
+ u32 id, rev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
addr = devm_ioremap_resource(dev, res);
@@ -947,6 +959,13 @@
return -EINVAL;
}
+ rev = readl(addr + IFI_CANFD_VER) & IFI_CANFD_VER_REV_MASK;
+ if (rev < IFI_CANFD_VER_REV_MIN_SUPPORTED) {
+ dev_err(dev, "This block is too old (rev %i), minimum supported is rev %i\n",
+ rev, IFI_CANFD_VER_REV_MIN_SUPPORTED);
+ return -EINVAL;
+ }
+
ndev = alloc_candev(sizeof(*priv), 1);
if (!ndev)
return -ENOMEM;
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index c9d61a6..3a75352 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -1179,7 +1179,7 @@
skb = alloc_can_skb(priv->netdev, &cf);
if (!skb) {
- stats->tx_dropped++;
+ stats->rx_dropped++;
return;
}
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index e2512ab..e13c9cd 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -61,6 +61,8 @@
#define ENA_MMIO_READ_TIMEOUT 0xFFFFFFFF
+#define ENA_REGS_ADMIN_INTR_MASK 1
+
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
@@ -232,11 +234,9 @@
tail_masked = admin_queue->sq.tail & queue_size_mask;
/* In case of queue FULL */
- cnt = admin_queue->sq.tail - admin_queue->sq.head;
+ cnt = atomic_read(&admin_queue->outstanding_cmds);
if (cnt >= admin_queue->q_depth) {
- pr_debug("admin queue is FULL (tail %d head %d depth: %d)\n",
- admin_queue->sq.tail, admin_queue->sq.head,
- admin_queue->q_depth);
+ pr_debug("admin queue is full.\n");
admin_queue->stats.out_of_space++;
return ERR_PTR(-ENOSPC);
}
@@ -508,15 +508,20 @@
static int ena_com_wait_and_process_admin_cq_polling(struct ena_comp_ctx *comp_ctx,
struct ena_com_admin_queue *admin_queue)
{
- unsigned long flags;
- u32 start_time;
+ unsigned long flags, timeout;
int ret;
- start_time = ((u32)jiffies_to_usecs(jiffies));
+ timeout = jiffies + ADMIN_CMD_TIMEOUT_US;
- while (comp_ctx->status == ENA_CMD_SUBMITTED) {
- if ((((u32)jiffies_to_usecs(jiffies)) - start_time) >
- ADMIN_CMD_TIMEOUT_US) {
+ while (1) {
+ spin_lock_irqsave(&admin_queue->q_lock, flags);
+ ena_com_handle_admin_completion(admin_queue);
+ spin_unlock_irqrestore(&admin_queue->q_lock, flags);
+
+ if (comp_ctx->status != ENA_CMD_SUBMITTED)
+ break;
+
+ if (time_is_before_jiffies(timeout)) {
pr_err("Wait for completion (polling) timeout\n");
/* ENA didn't have any completion */
spin_lock_irqsave(&admin_queue->q_lock, flags);
@@ -528,10 +533,6 @@
goto err;
}
- spin_lock_irqsave(&admin_queue->q_lock, flags);
- ena_com_handle_admin_completion(admin_queue);
- spin_unlock_irqrestore(&admin_queue->q_lock, flags);
-
msleep(100);
}
@@ -1449,6 +1450,12 @@
void ena_com_set_admin_polling_mode(struct ena_com_dev *ena_dev, bool polling)
{
+ u32 mask_value = 0;
+
+ if (polling)
+ mask_value = ENA_REGS_ADMIN_INTR_MASK;
+
+ writel(mask_value, ena_dev->reg_bar + ENA_REGS_INTR_MASK_OFF);
ena_dev->admin_queue.polling = polling;
}
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index bfeaec5..0d9ce08 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -1542,6 +1542,7 @@
"Failed to get TX queue handlers. TX queue num %d rc: %d\n",
qid, rc);
ena_com_destroy_io_queue(ena_dev, ena_qid);
+ return rc;
}
ena_com_update_numa_node(tx_ring->ena_com_io_cq, ctx.numa_node);
@@ -1606,6 +1607,7 @@
"Failed to get RX queue handlers. RX queue num %d rc: %d\n",
qid, rc);
ena_com_destroy_io_queue(ena_dev, ena_qid);
+ return rc;
}
ena_com_update_numa_node(rx_ring->ena_com_io_cq, ctx.numa_node);
@@ -2806,6 +2808,11 @@
{
int release_bars;
+ if (ena_dev->mem_bar)
+ devm_iounmap(&pdev->dev, ena_dev->mem_bar);
+
+ devm_iounmap(&pdev->dev, ena_dev->reg_bar);
+
release_bars = pci_select_bars(pdev, IORESOURCE_MEM) & ENA_BAR_MASK;
pci_release_selected_regions(pdev, release_bars);
}
@@ -2893,8 +2900,9 @@
goto err_free_ena_dev;
}
- ena_dev->reg_bar = ioremap(pci_resource_start(pdev, ENA_REG_BAR),
- pci_resource_len(pdev, ENA_REG_BAR));
+ ena_dev->reg_bar = devm_ioremap(&pdev->dev,
+ pci_resource_start(pdev, ENA_REG_BAR),
+ pci_resource_len(pdev, ENA_REG_BAR));
if (!ena_dev->reg_bar) {
dev_err(&pdev->dev, "failed to remap regs bar\n");
rc = -EFAULT;
@@ -2914,8 +2922,9 @@
ena_set_push_mode(pdev, ena_dev, &get_feat_ctx);
if (ena_dev->tx_mem_queue_type == ENA_ADMIN_PLACEMENT_POLICY_DEV) {
- ena_dev->mem_bar = ioremap_wc(pci_resource_start(pdev, ENA_MEM_BAR),
- pci_resource_len(pdev, ENA_MEM_BAR));
+ ena_dev->mem_bar = devm_ioremap_wc(&pdev->dev,
+ pci_resource_start(pdev, ENA_MEM_BAR),
+ pci_resource_len(pdev, ENA_MEM_BAR));
if (!ena_dev->mem_bar) {
rc = -EFAULT;
goto err_device_destroy;
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index 5390ae8..71611bd 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -560,6 +560,7 @@
xgene_enet_rd_csr(pdata, CLE_BYPASS_REG0_0_ADDR, &cb);
cb |= CFG_CLE_BYPASS_EN0;
CFG_CLE_IP_PROTOCOL0_SET(&cb, 3);
+ CFG_CLE_IP_HDR_LEN_SET(&cb, 0);
xgene_enet_wr_csr(pdata, CLE_BYPASS_REG0_0_ADDR, cb);
xgene_enet_rd_csr(pdata, CLE_BYPASS_REG1_0_ADDR, &cb);
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
index 06e598c..c82faf1 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
@@ -163,6 +163,7 @@
#define CFG_RXCLK_MUXSEL0_SET(dst, val) xgene_set_bits(dst, val, 26, 3)
#define CFG_CLE_IP_PROTOCOL0_SET(dst, val) xgene_set_bits(dst, val, 16, 2)
+#define CFG_CLE_IP_HDR_LEN_SET(dst, val) xgene_set_bits(dst, val, 8, 5)
#define CFG_CLE_DSTQID0_SET(dst, val) xgene_set_bits(dst, val, 0, 12)
#define CFG_CLE_FPSEL0_SET(dst, val) xgene_set_bits(dst, val, 16, 4)
#define CFG_MACMODE_SET(dst, val) xgene_set_bits(dst, val, 18, 2)
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
index 8158d46..fca2e42 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
@@ -505,14 +505,24 @@
return NETDEV_TX_OK;
}
-static void xgene_enet_skip_csum(struct sk_buff *skb)
+static void xgene_enet_rx_csum(struct sk_buff *skb)
{
+ struct net_device *ndev = skb->dev;
struct iphdr *iph = ip_hdr(skb);
- if (!ip_is_fragment(iph) ||
- (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)) {
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- }
+ if (!(ndev->features & NETIF_F_RXCSUM))
+ return;
+
+ if (skb->protocol != htons(ETH_P_IP))
+ return;
+
+ if (ip_is_fragment(iph))
+ return;
+
+ if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)
+ return;
+
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
}
static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
@@ -537,9 +547,9 @@
buf_pool->rx_skb[skb_index] = NULL;
/* checking for error */
- status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) ||
+ status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) |
GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
- if (unlikely(status > 2)) {
+ if (unlikely(status)) {
dev_kfree_skb_any(skb);
xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev),
status);
@@ -555,10 +565,7 @@
skb_checksum_none_assert(skb);
skb->protocol = eth_type_trans(skb, ndev);
- if (likely((ndev->features & NETIF_F_IP_CSUM) &&
- skb->protocol == htons(ETH_P_IP))) {
- xgene_enet_skip_csum(skb);
- }
+ xgene_enet_rx_csum(skb);
rx_ring->rx_packets++;
rx_ring->rx_bytes += datalen;
@@ -1673,6 +1680,30 @@
}
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_enet_acpi_match[] = {
+ { "APMC0D05", XGENE_ENET1},
+ { "APMC0D30", XGENE_ENET1},
+ { "APMC0D31", XGENE_ENET1},
+ { "APMC0D3F", XGENE_ENET1},
+ { "APMC0D26", XGENE_ENET2},
+ { "APMC0D25", XGENE_ENET2},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match);
+#endif
+
+static const struct of_device_id xgene_enet_of_match[] = {
+ {.compatible = "apm,xgene-enet", .data = (void *)XGENE_ENET1},
+ {.compatible = "apm,xgene1-sgenet", .data = (void *)XGENE_ENET1},
+ {.compatible = "apm,xgene1-xgenet", .data = (void *)XGENE_ENET1},
+ {.compatible = "apm,xgene2-sgenet", .data = (void *)XGENE_ENET2},
+ {.compatible = "apm,xgene2-xgenet", .data = (void *)XGENE_ENET2},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, xgene_enet_of_match);
+
static int xgene_enet_probe(struct platform_device *pdev)
{
struct net_device *ndev;
@@ -1725,7 +1756,7 @@
xgene_enet_setup_ops(pdata);
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
- ndev->features |= NETIF_F_TSO;
+ ndev->features |= NETIF_F_TSO | NETIF_F_RXCSUM;
spin_lock_init(&pdata->mss_lock);
}
ndev->hw_features = ndev->features;
@@ -1819,32 +1850,6 @@
xgene_enet_remove(pdev);
}
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id xgene_enet_acpi_match[] = {
- { "APMC0D05", XGENE_ENET1},
- { "APMC0D30", XGENE_ENET1},
- { "APMC0D31", XGENE_ENET1},
- { "APMC0D3F", XGENE_ENET1},
- { "APMC0D26", XGENE_ENET2},
- { "APMC0D25", XGENE_ENET2},
- { }
-};
-MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match);
-#endif
-
-#ifdef CONFIG_OF
-static const struct of_device_id xgene_enet_of_match[] = {
- {.compatible = "apm,xgene-enet", .data = (void *)XGENE_ENET1},
- {.compatible = "apm,xgene1-sgenet", .data = (void *)XGENE_ENET1},
- {.compatible = "apm,xgene1-xgenet", .data = (void *)XGENE_ENET1},
- {.compatible = "apm,xgene2-sgenet", .data = (void *)XGENE_ENET2},
- {.compatible = "apm,xgene2-xgenet", .data = (void *)XGENE_ENET2},
- {},
-};
-
-MODULE_DEVICE_TABLE(of, xgene_enet_of_match);
-#endif
-
static struct platform_driver xgene_enet_driver = {
.driver = {
.name = "xgene-enet",
diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
index be865b4..aba4853 100644
--- a/drivers/net/ethernet/arc/emac_main.c
+++ b/drivers/net/ethernet/arc/emac_main.c
@@ -210,39 +210,48 @@
continue;
}
- pktlen = info & LEN_MASK;
- stats->rx_packets++;
- stats->rx_bytes += pktlen;
- skb = rx_buff->skb;
- skb_put(skb, pktlen);
- skb->dev = ndev;
- skb->protocol = eth_type_trans(skb, ndev);
-
- dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
- dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
-
- /* Prepare the BD for next cycle */
- rx_buff->skb = netdev_alloc_skb_ip_align(ndev,
- EMAC_BUFFER_SIZE);
- if (unlikely(!rx_buff->skb)) {
+ /* Prepare the BD for next cycle. netif_receive_skb()
+ * only if new skb was allocated and mapped to avoid holes
+ * in the RX fifo.
+ */
+ skb = netdev_alloc_skb_ip_align(ndev, EMAC_BUFFER_SIZE);
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ netdev_err(ndev, "cannot allocate skb\n");
+ /* Return ownership to EMAC */
+ rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
stats->rx_errors++;
- /* Because receive_skb is below, increment rx_dropped */
stats->rx_dropped++;
continue;
}
- /* receive_skb only if new skb was allocated to avoid holes */
- netif_receive_skb(skb);
-
- addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data,
+ addr = dma_map_single(&ndev->dev, (void *)skb->data,
EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, addr)) {
if (net_ratelimit())
- netdev_err(ndev, "cannot dma map\n");
- dev_kfree_skb(rx_buff->skb);
+ netdev_err(ndev, "cannot map dma buffer\n");
+ dev_kfree_skb(skb);
+ /* Return ownership to EMAC */
+ rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
stats->rx_errors++;
+ stats->rx_dropped++;
continue;
}
+
+ /* unmap previosly mapped skb */
+ dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
+ dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
+
+ pktlen = info & LEN_MASK;
+ stats->rx_packets++;
+ stats->rx_bytes += pktlen;
+ skb_put(rx_buff->skb, pktlen);
+ rx_buff->skb->dev = ndev;
+ rx_buff->skb->protocol = eth_type_trans(rx_buff->skb, ndev);
+
+ netif_receive_skb(rx_buff->skb);
+
+ rx_buff->skb = skb;
dma_unmap_addr_set(rx_buff, addr, addr);
dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE);
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index c616387..c770ca3 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -169,8 +169,10 @@
/* Optional regulator for PHY */
priv->regulator = devm_regulator_get_optional(dev, "phy");
if (IS_ERR(priv->regulator)) {
- if (PTR_ERR(priv->regulator) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(priv->regulator) == -EPROBE_DEFER) {
+ err = -EPROBE_DEFER;
+ goto out_clk_disable;
+ }
dev_err(dev, "no regulator found\n");
priv->regulator = NULL;
}
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 744ed6d..91fbba5 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -707,37 +707,33 @@
struct bcm_sysport_tx_ring *ring)
{
struct net_device *ndev = priv->netdev;
- unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
unsigned int pkts_compl = 0, bytes_compl = 0;
+ unsigned int txbds_processed = 0;
struct bcm_sysport_cb *cb;
+ unsigned int txbds_ready;
+ unsigned int c_index;
u32 hw_ind;
/* Compute how many descriptors have been processed since last call */
hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index));
c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK;
- ring->p_index = (hw_ind & RING_PROD_INDEX_MASK);
-
- last_c_index = ring->c_index;
- num_tx_cbs = ring->size;
-
- c_index &= (num_tx_cbs - 1);
-
- if (c_index >= last_c_index)
- last_tx_cn = c_index - last_c_index;
- else
- last_tx_cn = num_tx_cbs - last_c_index + c_index;
+ txbds_ready = (c_index - ring->c_index) & RING_CONS_INDEX_MASK;
netif_dbg(priv, tx_done, ndev,
- "ring=%d c_index=%d last_tx_cn=%d last_c_index=%d\n",
- ring->index, c_index, last_tx_cn, last_c_index);
+ "ring=%d old_c_index=%u c_index=%u txbds_ready=%u\n",
+ ring->index, ring->c_index, c_index, txbds_ready);
- while (last_tx_cn-- > 0) {
- cb = ring->cbs + last_c_index;
+ while (txbds_processed < txbds_ready) {
+ cb = &ring->cbs[ring->clean_index];
bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl);
ring->desc_count++;
- last_c_index++;
- last_c_index &= (num_tx_cbs - 1);
+ txbds_processed++;
+
+ if (likely(ring->clean_index < ring->size - 1))
+ ring->clean_index++;
+ else
+ ring->clean_index = 0;
}
ring->c_index = c_index;
@@ -1207,6 +1203,7 @@
netif_tx_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
ring->index = index;
ring->size = size;
+ ring->clean_index = 0;
ring->alloc_size = ring->size;
ring->desc_cpu = p;
ring->desc_count = ring->size;
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
index 1c82e3d..07b0aaa 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.h
+++ b/drivers/net/ethernet/broadcom/bcmsysport.h
@@ -638,7 +638,7 @@
unsigned int desc_count; /* Number of descriptors */
unsigned int curr_desc; /* Current descriptor */
unsigned int c_index; /* Last consumer index */
- unsigned int p_index; /* Current producer index */
+ unsigned int clean_index; /* Current clean index */
struct bcm_sysport_cb *cbs; /* Transmit control blocks */
struct dma_desc *desc_cpu; /* CPU view of the descriptor */
struct bcm_sysport_priv *priv; /* private context backpointer */
diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c
index c16ec3a..1ee11b6 100644
--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
+++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
@@ -11,6 +11,7 @@
#include <linux/bcma/bcma.h>
#include <linux/brcmphy.h>
#include <linux/etherdevice.h>
+#include <linux/of_net.h>
#include "bgmac.h"
static inline bool bgmac_is_bcm4707_family(struct bcma_device *core)
@@ -96,7 +97,7 @@
struct ssb_sprom *sprom = &core->bus->sprom;
struct mii_bus *mii_bus;
struct bgmac *bgmac;
- u8 *mac;
+ const u8 *mac = NULL;
int err;
bgmac = kzalloc(sizeof(*bgmac), GFP_KERNEL);
@@ -110,21 +111,27 @@
bcma_set_drvdata(core, bgmac);
- switch (core->core_unit) {
- case 0:
- mac = sprom->et0mac;
- break;
- case 1:
- mac = sprom->et1mac;
- break;
- case 2:
- mac = sprom->et2mac;
- break;
- default:
- dev_err(bgmac->dev, "Unsupported core_unit %d\n",
- core->core_unit);
- err = -ENOTSUPP;
- goto err;
+ if (bgmac->dev->of_node)
+ mac = of_get_mac_address(bgmac->dev->of_node);
+
+ /* If no MAC address assigned via device tree, check SPROM */
+ if (!mac) {
+ switch (core->core_unit) {
+ case 0:
+ mac = sprom->et0mac;
+ break;
+ case 1:
+ mac = sprom->et1mac;
+ break;
+ case 2:
+ mac = sprom->et2mac;
+ break;
+ default:
+ dev_err(bgmac->dev, "Unsupported core_unit %d\n",
+ core->core_unit);
+ err = -ENOTSUPP;
+ goto err;
+ }
}
ether_addr_copy(bgmac->mac_addr, mac);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 0a5ee1d..31287ce 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -2026,6 +2026,7 @@
ETH_OVREHEAD +
mtu +
BNX2X_FW_RX_ALIGN_END;
+ fp->rx_buf_size = SKB_DATA_ALIGN(fp->rx_buf_size);
/* Note : rx_buf_size doesn't take into account NET_SKB_PAD */
if (fp->rx_buf_size + NET_SKB_PAD <= PAGE_SIZE)
fp->rx_frag_size = fp->rx_buf_size + NET_SKB_PAD;
@@ -3034,7 +3035,7 @@
del_timer_sync(&bp->timer);
- if (IS_PF(bp)) {
+ if (IS_PF(bp) && !BP_NOMCP(bp)) {
/* Set ALWAYS_ALIVE bit in shmem */
bp->fw_drv_pulse_wr_seq |= DRV_PULSE_ALWAYS_ALIVE;
bnx2x_drv_pulse(bp);
@@ -3120,7 +3121,7 @@
bp->cnic_loaded = false;
/* Clear driver version indication in shmem */
- if (IS_PF(bp))
+ if (IS_PF(bp) && !BP_NOMCP(bp))
bnx2x_update_mng_version(bp);
/* Check if there are pending parity attentions. If there are - set
@@ -3886,15 +3887,26 @@
/* when transmitting in a vf, start bd must hold the ethertype
* for fw to enforce it
*/
+ u16 vlan_tci = 0;
#ifndef BNX2X_STOP_ON_ERROR
- if (IS_VF(bp))
+ if (IS_VF(bp)) {
#endif
- tx_start_bd->vlan_or_ethertype =
- cpu_to_le16(ntohs(eth->h_proto));
+ /* Still need to consider inband vlan for enforced */
+ if (__vlan_get_tag(skb, &vlan_tci)) {
+ tx_start_bd->vlan_or_ethertype =
+ cpu_to_le16(ntohs(eth->h_proto));
+ } else {
+ tx_start_bd->bd_flags.as_bitfield |=
+ (X_ETH_INBAND_VLAN <<
+ ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT);
+ tx_start_bd->vlan_or_ethertype =
+ cpu_to_le16(vlan_tci);
+ }
#ifndef BNX2X_STOP_ON_ERROR
- else
+ } else {
/* used by FW for packet accounting */
tx_start_bd->vlan_or_ethertype = cpu_to_le16(pkt_prod);
+ }
#endif
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 5d958b5..554c408 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -9578,6 +9578,15 @@
do {
bp->common.shmem_base = REG_RD(bp, MISC_REG_SHARED_MEM_ADDR);
+
+ /* If we read all 0xFFs, means we are in PCI error state and
+ * should bail out to avoid crashes on adapter's FW reads.
+ */
+ if (bp->common.shmem_base == 0xFFFFFFFF) {
+ bp->flags |= NO_MCP_FLAG;
+ return -ENODEV;
+ }
+
if (bp->common.shmem_base) {
val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]);
if (val & SHR_MEM_VALIDITY_MB)
@@ -14312,7 +14321,10 @@
BNX2X_ERR("IO slot reset --> driver unload\n");
/* MCP should have been reset; Need to wait for validity */
- bnx2x_init_shmem(bp);
+ if (bnx2x_init_shmem(bp)) {
+ rtnl_unlock();
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) {
u32 v;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index bbb3641..3aa993b 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -1498,12 +1498,16 @@
if (BNXT_VF(bp))
goto async_event_process_exit;
- if (data1 & 0x20000) {
+
+ /* print unsupported speed warning in forced speed mode only */
+ if (!(link_info->autoneg & BNXT_AUTONEG_SPEED) &&
+ (data1 & 0x20000)) {
u16 fw_speed = link_info->force_link_speed;
u32 speed = bnxt_fw_to_ethtool_speed(fw_speed);
- netdev_warn(bp->dev, "Link speed %d no longer supported\n",
- speed);
+ if (speed != SPEED_UNKNOWN)
+ netdev_warn(bp->dev, "Link speed %d no longer supported\n",
+ speed);
}
set_bit(BNXT_LINK_SPEED_CHNG_SP_EVENT, &bp->sp_event);
/* fall thru */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
index 60e2af8..393cce3 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
@@ -68,7 +68,7 @@
netdev_err(bp->dev, "vf ndo called though sriov is disabled\n");
return -EINVAL;
}
- if (vf_id >= bp->pf.max_vfs) {
+ if (vf_id >= bp->pf.active_vfs) {
netdev_err(bp->dev, "Invalid VF id %d\n", vf_id);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index bb22d32..4ffbe85 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -8720,14 +8720,15 @@
tg3_mem_rx_release(tp);
tg3_mem_tx_release(tp);
- /* Protect tg3_get_stats64() from reading freed tp->hw_stats. */
- tg3_full_lock(tp, 0);
+ /* tp->hw_stats can be referenced safely:
+ * 1. under rtnl_lock
+ * 2. or under tp->lock if TG3_FLAG_INIT_COMPLETE is set.
+ */
if (tp->hw_stats) {
dma_free_coherent(&tp->pdev->dev, sizeof(struct tg3_hw_stats),
tp->hw_stats, tp->stats_mapping);
tp->hw_stats = NULL;
}
- tg3_full_unlock(tp);
}
/*
@@ -10049,6 +10050,16 @@
tw32(GRC_MODE, tp->grc_mode | val);
+ /* On one of the AMD platform, MRRS is restricted to 4000 because of
+ * south bridge limitation. As a workaround, Driver is setting MRRS
+ * to 2048 instead of default 4096.
+ */
+ if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL &&
+ tp->pdev->subsystem_device == TG3PCI_SUBDEVICE_ID_DELL_5762) {
+ val = tr32(TG3PCI_DEV_STATUS_CTRL) & ~MAX_READ_REQ_MASK;
+ tw32(TG3PCI_DEV_STATUS_CTRL, val | MAX_READ_REQ_SIZE_2048);
+ }
+
/* Setup the timer prescalar register. Clock is always 66Mhz. */
val = tr32(GRC_MISC_CFG);
val &= ~0xff;
@@ -14151,7 +14162,7 @@
struct tg3 *tp = netdev_priv(dev);
spin_lock_bh(&tp->lock);
- if (!tp->hw_stats) {
+ if (!tp->hw_stats || !tg3_flag(tp, INIT_COMPLETE)) {
*stats = tp->net_stats_prev;
spin_unlock_bh(&tp->lock);
return stats;
@@ -14228,7 +14239,8 @@
*/
if (tg3_asic_rev(tp) == ASIC_REV_57766 ||
tg3_asic_rev(tp) == ASIC_REV_5717 ||
- tg3_asic_rev(tp) == ASIC_REV_5719)
+ tg3_asic_rev(tp) == ASIC_REV_5719 ||
+ tg3_asic_rev(tp) == ASIC_REV_5720)
reset_phy = true;
err = tg3_restart_hw(tp, reset_phy);
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 3b5e98e..6e51b79 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -95,6 +95,7 @@
#define TG3PCI_SUBDEVICE_ID_DELL_JAGUAR 0x0106
#define TG3PCI_SUBDEVICE_ID_DELL_MERLOT 0x0109
#define TG3PCI_SUBDEVICE_ID_DELL_SLIM_MERLOT 0x010a
+#define TG3PCI_SUBDEVICE_ID_DELL_5762 0x07f0
#define TG3PCI_SUBVENDOR_ID_COMPAQ PCI_VENDOR_ID_COMPAQ
#define TG3PCI_SUBDEVICE_ID_COMPAQ_BANSHEE 0x007c
#define TG3PCI_SUBDEVICE_ID_COMPAQ_BANSHEE_2 0x009a
@@ -280,6 +281,9 @@
#define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */
#define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */
/* 0xa8 --> 0xb8 unused */
+#define TG3PCI_DEV_STATUS_CTRL 0x000000b4
+#define MAX_READ_REQ_SIZE_2048 0x00004000
+#define MAX_READ_REQ_MASK 0x00007000
#define TG3PCI_DUAL_MAC_CTRL 0x000000b8
#define DUAL_MAC_CTRL_CH_MASK 0x00000003
#define DUAL_MAC_CTRL_ID 0x00000004
diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c
index 0f68118..a36e386 100644
--- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c
+++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c
@@ -2845,7 +2845,7 @@
static void
bfa_ioc_get_adapter_manufacturer(struct bfa_ioc *ioc, char *manufacturer)
{
- memcpy(manufacturer, BFA_MFG_NAME, BFA_ADAPTER_MFG_NAME_LEN);
+ strncpy(manufacturer, BFA_MFG_NAME, BFA_ADAPTER_MFG_NAME_LEN);
}
static void
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 8a37012..c75d4ea9 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -1576,6 +1576,11 @@
nic->pdev = pdev;
nic->pnicvf = nic;
nic->max_queues = qcount;
+ /* If no of CPUs are too low, there won't be any queues left
+ * for XDP_TX, hence double it.
+ */
+ if (!nic->t88)
+ nic->max_queues *= 2;
/* MAP VF's configuration registers */
nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 0c2a32a..3ec32d7 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -2742,6 +2742,16 @@
return -EOPNOTSUPP;
}
+static netdev_features_t cxgb_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ /* Disable GRO, if RX_CSUM is disabled */
+ if (!(features & NETIF_F_RXCSUM))
+ features &= ~NETIF_F_GRO;
+
+ return features;
+}
+
static const struct net_device_ops cxgb4_netdev_ops = {
.ndo_open = cxgb_open,
.ndo_stop = cxgb_close,
@@ -2766,6 +2776,7 @@
#endif
.ndo_set_tx_maxrate = cxgb_set_tx_maxrate,
.ndo_setup_tc = cxgb_setup_tc,
+ .ndo_fix_features = cxgb_fix_features,
};
#ifdef CONFIG_PCI_IOV
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 9e073fb..ebeeb35 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -2596,7 +2596,6 @@
}
#define EEPROM_STAT_ADDR 0x7bfc
-#define VPD_SIZE 0x800
#define VPD_BASE 0x400
#define VPD_BASE_OLD 0
#define VPD_LEN 1024
@@ -2634,15 +2633,6 @@
if (!vpd)
return -ENOMEM;
- /* We have two VPD data structures stored in the adapter VPD area.
- * By default, Linux calculates the size of the VPD area by traversing
- * the first VPD area at offset 0x0, so we need to tell the OS what
- * our real VPD size is.
- */
- ret = pci_set_vpd_size(adapter->pdev, VPD_SIZE);
- if (ret < 0)
- goto out;
-
/* Card information normally starts at VPD_BASE but early cards had
* it at 0.
*/
@@ -6195,13 +6185,18 @@
if (!t4_fw_matches_chip(adap, fw_hdr))
return -EINVAL;
+ /* Disable FW_OK flag so that mbox commands with FW_OK flag set
+ * wont be sent when we are flashing FW.
+ */
+ adap->flags &= ~FW_OK;
+
ret = t4_fw_halt(adap, mbox, force);
if (ret < 0 && !force)
- return ret;
+ goto out;
ret = t4_load_fw(adap, fw_data, size);
if (ret < 0)
- return ret;
+ goto out;
/*
* Older versions of the firmware don't understand the new
@@ -6212,7 +6207,17 @@
* its header flags to see if it advertises the capability.
*/
reset = ((be32_to_cpu(fw_hdr->flags) & FW_HDR_FLAGS_RESET_HALT) == 0);
- return t4_fw_restart(adap, mbox, reset);
+ ret = t4_fw_restart(adap, mbox, reset);
+
+ /* Grab potentially new Firmware Device Log parameters so we can see
+ * how healthy the new Firmware is. It's okay to contact the new
+ * Firmware for these parameters even though, as far as it's
+ * concerned, we've never said "HELLO" to it ...
+ */
+ (void)t4_init_devlog_params(adap);
+out:
+ adap->flags |= FW_OK;
+ return ret;
}
/**
@@ -8083,7 +8088,16 @@
ret = t4_cim_read(adap, UP_UP_DBG_LA_DATA_A, 1, &la_buf[i]);
if (ret)
break;
- idx = (idx + 1) & UPDBGLARDPTR_M;
+
+ /* Bits 0-3 of UpDbgLaRdPtr can be between 0000 to 1001 to
+ * identify the 32-bit portion of the full 312-bit data
+ */
+ if (is_t6(adap->params.chip) && (idx & 0xf) >= 9)
+ idx = (idx & 0xff0) + 0x10;
+ else
+ idx++;
+ /* address can't exceed 0xfff */
+ idx &= UPDBGLARDPTR_M;
}
restart:
if (cfg & UPDBGLAEN_F) {
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index f3ed9ce..9d64e8e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -2616,8 +2616,8 @@
int t4vf_sge_init(struct adapter *adapter)
{
struct sge_params *sge_params = &adapter->params.sge;
- u32 fl0 = sge_params->sge_fl_buffer_size[0];
- u32 fl1 = sge_params->sge_fl_buffer_size[1];
+ u32 fl_small_pg = sge_params->sge_fl_buffer_size[0];
+ u32 fl_large_pg = sge_params->sge_fl_buffer_size[1];
struct sge *s = &adapter->sge;
/*
@@ -2625,9 +2625,20 @@
* the Physical Function Driver. Ideally we should be able to deal
* with _any_ configuration. Practice is different ...
*/
- if (fl0 != PAGE_SIZE || (fl1 != 0 && fl1 <= fl0)) {
+
+ /* We only bother using the Large Page logic if the Large Page Buffer
+ * is larger than our Page Size Buffer.
+ */
+ if (fl_large_pg <= fl_small_pg)
+ fl_large_pg = 0;
+
+ /* The Page Size Buffer must be exactly equal to our Page Size and the
+ * Large Page Size Buffer should be 0 (per above) or a power of 2.
+ */
+ if (fl_small_pg != PAGE_SIZE ||
+ (fl_large_pg & (fl_large_pg - 1)) != 0) {
dev_err(adapter->pdev_dev, "bad SGE FL buffer sizes [%d, %d]\n",
- fl0, fl1);
+ fl_small_pg, fl_large_pg);
return -EINVAL;
}
if ((sge_params->sge_control & RXPKTCPLMODE_F) !=
@@ -2639,8 +2650,8 @@
/*
* Now translate the adapter parameters into our internal forms.
*/
- if (fl1)
- s->fl_pg_order = ilog2(fl1) - PAGE_SHIFT;
+ if (fl_large_pg)
+ s->fl_pg_order = ilog2(fl_large_pg) - PAGE_SHIFT;
s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_F)
? 128 : 64);
s->pktshift = PKTSHIFT_G(sge_params->sge_control);
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 2625872..0437149 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -28,6 +28,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <net/ip.h>
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 9170918..fe00f71 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2371,6 +2371,10 @@
static inline void fec_enet_update_ethtool_stats(struct net_device *dev)
{
}
+
+static inline void fec_enet_clear_ethtool_stats(struct net_device *dev)
+{
+}
#endif /* !defined(CONFIG_M5272) */
static int fec_enet_nway_reset(struct net_device *dev)
@@ -3209,7 +3213,7 @@
}
#ifdef CONFIG_OF
-static void fec_reset_phy(struct platform_device *pdev)
+static int fec_reset_phy(struct platform_device *pdev)
{
int err, phy_reset;
bool active_high = false;
@@ -3217,7 +3221,7 @@
struct device_node *np = pdev->dev.of_node;
if (!np)
- return;
+ return 0;
of_property_read_u32(np, "phy-reset-duration", &msec);
/* A sane reset duration should not be longer than 1s */
@@ -3225,8 +3229,10 @@
msec = 1;
phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
- if (!gpio_is_valid(phy_reset))
- return;
+ if (phy_reset == -EPROBE_DEFER)
+ return phy_reset;
+ else if (!gpio_is_valid(phy_reset))
+ return 0;
active_high = of_property_read_bool(np, "phy-reset-active-high");
@@ -3235,7 +3241,7 @@
"phy-reset");
if (err) {
dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
- return;
+ return err;
}
if (msec > 20)
@@ -3244,14 +3250,17 @@
usleep_range(msec * 1000, msec * 1000 + 1000);
gpio_set_value_cansleep(phy_reset, !active_high);
+
+ return 0;
}
#else /* CONFIG_OF */
-static void fec_reset_phy(struct platform_device *pdev)
+static int fec_reset_phy(struct platform_device *pdev)
{
/*
* In case of platform probe, the reset has been done
* by machine code.
*/
+ return 0;
}
#endif /* CONFIG_OF */
@@ -3422,6 +3431,7 @@
if (ret) {
dev_err(&pdev->dev,
"Failed to enable phy regulator: %d\n", ret);
+ clk_disable_unprepare(fep->clk_ipg);
goto failed_regulator;
}
} else {
@@ -3434,7 +3444,9 @@
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- fec_reset_phy(pdev);
+ ret = fec_reset_phy(pdev);
+ if (ret)
+ goto failed_reset;
if (fep->bufdesc_ex)
fec_ptp_init(pdev);
@@ -3495,8 +3507,10 @@
fec_ptp_stop(pdev);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
+failed_reset:
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
failed_regulator:
- clk_disable_unprepare(fep->clk_ipg);
failed_clk_ipg:
fec_enet_clk_enable(ndev, false);
failed_clk:
@@ -3523,6 +3537,8 @@
fec_enet_mii_remove(fep);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(fep->phy_node);
diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
index 446c7b3..a10de1e 100644
--- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
@@ -381,7 +381,7 @@
{
const struct of_device_id *id =
of_match_device(fsl_pq_mdio_match, &pdev->dev);
- const struct fsl_pq_mdio_data *data = id->data;
+ const struct fsl_pq_mdio_data *data;
struct device_node *np = pdev->dev.of_node;
struct resource res;
struct device_node *tbi;
@@ -389,6 +389,13 @@
struct mii_bus *new_bus;
int err;
+ if (!id) {
+ dev_err(&pdev->dev, "Failed to match device\n");
+ return -ENODEV;
+ }
+
+ data = id->data;
+
dev_dbg(&pdev->dev, "found %s compatible node\n", id->compatible);
new_bus = mdiobus_alloc_size(sizeof(*priv));
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index 5779881..ec4b699 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -314,11 +314,10 @@
now = tmr_cnt_read(etsects);
now += delta;
tmr_cnt_write(etsects, now);
+ set_fipers(etsects);
spin_unlock_irqrestore(&etsects->lock, flags);
- set_fipers(etsects);
-
return 0;
}
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index f76d332..ef9bc26 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -2594,11 +2594,10 @@
} else if (ugeth->ug_info->uf_info.bd_mem_part ==
MEM_PART_MURAM) {
out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base,
- (u32) immrbar_virt_to_phys(ugeth->
- p_tx_bd_ring[i]));
+ (u32)qe_muram_dma(ugeth->p_tx_bd_ring[i]));
out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].
last_bd_completed_address,
- (u32) immrbar_virt_to_phys(endOfRing));
+ (u32)qe_muram_dma(endOfRing));
}
}
@@ -2844,8 +2843,7 @@
} else if (ugeth->ug_info->uf_info.bd_mem_part ==
MEM_PART_MURAM) {
out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
- (u32) immrbar_virt_to_phys(ugeth->
- p_rx_bd_ring[i]));
+ (u32)qe_muram_dma(ugeth->p_rx_bd_ring[i]));
}
/* rest of fields handled by QE */
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
index 2d0cb60..b7c8433 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
@@ -773,8 +773,9 @@
memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
/* update the current hash->queue mappings from the shadow RSS table */
- memcpy(indir, ppe_cb->rss_indir_table,
- HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
+ if (indir)
+ memcpy(indir, ppe_cb->rss_indir_table,
+ HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
return 0;
}
@@ -785,15 +786,19 @@
struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
/* set the RSS Hash Key if specififed by the user */
- if (key)
- hns_ppe_set_rss_key(ppe_cb, (u32 *)key);
+ if (key) {
+ memcpy(ppe_cb->rss_key, key, HNS_PPEV2_RSS_KEY_SIZE);
+ hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key);
+ }
- /* update the shadow RSS table with user specified qids */
- memcpy(ppe_cb->rss_indir_table, indir,
- HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
+ if (indir) {
+ /* update the shadow RSS table with user specified qids */
+ memcpy(ppe_cb->rss_indir_table, indir,
+ HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir));
- /* now update the hardware */
- hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+ /* now update the hardware */
+ hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+ }
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
index 1e1eb92..02a03bc 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c
@@ -86,12 +86,11 @@
dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0);
}
-/**
-*hns_gmac_get_en - get port enable
-*@mac_drv:mac device
-*@rx:rx enable
-*@tx:tx enable
-*/
+/* hns_gmac_get_en - get port enable
+ * @mac_drv:mac device
+ * @rx:rx enable
+ * @tx:tx enable
+ */
static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx)
{
struct mac_driver *drv = (struct mac_driver *)mac_drv;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
index c494fc5..62a1299 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
@@ -70,7 +70,7 @@
};
#define DSAF_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset))))
-#define HNS_DSAF_IS_DEBUG(dev) (dev->dsaf_mode == DSAF_MODE_DISABLE_SP)
+#define HNS_DSAF_IS_DEBUG(dev) ((dev)->dsaf_mode == DSAF_MODE_DISABLE_SP)
enum hal_dsaf_mode {
HRD_DSAF_NO_DSAF_MODE = 0x0,
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
index f0ed80d6..f3be9ac 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
@@ -430,7 +430,6 @@
static int hns_rcb_get_port_in_comm(
struct rcb_common_cb *rcb_common, int ring_idx)
{
-
return ring_idx / (rcb_common->max_q_per_vf * rcb_common->max_vfn);
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
index 8f4f0e8..d1c868c 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c
@@ -776,7 +776,7 @@
*/
static int hns_xgmac_get_sset_count(int stringset)
{
- if (stringset == ETH_SS_STATS)
+ if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS)
return ARRAY_SIZE(g_xgmac_stats_string);
return 0;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index c06845b..111e1aa 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -299,9 +299,9 @@
mtu);
}
-int hns_nic_net_xmit_hw(struct net_device *ndev,
- struct sk_buff *skb,
- struct hns_nic_ring_data *ring_data)
+netdev_tx_t hns_nic_net_xmit_hw(struct net_device *ndev,
+ struct sk_buff *skb,
+ struct hns_nic_ring_data *ring_data)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
struct hnae_ring *ring = ring_data->ring;
@@ -360,6 +360,10 @@
dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping);
netdev_tx_sent_queue(dev_queue, skb->len);
+ netif_trans_update(ndev);
+ ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_packets++;
+
wmb(); /* commit all data before submit */
assert(skb->queue_mapping < priv->ae_handle->q_num);
hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num);
@@ -511,7 +515,8 @@
int last_offset;
bool twobufs;
- twobufs = ((PAGE_SIZE < 8192) && hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);
+ twobufs = ((PAGE_SIZE < 8192) &&
+ hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);
desc = &ring->desc[ring->next_to_clean];
size = le16_to_cpu(desc->rx.size);
@@ -1407,17 +1412,11 @@
struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
- int ret;
assert(skb->queue_mapping < ndev->ae_handle->q_num);
- ret = hns_nic_net_xmit_hw(ndev, skb,
- &tx_ring_data(priv, skb->queue_mapping));
- if (ret == NETDEV_TX_OK) {
- netif_trans_update(ndev);
- ndev->stats.tx_bytes += skb->len;
- ndev->stats.tx_packets++;
- }
- return (netdev_tx_t)ret;
+
+ return hns_nic_net_xmit_hw(ndev, skb,
+ &tx_ring_data(priv, skb->queue_mapping));
}
static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu)
@@ -1700,7 +1699,7 @@
static void hns_nic_service_event_complete(struct hns_nic_priv *priv)
{
WARN_ON(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
-
+ /* make sure to commit the things */
smp_mb__before_atomic();
clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state);
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
index 5b412de..7bc6a6e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
@@ -91,8 +91,8 @@
void hns_nic_net_reset(struct net_device *ndev);
void hns_nic_net_reinit(struct net_device *netdev);
int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h);
-int hns_nic_net_xmit_hw(struct net_device *ndev,
- struct sk_buff *skb,
- struct hns_nic_ring_data *ring_data);
+netdev_tx_t hns_nic_net_xmit_hw(struct net_device *ndev,
+ struct sk_buff *skb,
+ struct hns_nic_ring_data *ring_data);
#endif /**__HNS_ENET_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index 87d5c94..6be0cae 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -1017,8 +1017,10 @@
cnt--;
return cnt;
- } else {
+ } else if (stringset == ETH_SS_STATS) {
return (HNS_NET_STATS_CNT + ops->get_sset_count(h, stringset));
+ } else {
+ return -EOPNOTSUPP;
}
}
@@ -1252,12 +1254,10 @@
ops = priv->ae_handle->dev->ops;
- /* currently hfunc can only be Toeplitz hash */
- if (key ||
- (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) {
+ netdev_err(netdev, "Invalid hfunc!\n");
return -EOPNOTSUPP;
- if (!indir)
- return 0;
+ }
return ops->set_rss(priv->ae_handle, indir, key, hfunc);
}
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 8f13919..5977b69 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -342,6 +342,7 @@
{
struct emac_regs __iomem *p = dev->emacp;
int n = 20;
+ bool __maybe_unused try_internal_clock = false;
DBG(dev, "reset" NL);
@@ -354,6 +355,7 @@
}
#ifdef CONFIG_PPC_DCR_NATIVE
+do_retry:
/*
* PPC460EX/GT Embedded Processor Advanced User's Manual
* section 28.10.1 Mode Register 0 (EMACx_MR0) states:
@@ -361,10 +363,19 @@
* of the EMAC. If none is present, select the internal clock
* (SDR0_ETH_CFG[EMACx_PHY_CLK] = 1).
* After a soft reset, select the external clock.
+ *
+ * The AR8035-A PHY Meraki MR24 does not provide a TX Clk if the
+ * ethernet cable is not attached. This causes the reset to timeout
+ * and the PHY detection code in emac_init_phy() is unable to
+ * communicate and detect the AR8035-A PHY. As a result, the emac
+ * driver bails out early and the user has no ethernet.
+ * In order to stay compatible with existing configurations, the
+ * driver will temporarily switch to the internal clock, after
+ * the first reset fails.
*/
if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
- if (dev->phy_address == 0xffffffff &&
- dev->phy_map == 0xffffffff) {
+ if (try_internal_clock || (dev->phy_address == 0xffffffff &&
+ dev->phy_map == 0xffffffff)) {
/* No PHY: select internal loop clock before reset */
dcri_clrset(SDR0, SDR0_ETH_CFG,
0, SDR0_ETH_CFG_ECS << dev->cell_index);
@@ -382,8 +393,15 @@
#ifdef CONFIG_PPC_DCR_NATIVE
if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
- if (dev->phy_address == 0xffffffff &&
- dev->phy_map == 0xffffffff) {
+ if (!n && !try_internal_clock) {
+ /* first attempt has timed out. */
+ n = 20;
+ try_internal_clock = true;
+ goto do_retry;
+ }
+
+ if (try_internal_clock || (dev->phy_address == 0xffffffff &&
+ dev->phy_map == 0xffffffff)) {
/* No PHY: restore external clock source after reset */
dcri_clrset(SDR0, SDR0_ETH_CFG,
SDR0_ETH_CFG_ECS << dev->cell_index, 0);
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 7c6c146..49094c9 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -511,6 +511,23 @@
return -ENOMEM;
}
+static void disable_sub_crqs(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->tx_scrq) {
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ if (adapter->tx_scrq[i])
+ disable_irq(adapter->tx_scrq[i]->irq);
+ }
+
+ if (adapter->rx_scrq) {
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ if (adapter->rx_scrq[i])
+ disable_irq(adapter->rx_scrq[i]->irq);
+ }
+}
+
static int ibmvnic_close(struct net_device *netdev)
{
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
@@ -519,6 +536,7 @@
int i;
adapter->closing = true;
+ disable_sub_crqs(adapter);
for (i = 0; i < adapter->req_rx_queues; i++)
napi_disable(&adapter->napi[i]);
diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h
index d7bdea7..8fd2458 100644
--- a/drivers/net/ethernet/intel/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -331,7 +331,8 @@
enum e1000_state_t {
__E1000_TESTING,
__E1000_RESETTING,
- __E1000_DOWN
+ __E1000_DOWN,
+ __E1000_DISABLED
};
#undef pr_fmt
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index f42129d..dd112aa 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -940,7 +940,7 @@
static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
- struct e1000_adapter *adapter;
+ struct e1000_adapter *adapter = NULL;
struct e1000_hw *hw;
static int cards_found;
@@ -950,6 +950,7 @@
u16 tmp = 0;
u16 eeprom_apme_mask = E1000_EEPROM_APME;
int bars, need_ioport;
+ bool disable_dev = false;
/* do not allocate ioport bars when not needed */
need_ioport = e1000_is_need_ioport(pdev);
@@ -1250,11 +1251,13 @@
iounmap(hw->ce4100_gbe_mdio_base_virt);
iounmap(hw->hw_addr);
err_ioremap:
+ disable_dev = !test_and_set_bit(__E1000_DISABLED, &adapter->flags);
free_netdev(netdev);
err_alloc_etherdev:
pci_release_selected_regions(pdev, bars);
err_pci_reg:
- pci_disable_device(pdev);
+ if (!adapter || disable_dev)
+ pci_disable_device(pdev);
return err;
}
@@ -1272,6 +1275,7 @@
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ bool disable_dev;
e1000_down_and_stop(adapter);
e1000_release_manageability(adapter);
@@ -1290,9 +1294,11 @@
iounmap(hw->flash_address);
pci_release_selected_regions(pdev, adapter->bars);
+ disable_dev = !test_and_set_bit(__E1000_DISABLED, &adapter->flags);
free_netdev(netdev);
- pci_disable_device(pdev);
+ if (disable_dev)
+ pci_disable_device(pdev);
}
/**
@@ -5166,7 +5172,8 @@
if (netif_running(netdev))
e1000_free_irq(adapter);
- pci_disable_device(pdev);
+ if (!test_and_set_bit(__E1000_DISABLED, &adapter->flags))
+ pci_disable_device(pdev);
return 0;
}
@@ -5210,6 +5217,10 @@
pr_err("Cannot enable PCI device from suspend\n");
return err;
}
+
+ /* flush memory to make sure state is correct */
+ smp_mb__before_atomic();
+ clear_bit(__E1000_DISABLED, &adapter->flags);
pci_set_master(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
@@ -5284,7 +5295,9 @@
if (netif_running(netdev))
e1000_down(adapter);
- pci_disable_device(pdev);
+
+ if (!test_and_set_bit(__E1000_DISABLED, &adapter->flags))
+ pci_disable_device(pdev);
/* Request a slot slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -5312,6 +5325,10 @@
pr_err("Cannot re-enable PCI device after reset.\n");
return PCI_ERS_RESULT_DISCONNECT;
}
+
+ /* flush memory to make sure state is correct */
+ smp_mb__before_atomic();
+ clear_bit(__E1000_DISABLED, &adapter->flags);
pci_set_master(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 0feddf3..825ec8f 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -1182,6 +1182,7 @@
struct e1000_hw *hw = &adapter->hw;
if (er32(TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID) {
+ struct sk_buff *skb = adapter->tx_hwtstamp_skb;
struct skb_shared_hwtstamps shhwtstamps;
u64 txstmp;
@@ -1190,9 +1191,14 @@
e1000e_systim_to_hwtstamp(adapter, &shhwtstamps, txstmp);
- skb_tstamp_tx(adapter->tx_hwtstamp_skb, &shhwtstamps);
- dev_kfree_skb_any(adapter->tx_hwtstamp_skb);
+ /* Clear the global tx_hwtstamp_skb pointer and force writes
+ * prior to notifying the stack of a Tx timestamp.
+ */
adapter->tx_hwtstamp_skb = NULL;
+ wmb(); /* force write prior to skb_tstamp_tx */
+
+ skb_tstamp_tx(skb, &shhwtstamps);
+ dev_kfree_skb_any(skb);
} else if (time_after(jiffies, adapter->tx_hwtstamp_start
+ adapter->tx_timeout_factor * HZ)) {
dev_kfree_skb_any(adapter->tx_hwtstamp_skb);
@@ -3528,6 +3534,12 @@
switch (hw->mac.type) {
case e1000_pch2lan:
+ /* Stable 96MHz frequency */
+ incperiod = INCPERIOD_96MHz;
+ incvalue = INCVALUE_96MHz;
+ shift = INCVALUE_SHIFT_96MHz;
+ adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHz;
+ break;
case e1000_pch_lpt:
if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) {
/* Stable 96MHz frequency */
@@ -6639,12 +6651,17 @@
static int e1000e_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ int rc;
e1000e_flush_lpic(pdev);
e1000e_pm_freeze(dev);
- return __e1000_shutdown(pdev, false);
+ rc = __e1000_shutdown(pdev, false);
+ if (rc)
+ e1000e_pm_thaw(dev);
+
+ return rc;
}
static int e1000e_pm_resume(struct device *dev)
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index 5241e08..7041d83 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -942,7 +942,7 @@
memset(data, 0, sizeof(*data) * FM10K_TEST_LEN);
- if (FM10K_REMOVED(hw)) {
+ if (FM10K_REMOVED(hw->hw_addr)) {
netif_err(interface, drv, dev,
"Interface removed - test blocked\n");
eth_test->flags |= ETH_TEST_FL_FAILED;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 92bc884..f456946 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -1135,6 +1135,11 @@
struct i40e_hw *hw = &np->vsi->back->hw;
u32 val;
+#define X722_EEPROM_SCOPE_LIMIT 0x5B9FFF
+ if (hw->mac.type == I40E_MAC_X722) {
+ val = X722_EEPROM_SCOPE_LIMIT + 1;
+ return val;
+ }
val = (rd32(hw, I40E_GLPCI_LBARCTRL)
& I40E_GLPCI_LBARCTRL_FL_SIZE_MASK)
>> I40E_GLPCI_LBARCTRL_FL_SIZE_SHIFT;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index becffd1..57c7456 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -11142,10 +11142,12 @@
round_jiffies(jiffies + pf->service_timer_period));
/* add this PF to client device list and launch a client service task */
- err = i40e_lan_add_device(pf);
- if (err)
- dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n",
- err);
+ if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
+ err = i40e_lan_add_device(pf);
+ if (err)
+ dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n",
+ err);
+ }
#ifdef I40E_FCOE
/* create FCoE interface */
@@ -11323,10 +11325,11 @@
i40e_vsi_release(pf->vsi[pf->lan_vsi]);
/* remove attached clients */
- ret_code = i40e_lan_del_device(pf);
- if (ret_code) {
- dev_warn(&pdev->dev, "Failed to delete client device: %d\n",
- ret_code);
+ if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
+ ret_code = i40e_lan_del_device(pf);
+ if (ret_code)
+ dev_warn(&pdev->dev, "Failed to delete client device: %d\n",
+ ret_code);
}
/* shutdown and destroy the HMC */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
index 954efe3..abe290b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
@@ -292,14 +292,14 @@
{
enum i40e_status_code ret_code = 0;
- if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) {
- ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
- if (!ret_code) {
+ ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
+ if (!ret_code) {
+ if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) {
ret_code = i40e_read_nvm_word_aq(hw, offset, data);
- i40e_release_nvm(hw);
+ } else {
+ ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
}
- } else {
- ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
+ i40e_release_nvm(hw);
}
return ret_code;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index c543039..2e12ccf 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -1820,6 +1820,7 @@
*/
if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) {
dev_kfree_skb_any(skb);
+ skb = NULL;
continue;
}
@@ -2670,10 +2671,30 @@
/* Walk through fragments adding latest fragment, testing it, and
* then removing stale fragments from the sum.
*/
- stale = &skb_shinfo(skb)->frags[0];
- for (;;) {
+ for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
+ int stale_size = skb_frag_size(stale);
+
sum += skb_frag_size(frag++);
+ /* The stale fragment may present us with a smaller
+ * descriptor than the actual fragment size. To account
+ * for that we need to remove all the data on the front and
+ * figure out what the remainder would be in the last
+ * descriptor associated with the fragment.
+ */
+ if (stale_size > I40E_MAX_DATA_PER_TXD) {
+ int align_pad = -(stale->page_offset) &
+ (I40E_MAX_READ_REQ_SIZE - 1);
+
+ sum -= align_pad;
+ stale_size -= align_pad;
+
+ do {
+ sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ } while (stale_size > I40E_MAX_DATA_PER_TXD);
+ }
+
/* if sum is negative we failed to make sufficient progress */
if (sum < 0)
return true;
@@ -2681,7 +2702,7 @@
if (!nr_frags--)
break;
- sum -= skb_frag_size(stale++);
+ sum -= stale_size;
}
return false;
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index c03800d..7bfed44 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -1262,6 +1262,7 @@
*/
if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) {
dev_kfree_skb_any(skb);
+ skb = NULL;
continue;
}
@@ -1872,10 +1873,30 @@
/* Walk through fragments adding latest fragment, testing it, and
* then removing stale fragments from the sum.
*/
- stale = &skb_shinfo(skb)->frags[0];
- for (;;) {
+ for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
+ int stale_size = skb_frag_size(stale);
+
sum += skb_frag_size(frag++);
+ /* The stale fragment may present us with a smaller
+ * descriptor than the actual fragment size. To account
+ * for that we need to remove all the data on the front and
+ * figure out what the remainder would be in the last
+ * descriptor associated with the fragment.
+ */
+ if (stale_size > I40E_MAX_DATA_PER_TXD) {
+ int align_pad = -(stale->page_offset) &
+ (I40E_MAX_READ_REQ_SIZE - 1);
+
+ sum -= align_pad;
+ stale_size -= align_pad;
+
+ do {
+ sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
+ } while (stale_size > I40E_MAX_DATA_PER_TXD);
+ }
+
/* if sum is negative we failed to make sufficient progress */
if (sum < 0)
return true;
@@ -1883,7 +1904,7 @@
if (!nr_frags--)
break;
- sum -= skb_frag_size(stale++);
+ sum -= stale_size;
}
return false;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index ddf478d..614f93e 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -154,6 +154,7 @@
adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG;
caps = I40E_VIRTCHNL_VF_OFFLOAD_L2 |
+ I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF |
I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ |
I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG |
I40E_VIRTCHNL_VF_OFFLOAD_VLAN |
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index a7895c4..9eb9b68 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -721,6 +721,7 @@
**/
static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
{
+ struct sk_buff *skb = adapter->ptp_tx_skb;
struct e1000_hw *hw = &adapter->hw;
struct skb_shared_hwtstamps shhwtstamps;
u64 regval;
@@ -748,10 +749,17 @@
shhwtstamps.hwtstamp =
ktime_add_ns(shhwtstamps.hwtstamp, adjust);
- skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
- dev_kfree_skb_any(adapter->ptp_tx_skb);
+ /* Clear the lock early before calling skb_tstamp_tx so that
+ * applications are not woken up before the lock bit is clear. We use
+ * a copy of the skb pointer to ensure other threads can't change it
+ * while we're notifying the stack.
+ */
adapter->ptp_tx_skb = NULL;
clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
+
+ /* Notify the stack and free the skb after we've unlocked */
+ skb_tstamp_tx(skb, &shhwtstamps);
+ dev_kfree_skb_any(skb);
}
/**
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index 508e72c..d665de8 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -80,7 +80,7 @@
#define IXGBEVF_QUEUE_STATS_LEN ( \
(((struct ixgbevf_adapter *)netdev_priv(netdev))->num_tx_queues + \
((struct ixgbevf_adapter *)netdev_priv(netdev))->num_rx_queues) * \
- (sizeof(struct ixgbe_stats) / sizeof(u64)))
+ (sizeof(struct ixgbevf_stats) / sizeof(u64)))
#define IXGBEVF_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbevf_gstrings_stats)
#define IXGBEVF_STATS_LEN (IXGBEVF_GLOBAL_STATS_LEN + IXGBEVF_QUEUE_STATS_LEN)
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 941c8e2..93ab0b3 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -5079,7 +5079,7 @@
INIT_WORK(&hw->restart_work, sky2_restart);
pci_set_drvdata(pdev, hw);
- pdev->d3_delay = 150;
+ pdev->d3_delay = 200;
return 0;
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 4832223..20de37a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1842,11 +1842,12 @@
/* set GE2 TUNE */
regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
- /* GE1, Force 1000M/FD, FC ON */
- mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
-
- /* GE2, Force 1000M/FD, FC ON */
- mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1));
+ /* Set linkdown as the default for each GMAC. Its own MCR would be set
+ * up with the more appropriate value when mtk_phy_link_adjust call is
+ * being invoked.
+ */
+ for (i = 0; i < MTK_MAC_COUNT; i++)
+ mtk_w32(eth, 0, MTK_MAC_MCR(i));
/* Enable RX VLan Offloading */
mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
index b04760a..af2e6ea 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
@@ -156,57 +156,63 @@
static u8 mlx4_en_dcbnl_set_all(struct net_device *netdev)
{
struct mlx4_en_priv *priv = netdev_priv(netdev);
+ struct mlx4_en_port_profile *prof = priv->prof;
struct mlx4_en_dev *mdev = priv->mdev;
+ u8 tx_pause, tx_ppp, rx_pause, rx_ppp;
if (!(priv->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return 1;
if (priv->cee_config.pfc_state) {
int tc;
+ rx_ppp = prof->rx_ppp;
+ tx_ppp = prof->tx_ppp;
- priv->prof->rx_pause = 0;
- priv->prof->tx_pause = 0;
for (tc = 0; tc < CEE_DCBX_MAX_PRIO; tc++) {
u8 tc_mask = 1 << tc;
switch (priv->cee_config.dcb_pfc[tc]) {
case pfc_disabled:
- priv->prof->tx_ppp &= ~tc_mask;
- priv->prof->rx_ppp &= ~tc_mask;
+ tx_ppp &= ~tc_mask;
+ rx_ppp &= ~tc_mask;
break;
case pfc_enabled_full:
- priv->prof->tx_ppp |= tc_mask;
- priv->prof->rx_ppp |= tc_mask;
+ tx_ppp |= tc_mask;
+ rx_ppp |= tc_mask;
break;
case pfc_enabled_tx:
- priv->prof->tx_ppp |= tc_mask;
- priv->prof->rx_ppp &= ~tc_mask;
+ tx_ppp |= tc_mask;
+ rx_ppp &= ~tc_mask;
break;
case pfc_enabled_rx:
- priv->prof->tx_ppp &= ~tc_mask;
- priv->prof->rx_ppp |= tc_mask;
+ tx_ppp &= ~tc_mask;
+ rx_ppp |= tc_mask;
break;
default:
break;
}
}
- en_dbg(DRV, priv, "Set pfc on\n");
+ rx_pause = !!(rx_ppp || tx_ppp) ? 0 : prof->rx_pause;
+ tx_pause = !!(rx_ppp || tx_ppp) ? 0 : prof->tx_pause;
} else {
- priv->prof->rx_pause = 1;
- priv->prof->tx_pause = 1;
- en_dbg(DRV, priv, "Set pfc off\n");
+ rx_ppp = 0;
+ tx_ppp = 0;
+ rx_pause = prof->rx_pause;
+ tx_pause = prof->tx_pause;
}
if (mlx4_SET_PORT_general(mdev->dev, priv->port,
priv->rx_skb_size + ETH_FCS_LEN,
- priv->prof->tx_pause,
- priv->prof->tx_ppp,
- priv->prof->rx_pause,
- priv->prof->rx_ppp)) {
+ tx_pause, tx_ppp, rx_pause, rx_ppp)) {
en_err(priv, "Failed setting pause params\n");
return 1;
}
+ prof->tx_ppp = tx_ppp;
+ prof->rx_ppp = rx_ppp;
+ prof->tx_pause = tx_pause;
+ prof->rx_pause = rx_pause;
+
return 0;
}
@@ -310,6 +316,7 @@
}
switch (ets->tc_tsa[i]) {
+ case IEEE_8021QAZ_TSA_VENDOR:
case IEEE_8021QAZ_TSA_STRICT:
break;
case IEEE_8021QAZ_TSA_ETS:
@@ -347,6 +354,10 @@
/* higher TC means higher priority => lower pg */
for (i = IEEE_8021QAZ_MAX_TCS - 1; i >= 0; i--) {
switch (ets->tc_tsa[i]) {
+ case IEEE_8021QAZ_TSA_VENDOR:
+ pg[i] = MLX4_EN_TC_VENDOR;
+ tc_tx_bw[i] = MLX4_EN_BW_MAX;
+ break;
case IEEE_8021QAZ_TSA_STRICT:
pg[i] = num_strict++;
tc_tx_bw[i] = MLX4_EN_BW_MAX;
@@ -403,6 +414,7 @@
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_port_profile *prof = priv->prof;
struct mlx4_en_dev *mdev = priv->mdev;
+ u32 tx_pause, tx_ppp, rx_pause, rx_ppp;
int err;
en_dbg(DRV, priv, "cap: 0x%x en: 0x%x mbc: 0x%x delay: %d\n",
@@ -411,23 +423,26 @@
pfc->mbc,
pfc->delay);
- prof->rx_pause = !pfc->pfc_en;
- prof->tx_pause = !pfc->pfc_en;
- prof->rx_ppp = pfc->pfc_en;
- prof->tx_ppp = pfc->pfc_en;
+ rx_pause = prof->rx_pause && !pfc->pfc_en;
+ tx_pause = prof->tx_pause && !pfc->pfc_en;
+ rx_ppp = pfc->pfc_en;
+ tx_ppp = pfc->pfc_en;
err = mlx4_SET_PORT_general(mdev->dev, priv->port,
priv->rx_skb_size + ETH_FCS_LEN,
- prof->tx_pause,
- prof->tx_ppp,
- prof->rx_pause,
- prof->rx_ppp);
- if (err)
+ tx_pause, tx_ppp, rx_pause, rx_ppp);
+ if (err) {
en_err(priv, "Failed setting pause params\n");
- else
- mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
- prof->rx_ppp, prof->rx_pause,
- prof->tx_ppp, prof->tx_pause);
+ return err;
+ }
+
+ mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
+ rx_ppp, rx_pause, tx_ppp, tx_pause);
+
+ prof->tx_ppp = tx_ppp;
+ prof->rx_ppp = rx_ppp;
+ prof->rx_pause = rx_pause;
+ prof->tx_pause = tx_pause;
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index bdda17d..9a4c4f8 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -970,6 +970,22 @@
if (!coal->tx_max_coalesced_frames_irq)
return -EINVAL;
+ if (coal->tx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME ||
+ coal->rx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME ||
+ coal->rx_coalesce_usecs_low > MLX4_EN_MAX_COAL_TIME ||
+ coal->rx_coalesce_usecs_high > MLX4_EN_MAX_COAL_TIME) {
+ netdev_info(dev, "%s: maximum coalesce time supported is %d usecs\n",
+ __func__, MLX4_EN_MAX_COAL_TIME);
+ return -ERANGE;
+ }
+
+ if (coal->tx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS ||
+ coal->rx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS) {
+ netdev_info(dev, "%s: maximum coalesced frames supported is %d\n",
+ __func__, MLX4_EN_MAX_COAL_PKTS);
+ return -ERANGE;
+ }
+
priv->rx_frames = (coal->rx_max_coalesced_frames ==
MLX4_EN_AUTO_CONF) ?
MLX4_EN_RX_COAL_TARGET :
@@ -1003,27 +1019,32 @@
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
+ u8 tx_pause, tx_ppp, rx_pause, rx_ppp;
int err;
if (pause->autoneg)
return -EINVAL;
- priv->prof->tx_pause = pause->tx_pause != 0;
- priv->prof->rx_pause = pause->rx_pause != 0;
+ tx_pause = !!(pause->tx_pause);
+ rx_pause = !!(pause->rx_pause);
+ rx_ppp = priv->prof->rx_ppp && !(tx_pause || rx_pause);
+ tx_ppp = priv->prof->tx_ppp && !(tx_pause || rx_pause);
+
err = mlx4_SET_PORT_general(mdev->dev, priv->port,
priv->rx_skb_size + ETH_FCS_LEN,
- priv->prof->tx_pause,
- priv->prof->tx_ppp,
- priv->prof->rx_pause,
- priv->prof->rx_ppp);
- if (err)
- en_err(priv, "Failed setting pause params\n");
- else
- mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
- priv->prof->rx_ppp,
- priv->prof->rx_pause,
- priv->prof->tx_ppp,
- priv->prof->tx_pause);
+ tx_pause, tx_ppp, rx_pause, rx_ppp);
+ if (err) {
+ en_err(priv, "Failed setting pause params, err = %d\n", err);
+ return err;
+ }
+
+ mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
+ rx_ppp, rx_pause, tx_ppp, tx_pause);
+
+ priv->prof->tx_pause = tx_pause;
+ priv->prof->rx_pause = rx_pause;
+ priv->prof->tx_ppp = tx_ppp;
+ priv->prof->rx_ppp = rx_ppp;
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index bf7628d..22c3fdd 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -163,9 +163,9 @@
params->udp_rss = 0;
}
for (i = 1; i <= MLX4_MAX_PORTS; i++) {
- params->prof[i].rx_pause = 1;
+ params->prof[i].rx_pause = !(pfcrx || pfctx);
params->prof[i].rx_ppp = pfcrx;
- params->prof[i].tx_pause = 1;
+ params->prof[i].tx_pause = !(pfcrx || pfctx);
params->prof[i].tx_ppp = pfctx;
params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE;
params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index d223e7c..0160c93 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -3125,6 +3125,13 @@
priv->msg_enable = MLX4_EN_MSG_LEVEL;
#ifdef CONFIG_MLX4_EN_DCB
if (!mlx4_is_slave(priv->mdev->dev)) {
+ u8 prio;
+
+ for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; ++prio) {
+ priv->ets.prio_tc[prio] = prio;
+ priv->ets.tc_tsa[prio] = IEEE_8021QAZ_TSA_VENDOR;
+ }
+
priv->dcbx_cap = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_HOST |
DCB_CAP_DCBX_VER_IEEE;
priv->flags |= MLX4_EN_DCB_ENABLED;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 5411ca4..cb7c3ef 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -2983,6 +2983,7 @@
mlx4_err(dev, "Failed to create file for port %d\n", port);
devlink_port_unregister(&info->devlink_port);
info->port = -1;
+ return err;
}
sprintf(info->dev_mtu_name, "mlx4_port%d_mtu", port);
@@ -3004,9 +3005,10 @@
&info->port_attr);
devlink_port_unregister(&info->devlink_port);
info->port = -1;
+ return err;
}
- return err;
+ return 0;
}
static void mlx4_cleanup_port_info(struct mlx4_port_info *info)
diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index 1a670b6..0710b36 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -35,6 +35,7 @@
#include <linux/etherdevice.h>
#include <linux/mlx4/cmd.h>
+#include <linux/mlx4/qp.h>
#include <linux/export.h>
#include "mlx4.h"
@@ -985,16 +986,21 @@
if (IS_ERR(mailbox))
return PTR_ERR(mailbox);
+ if (!mlx4_qp_lookup(dev, rule->qpn)) {
+ mlx4_err_rule(dev, "QP doesn't exist\n", rule);
+ ret = -EINVAL;
+ goto out;
+ }
+
trans_rule_ctrl_to_hw(rule, mailbox->buf);
size += sizeof(struct mlx4_net_trans_rule_hw_ctrl);
list_for_each_entry(cur, &rule->list, list) {
ret = parse_trans_rule(dev, cur, mailbox->buf + size);
- if (ret < 0) {
- mlx4_free_cmd_mailbox(dev, mailbox);
- return ret;
- }
+ if (ret < 0)
+ goto out;
+
size += ret;
}
@@ -1021,6 +1027,7 @@
}
}
+out:
mlx4_free_cmd_mailbox(dev, mailbox);
return ret;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index df0f396..247d340 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -141,6 +141,9 @@
#define MLX4_EN_TX_COAL_PKTS 16
#define MLX4_EN_TX_COAL_TIME 0x10
+#define MLX4_EN_MAX_COAL_PKTS U16_MAX
+#define MLX4_EN_MAX_COAL_TIME U16_MAX
+
#define MLX4_EN_RX_RATE_LOW 400000
#define MLX4_EN_RX_COAL_TIME_LOW 0
#define MLX4_EN_RX_RATE_HIGH 450000
@@ -472,6 +475,7 @@
#define MLX4_EN_BW_MIN 1
#define MLX4_EN_BW_MAX 100 /* Utilize 100% of the line */
+#define MLX4_EN_TC_VENDOR 0
#define MLX4_EN_TC_ETS 7
enum dcb_pfc_type {
@@ -542,8 +546,8 @@
u16 rx_usecs_low;
u32 pkt_rate_high;
u16 rx_usecs_high;
- u16 sample_interval;
- u16 adaptive_rx_coal;
+ u32 sample_interval;
+ u32 adaptive_rx_coal;
u32 msg_enable;
u32 loopback_ok;
u32 validate_loopback;
diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c
index 6143113..474ff36 100644
--- a/drivers/net/ethernet/mellanox/mlx4/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx4/qp.c
@@ -387,6 +387,19 @@
__mlx4_qp_free_icm(dev, qpn);
}
+struct mlx4_qp *mlx4_qp_lookup(struct mlx4_dev *dev, u32 qpn)
+{
+ struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table;
+ struct mlx4_qp *qp;
+
+ spin_lock(&qp_table->lock);
+
+ qp = __mlx4_qp_lookup(dev, qpn);
+
+ spin_unlock(&qp_table->lock);
+ return qp;
+}
+
int mlx4_qp_alloc(struct mlx4_dev *dev, int qpn, struct mlx4_qp *qp, gfp_t gfp)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -474,6 +487,12 @@
}
if (attr & MLX4_UPDATE_QP_QOS_VPORT) {
+ if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP)) {
+ mlx4_warn(dev, "Granular QoS per VF is not enabled\n");
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
qp_mask |= 1ULL << MLX4_UPD_QP_MASK_QOS_VPP;
cmd->qp_context.qos_vport = params->qos_vport;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 1822382..d6b06be 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -5048,6 +5048,7 @@
&tracker->res_tree[RES_FS_RULE]);
list_del(&fs_rule->com.list);
spin_unlock_irq(mlx4_tlock(dev));
+ kfree(fs_rule->mirr_mbox);
kfree(fs_rule);
state = 0;
break;
@@ -5214,6 +5215,13 @@
mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex);
}
+static void update_qos_vpp(struct mlx4_update_qp_context *ctx,
+ struct mlx4_vf_immed_vlan_work *work)
+{
+ ctx->qp_mask |= cpu_to_be64(1ULL << MLX4_UPD_QP_MASK_QOS_VPP);
+ ctx->qp_context.qos_vport = work->qos_vport;
+}
+
void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
{
struct mlx4_vf_immed_vlan_work *work =
@@ -5328,11 +5336,10 @@
qp->sched_queue & 0xC7;
upd_context->qp_context.pri_path.sched_queue |=
((work->qos & 0x7) << 3);
- upd_context->qp_mask |=
- cpu_to_be64(1ULL <<
- MLX4_UPD_QP_MASK_QOS_VPP);
- upd_context->qp_context.qos_vport =
- work->qos_vport;
+
+ if (dev->caps.flags2 &
+ MLX4_DEV_CAP_FLAG2_QOS_VPP)
+ update_qos_vpp(upd_context, work);
}
err = mlx4_cmd(dev, mailbox->dma,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 38981db..2d235e8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -2741,6 +2741,9 @@
mutex_unlock(&priv->state_lock);
+ if (mlx5e_vxlan_allowed(priv->mdev))
+ udp_tunnel_get_rx_info(netdev);
+
return err;
}
@@ -3785,13 +3788,6 @@
if (netdev->reg_state != NETREG_REGISTERED)
return;
- /* Device already registered: sync netdev system state */
- if (mlx5e_vxlan_allowed(mdev)) {
- rtnl_lock();
- udp_tunnel_get_rx_info(netdev);
- rtnl_unlock();
- }
-
queue_work(priv->wq, &priv->set_rx_mode_work);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index a8966e6..5d6eab1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1924,26 +1924,35 @@
memset(vf_stats, 0, sizeof(*vf_stats));
vf_stats->rx_packets =
MLX5_GET_CTR(out, received_eth_unicast.packets) +
+ MLX5_GET_CTR(out, received_ib_unicast.packets) +
MLX5_GET_CTR(out, received_eth_multicast.packets) +
+ MLX5_GET_CTR(out, received_ib_multicast.packets) +
MLX5_GET_CTR(out, received_eth_broadcast.packets);
vf_stats->rx_bytes =
MLX5_GET_CTR(out, received_eth_unicast.octets) +
+ MLX5_GET_CTR(out, received_ib_unicast.octets) +
MLX5_GET_CTR(out, received_eth_multicast.octets) +
+ MLX5_GET_CTR(out, received_ib_multicast.octets) +
MLX5_GET_CTR(out, received_eth_broadcast.octets);
vf_stats->tx_packets =
MLX5_GET_CTR(out, transmitted_eth_unicast.packets) +
+ MLX5_GET_CTR(out, transmitted_ib_unicast.packets) +
MLX5_GET_CTR(out, transmitted_eth_multicast.packets) +
+ MLX5_GET_CTR(out, transmitted_ib_multicast.packets) +
MLX5_GET_CTR(out, transmitted_eth_broadcast.packets);
vf_stats->tx_bytes =
MLX5_GET_CTR(out, transmitted_eth_unicast.octets) +
+ MLX5_GET_CTR(out, transmitted_ib_unicast.octets) +
MLX5_GET_CTR(out, transmitted_eth_multicast.octets) +
+ MLX5_GET_CTR(out, transmitted_ib_multicast.octets) +
MLX5_GET_CTR(out, transmitted_eth_broadcast.octets);
vf_stats->multicast =
- MLX5_GET_CTR(out, received_eth_multicast.packets);
+ MLX5_GET_CTR(out, received_eth_multicast.packets) +
+ MLX5_GET_CTR(out, received_ib_multicast.packets);
vf_stats->broadcast =
MLX5_GET_CTR(out, received_eth_broadcast.packets);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index 331a6ca..5f3402b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -153,6 +153,7 @@
static void del_flow_table(struct fs_node *node);
static void del_flow_group(struct fs_node *node);
static void del_fte(struct fs_node *node);
+static void cleanup_root_ns(struct mlx5_flow_root_namespace *root_ns);
static void tree_init_node(struct fs_node *node,
unsigned int refcount,
@@ -1690,24 +1691,28 @@
static int init_root_ns(struct mlx5_flow_steering *steering)
{
+ int err;
steering->root_ns = create_root_ns(steering, FS_FT_NIC_RX);
if (!steering->root_ns)
- goto cleanup;
+ return -ENOMEM;
- if (init_root_tree(steering, &root_fs, &steering->root_ns->ns.node))
- goto cleanup;
+ err = init_root_tree(steering, &root_fs, &steering->root_ns->ns.node);
+ if (err)
+ goto out_err;
set_prio_attrs(steering->root_ns);
- if (create_anchor_flow_table(steering))
- goto cleanup;
+ err = create_anchor_flow_table(steering);
+ if (err)
+ goto out_err;
return 0;
-cleanup:
- mlx5_cleanup_fs(steering->dev);
- return -ENOMEM;
+out_err:
+ cleanup_root_ns(steering->root_ns);
+ steering->root_ns = NULL;
+ return err;
}
static void clean_tree(struct fs_node *node)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 981cd1d..3c183b8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -548,7 +548,6 @@
struct mlx5_priv *priv = &mdev->priv;
struct msix_entry *msix = priv->msix_arr;
int irq = msix[i + MLX5_EQ_VEC_COMP_BASE].vector;
- int err;
if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) {
mlx5_core_warn(mdev, "zalloc_cpumask_var failed");
@@ -558,18 +557,11 @@
cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
priv->irq_info[i].mask);
- err = irq_set_affinity_hint(irq, priv->irq_info[i].mask);
- if (err) {
- mlx5_core_warn(mdev, "irq_set_affinity_hint failed,irq 0x%.4x",
- irq);
- goto err_clear_mask;
- }
+ if (IS_ENABLED(CONFIG_SMP) &&
+ irq_set_affinity_hint(irq, priv->irq_info[i].mask))
+ mlx5_core_warn(mdev, "irq_set_affinity_hint failed, irq 0x%.4x", irq);
return 0;
-
-err_clear_mask:
- free_cpumask_var(priv->irq_info[i].mask);
- return err;
}
static void mlx5_irq_clear_affinity_hint(struct mlx5_core_dev *mdev, int i)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 1e2c8ec..60e1edc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -809,6 +809,7 @@
bool dynamic)
{
char *sfd_pl;
+ u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
@@ -818,9 +819,16 @@
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
mac, fid, action, local_port);
+ num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
- kfree(sfd_pl);
+ if (err)
+ goto out;
+ if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+ err = -EBUSY;
+
+out:
+ kfree(sfd_pl);
return err;
}
@@ -845,6 +853,7 @@
bool adding, bool dynamic)
{
char *sfd_pl;
+ u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
@@ -855,9 +864,16 @@
mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
lag_vid, lag_id);
+ num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
- kfree(sfd_pl);
+ if (err)
+ goto out;
+ if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+ err = -EBUSY;
+
+out:
+ kfree(sfd_pl);
return err;
}
@@ -891,6 +907,7 @@
u16 fid, u16 mid, bool adding)
{
char *sfd_pl;
+ u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
@@ -900,7 +917,15 @@
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid,
MLXSW_REG_SFD_REC_ACTION_NOP, mid);
+ num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+ if (err)
+ goto out;
+
+ if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
+ err = -EBUSY;
+
+out:
kfree(sfd_pl);
return err;
}
@@ -1423,8 +1448,7 @@
err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
adding, true);
if (err) {
- if (net_ratelimit())
- netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
+ dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
return;
}
@@ -1484,8 +1508,7 @@
err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
adding, true);
if (err) {
- if (net_ratelimit())
- netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
+ dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
return;
}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 4ca82bd..eee6e59 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -854,6 +854,8 @@
netdev_tx_sent_queue(nd_q, txbuf->real_len);
+ skb_tx_timestamp(skb);
+
tx_ring->wr_p += nr_frags + 1;
if (nfp_net_tx_ring_should_stop(tx_ring))
nfp_net_tx_ring_stop(nd_q, tx_ring);
@@ -866,8 +868,6 @@
tx_ring->wr_ptr_add = 0;
}
- skb_tx_timestamp(skb);
-
return NETDEV_TX_OK;
err_unmap:
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c
index b8d5270..e306765 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c
@@ -247,7 +247,7 @@
cmd.req.arg3 = 0;
if (recv_ctx->state == NX_HOST_CTX_STATE_ACTIVE)
- netxen_issue_cmd(adapter, &cmd);
+ rcode = netxen_issue_cmd(adapter, &cmd);
if (rcode != NX_RCODE_SUCCESS)
return -EIO;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
index ed014bd..457e304 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
@@ -271,16 +271,34 @@
u32 per_vf_tids;
};
-static void qed_cxt_tm_iids(struct qed_cxt_mngr *p_mngr,
+static void qed_cxt_tm_iids(struct qed_hwfn *p_hwfn,
+ struct qed_cxt_mngr *p_mngr,
struct qed_tm_iids *iids)
{
- u32 i, j;
+ bool tm_vf_required = false;
+ bool tm_required = false;
+ int i, j;
- for (i = 0; i < MAX_CONN_TYPES; i++) {
+ /* Timers is a special case -> we don't count how many cids require
+ * timers but what's the max cid that will be used by the timer block.
+ * therefore we traverse in reverse order, and once we hit a protocol
+ * that requires the timers memory, we'll sum all the protocols up
+ * to that one.
+ */
+ for (i = MAX_CONN_TYPES - 1; i >= 0; i--) {
struct qed_conn_type_cfg *p_cfg = &p_mngr->conn_cfg[i];
- if (tm_cid_proto(i)) {
+ if (tm_cid_proto(i) || tm_required) {
+ if (p_cfg->cid_count)
+ tm_required = true;
+
iids->pf_cids += p_cfg->cid_count;
+ }
+
+ if (tm_cid_proto(i) || tm_vf_required) {
+ if (p_cfg->cids_per_vf)
+ tm_vf_required = true;
+
iids->per_vf_cids += p_cfg->cids_per_vf;
}
}
@@ -696,7 +714,7 @@
/* TM PF */
p_cli = &p_mngr->clients[ILT_CLI_TM];
- qed_cxt_tm_iids(p_mngr, &tm_iids);
+ qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids);
total = tm_iids.pf_cids + tm_iids.pf_tids_total;
if (total) {
p_blk = &p_cli->pf_blks[0];
@@ -1591,7 +1609,7 @@
u8 i;
memset(&tm_iids, 0, sizeof(tm_iids));
- qed_cxt_tm_iids(p_mngr, &tm_iids);
+ qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids);
/* @@@TBD No pre-scan for now */
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index afe5e57..d023137 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -850,7 +850,7 @@
NULL) +
qed_cxt_get_proto_cid_count(p_hwfn, PROTOCOLID_ETH,
NULL);
- norm_regsize = roundup(QED_PF_DEMS_SIZE * non_pwm_conn, 4096);
+ norm_regsize = roundup(QED_PF_DEMS_SIZE * non_pwm_conn, PAGE_SIZE);
min_addr_reg1 = norm_regsize / 4096;
pwm_regsize = db_bar_size - norm_regsize;
@@ -1628,6 +1628,9 @@
DP_NOTICE(p_hwfn, "Unknown Speed in 0x%08x\n", link_temp);
}
+ p_hwfn->mcp_info->link_capabilities.default_speed_autoneg =
+ link->speed.autoneg;
+
link_temp &= NVM_CFG1_PORT_DRV_FLOW_CONTROL_MASK;
link_temp >>= NVM_CFG1_PORT_DRV_FLOW_CONTROL_OFFSET;
link->pause.autoneg = !!(link_temp &
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 333c744..0b949c6 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -711,7 +711,8 @@
cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors -
cdev->num_hwfns;
- if (!IS_ENABLED(CONFIG_QED_RDMA))
+ if (!IS_ENABLED(CONFIG_QED_RDMA) ||
+ QED_LEADING_HWFN(cdev)->hw_info.personality != QED_PCI_ETH_ROCE)
return 0;
for_each_hwfn(cdev, i)
@@ -1239,7 +1240,7 @@
/* TODO - at the moment assume supported and advertised speed equal */
if_link->supported_caps = QED_LM_FIBRE_BIT;
- if (params.speed.autoneg)
+ if (link_caps.default_speed_autoneg)
if_link->supported_caps |= QED_LM_Autoneg_BIT;
if (params.pause.autoneg ||
(params.pause.forced_rx && params.pause.forced_tx))
@@ -1249,6 +1250,10 @@
if_link->supported_caps |= QED_LM_Pause_BIT;
if_link->advertised_caps = if_link->supported_caps;
+ if (params.speed.autoneg)
+ if_link->advertised_caps |= QED_LM_Autoneg_BIT;
+ else
+ if_link->advertised_caps &= ~QED_LM_Autoneg_BIT;
if (params.speed.advertised_speeds &
NVM_CFG1_PORT_DRV_SPEED_CAPABILITY_MASK_1G)
if_link->advertised_caps |= QED_LM_1000baseT_Half_BIT |
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index dff520e..7b7a84d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -35,6 +35,7 @@
struct qed_mcp_link_capabilities {
u32 speed_capabilities;
+ bool default_speed_autoneg;
};
struct qed_mcp_link_state {
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
index d2d6621..48bc5c1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
@@ -3573,6 +3573,7 @@
void qed_inform_vf_link_state(struct qed_hwfn *hwfn)
{
+ struct qed_hwfn *lead_hwfn = QED_LEADING_HWFN(hwfn->cdev);
struct qed_mcp_link_capabilities caps;
struct qed_mcp_link_params params;
struct qed_mcp_link_state link;
@@ -3589,9 +3590,15 @@
if (!vf_info)
continue;
- memcpy(¶ms, qed_mcp_get_link_params(hwfn), sizeof(params));
- memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link));
- memcpy(&caps, qed_mcp_get_link_capabilities(hwfn),
+ /* Only hwfn0 is actually interested in the link speed.
+ * But since only it would receive an MFW indication of link,
+ * need to take configuration from it - otherwise things like
+ * rate limiting for hwfn1 VF would not work.
+ */
+ memcpy(¶ms, qed_mcp_get_link_params(lead_hwfn),
+ sizeof(params));
+ memcpy(&link, qed_mcp_get_link_state(lead_hwfn), sizeof(link));
+ memcpy(&caps, qed_mcp_get_link_capabilities(lead_hwfn),
sizeof(caps));
/* Modify link according to the VF's configured link state */
diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c
index abf5bf1..0645124 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_vf.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c
@@ -204,7 +204,7 @@
/* send acquire request */
rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp));
if (rc)
- return rc;
+ goto exit;
/* copy acquire response from buffer to p_hwfn */
memcpy(&p_iov->acquire_resp, resp, sizeof(p_iov->acquire_resp));
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
index 509b596..bd1ec70 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
@@ -341,7 +341,7 @@
}
return -EIO;
}
- usleep_range(1000, 1500);
+ udelay(1200);
}
if (id_reg)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
index d710705..2f656f3 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
@@ -128,6 +128,8 @@
return 0;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos)
+ return 0;
pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
index be258d9..e3223f2 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
@@ -765,7 +765,7 @@
sizeof(struct mpi_coredump_global_header);
mpi_coredump->mpi_global_header.imageSize =
sizeof(struct ql_mpi_coredump);
- memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump",
+ strncpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump",
sizeof(mpi_coredump->mpi_global_header.idString));
/* Get generic NIC reg dump */
@@ -1255,7 +1255,7 @@
sizeof(struct mpi_coredump_global_header);
mpi_coredump->mpi_global_header.imageSize =
sizeof(struct ql_reg_dump);
- memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump",
+ strncpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump",
sizeof(mpi_coredump->mpi_global_header.idString));
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 6e2add9..8bbb55f 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -296,8 +296,9 @@
/* Allocate rx SKB if we don't have one available. */
if (!qca->rx_skb) {
- qca->rx_skb = netdev_alloc_skb(net_dev,
- net_dev->mtu + VLAN_ETH_HLEN);
+ qca->rx_skb = netdev_alloc_skb_ip_align(net_dev,
+ net_dev->mtu +
+ VLAN_ETH_HLEN);
if (!qca->rx_skb) {
netdev_dbg(net_dev, "out of RX resources\n");
qca->stats.out_of_mem++;
@@ -377,7 +378,7 @@
qca->rx_skb, qca->rx_skb->dev);
qca->rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx_ni(qca->rx_skb);
- qca->rx_skb = netdev_alloc_skb(net_dev,
+ qca->rx_skb = netdev_alloc_skb_ip_align(net_dev,
net_dev->mtu + VLAN_ETH_HLEN);
if (!qca->rx_skb) {
netdev_dbg(net_dev, "out of RX resources\n");
@@ -759,7 +760,8 @@
if (!qca->rx_buffer)
return -ENOBUFS;
- qca->rx_skb = netdev_alloc_skb(dev, qca->net_dev->mtu + VLAN_ETH_HLEN);
+ qca->rx_skb = netdev_alloc_skb_ip_align(dev, qca->net_dev->mtu +
+ VLAN_ETH_HLEN);
if (!qca->rx_skb) {
kfree(qca->rx_buffer);
netdev_info(qca->net_dev, "Failed to allocate RX sk_buff.\n");
diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c
index da4c2d8..1420dfb 100644
--- a/drivers/net/ethernet/realtek/8139too.c
+++ b/drivers/net/ethernet/realtek/8139too.c
@@ -2233,7 +2233,7 @@
struct rtl8139_private *tp = netdev_priv(dev);
const int irq = tp->pci_dev->irq;
- disable_irq(irq);
+ disable_irq_nosync(irq);
rtl8139_interrupt(irq, dev);
enable_irq(irq);
}
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 18e68c9..59b932d 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -4861,6 +4861,9 @@
static void rtl_pll_power_up(struct rtl8169_private *tp)
{
rtl_generic_op(tp, tp->pll_power_ops.up);
+
+ /* give MAC/PHY some time to resume */
+ msleep(20);
}
static void rtl_init_pll_power_ops(struct rtl8169_private *tp)
@@ -8446,12 +8449,12 @@
goto err_out_msi_5;
}
+ pci_set_drvdata(pdev, dev);
+
rc = register_netdev(dev);
if (rc < 0)
goto err_out_cnt_6;
- pci_set_drvdata(pdev, dev);
-
netif_info(tp, probe, dev, "%s at 0x%p, %pM, XID %08x IRQ %d\n",
rtl_chip_infos[chipset].name, ioaddr, dev->dev_addr,
(u32)(RTL_R32(TxConfig) & 0x9cf0f8ff), pdev->irq);
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index b6816ae..c8fd99b 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -3133,7 +3133,7 @@
/* MDIO bus init */
ret = sh_mdio_init(mdp, pd);
if (ret) {
- dev_err(&ndev->dev, "failed to initialise MDIO\n");
+ dev_err(&pdev->dev, "failed to initialise MDIO\n");
goto out_release;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index 10d6059..f4074e2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -38,6 +38,7 @@
{
u32 value = readl(ioaddr + PTP_TCR);
unsigned long data;
+ u32 reg_value;
/* For GMAC3.x, 4.x versions, convert the ptp_clock to nano second
* formula = (1/ptp_clock) * 1000000000
@@ -54,10 +55,11 @@
data &= PTP_SSIR_SSINC_MASK;
+ reg_value = data;
if (gmac4)
- data = data << GMAC4_PTP_SSIR_SSINC_SHIFT;
+ reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT;
- writel(data, ioaddr + PTP_SSIR);
+ writel(reg_value, ioaddr + PTP_SSIR);
return data;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 98bbb91..c212d1d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -478,7 +478,10 @@
/* PTP v1, UDP, any kind of event packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
/* take time stamp for all event messages */
- snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+ if (priv->plat->has_gmac4)
+ snap_type_sel = PTP_GMAC4_TCR_SNAPTYPSEL_1;
+ else
+ snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
@@ -510,7 +513,10 @@
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
ptp_v2 = PTP_TCR_TSVER2ENA;
/* take time stamp for all event messages */
- snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+ if (priv->plat->has_gmac4)
+ snap_type_sel = PTP_GMAC4_TCR_SNAPTYPSEL_1;
+ else
+ snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
@@ -544,7 +550,10 @@
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
ptp_v2 = PTP_TCR_TSVER2ENA;
/* take time stamp for all event messages */
- snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+ if (priv->plat->has_gmac4)
+ snap_type_sel = PTP_GMAC4_TCR_SNAPTYPSEL_1;
+ else
+ snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
index c06938c..174777c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
@@ -63,7 +63,8 @@
/* Enable Snapshot for Messages Relevant to Master */
#define PTP_TCR_TSMSTRENA BIT(15)
/* Select PTP packets for Taking Snapshots */
-#define PTP_TCR_SNAPTYPSEL_1 GENMASK(17, 16)
+#define PTP_TCR_SNAPTYPSEL_1 BIT(16)
+#define PTP_GMAC4_TCR_SNAPTYPSEL_1 GENMASK(17, 16)
/* Enable MAC address for PTP Frame Filtering */
#define PTP_TCR_TSENMACADDR BIT(18)
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index a2371aa..e45e2f1 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -3442,7 +3442,7 @@
len = (val & RCR_ENTRY_L2_LEN) >>
RCR_ENTRY_L2_LEN_SHIFT;
- len -= ETH_FCS_LEN;
+ append_size = len + ETH_HLEN + ETH_FCS_LEN;
addr = (val & RCR_ENTRY_PKT_BUF_ADDR) <<
RCR_ENTRY_PKT_BUF_ADDR_SHIFT;
@@ -3452,7 +3452,6 @@
RCR_ENTRY_PKTBUFSZ_SHIFT];
off = addr & ~PAGE_MASK;
- append_size = rcr_size;
if (num_rcr == 1) {
int ptype;
@@ -3465,7 +3464,7 @@
else
skb_checksum_none_assert(skb);
} else if (!(val & RCR_ENTRY_MULTI))
- append_size = len - skb->len;
+ append_size = append_size - skb->len;
niu_rx_skb_append(skb, page, off, append_size, rcr_size);
if ((page->index + rp->rbr_block_size) - rcr_size == addr) {
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 3f1971d..d7cb205 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -124,7 +124,7 @@
#define RX_PRIORITY_MAPPING 0x76543210
#define TX_PRIORITY_MAPPING 0x33221100
-#define CPDMA_TX_PRIORITY_MAP 0x01234567
+#define CPDMA_TX_PRIORITY_MAP 0x76543210
#define CPSW_VLAN_AWARE BIT(1)
#define CPSW_ALE_VLAN_AWARE 1
@@ -282,6 +282,10 @@
/* Bit definitions for the CPSW1_TS_SEQ_LTYPE register */
#define CPSW_V1_SEQ_ID_OFS_SHIFT 16
+#define CPSW_MAX_BLKS_TX 15
+#define CPSW_MAX_BLKS_TX_SHIFT 4
+#define CPSW_MAX_BLKS_RX 5
+
struct cpsw_host_regs {
u32 max_blks;
u32 blk_cnt;
@@ -901,7 +905,8 @@
/* set speed_in input in case RMII mode is used in 100Mbps */
if (phy->speed == 100)
mac_control |= BIT(15);
- else if (phy->speed == 10)
+ /* in band mode only works in 10Mbps RGMII mode */
+ else if ((phy->speed == 10) && phy_interface_is_rgmii(phy))
mac_control |= BIT(18); /* In Band mode */
if (priv->rx_pause)
@@ -1136,6 +1141,8 @@
cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
HOST_PORT_NUM, ALE_VLAN |
ALE_SECURE, slave->port_vlan);
+ cpsw_ale_control_set(cpsw->ale, slave_port,
+ ALE_PORT_DROP_UNKNOWN_VLAN, 1);
}
static void soft_reset_slave(struct cpsw_slave *slave)
@@ -1159,11 +1166,23 @@
switch (cpsw->version) {
case CPSW_VERSION_1:
slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP);
+ /* Increase RX FIFO size to 5 for supporting fullduplex
+ * flow control mode
+ */
+ slave_write(slave,
+ (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
+ CPSW_MAX_BLKS_RX, CPSW1_MAX_BLKS);
break;
case CPSW_VERSION_2:
case CPSW_VERSION_3:
case CPSW_VERSION_4:
slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP);
+ /* Increase RX FIFO size to 5 for supporting fullduplex
+ * flow control mode
+ */
+ slave_write(slave,
+ (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
+ CPSW_MAX_BLKS_RX, CPSW2_MAX_BLKS);
break;
}
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 3c1f89a..92ad43e 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -209,6 +209,7 @@
struct genevehdr *gnvh = geneve_hdr(skb);
struct metadata_dst *tun_dst = NULL;
struct pcpu_sw_netstats *stats;
+ unsigned int len;
int err = 0;
void *oiph;
@@ -222,8 +223,10 @@
tun_dst = udp_tun_rx_dst(skb, geneve_get_sk_family(gs), flags,
vni_to_tunnel_id(gnvh->vni),
gnvh->opt_len * 4);
- if (!tun_dst)
+ if (!tun_dst) {
+ geneve->dev->stats.rx_dropped++;
goto drop;
+ }
/* Update tunnel dst according to Geneve options. */
ip_tunnel_info_opts_set(&tun_dst->u.tun_info,
gnvh->options, gnvh->opt_len * 4);
@@ -231,8 +234,11 @@
/* Drop packets w/ critical options,
* since we don't support any...
*/
- if (gnvh->critical)
+ if (gnvh->critical) {
+ geneve->dev->stats.rx_frame_errors++;
+ geneve->dev->stats.rx_errors++;
goto drop;
+ }
}
skb_reset_mac_header(skb);
@@ -243,8 +249,10 @@
skb_dst_set(skb, &tun_dst->dst);
/* Ignore packet loops (and multicast echo) */
- if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr))
+ if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr)) {
+ geneve->dev->stats.rx_errors++;
goto drop;
+ }
oiph = skb_network_header(skb);
skb_reset_network_header(skb);
@@ -276,13 +284,15 @@
}
}
- stats = this_cpu_ptr(geneve->dev->tstats);
- u64_stats_update_begin(&stats->syncp);
- stats->rx_packets++;
- stats->rx_bytes += skb->len;
- u64_stats_update_end(&stats->syncp);
-
- gro_cells_receive(&geneve->gro_cells, skb);
+ len = skb->len;
+ err = gro_cells_receive(&geneve->gro_cells, skb);
+ if (likely(err == NET_RX_SUCCESS)) {
+ stats = this_cpu_ptr(geneve->dev->tstats);
+ u64_stats_update_begin(&stats->syncp);
+ stats->rx_packets++;
+ stats->rx_bytes += len;
+ u64_stats_update_end(&stats->syncp);
+ }
return;
drop:
/* Consume bad packet */
@@ -332,7 +342,7 @@
struct geneve_sock *gs;
int opts_len;
- /* Need Geneve and inner Ethernet header to be present */
+ /* Need UDP and Geneve header to be present */
if (unlikely(!pskb_may_pull(skb, GENEVE_BASE_HLEN)))
goto drop;
@@ -355,8 +365,10 @@
opts_len = geneveh->opt_len * 4;
if (iptunnel_pull_header(skb, GENEVE_BASE_HLEN + opts_len,
htons(ETH_P_TEB),
- !net_eq(geneve->net, dev_net(geneve->dev))))
+ !net_eq(geneve->net, dev_net(geneve->dev)))) {
+ geneve->dev->stats.rx_dropped++;
goto drop;
+ }
geneve_rx(geneve, gs, skb);
return 0;
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index 4bad0b8..27160d1 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -576,6 +576,8 @@
case HDLCDRVCTL_CALIBRATE:
if(!capable(CAP_SYS_RAWIO))
return -EPERM;
+ if (s->par.bitrate <= 0)
+ return -EINVAL;
if (bi.data.calibrate > INT_MAX / s->par.bitrate)
return -EINVAL;
s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index c2ac39a..14f58b6 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -151,6 +151,13 @@
sizeof(struct nvsp_message),
(unsigned long)revoke_packet,
VM_PKT_DATA_INBAND, 0);
+ /* If the failure is because the channel is rescinded;
+ * ignore the failure since we cannot send on a rescinded
+ * channel. This would allow us to properly cleanup
+ * even when the channel is rescinded.
+ */
+ if (device->channel->rescind)
+ ret = 0;
/*
* If we failed here, we might as well return and
* have a leak rather than continue and a bugchk
@@ -211,6 +218,15 @@
sizeof(struct nvsp_message),
(unsigned long)revoke_packet,
VM_PKT_DATA_INBAND, 0);
+
+ /* If the failure is because the channel is rescinded;
+ * ignore the failure since we cannot send on a rescinded
+ * channel. This would allow us to properly cleanup
+ * even when the channel is rescinded.
+ */
+ if (device->channel->rescind)
+ ret = 0;
+
/* If we failed here, we might as well return and
* have a leak rather than continue and a bugchk
*/
diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c
index f355df7..1b980f1 100644
--- a/drivers/net/ieee802154/adf7242.c
+++ b/drivers/net/ieee802154/adf7242.c
@@ -888,7 +888,7 @@
.set_cca_ed_level = adf7242_set_cca_ed_level,
};
-static void adf7242_debug(u8 irq1)
+static void adf7242_debug(struct adf7242_local *lp, u8 irq1)
{
#ifdef DEBUG
u8 stat;
@@ -932,7 +932,7 @@
dev_err(&lp->spi->dev, "%s :ERROR IRQ1 = 0x%X\n",
__func__, irq1);
- adf7242_debug(irq1);
+ adf7242_debug(lp, irq1);
xmit = test_bit(FLAG_XMIT, &lp->flags);
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index 627eb82..c747ab6 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -299,6 +299,10 @@
if (dev_forward_skb(ipvlan->dev, skb) == NET_RX_SUCCESS)
success = true;
} else {
+ if (!ether_addr_equal_64bits(eth_hdr(skb)->h_dest,
+ ipvlan->phy_dev->dev_addr))
+ skb->pkt_type = PACKET_OTHERHOST;
+
ret = RX_HANDLER_ANOTHER;
success = true;
}
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 2caac0c..365a48c 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -742,7 +742,12 @@
macsec_fill_iv(iv, secy->sci, pn);
sg_init_table(sg, ret);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ ret = skb_to_sgvec(skb, sg, 0, skb->len);
+ if (unlikely(ret < 0)) {
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+ }
if (tx_sc->encrypt) {
int len = skb->len - macsec_hdr_len(sci_present) -
@@ -949,7 +954,11 @@
macsec_fill_iv(iv, sci, ntohl(hdr->packet_number));
sg_init_table(sg, ret);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ ret = skb_to_sgvec(skb, sg, 0, skb->len);
+ if (unlikely(ret < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+ }
if (hdr->tci_an & MACSEC_TCI_E) {
/* confidentiality: ethernet + macsec header
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 6d55049..e8ad4d0 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -1377,9 +1377,14 @@
return 0;
unregister_netdev:
+ /* macvlan_uninit would free the macvlan port */
unregister_netdevice(dev);
+ return err;
destroy_macvlan_port:
- if (create)
+ /* the macvlan port may be freed by macvlan_uninit when fail to register.
+ * so we destroy the macvlan port only when it's valid.
+ */
+ if (create && macvlan_port_get_rtnl(dev))
macvlan_port_destroy(port->dev);
return err;
}
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 963838d..599ce24 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -122,10 +122,9 @@
pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
if (pb == NULL) {
ret_val = -ENOMEM;
- goto err_parent_bus;
+ goto err_pb_kz;
}
-
pb->switch_data = data;
pb->switch_fn = switch_fn;
pb->current_child = -1;
@@ -154,6 +153,7 @@
cb->mii_bus = mdiobus_alloc();
if (!cb->mii_bus) {
ret_val = -ENOMEM;
+ devm_kfree(dev, cb);
of_node_put(child_bus_node);
break;
}
@@ -170,7 +170,6 @@
mdiobus_free(cb->mii_bus);
devm_kfree(dev, cb);
} else {
- of_node_get(child_bus_node);
cb->next = pb->children;
pb->children = cb;
}
@@ -181,9 +180,11 @@
return 0;
}
+ devm_kfree(dev, pb);
+err_pb_kz:
/* balance the reference of_mdio_find_bus() took */
- put_device(&pb->mii_bus->dev);
-
+ if (!mux_bus)
+ put_device(&parent_bus->dev);
err_parent_bus:
of_node_put(parent_bus_node);
return ret_val;
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
index 1352965..6425ce0 100644
--- a/drivers/net/phy/mdio-sun4i.c
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -118,8 +118,10 @@
data->regulator = devm_regulator_get(&pdev->dev, "phy");
if (IS_ERR(data->regulator)) {
- if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(data->regulator) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_out_free_mdiobus;
+ }
dev_info(&pdev->dev, "no regulator found\n");
data->regulator = NULL;
diff --git a/drivers/net/phy/mdio-xgene.c b/drivers/net/phy/mdio-xgene.c
index 92af182..20fbcc9 100644
--- a/drivers/net/phy/mdio-xgene.c
+++ b/drivers/net/phy/mdio-xgene.c
@@ -197,8 +197,11 @@
}
ret = xgene_enet_ecc_init(pdata);
- if (ret)
+ if (ret) {
+ if (pdata->dev->of_node)
+ clk_disable_unprepare(pdata->clk);
return ret;
+ }
xgene_gmac_reset(pdata);
return 0;
@@ -229,7 +232,7 @@
val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) |
SET_VAL(HSTMIIMWRDAT, data);
- xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, data);
+ xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE);
xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
@@ -311,6 +314,30 @@
}
#endif
+static const struct of_device_id xgene_mdio_of_match[] = {
+ {
+ .compatible = "apm,xgene-mdio-rgmii",
+ .data = (void *)XGENE_MDIO_RGMII
+ },
+ {
+ .compatible = "apm,xgene-mdio-xfi",
+ .data = (void *)XGENE_MDIO_XFI
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_mdio_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_mdio_acpi_match[] = {
+ { "APMC0D65", XGENE_MDIO_RGMII },
+ { "APMC0D66", XGENE_MDIO_XFI },
+ { }
+};
+
+MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match);
+#endif
+
+
static int xgene_mdio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -364,8 +391,10 @@
return ret;
mdio_bus = mdiobus_alloc();
- if (!mdio_bus)
- return -ENOMEM;
+ if (!mdio_bus) {
+ ret = -ENOMEM;
+ goto out_clk;
+ }
mdio_bus->name = "APM X-Gene MDIO bus";
@@ -394,7 +423,7 @@
mdio_bus->phy_mask = ~0;
ret = mdiobus_register(mdio_bus);
if (ret)
- goto out;
+ goto out_mdiobus;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1,
acpi_register_phy, NULL, mdio_bus, NULL);
@@ -402,16 +431,20 @@
}
if (ret)
- goto out;
+ goto out_mdiobus;
pdata->mdio_bus = mdio_bus;
xgene_mdio_status = true;
return 0;
-out:
+out_mdiobus:
mdiobus_free(mdio_bus);
+out_clk:
+ if (dev->of_node)
+ clk_disable_unprepare(pdata->clk);
+
return ret;
}
@@ -430,32 +463,6 @@
return 0;
}
-#ifdef CONFIG_OF
-static const struct of_device_id xgene_mdio_of_match[] = {
- {
- .compatible = "apm,xgene-mdio-rgmii",
- .data = (void *)XGENE_MDIO_RGMII
- },
- {
- .compatible = "apm,xgene-mdio-xfi",
- .data = (void *)XGENE_MDIO_XFI
- },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, xgene_mdio_of_match);
-#endif
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id xgene_mdio_acpi_match[] = {
- { "APMC0D65", XGENE_MDIO_RGMII },
- { "APMC0D66", XGENE_MDIO_XFI },
- { }
-};
-
-MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match);
-#endif
-
static struct platform_driver xgene_mdio_driver = {
.driver = {
.name = "xgene-mdio",
diff --git a/drivers/net/phy/mdio-xgene.h b/drivers/net/phy/mdio-xgene.h
index 354241b..594a11d 100644
--- a/drivers/net/phy/mdio-xgene.h
+++ b/drivers/net/phy/mdio-xgene.h
@@ -132,10 +132,6 @@
#define GET_BIT(field, src) \
xgene_enet_get_field_value(field ## _POS, 1, src)
-static const struct of_device_id xgene_mdio_of_match[];
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id xgene_mdio_acpi_match[];
-#endif
int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg);
int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data);
struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 6e12401..4d21764 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -148,6 +148,12 @@
if (phydev->drv->aneg_done)
return phydev->drv->aneg_done(phydev);
+ /* Avoid genphy_aneg_done() if the Clause 45 PHY does not
+ * implement Clause 22 registers
+ */
+ if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
+ return -EINVAL;
+
return genphy_aneg_done(phydev);
}
@@ -925,7 +931,7 @@
break;
case PHY_HALTED:
/* make sure interrupts are re-enabled for the PHY */
- if (phydev->irq != PHY_POLL) {
+ if (phy_interrupt_is_valid(phydev)) {
err = phy_enable_interrupts(phydev);
if (err < 0)
break;
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index fc4c2cc..1e4969d 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -255,7 +255,7 @@
/* Prototypes. */
static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct file *file, unsigned int cmd, unsigned long arg);
-static void ppp_xmit_process(struct ppp *ppp);
+static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb);
static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
static void ppp_push(struct ppp *ppp);
static void ppp_channel_push(struct channel *pch);
@@ -511,13 +511,12 @@
goto out;
}
- skb_queue_tail(&pf->xq, skb);
-
switch (pf->kind) {
case INTERFACE:
- ppp_xmit_process(PF_TO_PPP(pf));
+ ppp_xmit_process(PF_TO_PPP(pf), skb);
break;
case CHANNEL:
+ skb_queue_tail(&pf->xq, skb);
ppp_channel_push(PF_TO_CHANNEL(pf));
break;
}
@@ -1261,8 +1260,8 @@
put_unaligned_be16(proto, pp);
skb_scrub_packet(skb, !net_eq(ppp->ppp_net, dev_net(dev)));
- skb_queue_tail(&ppp->file.xq, skb);
- ppp_xmit_process(ppp);
+ ppp_xmit_process(ppp, skb);
+
return NETDEV_TX_OK;
outf:
@@ -1416,13 +1415,14 @@
*/
/* Called to do any work queued up on the transmit side that can now be done */
-static void __ppp_xmit_process(struct ppp *ppp)
+static void __ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
{
- struct sk_buff *skb;
-
ppp_xmit_lock(ppp);
if (!ppp->closing) {
ppp_push(ppp);
+
+ if (skb)
+ skb_queue_tail(&ppp->file.xq, skb);
while (!ppp->xmit_pending &&
(skb = skb_dequeue(&ppp->file.xq)))
ppp_send_frame(ppp, skb);
@@ -1436,7 +1436,7 @@
ppp_xmit_unlock(ppp);
}
-static void ppp_xmit_process(struct ppp *ppp)
+static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
{
local_bh_disable();
@@ -1444,7 +1444,7 @@
goto err;
(*this_cpu_ptr(ppp->xmit_recursion))++;
- __ppp_xmit_process(ppp);
+ __ppp_xmit_process(ppp, skb);
(*this_cpu_ptr(ppp->xmit_recursion))--;
local_bh_enable();
@@ -1454,6 +1454,8 @@
err:
local_bh_enable();
+ kfree_skb(skb);
+
if (net_ratelimit())
netdev_err(ppp->dev, "recursion detected\n");
}
@@ -1938,7 +1940,7 @@
if (skb_queue_empty(&pch->file.xq)) {
ppp = pch->ppp;
if (ppp)
- __ppp_xmit_process(ppp);
+ __ppp_xmit_process(ppp, NULL);
}
}
@@ -3157,6 +3159,15 @@
goto outl;
ppp_lock(ppp);
+ spin_lock_bh(&pch->downl);
+ if (!pch->chan) {
+ /* Don't connect unregistered channels */
+ spin_unlock_bh(&pch->downl);
+ ppp_unlock(ppp);
+ ret = -ENOTCONN;
+ goto outl;
+ }
+ spin_unlock_bh(&pch->downl);
if (pch->file.hdrlen > ppp->file.hdrlen)
ppp->file.hdrlen = pch->file.hdrlen;
hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index dc36c2e..fa2c7bd 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -620,6 +620,10 @@
lock_sock(sk);
error = -EINVAL;
+
+ if (sockaddr_len != sizeof(struct sockaddr_pppox))
+ goto end;
+
if (sp->sa_protocol != PX_PROTO_OE)
goto end;
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 1951b10..3045c96 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -465,7 +465,6 @@
po->chan.mtu = dst_mtu(&rt->dst);
if (!po->chan.mtu)
po->chan.mtu = PPP_MRU;
- ip_rt_put(rt);
po->chan.mtu -= PPTP_HEADER_OVERHEAD;
po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c
index 27ed252..cfd81eb 100644
--- a/drivers/net/slip/slhc.c
+++ b/drivers/net/slip/slhc.c
@@ -509,6 +509,10 @@
if(x < 0 || x > comp->rslot_limit)
goto bad;
+ /* Check if the cstate is initialized */
+ if (!comp->rstate[x].initialized)
+ goto bad;
+
comp->flags &=~ SLF_TOSS;
comp->recv_current = x;
} else {
@@ -673,6 +677,7 @@
if (cs->cs_tcp.doff > 5)
memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
+ cs->initialized = true;
/* Put headers back on packet
* Neither header checksum is recalculated
*/
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 2668170..3696368 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -261,6 +261,17 @@
}
}
+static bool __team_option_inst_tmp_find(const struct list_head *opts,
+ const struct team_option_inst *needle)
+{
+ struct team_option_inst *opt_inst;
+
+ list_for_each_entry(opt_inst, opts, tmp_list)
+ if (opt_inst == needle)
+ return true;
+ return false;
+}
+
static int __team_options_register(struct team *team,
const struct team_option *option,
size_t option_count)
@@ -1067,14 +1078,11 @@
}
#ifdef CONFIG_NET_POLL_CONTROLLER
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int __team_port_enable_netpoll(struct team_port *port)
{
struct netpoll *np;
int err;
- if (!team->dev->npinfo)
- return 0;
-
np = kzalloc(sizeof(*np), GFP_KERNEL);
if (!np)
return -ENOMEM;
@@ -1088,6 +1096,14 @@
return err;
}
+static int team_port_enable_netpoll(struct team_port *port)
+{
+ if (!port->team->dev->npinfo)
+ return 0;
+
+ return __team_port_enable_netpoll(port);
+}
+
static void team_port_disable_netpoll(struct team_port *port)
{
struct netpoll *np = port->np;
@@ -1102,7 +1118,7 @@
kfree(np);
}
#else
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int team_port_enable_netpoll(struct team_port *port)
{
return 0;
}
@@ -1203,11 +1219,6 @@
goto err_dev_open;
}
- netif_addr_lock_bh(dev);
- dev_uc_sync_multiple(port_dev, dev);
- dev_mc_sync_multiple(port_dev, dev);
- netif_addr_unlock_bh(dev);
-
err = vlan_vids_add_by_dev(port_dev, dev);
if (err) {
netdev_err(dev, "Failed to add vlan ids to device %s\n",
@@ -1215,7 +1226,7 @@
goto err_vids_add;
}
- err = team_port_enable_netpoll(team, port);
+ err = team_port_enable_netpoll(port);
if (err) {
netdev_err(dev, "Failed to enable netpoll on device %s\n",
portname);
@@ -1247,6 +1258,11 @@
goto err_option_port_add;
}
+ netif_addr_lock_bh(dev);
+ dev_uc_sync_multiple(port_dev, dev);
+ dev_mc_sync_multiple(port_dev, dev);
+ netif_addr_unlock_bh(dev);
+
port->index = -1;
list_add_tail_rcu(&port->list, &team->port_list);
team_port_enable(team, port);
@@ -1271,8 +1287,6 @@
vlan_vids_del_by_dev(port_dev, dev);
err_vids_add:
- dev_uc_unsync(port_dev, dev);
- dev_mc_unsync(port_dev, dev);
dev_close(port_dev);
err_dev_open:
@@ -1910,7 +1924,7 @@
mutex_lock(&team->lock);
list_for_each_entry(port, &team->port_list, list) {
- err = team_port_enable_netpoll(team, port);
+ err = __team_port_enable_netpoll(port);
if (err) {
__team_netpoll_cleanup(team);
break;
@@ -2403,7 +2417,7 @@
if (!nlh) {
err = __send_and_alloc_skb(&skb, team, portid, send_func);
if (err)
- goto errout;
+ return err;
goto send_done;
}
@@ -2571,6 +2585,14 @@
if (err)
goto team_put;
opt_inst->changed = true;
+
+ /* dumb/evil user-space can send us duplicate opt,
+ * keep only the last one
+ */
+ if (__team_option_inst_tmp_find(&opt_inst_list,
+ opt_inst))
+ continue;
+
list_add(&opt_inst->tmp_list, &opt_inst_list);
}
if (!opt_found) {
@@ -2688,7 +2710,7 @@
if (!nlh) {
err = __send_and_alloc_skb(&skb, team, portid, send_func);
if (err)
- goto errout;
+ return err;
goto send_done;
}
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 1fca002..99424c8 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -530,6 +530,7 @@
#define REALTEK_VENDOR_ID 0x0bda
#define SAMSUNG_VENDOR_ID 0x04e8
#define LENOVO_VENDOR_ID 0x17ef
+#define LINKSYS_VENDOR_ID 0x13b1
#define NVIDIA_VENDOR_ID 0x0955
#define HP_VENDOR_ID 0x03f0
@@ -719,6 +720,15 @@
.driver_info = 0,
},
+#if IS_ENABLED(CONFIG_USB_RTL8152)
+/* Linksys USB3GIGV1 Ethernet Adapter */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(LINKSYS_VENDOR_ID, 0x0041, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+#endif
+
/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
{
USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
@@ -774,6 +784,12 @@
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&wwan_info,
}, {
+ /* Cinterion AHS3 modem by GEMALTO */
+ USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0055, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_info,
+}, {
/* Telit modules */
USB_VENDOR_AND_INTERFACE_INFO(0x1bc7, USB_CLASS_COMM,
USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index dc6d3b0..feb61ea 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1118,6 +1118,7 @@
u16 n = 0, index, ndplen;
u8 ready2send = 0;
u32 delayed_ndp_size;
+ size_t padding_count;
/* When our NDP gets written in cdc_ncm_ndp(), then skb_out->len gets updated
* accordingly. Otherwise, we should check here.
@@ -1274,11 +1275,13 @@
* a ZLP after full sized NTBs.
*/
if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
- skb_out->len > ctx->min_tx_pkt)
- memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0,
- ctx->tx_max - skb_out->len);
- else if (skb_out->len < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0)
+ skb_out->len > ctx->min_tx_pkt) {
+ padding_count = ctx->tx_max - skb_out->len;
+ memset(skb_put(skb_out, padding_count), 0, padding_count);
+ } else if (skb_out->len < ctx->tx_max &&
+ (skb_out->len % dev->maxpacket) == 0) {
*skb_put(skb_out, 1) = 0; /* force short packet */
+ }
/* set final frame length */
nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index c53385a..f5a9667 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -873,7 +873,8 @@
offset += 0x100;
else
ret = -EINVAL;
- ret = lan78xx_read_raw_otp(dev, offset, length, data);
+ if (!ret)
+ ret = lan78xx_read_raw_otp(dev, offset, length, data);
}
return ret;
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index e1e5e84..8daf5db 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -531,7 +531,7 @@
static const struct driver_info qmi_wwan_info = {
.description = "WWAN/QMI device",
- .flags = FLAG_WWAN,
+ .flags = FLAG_WWAN | FLAG_SEND_ZLP,
.bind = qmi_wwan_bind,
.unbind = qmi_wwan_unbind,
.manage_power = qmi_wwan_manage_power,
@@ -540,7 +540,7 @@
static const struct driver_info qmi_wwan_info_quirk_dtr = {
.description = "WWAN/QMI device",
- .flags = FLAG_WWAN,
+ .flags = FLAG_WWAN | FLAG_SEND_ZLP,
.bind = qmi_wwan_bind,
.unbind = qmi_wwan_unbind,
.manage_power = qmi_wwan_manage_power,
@@ -803,11 +803,16 @@
{QMI_FIXED_INTF(0x05c6, 0x9080, 8)},
{QMI_FIXED_INTF(0x05c6, 0x9083, 3)},
{QMI_FIXED_INTF(0x05c6, 0x9084, 4)},
+ {QMI_FIXED_INTF(0x05c6, 0x90b2, 3)}, /* ublox R410M */
{QMI_FIXED_INTF(0x05c6, 0x920d, 0)},
{QMI_FIXED_INTF(0x05c6, 0x920d, 5)},
+ {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */
{QMI_FIXED_INTF(0x0846, 0x68a2, 8)},
{QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */
{QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 3)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 4)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 5)}, /* Wistron NeWeb D18Q1 */
{QMI_FIXED_INTF(0x16d8, 0x6003, 0)}, /* CMOTech 6003 */
{QMI_FIXED_INTF(0x16d8, 0x6007, 0)}, /* CMOTech CHE-628S */
{QMI_FIXED_INTF(0x16d8, 0x6008, 0)}, /* CMOTech CMU-301 */
@@ -914,6 +919,7 @@
{QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */
{QMI_QUIRK_SET_DTR(0x1bc7, 0x1040, 2)}, /* Telit LE922A */
{QMI_FIXED_INTF(0x1bc7, 0x1100, 3)}, /* Telit ME910 */
+ {QMI_FIXED_INTF(0x1bc7, 0x1101, 3)}, /* Telit ME910 dual modem */
{QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1bc7, 0x1201, 2)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)}, /* XS Stick W100-2 from 4G Systems */
@@ -939,6 +945,7 @@
{QMI_FIXED_INTF(0x413c, 0x81b6, 8)}, /* Dell Wireless 5811e */
{QMI_FIXED_INTF(0x413c, 0x81b6, 10)}, /* Dell Wireless 5811e */
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+ {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
{QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
@@ -1036,6 +1043,18 @@
id->driver_info = (unsigned long)&qmi_wwan_info;
}
+ /* There are devices where the same interface number can be
+ * configured as different functions. We should only bind to
+ * vendor specific functions when matching on interface number
+ */
+ if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER &&
+ desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) {
+ dev_dbg(&intf->dev,
+ "Rejecting interface number match for class %02x\n",
+ desc->bInterfaceClass);
+ return -ENODEV;
+ }
+
/* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */
if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) {
dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n");
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index b2d7c7e..3cdfa24 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -519,6 +519,7 @@
#define VENDOR_ID_REALTEK 0x0bda
#define VENDOR_ID_SAMSUNG 0x04e8
#define VENDOR_ID_LENOVO 0x17ef
+#define VENDOR_ID_LINKSYS 0x13b1
#define VENDOR_ID_NVIDIA 0x0955
#define MCU_TYPE_PLA 0x0100
@@ -4506,6 +4507,7 @@
{REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041)},
{REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA, 0x09ff)},
{}
};
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index fbc853e..ee7460e 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -425,6 +425,9 @@
if (ifmp && (dev->ifindex != 0))
peer->ifindex = ifmp->ifi_index;
+ peer->gso_max_size = dev->gso_max_size;
+ peer->gso_max_segs = dev->gso_max_segs;
+
err = register_netdevice(peer);
put_net(net);
net = NULL;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 1568aed..472ed6d 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -529,7 +529,12 @@
hdr = skb_vnet_hdr(skb);
sg_init_table(rq->sg, 2);
sg_set_buf(rq->sg, hdr, vi->hdr_len);
- skb_to_sgvec(skb, rq->sg + 1, 0, skb->len);
+
+ err = skb_to_sgvec(skb, rq->sg + 1, 0, skb->len);
+ if (unlikely(err < 0)) {
+ dev_kfree_skb(skb);
+ return err;
+ }
err = virtqueue_add_inbuf(rq->vq, rq->sg, 2, skb, gfp);
if (err < 0)
@@ -831,7 +836,7 @@
struct virtio_net_hdr_mrg_rxbuf *hdr;
const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
struct virtnet_info *vi = sq->vq->vdev->priv;
- unsigned num_sg;
+ int num_sg;
unsigned hdr_len = vi->hdr_len;
bool can_push;
@@ -858,11 +863,16 @@
if (can_push) {
__skb_push(skb, hdr_len);
num_sg = skb_to_sgvec(skb, sq->sg, 0, skb->len);
+ if (unlikely(num_sg < 0))
+ return num_sg;
/* Pull header back to avoid skew in tx bytes calculations. */
__skb_pull(skb, hdr_len);
} else {
sg_set_buf(sq->sg, hdr, hdr_len);
- num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1;
+ num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len);
+ if (unlikely(num_sg < 0))
+ return num_sg;
+ num_sg++;
}
return virtqueue_add_outbuf(sq->vq, sq->sg, num_sg, skb, GFP_ATOMIC);
}
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 4afba17..c999b10 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -369,6 +369,11 @@
gdesc = tq->comp_ring.base + tq->comp_ring.next2proc;
while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) {
+ /* Prevent any &gdesc->tcd field from being (speculatively)
+ * read before (&gdesc->tcd)->gen is read.
+ */
+ dma_rmb();
+
completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX(
&gdesc->tcd), tq, adapter->pdev,
adapter);
@@ -1099,6 +1104,11 @@
gdesc->txd.tci = skb_vlan_tag_get(skb);
}
+ /* Ensure that the write to (&gdesc->txd)->gen will be observed after
+ * all other writes to &gdesc->txd.
+ */
+ dma_wmb();
+
/* finally flips the GEN bit of the SOP desc. */
gdesc->dword[2] = cpu_to_le32(le32_to_cpu(gdesc->dword[2]) ^
VMXNET3_TXD_GEN);
@@ -1286,6 +1296,12 @@
*/
break;
}
+
+ /* Prevent any rcd field from being (speculatively) read before
+ * rcd->gen is read.
+ */
+ dma_rmb();
+
BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2 &&
rcd->rqID != rq->dataRingQid);
idx = rcd->rxdIdx;
@@ -1515,6 +1531,12 @@
ring->next2comp = idx;
num_to_alloc = vmxnet3_cmd_ring_desc_avail(ring);
ring = rq->rx_ring + ring_idx;
+
+ /* Ensure that the writes to rxd->gen bits will be observed
+ * after all other writes to rxd objects.
+ */
+ dma_wmb();
+
while (num_to_alloc) {
vmxnet3_getRxDesc(rxd, &ring->base[ring->next2fill].rxd,
&rxCmdDesc);
@@ -2675,7 +2697,7 @@
/* ==================== initialization and cleanup routines ============ */
static int
-vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter, bool *dma64)
+vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter)
{
int err;
unsigned long mmio_start, mmio_len;
@@ -2687,30 +2709,12 @@
return err;
}
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
- if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
- dev_err(&pdev->dev,
- "pci_set_consistent_dma_mask failed\n");
- err = -EIO;
- goto err_set_mask;
- }
- *dma64 = true;
- } else {
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
- dev_err(&pdev->dev,
- "pci_set_dma_mask failed\n");
- err = -EIO;
- goto err_set_mask;
- }
- *dma64 = false;
- }
-
err = pci_request_selected_regions(pdev, (1 << 2) - 1,
vmxnet3_driver_name);
if (err) {
dev_err(&pdev->dev,
"Failed to request region for adapter: error %d\n", err);
- goto err_set_mask;
+ goto err_enable_device;
}
pci_set_master(pdev);
@@ -2738,7 +2742,7 @@
iounmap(adapter->hw_addr0);
err_ioremap:
pci_release_selected_regions(pdev, (1 << 2) - 1);
-err_set_mask:
+err_enable_device:
pci_disable_device(pdev);
return err;
}
@@ -2962,6 +2966,11 @@
/* we need to enable NAPI, otherwise dev_close will deadlock */
for (i = 0; i < adapter->num_rx_queues; i++)
napi_enable(&adapter->rx_queue[i].napi);
+ /*
+ * Need to clear the quiesce bit to ensure that vmxnet3_close
+ * can quiesce the device properly
+ */
+ clear_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
dev_close(adapter->netdev);
}
@@ -3241,7 +3250,7 @@
#endif
};
int err;
- bool dma64 = false; /* stupid gcc */
+ bool dma64;
u32 ver;
struct net_device *netdev;
struct vmxnet3_adapter *adapter;
@@ -3287,6 +3296,24 @@
adapter->rx_ring_size = VMXNET3_DEF_RX_RING_SIZE;
adapter->rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE;
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
+ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
+ dev_err(&pdev->dev,
+ "pci_set_consistent_dma_mask failed\n");
+ err = -EIO;
+ goto err_set_mask;
+ }
+ dma64 = true;
+ } else {
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
+ dev_err(&pdev->dev,
+ "pci_set_dma_mask failed\n");
+ err = -EIO;
+ goto err_set_mask;
+ }
+ dma64 = false;
+ }
+
spin_lock_init(&adapter->cmd_lock);
adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter,
sizeof(struct vmxnet3_adapter),
@@ -3294,7 +3321,7 @@
if (dma_mapping_error(&adapter->pdev->dev, adapter->adapter_pa)) {
dev_err(&pdev->dev, "Failed to map dma\n");
err = -EFAULT;
- goto err_dma_map;
+ goto err_set_mask;
}
adapter->shared = dma_alloc_coherent(
&adapter->pdev->dev,
@@ -3345,7 +3372,7 @@
}
#endif /* VMXNET3_RSS */
- err = vmxnet3_alloc_pci_resources(adapter, &dma64);
+ err = vmxnet3_alloc_pci_resources(adapter);
if (err < 0)
goto err_alloc_pci;
@@ -3487,7 +3514,7 @@
err_alloc_shared:
dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa,
sizeof(struct vmxnet3_adapter), PCI_DMA_TODEVICE);
-err_dma_map:
+err_set_mask:
free_netdev(netdev);
return err;
}
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 346e486..42c9480 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -585,13 +585,15 @@
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
- if (!IS_ERR(neigh))
+ if (!IS_ERR(neigh)) {
ret = dst_neigh_output(dst, neigh, skb);
+ rcu_read_unlock_bh();
+ return ret;
+ }
rcu_read_unlock_bh();
err:
- if (unlikely(ret < 0))
- vrf_tx_error(skb->dev, skb);
+ vrf_tx_error(skb->dev, skb);
return ret;
}
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 983e941..28afdf2 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -930,7 +930,7 @@
return false;
/* Don't migrate static entries, drop packets */
- if (f->state & NUD_NOARP)
+ if (f->state & (NUD_PERMANENT | NUD_NOARP))
return true;
if (net_ratelimit())
@@ -2816,17 +2816,21 @@
static int vxlan_sock_add(struct vxlan_dev *vxlan)
{
- bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA;
+ bool ipv6 = vxlan->flags & VXLAN_F_IPV6 || metadata;
+ bool ipv4 = !ipv6 || metadata;
int ret = 0;
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
#if IS_ENABLED(CONFIG_IPV6)
RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
- if (ipv6 || metadata)
+ if (ipv6) {
ret = __vxlan_sock_add(vxlan, true);
+ if (ret < 0 && ret != -EAFNOSUPPORT)
+ ipv4 = false;
+ }
#endif
- if (!ret && (!ipv6 || metadata))
+ if (ipv4)
ret = __vxlan_sock_add(vxlan, false);
if (ret < 0)
vxlan_sock_release(vxlan);
@@ -2912,6 +2916,11 @@
return -EINVAL;
}
+ if (lowerdev) {
+ dev->gso_max_size = lowerdev->gso_max_size;
+ dev->gso_max_segs = lowerdev->gso_max_segs;
+ }
+
if (conf->mtu) {
err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false);
if (err)
diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
index 6564753..a8bd68f 100644
--- a/drivers/net/wan/fsl_ucc_hdlc.c
+++ b/drivers/net/wan/fsl_ucc_hdlc.c
@@ -137,7 +137,7 @@
priv->tx_ring_size = TX_BD_RING_LEN;
/* Alloc Rx BD */
priv->rx_bd_base = dma_alloc_coherent(priv->dev,
- RX_BD_RING_LEN * sizeof(struct qe_bd *),
+ RX_BD_RING_LEN * sizeof(struct qe_bd),
&priv->dma_rx_bd, GFP_KERNEL);
if (!priv->rx_bd_base) {
@@ -148,7 +148,7 @@
/* Alloc Tx BD */
priv->tx_bd_base = dma_alloc_coherent(priv->dev,
- TX_BD_RING_LEN * sizeof(struct qe_bd *),
+ TX_BD_RING_LEN * sizeof(struct qe_bd),
&priv->dma_tx_bd, GFP_KERNEL);
if (!priv->tx_bd_base) {
@@ -158,7 +158,7 @@
}
/* Alloc parameter ram for ucc hdlc */
- priv->ucc_pram_offset = qe_muram_alloc(sizeof(priv->ucc_pram),
+ priv->ucc_pram_offset = qe_muram_alloc(sizeof(struct ucc_hdlc_param),
ALIGNMENT_OF_UCC_HDLC_PRAM);
if (priv->ucc_pram_offset < 0) {
@@ -295,11 +295,11 @@
qe_muram_free(priv->ucc_pram_offset);
free_tx_bd:
dma_free_coherent(priv->dev,
- TX_BD_RING_LEN * sizeof(struct qe_bd *),
+ TX_BD_RING_LEN * sizeof(struct qe_bd),
priv->tx_bd_base, priv->dma_tx_bd);
free_rx_bd:
dma_free_coherent(priv->dev,
- RX_BD_RING_LEN * sizeof(struct qe_bd *),
+ RX_BD_RING_LEN * sizeof(struct qe_bd),
priv->rx_bd_base, priv->dma_rx_bd);
free_uccf:
ucc_fast_free(priv->uccf);
@@ -454,7 +454,7 @@
static int hdlc_rx_done(struct ucc_hdlc_private *priv, int rx_work_limit)
{
struct net_device *dev = priv->ndev;
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
hdlc_device *hdlc = dev_to_hdlc(dev);
struct qe_bd *bd;
u32 bd_status;
@@ -688,7 +688,7 @@
if (priv->rx_bd_base) {
dma_free_coherent(priv->dev,
- RX_BD_RING_LEN * sizeof(struct qe_bd *),
+ RX_BD_RING_LEN * sizeof(struct qe_bd),
priv->rx_bd_base, priv->dma_rx_bd);
priv->rx_bd_base = NULL;
@@ -697,7 +697,7 @@
if (priv->tx_bd_base) {
dma_free_coherent(priv->dev,
- TX_BD_RING_LEN * sizeof(struct qe_bd *),
+ TX_BD_RING_LEN * sizeof(struct qe_bd),
priv->tx_bd_base, priv->dma_tx_bd);
priv->tx_bd_base = NULL;
@@ -1002,7 +1002,7 @@
struct device_node *np = pdev->dev.of_node;
struct ucc_hdlc_private *uhdlc_priv = NULL;
struct ucc_tdm_info *ut_info;
- struct ucc_tdm *utdm;
+ struct ucc_tdm *utdm = NULL;
struct resource res;
struct net_device *dev;
hdlc_device *hdlc;
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 47fdb87..8a9aced 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -574,7 +574,10 @@
ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0,
0, NULL);
proto->restart_counter--;
- } else
+ } else if (netif_carrier_ok(proto->dev))
+ ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0,
+ 0, NULL);
+ else
ppp_cp_event(proto->dev, proto->pid, TO_BAD, 0, 0,
0, NULL);
break;
diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c
index db36385..2b06499 100644
--- a/drivers/net/wan/pc300too.c
+++ b/drivers/net/wan/pc300too.c
@@ -347,6 +347,7 @@
card->rambase == NULL) {
pr_err("ioremap() failed\n");
pc300_pci_remove_one(pdev);
+ return -ENOMEM;
}
/* PLX PCI 9050 workaround for local configuration register read bug */
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
index 7d3231a..82bdec7 100644
--- a/drivers/net/wireless/ath/ath10k/bmi.h
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -83,6 +83,8 @@
#define BMI_NVRAM_SEG_NAME_SZ 16
#define BMI_PARAM_GET_EEPROM_BOARD_ID 0x10
+#define BMI_PARAM_GET_FLASH_BOARD_ID 0x8000
+#define BMI_PARAM_FLASH_SECTION_ALL 0x10000
#define ATH10K_BMI_BOARD_ID_FROM_OTP_MASK 0x7c00
#define ATH10K_BMI_BOARD_ID_FROM_OTP_LSB 10
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index 0b4d796..041ef3b 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -1059,7 +1059,7 @@
*/
BUILD_BUG_ON(2 * TARGET_NUM_MSDU_DESC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
- BUILD_BUG_ON(2 * TARGET_10X_NUM_MSDU_DESC >
+ BUILD_BUG_ON(2 * TARGET_10_4_NUM_MSDU_DESC_PFC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
BUILD_BUG_ON(2 * TARGET_TLV_NUM_MSDU_DESC >
(CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 7b3017f..65ad7a1 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -652,7 +652,7 @@
{
u32 result, address;
u8 board_id, chip_id;
- int ret;
+ int ret, bmi_board_id_param;
address = ar->hw_params.patch_load_addr;
@@ -676,8 +676,13 @@
return ret;
}
- ret = ath10k_bmi_execute(ar, address, BMI_PARAM_GET_EEPROM_BOARD_ID,
- &result);
+ if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
+ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE)
+ bmi_board_id_param = BMI_PARAM_GET_FLASH_BOARD_ID;
+ else
+ bmi_board_id_param = BMI_PARAM_GET_EEPROM_BOARD_ID;
+
+ ret = ath10k_bmi_execute(ar, address, bmi_board_id_param, &result);
if (ret) {
ath10k_err(ar, "could not execute otp for board id check: %d\n",
ret);
@@ -739,6 +744,11 @@
return ret;
}
+ /* As of now pre-cal is valid for 10_4 variants */
+ if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
+ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE)
+ bmi_otp_exe_param = BMI_PARAM_FLASH_SECTION_ALL;
+
ret = ath10k_bmi_execute(ar, address, bmi_otp_exe_param, &result);
if (ret) {
ath10k_err(ar, "could not execute otp (%d)\n", ret);
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 82a4c67..0dadc60 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -624,17 +624,21 @@
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
- char buf[32];
+ char buf[32] = {0};
+ ssize_t rc;
int ret;
- simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+ /* filter partial writes and invalid commands */
+ if (*ppos != 0 || count >= sizeof(buf) || count == 0)
+ return -EINVAL;
- /* make sure that buf is null terminated */
- buf[sizeof(buf) - 1] = 0;
+ rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+ if (rc < 0)
+ return rc;
/* drop the possible '\n' from the end */
- if (buf[count - 1] == '\n')
- buf[count - 1] = 0;
+ if (buf[*ppos - 1] == '\n')
+ buf[*ppos - 1] = '\0';
mutex_lock(&ar->conf_mutex);
@@ -1942,6 +1946,15 @@
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
+ struct ath10k_vif *arvif;
+
+ /* Just check for for the first vif alone, as all the vifs will be
+ * sharing the same channel and if the channel is disabled, all the
+ * vifs will share the same 'is_started' state.
+ */
+ arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);
+ if (!arvif->is_started)
+ return -EINVAL;
ieee80211_radar_detected(ar->hw);
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 17ab8ef..5aa5df2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -2505,7 +2505,7 @@
}
break;
case WMI_VDEV_TYPE_STA:
- if (vif->bss_conf.qos)
+ if (sta->wme)
arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
case WMI_VDEV_TYPE_IBSS:
@@ -5819,9 +5819,8 @@
sta->addr, smps, err);
}
- if (changed & IEEE80211_RC_SUPP_RATES_CHANGED ||
- changed & IEEE80211_RC_NSS_CHANGED) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n",
+ if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
sta->addr);
err = ath10k_station_assoc(ar, arvif->vif, sta, true);
@@ -6054,6 +6053,16 @@
"mac vdev %d peer delete %pM sta %pK (sta gone)\n",
arvif->vdev_id, sta->addr, sta);
+ if (sta->tdls) {
+ ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
+ sta,
+ WMI_TDLS_PEER_STATE_TEARDOWN);
+ if (ret)
+ ath10k_warn(ar, "failed to update tdls peer state for %pM state %d: %i\n",
+ sta->addr,
+ WMI_TDLS_PEER_STATE_TEARDOWN, ret);
+ }
+
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
@@ -7070,7 +7079,7 @@
lockdep_assert_held(&ar->data_lock);
WARN_ON(ctx && vifs);
- WARN_ON(vifs && n_vifs != 1);
+ WARN_ON(vifs && !n_vifs);
/* FIXME: Sort of an optimization and a workaround. Peers and vifs are
* on a linked list now. Doing a lookup peer -> vif -> chanctx for each
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 54df425..e518b64 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -3638,6 +3638,11 @@
spin_lock_bh(&ar->data_lock);
ch = ar->rx_channel;
+
+ /* fetch target operating channel during channel change */
+ if (!ch)
+ ch = ar->tgt_oper_chan;
+
spin_unlock_bh(&ar->data_lock);
if (!ch) {
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 1b243c8..9b8562f 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -5017,7 +5017,8 @@
#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
#define WMI_TXBF_STS_CAP_OFFSET_LSB 4
-#define WMI_TXBF_STS_CAP_OFFSET_MASK 0xf0
+#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70
+#define WMI_TXBF_CONF_IMPLICIT_BF BIT(7)
#define WMI_BF_SOUND_DIM_OFFSET_LSB 8
#define WMI_BF_SOUND_DIM_OFFSET_MASK 0xf00
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index 4f8d9ed..4a10544 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -939,7 +939,10 @@
}
for (i = 0; i < eesize; ++i) {
- AR5K_EEPROM_READ(i, val);
+ if (!ath5k_hw_nvram_read(ah, i, &val)) {
+ ret = -EIO;
+ goto freebuf;
+ }
buf[i] = val;
}
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index a35f78b..acef4ec9 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1603,6 +1603,10 @@
int count = 50;
u32 reg, last_val;
+ /* Check if chip failed to wake up */
+ if (REG_READ(ah, AR_CFG) == 0xdeadbeef)
+ return false;
+
if (AR_SREV_9300(ah))
return !ath9k_hw_detect_mac_hang(ah);
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index f850603..404fc30 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -254,8 +254,12 @@
EXPORT_SYMBOL(ath_is_49ghz_allowed);
/* Frequency is one where radar detection is required */
-static bool ath_is_radar_freq(u16 center_freq)
+static bool ath_is_radar_freq(u16 center_freq,
+ struct ath_regulatory *reg)
+
{
+ if (reg->country_code == CTRY_INDIA)
+ return (center_freq >= 5500 && center_freq <= 5700);
return (center_freq >= 5260 && center_freq <= 5700);
}
@@ -306,7 +310,7 @@
enum nl80211_reg_initiator initiator,
struct ieee80211_channel *ch)
{
- if (ath_is_radar_freq(ch->center_freq) ||
+ if (ath_is_radar_freq(ch->center_freq, reg) ||
(ch->flags & IEEE80211_CHAN_RADAR))
return;
@@ -395,8 +399,9 @@
}
}
-/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */
-static void ath_reg_apply_radar_flags(struct wiphy *wiphy)
+/* Always apply Radar/DFS rules on freq range 5500 MHz - 5700 MHz */
+static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
+ struct ath_regulatory *reg)
{
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
@@ -409,7 +414,7 @@
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
- if (!ath_is_radar_freq(ch->center_freq))
+ if (!ath_is_radar_freq(ch->center_freq, reg))
continue;
/* We always enable radar detection/DFS on this
* frequency range. Additionally we also apply on
@@ -505,7 +510,7 @@
struct ath_common *common = container_of(reg, struct ath_common,
regulatory);
/* We always apply this */
- ath_reg_apply_radar_flags(wiphy);
+ ath_reg_apply_radar_flags(wiphy, reg);
/*
* This would happen when we have sent a custom regulatory request
@@ -653,7 +658,7 @@
}
wiphy_apply_custom_regulatory(wiphy, regd);
- ath_reg_apply_radar_flags(wiphy);
+ ath_reg_apply_radar_flags(wiphy, reg);
ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
return 0;
}
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index f7c63cf..f70420f 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -21,11 +21,16 @@
#include "ftm.h"
#define WIL_MAX_ROC_DURATION_MS 5000
+#define CTRY_CHINA "CN"
bool disable_ap_sme;
module_param(disable_ap_sme, bool, 0444);
MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
+static bool country_specific_board_file;
+module_param(country_specific_board_file, bool, 0444);
+MODULE_PARM_DESC(country_specific_board_file, " switch board file upon regulatory domain change (Default: false)");
+
static bool ignore_reg_hints = true;
module_param(ignore_reg_hints, bool, 0444);
MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)");
@@ -1984,6 +1989,64 @@
return 0;
}
+static int wil_switch_board_file(struct wil6210_priv *wil,
+ const u8 *new_regdomain)
+{
+ int rc = 0;
+
+ if (!country_specific_board_file)
+ return 0;
+
+ if (memcmp(wil->regdomain, CTRY_CHINA, 2) == 0) {
+ wil_info(wil, "moving out of China reg domain, use default board file\n");
+ wil->board_file_country[0] = '\0';
+ } else if (memcmp(new_regdomain, CTRY_CHINA, 2) == 0) {
+ wil_info(wil, "moving into China reg domain, use country specific board file\n");
+ strlcpy(wil->board_file_country, CTRY_CHINA,
+ sizeof(wil->board_file_country));
+ } else {
+ return 0;
+ }
+
+ /* need to switch board file - reset the device */
+
+ mutex_lock(&wil->mutex);
+
+ if (!netif_running(wil_to_ndev(wil)) || wil_is_recovery_blocked(wil))
+ /* new board file will be used in next FW load */
+ goto out;
+
+ __wil_down(wil);
+ rc = __wil_up(wil);
+
+out:
+ mutex_unlock(&wil->mutex);
+ return rc;
+}
+
+static void wil_cfg80211_reg_notify(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ wil_info(wil, "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n",
+ request->alpha2[0], request->alpha2[1],
+ request->intersect ? " intersect" : "",
+ request->processed ? " processed" : "",
+ request->initiator, request->user_reg_hint_type);
+
+ if (memcmp(wil->regdomain, request->alpha2, 2) == 0)
+ /* reg domain did not change */
+ return;
+
+ rc = wil_switch_board_file(wil, request->alpha2);
+ if (rc)
+ wil_err(wil, "switch board file failed %d\n", rc);
+
+ memcpy(wil->regdomain, request->alpha2, 2);
+}
+
static struct cfg80211_ops wil_cfg80211_ops = {
.add_virtual_intf = wil_cfg80211_add_iface,
.del_virtual_intf = wil_cfg80211_del_iface,
@@ -2055,6 +2118,8 @@
wiphy->mgmt_stypes = wil_mgmt_stypes;
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+ wiphy->reg_notifier = wil_cfg80211_reg_notify;
+
wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
wiphy->vendor_commands = wil_nl80211_vendor_commands;
wiphy->vendor_events = wil_nl80211_vendor_events;
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 2baa6cf..f37254d 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -26,6 +26,7 @@
#define WAIT_FOR_HALP_VOTE_MS 100
#define WAIT_FOR_SCAN_ABORT_MS 1000
+#define WIL_BOARD_FILE_MAX_NAMELEN 128
bool debug_fw; /* = false; */
module_param(debug_fw, bool, 0444);
@@ -946,6 +947,30 @@
le32_to_cpus(&r->head);
}
+/* construct actual board file name to use */
+void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len)
+{
+ const char *board_file = WIL_BOARD_FILE_NAME;
+ const char *ext;
+ int prefix_len;
+
+ if (wil->board_file_country[0] == '\0') {
+ strlcpy(buf, board_file, len);
+ return;
+ }
+
+ /* use country specific board file */
+ if (len < strlen(board_file) + 4 /* for _XX and terminating null */)
+ return;
+
+ ext = strrchr(board_file, '.');
+ prefix_len = (ext ? ext - board_file : strlen(board_file));
+ snprintf(buf, len, "%.*s_%.2s",
+ prefix_len, board_file, wil->board_file_country);
+ if (ext)
+ strlcat(buf, ext, len);
+}
+
static int wil_get_bl_info(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
@@ -1260,8 +1285,12 @@
wil_set_oob_mode(wil, oob_mode);
if (load_fw) {
+ char board_file[WIL_BOARD_FILE_MAX_NAMELEN];
+
+ board_file[0] = '\0';
+ wil_get_board_file(wil, board_file, sizeof(board_file));
wil_info(wil, "Use firmware <%s> + board <%s>\n",
- wil->wil_fw_name, WIL_BOARD_FILE_NAME);
+ wil->wil_fw_name, board_file);
if (!no_flash)
wil_bl_prepare_halt(wil);
@@ -1273,11 +1302,9 @@
if (rc)
goto out;
if (wil->brd_file_addr)
- rc = wil_request_board(wil, WIL_BOARD_FILE_NAME);
+ rc = wil_request_board(wil, board_file);
else
- rc = wil_request_firmware(wil,
- WIL_BOARD_FILE_NAME,
- true);
+ rc = wil_request_firmware(wil, board_file, true);
if (rc)
goto out;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 1875387..0f34c43 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -33,10 +33,8 @@
MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false");
#ifdef CONFIG_PM
-#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_notify(struct notifier_block *notify_block,
unsigned long mode, void *unused);
-#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
static
@@ -354,7 +352,6 @@
}
#ifdef CONFIG_PM
-#ifdef CONFIG_PM_SLEEP
wil->pm_notify.notifier_call = wil6210_pm_notify;
rc = register_pm_notifier(&wil->pm_notify);
if (rc)
@@ -362,7 +359,6 @@
* be prevented in a later phase if needed
*/
wil_err(wil, "register_pm_notifier failed: %d\n", rc);
-#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
wil6210_debugfs_init(wil);
@@ -397,9 +393,7 @@
wil_dbg_misc(wil, "pcie_remove\n");
#ifdef CONFIG_PM
-#ifdef CONFIG_PM_SLEEP
unregister_pm_notifier(&wil->pm_notify);
-#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
wil_pm_runtime_forbid(wil);
@@ -428,7 +422,6 @@
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
#ifdef CONFIG_PM
-#ifdef CONFIG_PM_SLEEP
static int wil6210_suspend(struct device *dev, bool is_runtime)
{
@@ -546,7 +539,6 @@
{
return wil6210_resume(dev, false);
}
-#endif /* CONFIG_PM_SLEEP */
static int wil6210_pm_runtime_idle(struct device *dev)
{
@@ -578,10 +570,12 @@
#endif /* CONFIG_PM */
static const struct dev_pm_ops wil6210_pm_ops = {
+#ifdef CONFIG_PM
SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
SET_RUNTIME_PM_OPS(wil6210_pm_runtime_suspend,
wil6210_pm_runtime_resume,
wil6210_pm_runtime_idle)
+#endif /* CONFIG_PM */
};
static struct pci_driver wil6210_driver = {
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index 14533ed..0a96518 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -33,7 +33,11 @@
if (wmi_only || debug_fw) {
wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
wmi_only ? "wmi_only" : "debug_fw");
- rc = -EPERM;
+ rc = -EBUSY;
+ goto out;
+ }
+ if (is_runtime && !wil->platform_ops.suspend) {
+ rc = -EBUSY;
goto out;
}
if (!(ndev->flags & IFF_UP)) {
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index dcec343..d0fecd9 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -323,6 +323,8 @@
return -ENOMEM;
}
+ d->mac.pn_15_0 = 0;
+ d->mac.pn_47_16 = 0;
d->dma.d0 = RX_DMA_D0_CMD_DMA_RT | RX_DMA_D0_CMD_DMA_IT;
wil_desc_addr_set(&d->dma.addr, pa);
/* ip_length don't care */
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 9b68663..2b71deb 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -683,6 +683,7 @@
const char *hw_name;
const char *wil_fw_name;
char *board_file;
+ char board_file_country[3]; /* alpha2 */
u32 brd_file_addr;
u32 brd_file_max_size;
DECLARE_BITMAP(hw_capa, hw_capa_last);
@@ -796,11 +797,11 @@
} snr_thresh;
int fw_calib_result;
+ /* current reg domain configured in kernel */
+ char regdomain[3]; /* alpha2 */
#ifdef CONFIG_PM
-#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
-#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
bool suspend_resp_rcvd;
@@ -875,6 +876,8 @@
wil_w(wil, reg, wil_r(wil, reg) & ~val);
}
+void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len);
+
#if defined(CONFIG_DYNAMIC_DEBUG)
#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 54354a3..538457e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -2776,7 +2776,6 @@
struct brcmf_bss_info_le *bi)
{
struct wiphy *wiphy = cfg_to_wiphy(cfg);
- struct ieee80211_channel *notify_channel;
struct cfg80211_bss *bss;
struct ieee80211_supported_band *band;
struct brcmu_chan ch;
@@ -2786,7 +2785,7 @@
u16 notify_interval;
u8 *notify_ie;
size_t notify_ielen;
- s32 notify_signal;
+ struct cfg80211_inform_bss bss_data = {};
if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
brcmf_err("Bss info is larger than buffer. Discarding\n");
@@ -2806,27 +2805,28 @@
band = wiphy->bands[NL80211_BAND_5GHZ];
freq = ieee80211_channel_to_frequency(channel, band->band);
- notify_channel = ieee80211_get_channel(wiphy, freq);
+ bss_data.chan = ieee80211_get_channel(wiphy, freq);
+ bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
+ bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
notify_capability = le16_to_cpu(bi->capability);
notify_interval = le16_to_cpu(bi->beacon_period);
notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
notify_ielen = le32_to_cpu(bi->ie_length);
- notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+ bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;
brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
- brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
+ brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);
- bss = cfg80211_inform_bss(wiphy, notify_channel,
- CFG80211_BSS_FTYPE_UNKNOWN,
- (const u8 *)bi->BSSID,
- 0, notify_capability,
- notify_interval, notify_ie,
- notify_ielen, notify_signal,
- GFP_KERNEL);
+ bss = cfg80211_inform_bss_data(wiphy, &bss_data,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ (const u8 *)bi->BSSID,
+ 0, notify_capability,
+ notify_interval, notify_ie,
+ notify_ielen, GFP_KERNEL);
if (!bss)
return -ENOMEM;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 85d949e..f78d91b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -462,25 +462,23 @@
* @dev_addr: optional device address.
*
* P2P needs mac addresses for P2P device and interface. If no device
- * address it specified, these are derived from the primary net device, ie.
- * the permanent ethernet address of the device.
+ * address it specified, these are derived from a random ethernet
+ * address.
*/
static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
{
- struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
- bool local_admin = false;
+ bool random_addr = false;
- if (!dev_addr || is_zero_ether_addr(dev_addr)) {
- dev_addr = pri_ifp->mac_addr;
- local_admin = true;
- }
+ if (!dev_addr || is_zero_ether_addr(dev_addr))
+ random_addr = true;
- /* Generate the P2P Device Address. This consists of the device's
- * primary MAC address with the locally administered bit set.
+ /* Generate the P2P Device Address obtaining a random ethernet
+ * address with the locally administered bit set.
*/
- memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
- if (local_admin)
- p2p->dev_addr[0] |= 0x02;
+ if (random_addr)
+ eth_random_addr(p2p->dev_addr);
+ else
+ memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
/* Generate the P2P Interface Address. If the discovery and connection
* BSSCFGs need to simultaneously co-exist, then this address must be
diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile
index b49d089..318076f 100644
--- a/drivers/net/wireless/cnss2/Makefile
+++ b/drivers/net/wireless/cnss2/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_CNSS2) += cnss2.o
cnss2-y := main.o
+cnss2-y += bus.o
cnss2-y += debug.o
cnss2-y += pci.o
cnss2-y += power.o
diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c
new file mode 100644
index 0000000..c35d661
--- /dev/null
+++ b/drivers/net/wireless/cnss2/bus.c
@@ -0,0 +1,334 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "bus.h"
+#include "debug.h"
+#include "pci.h"
+
+enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev)
+{
+ if (!dev)
+ return CNSS_BUS_NONE;
+
+ if (!dev->bus)
+ return CNSS_BUS_NONE;
+
+ if (memcmp(dev->bus->name, "pci", 3) == 0)
+ return CNSS_BUS_PCI;
+ else
+ return CNSS_BUS_NONE;
+}
+
+enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id)
+{
+ switch (device_id) {
+ case QCA6174_DEVICE_ID:
+ case QCA6290_EMULATION_DEVICE_ID:
+ case QCA6290_DEVICE_ID:
+ return CNSS_BUS_PCI;
+ default:
+ cnss_pr_err("Unknown device_id: 0x%lx\n", device_id);
+ return CNSS_BUS_NONE;
+ }
+}
+
+void *cnss_bus_dev_to_bus_priv(struct device *dev)
+{
+ if (!dev)
+ return NULL;
+
+ switch (cnss_get_dev_bus_type(dev)) {
+ case CNSS_BUS_PCI:
+ return cnss_get_pci_priv(to_pci_dev(dev));
+ default:
+ return NULL;
+ }
+}
+
+struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev)
+{
+ void *bus_priv;
+
+ if (!dev)
+ return cnss_get_plat_priv(NULL);
+
+ bus_priv = cnss_bus_dev_to_bus_priv(dev);
+ if (!bus_priv)
+ return NULL;
+
+ switch (cnss_get_dev_bus_type(dev)) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_priv_to_plat_priv(bus_priv);
+ default:
+ return NULL;
+ }
+}
+
+int cnss_bus_init(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_init(plat_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+void cnss_bus_deinit(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ cnss_pci_deinit(plat_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return;
+ }
+}
+
+int cnss_bus_load_m3(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_load_m3(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_alloc_fw_mem(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_get_wake_msi(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_force_fw_assert_hdlr(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+void cnss_bus_fw_boot_timeout_hdlr(unsigned long data)
+{
+ struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data;
+
+ if (!plat_priv)
+ return;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_fw_boot_timeout_hdlr(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return;
+ }
+}
+
+void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv, bool in_panic)
+{
+ if (!plat_priv)
+ return;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_collect_dump_info(plat_priv->bus_priv,
+ in_panic);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return;
+ }
+}
+
+int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_call_driver_probe(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_bus_call_driver_remove(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_dev_powerup(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_dev_shutdown(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_dev_crash_shutdown(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_dev_crash_shutdown(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_dev_ramdump(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_dev_ramdump(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_register_driver_hdlr(plat_priv->bus_priv, data);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_unregister_driver_hdlr(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv,
+ int modem_current_status)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_call_driver_modem_status(plat_priv->bus_priv,
+ modem_current_status);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h
new file mode 100644
index 0000000..532438a
--- /dev/null
+++ b/drivers/net/wireless/cnss2/bus.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CNSS_BUS_H
+#define _CNSS_BUS_H
+
+#include "main.h"
+
+#define QCA6174_VENDOR_ID 0x168C
+#define QCA6174_DEVICE_ID 0x003E
+#define QCA6174_REV_ID_OFFSET 0x08
+#define QCA6174_REV3_VERSION 0x5020000
+#define QCA6174_REV3_2_VERSION 0x5030000
+#define QCA6290_VENDOR_ID 0x17CB
+#define QCA6290_DEVICE_ID 0x1100
+#define QCA6290_EMULATION_VENDOR_ID 0x168C
+#define QCA6290_EMULATION_DEVICE_ID 0xABCD
+
+enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev);
+enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id);
+void *cnss_bus_dev_to_bus_priv(struct device *dev);
+struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev);
+int cnss_bus_init(struct cnss_plat_data *plat_priv);
+void cnss_bus_deinit(struct cnss_plat_data *plat_priv);
+int cnss_bus_load_m3(struct cnss_plat_data *plat_priv);
+int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv);
+u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv);
+int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv);
+void cnss_bus_fw_boot_timeout_hdlr(unsigned long data);
+void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv,
+ bool in_panic);
+int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv);
+int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv);
+int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv);
+int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv);
+int cnss_bus_dev_crash_shutdown(struct cnss_plat_data *plat_priv);
+int cnss_bus_dev_ramdump(struct cnss_plat_data *plat_priv);
+int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data);
+int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv);
+int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv,
+ int modem_current_status);
+
+#endif /* _CNSS_BUS_H */
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index 9142758..76ad51c 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -23,8 +23,8 @@
#include <soc/qcom/subsystem_notif.h>
#include "main.h"
+#include "bus.h"
#include "debug.h"
-#include "pci.h"
#define CNSS_DUMP_FORMAT_VER 0x11
#define CNSS_DUMP_FORMAT_VER_V2 0x22
@@ -37,7 +37,6 @@
#define FW_READY_TIMEOUT 20000
#define FW_ASSERT_TIMEOUT 5000
#define CNSS_EVENT_PENDING 2989
-#define WAKE_MSI_NAME "WAKE"
static struct cnss_plat_data *plat_env;
@@ -55,13 +54,6 @@
MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest");
#endif
-enum cnss_debug_quirks {
- LINK_DOWN_SELF_RECOVERY,
- SKIP_DEVICE_BOOT,
- USE_CORE_ONLY_FW,
- SKIP_RECOVERY,
-};
-
unsigned long quirks;
#ifdef CONFIG_CNSS2_DEBUG
module_param(quirks, ulong, 0600);
@@ -87,64 +79,17 @@
void *data;
};
-static enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev)
-{
- if (!dev)
- return CNSS_BUS_NONE;
-
- if (!dev->bus)
- return CNSS_BUS_NONE;
-
- if (memcmp(dev->bus->name, "pci", 3) == 0)
- return CNSS_BUS_PCI;
- else
- return CNSS_BUS_NONE;
-}
-
static void cnss_set_plat_priv(struct platform_device *plat_dev,
struct cnss_plat_data *plat_priv)
{
plat_env = plat_priv;
}
-static struct cnss_plat_data *cnss_get_plat_priv(struct platform_device
- *plat_dev)
+struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev)
{
return plat_env;
}
-void *cnss_bus_dev_to_bus_priv(struct device *dev)
-{
- if (!dev)
- return NULL;
-
- switch (cnss_get_dev_bus_type(dev)) {
- case CNSS_BUS_PCI:
- return cnss_get_pci_priv(to_pci_dev(dev));
- default:
- return NULL;
- }
-}
-
-struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev)
-{
- void *bus_priv;
-
- if (!dev)
- return cnss_get_plat_priv(NULL);
-
- bus_priv = cnss_bus_dev_to_bus_priv(dev);
- if (!bus_priv)
- return NULL;
-
- switch (cnss_get_dev_bus_type(dev)) {
- case CNSS_BUS_PCI:
- return cnss_pci_priv_to_plat_priv(bus_priv);
- default:
- return NULL;
- }
-}
-
static int cnss_pm_notify(struct notifier_block *b,
unsigned long event, void *p)
{
@@ -274,23 +219,6 @@
}
EXPORT_SYMBOL(cnss_get_platform_cap);
-int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info)
-{
- int ret = 0;
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
- void *bus_priv = cnss_bus_dev_to_bus_priv(dev);
-
- if (!plat_priv)
- return -ENODEV;
-
- ret = cnss_pci_get_bar_info(bus_priv, &info->va, &info->pa);
- if (ret)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL(cnss_get_soc_info);
-
void cnss_request_pm_qos(struct device *dev, u32 qos_val)
{
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
@@ -506,22 +434,14 @@
}
EXPORT_SYMBOL(cnss_set_fw_log_mode);
-u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv)
+bool *cnss_get_qmi_bypass(void)
{
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
- int ret, num_vectors;
- u32 user_base_data, base_vector;
+ return &qmi_bypass;
+}
- ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev,
- WAKE_MSI_NAME, &num_vectors,
- &user_base_data, &base_vector);
-
- if (ret) {
- cnss_pr_err("WAKE MSI is not valid\n");
- return 0;
- }
-
- return user_base_data;
+unsigned long *cnss_get_debug_quirks(void)
+{
+ return &quirks;
}
static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv)
@@ -541,7 +461,7 @@
if (ret)
goto out;
- ret = cnss_pci_load_m3(plat_priv->bus_priv);
+ ret = cnss_bus_load_m3(plat_priv);
if (ret)
goto out;
@@ -554,79 +474,6 @@
return ret;
}
-static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv)
-{
- int ret = 0;
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) {
- clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
- cnss_pr_dbg("Skip driver probe\n");
- goto out;
- }
-
- if (!plat_priv->driver_ops) {
- cnss_pr_err("driver_ops is NULL\n");
- ret = -EINVAL;
- goto out;
- }
-
- if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
- test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) {
- ret = plat_priv->driver_ops->reinit(pci_priv->pci_dev,
- pci_priv->pci_device_id);
- if (ret) {
- cnss_pr_err("Failed to reinit host driver, err = %d\n",
- ret);
- goto out;
- }
- clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
- } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) {
- ret = plat_priv->driver_ops->probe(pci_priv->pci_dev,
- pci_priv->pci_device_id);
- if (ret) {
- cnss_pr_err("Failed to probe host driver, err = %d\n",
- ret);
- goto out;
- }
- clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
- clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
- set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
- }
-
- return 0;
-
-out:
- return ret;
-}
-
-static int cnss_driver_call_remove(struct cnss_plat_data *plat_priv)
-{
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) ||
- test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) ||
- test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) {
- cnss_pr_dbg("Skip driver remove\n");
- return 0;
- }
-
- if (!plat_priv->driver_ops) {
- cnss_pr_err("driver_ops is NULL\n");
- return -EINVAL;
- }
-
- if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
- test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) {
- plat_priv->driver_ops->shutdown(pci_priv->pci_dev);
- } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
- plat_priv->driver_ops->remove(pci_priv->pci_dev);
- clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
- }
-
- return 0;
-}
-
static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv)
{
int ret = 0;
@@ -650,7 +497,7 @@
QMI_WLFW_CALIBRATION_V01);
} else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) ||
test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
- ret = cnss_driver_call_probe(plat_priv);
+ ret = cnss_bus_call_driver_probe(plat_priv);
} else {
complete(&plat_priv->power_up_complete);
}
@@ -663,9 +510,7 @@
return 0;
shutdown:
- cnss_pci_stop_mhi(plat_priv->bus_priv);
- cnss_suspend_pci_link(plat_priv->bus_priv);
- cnss_power_off_device(plat_priv);
+ cnss_bus_dev_shutdown(plat_priv);
clear_bit(CNSS_FW_READY, &plat_priv->driver_state);
clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state);
@@ -837,44 +682,6 @@
}
EXPORT_SYMBOL(cnss_power_down);
-int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
-{
- int ret = 0;
- struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL);
-
- if (!plat_priv) {
- cnss_pr_err("plat_priv is NULL!\n");
- return -ENODEV;
- }
-
- if (plat_priv->driver_ops) {
- cnss_pr_err("Driver has already registered!\n");
- return -EEXIST;
- }
-
- ret = cnss_driver_event_post(plat_priv,
- CNSS_DRIVER_EVENT_REGISTER_DRIVER,
- CNSS_EVENT_SYNC_UNINTERRUPTIBLE,
- driver_ops);
- return ret;
-}
-EXPORT_SYMBOL(cnss_wlan_register_driver);
-
-void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops)
-{
- struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL);
-
- if (!plat_priv) {
- cnss_pr_err("plat_priv is NULL!\n");
- return;
- }
-
- cnss_driver_event_post(plat_priv,
- CNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
- CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
-}
-EXPORT_SYMBOL(cnss_wlan_unregister_driver);
-
static int cnss_get_resources(struct cnss_plat_data *plat_priv)
{
int ret = 0;
@@ -906,13 +713,11 @@
{
struct cnss_plat_data *plat_priv =
container_of(nb, struct cnss_plat_data, modem_nb);
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
struct cnss_esoc_info *esoc_info;
- struct cnss_wlan_driver *driver_ops;
cnss_pr_dbg("Modem notifier: event %lu\n", code);
- if (!pci_priv)
+ if (!plat_priv)
return NOTIFY_DONE;
esoc_info = &plat_priv->esoc_info;
@@ -924,13 +729,10 @@
else
return NOTIFY_DONE;
- driver_ops = plat_priv->driver_ops;
- if (!driver_ops || !driver_ops->modem_status)
+ if (!cnss_bus_call_driver_modem_status(plat_priv,
+ esoc_info->modem_current_status))
return NOTIFY_DONE;
- driver_ops->modem_status(pci_priv->pci_dev,
- esoc_info->modem_current_status);
-
return NOTIFY_OK;
}
@@ -1004,237 +806,6 @@
devm_unregister_esoc_client(dev, esoc_info->esoc_desc);
}
-static int cnss_qca6174_powerup(struct cnss_plat_data *plat_priv)
-{
- int ret = 0;
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- if (!pci_priv) {
- cnss_pr_err("pci_priv is NULL!\n");
- return -ENODEV;
- }
-
- ret = cnss_power_on_device(plat_priv);
- if (ret) {
- cnss_pr_err("Failed to power on device, err = %d\n", ret);
- goto out;
- }
-
- ret = cnss_resume_pci_link(pci_priv);
- if (ret) {
- cnss_pr_err("Failed to resume PCI link, err = %d\n", ret);
- goto power_off;
- }
-
- ret = cnss_driver_call_probe(plat_priv);
- if (ret)
- goto suspend_link;
-
- return 0;
-suspend_link:
- cnss_suspend_pci_link(pci_priv);
-power_off:
- cnss_power_off_device(plat_priv);
-out:
- return ret;
-}
-
-static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv)
-{
- int ret = 0;
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- if (!pci_priv)
- return -ENODEV;
-
- cnss_pm_request_resume(pci_priv);
-
- cnss_driver_call_remove(plat_priv);
-
- cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev,
- CNSS_BUS_WIDTH_NONE);
- cnss_pci_set_monitor_wake_intr(pci_priv, false);
- cnss_pci_set_auto_suspended(pci_priv, 0);
-
- ret = cnss_suspend_pci_link(pci_priv);
- if (ret)
- cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret);
-
- cnss_power_off_device(plat_priv);
-
- clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
-
- return ret;
-}
-
-static void cnss_qca6174_crash_shutdown(struct cnss_plat_data *plat_priv)
-{
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- if (!plat_priv->driver_ops)
- return;
-
- plat_priv->driver_ops->crash_shutdown(pci_priv->pci_dev);
-}
-
-static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv)
-{
- int ret = 0;
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
- unsigned int timeout;
-
- if (!pci_priv) {
- cnss_pr_err("pci_priv is NULL!\n");
- return -ENODEV;
- }
-
- if (plat_priv->ramdump_info_v2.dump_data_valid ||
- test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
- cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT);
- cnss_pci_clear_dump_info(pci_priv);
- }
-
- ret = cnss_power_on_device(plat_priv);
- if (ret) {
- cnss_pr_err("Failed to power on device, err = %d\n", ret);
- goto out;
- }
-
- ret = cnss_resume_pci_link(pci_priv);
- if (ret) {
- cnss_pr_err("Failed to resume PCI link, err = %d\n", ret);
- goto power_off;
- }
-
- timeout = cnss_get_qmi_timeout();
-
- ret = cnss_pci_start_mhi(pci_priv);
- if (ret) {
- cnss_pr_err("Failed to start MHI, err = %d\n", ret);
- if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) &&
- !pci_priv->pci_link_down_ind && timeout)
- mod_timer(&plat_priv->fw_boot_timer,
- jiffies + msecs_to_jiffies(timeout >> 1));
- return 0;
- }
-
- if (test_bit(USE_CORE_ONLY_FW, &quirks)) {
- clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state);
- clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
- return 0;
- }
-
- cnss_set_pin_connect_status(plat_priv);
-
- if (qmi_bypass) {
- ret = cnss_driver_call_probe(plat_priv);
- if (ret)
- goto stop_mhi;
- } else if (timeout) {
- mod_timer(&plat_priv->fw_boot_timer,
- jiffies + msecs_to_jiffies(timeout << 1));
- }
-
- return 0;
-
-stop_mhi:
- cnss_pci_stop_mhi(pci_priv);
- cnss_suspend_pci_link(pci_priv);
-power_off:
- cnss_power_off_device(plat_priv);
-out:
- return ret;
-}
-
-static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv)
-{
- int ret = 0;
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- if (!pci_priv)
- return -ENODEV;
-
- cnss_pm_request_resume(pci_priv);
-
- cnss_driver_call_remove(plat_priv);
-
- cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev,
- CNSS_BUS_WIDTH_NONE);
- cnss_pci_set_monitor_wake_intr(pci_priv, false);
- cnss_pci_set_auto_suspended(pci_priv, 0);
-
- cnss_pci_stop_mhi(pci_priv);
-
- ret = cnss_suspend_pci_link(pci_priv);
- if (ret)
- cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret);
-
- cnss_power_off_device(plat_priv);
-
- clear_bit(CNSS_FW_READY, &plat_priv->driver_state);
- clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state);
- clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
-
- return ret;
-}
-
-static void cnss_qca6290_crash_shutdown(struct cnss_plat_data *plat_priv)
-{
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n",
- plat_priv->driver_state);
-
- if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
- cnss_pr_dbg("Ignore crash shutdown\n");
- return;
- }
-
- cnss_pci_collect_dump_info(pci_priv, true);
-}
-
-static int cnss_powerup(struct cnss_plat_data *plat_priv)
-{
- int ret;
-
- switch (plat_priv->device_id) {
- case QCA6174_DEVICE_ID:
- ret = cnss_qca6174_powerup(plat_priv);
- break;
- case QCA6290_EMULATION_DEVICE_ID:
- case QCA6290_DEVICE_ID:
- ret = cnss_qca6290_powerup(plat_priv);
- break;
- default:
- cnss_pr_err("Unknown device_id found: 0x%lx\n",
- plat_priv->device_id);
- ret = -ENODEV;
- }
-
- return ret;
-}
-
-static int cnss_shutdown(struct cnss_plat_data *plat_priv)
-{
- int ret;
-
- switch (plat_priv->device_id) {
- case QCA6174_DEVICE_ID:
- ret = cnss_qca6174_shutdown(plat_priv);
- break;
- case QCA6290_EMULATION_DEVICE_ID:
- case QCA6290_DEVICE_ID:
- ret = cnss_qca6290_shutdown(plat_priv);
- break;
- default:
- cnss_pr_err("Unknown device_id found: 0x%lx\n",
- plat_priv->device_id);
- ret = -ENODEV;
- }
-
- return ret;
-}
-
static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc)
{
struct cnss_plat_data *plat_priv;
@@ -1255,7 +826,7 @@
return 0;
}
- return cnss_powerup(plat_priv);
+ return cnss_bus_dev_powerup(plat_priv);
}
static int cnss_subsys_shutdown(const struct subsys_desc *subsys_desc,
@@ -1279,110 +850,9 @@
return 0;
}
- return cnss_shutdown(plat_priv);
+ return cnss_bus_dev_shutdown(plat_priv);
}
-static int cnss_qca6290_ramdump(struct cnss_plat_data *plat_priv)
-{
- struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2;
- struct cnss_dump_data *dump_data = &info_v2->dump_data;
- struct cnss_dump_seg *dump_seg = info_v2->dump_data_vaddr;
- struct ramdump_segment *ramdump_segs, *s;
- int i, ret = 0;
-
- if (!info_v2->dump_data_valid ||
- dump_data->nentries == 0)
- return 0;
-
- ramdump_segs = kcalloc(dump_data->nentries,
- sizeof(*ramdump_segs),
- GFP_KERNEL);
- if (!ramdump_segs)
- return -ENOMEM;
-
- s = ramdump_segs;
- for (i = 0; i < dump_data->nentries; i++) {
- s->address = dump_seg->address;
- s->v_address = dump_seg->v_address;
- s->size = dump_seg->size;
- s++;
- dump_seg++;
- }
-
- ret = do_elf_ramdump(info_v2->ramdump_dev, ramdump_segs,
- dump_data->nentries);
- kfree(ramdump_segs);
-
- cnss_pci_set_mhi_state(plat_priv->bus_priv, CNSS_MHI_DEINIT);
- cnss_pci_clear_dump_info(plat_priv->bus_priv);
-
- return ret;
-}
-
-static int cnss_qca6174_ramdump(struct cnss_plat_data *plat_priv)
-{
- int ret = 0;
- struct cnss_ramdump_info *ramdump_info;
- struct ramdump_segment segment;
-
- ramdump_info = &plat_priv->ramdump_info;
- if (!ramdump_info->ramdump_size)
- return -EINVAL;
-
- memset(&segment, 0, sizeof(segment));
- segment.v_address = ramdump_info->ramdump_va;
- segment.size = ramdump_info->ramdump_size;
- ret = do_ramdump(ramdump_info->ramdump_dev, &segment, 1);
-
- return ret;
-}
-
-static int cnss_subsys_ramdump(int enable,
- const struct subsys_desc *subsys_desc)
-{
- int ret = 0;
- struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev);
-
- if (!plat_priv) {
- cnss_pr_err("plat_priv is NULL!\n");
- return -ENODEV;
- }
-
- if (!enable)
- return 0;
-
- switch (plat_priv->device_id) {
- case QCA6174_DEVICE_ID:
- ret = cnss_qca6174_ramdump(plat_priv);
- break;
- case QCA6290_EMULATION_DEVICE_ID:
- case QCA6290_DEVICE_ID:
- ret = cnss_qca6290_ramdump(plat_priv);
- break;
- default:
- cnss_pr_err("Unknown device_id found: 0x%lx\n",
- plat_priv->device_id);
- ret = -ENODEV;
- }
-
- return ret;
-}
-
-void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size)
-{
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
- struct cnss_ramdump_info *ramdump_info;
-
- if (!plat_priv)
- return NULL;
-
- ramdump_info = &plat_priv->ramdump_info;
- *size = ramdump_info->ramdump_size;
-
- return ramdump_info->ramdump_va;
-}
-EXPORT_SYMBOL(cnss_get_virt_ramdump_mem);
-
void cnss_device_crashed(struct device *dev)
{
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
@@ -1405,24 +875,44 @@
struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev);
if (!plat_priv) {
- cnss_pr_err("plat_priv is NULL!\n");
+ cnss_pr_err("plat_priv is NULL\n");
return;
}
- switch (plat_priv->device_id) {
- case QCA6174_DEVICE_ID:
- cnss_qca6174_crash_shutdown(plat_priv);
- break;
- case QCA6290_EMULATION_DEVICE_ID:
- case QCA6290_DEVICE_ID:
- cnss_qca6290_crash_shutdown(plat_priv);
- break;
- default:
- cnss_pr_err("Unknown device_id found: 0x%lx\n",
- plat_priv->device_id);
- }
+ cnss_bus_dev_crash_shutdown(plat_priv);
}
+static int cnss_subsys_ramdump(int enable,
+ const struct subsys_desc *subsys_desc)
+{
+ struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev);
+
+ if (!plat_priv) {
+ cnss_pr_err("plat_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ if (!enable)
+ return 0;
+
+ return cnss_bus_dev_ramdump(plat_priv);
+}
+
+void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size)
+{
+ struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
+ struct cnss_ramdump_info *ramdump_info;
+
+ if (!plat_priv)
+ return NULL;
+
+ ramdump_info = &plat_priv->ramdump_info;
+ *size = ramdump_info->ramdump_size;
+
+ return ramdump_info->ramdump_va;
+}
+EXPORT_SYMBOL(cnss_get_virt_ramdump_mem);
+
static const char *cnss_recovery_reason_to_str(enum cnss_recovery_reason reason)
{
switch (reason) {
@@ -1442,7 +932,6 @@
static int cnss_do_recovery(struct cnss_plat_data *plat_priv,
enum cnss_recovery_reason reason)
{
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
struct cnss_subsys_info *subsys_info =
&plat_priv->subsys_info;
@@ -1451,11 +940,6 @@
if (plat_priv->device_id == QCA6174_DEVICE_ID)
goto self_recovery;
- if (plat_priv->driver_ops &&
- test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state))
- plat_priv->driver_ops->update_status(pci_priv->pci_dev,
- CNSS_RECOVERY);
-
if (test_bit(SKIP_RECOVERY, &quirks)) {
cnss_pr_dbg("Skip device recovery\n");
return 0;
@@ -1468,7 +952,7 @@
break;
case CNSS_REASON_RDDM:
clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state);
- cnss_pci_collect_dump_info(pci_priv, false);
+ cnss_bus_collect_dump_info(plat_priv, false);
break;
case CNSS_REASON_DEFAULT:
case CNSS_REASON_TIMEOUT:
@@ -1488,8 +972,8 @@
return 0;
self_recovery:
- cnss_shutdown(plat_priv);
- cnss_powerup(plat_priv);
+ cnss_bus_dev_shutdown(plat_priv);
+ cnss_bus_dev_powerup(plat_priv);
return 0;
}
@@ -1575,28 +1059,6 @@
}
EXPORT_SYMBOL(cnss_schedule_recovery);
-static int cnss_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv)
-{
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
- int ret;
-
- ret = cnss_pci_set_mhi_state(plat_priv->bus_priv,
- CNSS_MHI_TRIGGER_RDDM);
- if (ret) {
- cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret);
- cnss_schedule_recovery(&pci_priv->pci_dev->dev,
- CNSS_REASON_DEFAULT);
- return 0;
- }
-
- if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) {
- mod_timer(&plat_priv->fw_boot_timer,
- jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT));
- }
-
- return 0;
-}
-
int cnss_force_fw_assert(struct device *dev)
{
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
@@ -1624,49 +1086,12 @@
}
EXPORT_SYMBOL(cnss_force_fw_assert);
-void fw_boot_timeout(unsigned long data)
-{
- struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data;
- struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
-
- cnss_pr_err("Timeout waiting for FW ready indication!\n");
-
- cnss_schedule_recovery(&pci_priv->pci_dev->dev,
- CNSS_REASON_TIMEOUT);
-}
-
-static int cnss_register_driver_hdlr(struct cnss_plat_data *plat_priv,
- void *data)
-{
- int ret = 0;
-
- set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
- plat_priv->driver_ops = data;
-
- ret = cnss_powerup(plat_priv);
- if (ret) {
- clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
- plat_priv->driver_ops = NULL;
- }
-
- return ret;
-}
-
-static int cnss_unregister_driver_hdlr(struct cnss_plat_data *plat_priv)
-{
- set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
- cnss_shutdown(plat_priv);
- plat_priv->driver_ops = NULL;
-
- return 0;
-}
-
static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv)
{
int ret = 0;
set_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state);
- ret = cnss_powerup(plat_priv);
+ ret = cnss_bus_dev_powerup(plat_priv);
if (ret)
clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state);
@@ -1675,8 +1100,9 @@
static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv)
{
+ plat_priv->cal_done = true;
cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01);
- cnss_shutdown(plat_priv);
+ cnss_bus_dev_shutdown(plat_priv);
clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state);
return 0;
@@ -1684,12 +1110,12 @@
static int cnss_power_up_hdlr(struct cnss_plat_data *plat_priv)
{
- return cnss_powerup(plat_priv);
+ return cnss_bus_dev_powerup(plat_priv);
}
static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv)
{
- cnss_shutdown(plat_priv);
+ cnss_bus_dev_shutdown(plat_priv);
return 0;
}
@@ -1730,7 +1156,7 @@
ret = cnss_wlfw_server_exit(plat_priv);
break;
case CNSS_DRIVER_EVENT_REQUEST_MEM:
- ret = cnss_pci_alloc_fw_mem(plat_priv->bus_priv);
+ ret = cnss_bus_alloc_fw_mem(plat_priv);
if (ret)
break;
ret = cnss_wlfw_respond_mem_send_sync(plat_priv);
@@ -1748,18 +1174,18 @@
ret = cnss_cold_boot_cal_done_hdlr(plat_priv);
break;
case CNSS_DRIVER_EVENT_REGISTER_DRIVER:
- ret = cnss_register_driver_hdlr(plat_priv,
- event->data);
+ ret = cnss_bus_register_driver_hdlr(plat_priv,
+ event->data);
break;
case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
- ret = cnss_unregister_driver_hdlr(plat_priv);
+ ret = cnss_bus_unregister_driver_hdlr(plat_priv);
break;
case CNSS_DRIVER_EVENT_RECOVERY:
ret = cnss_driver_recovery_hdlr(plat_priv,
event->data);
break;
case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT:
- ret = cnss_force_fw_assert_hdlr(plat_priv);
+ ret = cnss_bus_force_fw_assert_hdlr(plat_priv);
break;
case CNSS_DRIVER_EVENT_POWER_UP:
ret = cnss_power_up_hdlr(plat_priv);
@@ -1799,20 +1225,7 @@
subsys_info = &plat_priv->subsys_info;
- switch (plat_priv->device_id) {
- case QCA6174_DEVICE_ID:
- subsys_info->subsys_desc.name = "AR6320";
- break;
- case QCA6290_EMULATION_DEVICE_ID:
- case QCA6290_DEVICE_ID:
- subsys_info->subsys_desc.name = "QCA6290";
- break;
- default:
- cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id);
- ret = -ENODEV;
- goto out;
- }
-
+ subsys_info->subsys_desc.name = "wlan";
subsys_info->subsys_desc.owner = THIS_MODULE;
subsys_info->subsys_desc.powerup = cnss_subsys_powerup;
subsys_info->subsys_desc.shutdown = cnss_subsys_shutdown;
@@ -1874,7 +1287,7 @@
return msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
}
-static int cnss_qca6174_register_ramdump(struct cnss_plat_data *plat_priv)
+static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct device *dev;
@@ -1925,7 +1338,7 @@
return ret;
}
-static void cnss_qca6174_unregister_ramdump(struct cnss_plat_data *plat_priv)
+static void cnss_unregister_ramdump_v1(struct cnss_plat_data *plat_priv)
{
struct device *dev;
struct cnss_ramdump_info *ramdump_info;
@@ -1942,7 +1355,7 @@
ramdump_info->ramdump_pa);
}
-static int cnss_qca6290_register_ramdump(struct cnss_plat_data *plat_priv)
+static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct cnss_subsys_info *subsys_info;
@@ -1998,7 +1411,7 @@
return ret;
}
-static void cnss_qca6290_unregister_ramdump(struct cnss_plat_data *plat_priv)
+static void cnss_unregister_ramdump_v2(struct cnss_plat_data *plat_priv)
{
struct cnss_ramdump_info_v2 *info_v2;
@@ -2018,11 +1431,11 @@
switch (plat_priv->device_id) {
case QCA6174_DEVICE_ID:
- ret = cnss_qca6174_register_ramdump(plat_priv);
+ ret = cnss_register_ramdump_v1(plat_priv);
break;
case QCA6290_EMULATION_DEVICE_ID:
case QCA6290_DEVICE_ID:
- ret = cnss_qca6290_register_ramdump(plat_priv);
+ ret = cnss_register_ramdump_v2(plat_priv);
break;
default:
cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id);
@@ -2036,11 +1449,11 @@
{
switch (plat_priv->device_id) {
case QCA6174_DEVICE_ID:
- cnss_qca6174_unregister_ramdump(plat_priv);
+ cnss_unregister_ramdump_v1(plat_priv);
break;
case QCA6290_EMULATION_DEVICE_ID:
case QCA6290_DEVICE_ID:
- cnss_qca6290_unregister_ramdump(plat_priv);
+ cnss_unregister_ramdump_v2(plat_priv);
break;
default:
cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id);
@@ -2216,6 +1629,7 @@
plat_priv->plat_dev = plat_dev;
plat_priv->device_id = device_id->driver_data;
+ plat_priv->bus_type = cnss_get_bus_type(plat_priv->device_id);
cnss_set_plat_priv(plat_dev, plat_priv);
platform_set_drvdata(plat_dev, plat_priv);
@@ -2228,14 +1642,14 @@
if (ret)
goto free_res;
- ret = cnss_pci_init(plat_priv);
+ ret = cnss_bus_init(plat_priv);
if (ret)
goto power_off;
}
ret = cnss_register_esoc(plat_priv);
if (ret)
- goto deinit_pci;
+ goto deinit_bus;
ret = cnss_register_bus_scale(plat_priv);
if (ret)
@@ -2257,8 +1671,8 @@
if (ret)
goto deinit_qmi;
- setup_timer(&plat_priv->fw_boot_timer,
- fw_boot_timeout, (unsigned long)plat_priv);
+ setup_timer(&plat_priv->fw_boot_timer, cnss_bus_fw_boot_timeout_hdlr,
+ (unsigned long)plat_priv);
register_pm_notifier(&cnss_pm_notifier);
@@ -2284,9 +1698,9 @@
cnss_unregister_bus_scale(plat_priv);
unreg_esoc:
cnss_unregister_esoc(plat_priv);
-deinit_pci:
+deinit_bus:
if (!test_bit(SKIP_DEVICE_BOOT, &quirks))
- cnss_pci_deinit(plat_priv);
+ cnss_bus_deinit(plat_priv);
power_off:
if (!test_bit(SKIP_DEVICE_BOOT, &quirks))
cnss_power_off_device(plat_priv);
@@ -2313,7 +1727,7 @@
cnss_remove_sysfs(plat_priv);
cnss_unregister_bus_scale(plat_priv);
cnss_unregister_esoc(plat_priv);
- cnss_pci_deinit(plat_priv);
+ cnss_bus_deinit(plat_priv);
cnss_put_resources(plat_priv);
platform_set_drvdata(plat_dev, NULL);
plat_env = NULL;
diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h
index c11b206..509974a0 100644
--- a/drivers/net/wireless/cnss2/main.h
+++ b/drivers/net/wireless/cnss2/main.h
@@ -107,6 +107,7 @@
void *va;
phys_addr_t pa;
bool valid;
+ u32 type;
};
enum cnss_fw_dump_type {
@@ -169,9 +170,17 @@
u32 host_pin_result;
};
+enum cnss_debug_quirks {
+ LINK_DOWN_SELF_RECOVERY,
+ SKIP_DEVICE_BOOT,
+ USE_CORE_ONLY_FW,
+ SKIP_RECOVERY,
+};
+
struct cnss_plat_data {
struct platform_device *plat_dev;
void *bus_priv;
+ enum cnss_dev_bus_type bus_type;
struct cnss_vreg_info *vreg_info;
struct cnss_pinctrl_info pinctrl_info;
struct cnss_subsys_info subsys_info;
@@ -183,7 +192,6 @@
struct cnss_platform_cap cap;
struct pm_qos_request qos_request;
unsigned long device_id;
- struct cnss_wlan_driver *driver_ops;
enum cnss_driver_status driver_status;
u32 recovery_count;
unsigned long driver_state;
@@ -198,7 +206,8 @@
struct wlfw_rf_board_info_s_v01 board_info;
struct wlfw_soc_info_s_v01 soc_info;
struct wlfw_fw_version_info_s_v01 fw_version_info;
- struct cnss_fw_mem fw_mem;
+ u32 fw_mem_seg_len;
+ struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
struct cnss_fw_mem m3_mem;
struct cnss_pin_connect_result pin_result;
struct dentry *root_dentry;
@@ -210,10 +219,12 @@
u32 diag_reg_read_mem_type;
u32 diag_reg_read_len;
u8 *diag_reg_read_buf;
+ bool cal_done;
};
-void *cnss_bus_dev_to_bus_priv(struct device *dev);
-struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev);
+struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
+bool *cnss_get_qmi_bypass(void);
+unsigned long *cnss_get_debug_quirks(void);
int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
enum cnss_driver_event_type type,
u32 flags, void *data);
@@ -226,6 +237,5 @@
int cnss_register_ramdump(struct cnss_plat_data *plat_priv);
void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv);
void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv);
-u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv);
#endif /* _CNSS_MAIN_H */
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 9d1fbf9..e1704fb 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -17,8 +17,10 @@
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/memblock.h>
+#include <soc/qcom/ramdump.h>
#include "main.h"
+#include "bus.h"
#include "debug.h"
#include "pci.h"
@@ -43,6 +45,10 @@
#define MAX_M3_FILE_NAME_LENGTH 13
#define DEFAULT_M3_FILE_NAME "m3.bin"
+#define WAKE_MSI_NAME "WAKE"
+
+#define FW_ASSERT_TIMEOUT 5000
+
static DEFINE_SPINLOCK(pci_link_down_lock);
static unsigned int pci_link_down_panic;
@@ -222,6 +228,510 @@
}
EXPORT_SYMBOL(cnss_pci_link_down);
+int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv;
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ plat_priv = pci_priv->plat_priv;
+
+ if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) {
+ clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
+ cnss_pr_dbg("Skip driver probe\n");
+ goto out;
+ }
+
+ if (!pci_priv->driver_ops) {
+ cnss_pr_err("driver_ops is NULL\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
+ test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) {
+ ret = pci_priv->driver_ops->reinit(pci_priv->pci_dev,
+ pci_priv->pci_device_id);
+ if (ret) {
+ cnss_pr_err("Failed to reinit host driver, err = %d\n",
+ ret);
+ goto out;
+ }
+ clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
+ } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) {
+ ret = pci_priv->driver_ops->probe(pci_priv->pci_dev,
+ pci_priv->pci_device_id);
+ if (ret) {
+ cnss_pr_err("Failed to probe host driver, err = %d\n",
+ ret);
+ goto out;
+ }
+ clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
+ clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
+ set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv)
+{
+ struct cnss_plat_data *plat_priv;
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ plat_priv = pci_priv->plat_priv;
+
+ if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) ||
+ test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) ||
+ test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) {
+ cnss_pr_dbg("Skip driver remove\n");
+ return 0;
+ }
+
+ if (!pci_priv->driver_ops) {
+ cnss_pr_err("driver_ops is NULL\n");
+ return -EINVAL;
+ }
+
+ if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
+ test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) {
+ pci_priv->driver_ops->shutdown(pci_priv->pci_dev);
+ } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
+ pci_priv->driver_ops->remove(pci_priv->pci_dev);
+ clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
+ }
+
+ return 0;
+}
+
+int cnss_pci_call_driver_modem_status(struct cnss_pci_data *pci_priv,
+ int modem_current_status)
+{
+ struct cnss_wlan_driver *driver_ops;
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ driver_ops = pci_priv->driver_ops;
+ if (!driver_ops || !driver_ops->modem_status)
+ return -EINVAL;
+
+ driver_ops->modem_status(pci_priv->pci_dev, modem_current_status);
+
+ return 0;
+}
+
+static int cnss_qca6174_powerup(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
+ ret = cnss_power_on_device(plat_priv);
+ if (ret) {
+ cnss_pr_err("Failed to power on device, err = %d\n", ret);
+ goto out;
+ }
+
+ ret = cnss_resume_pci_link(pci_priv);
+ if (ret) {
+ cnss_pr_err("Failed to resume PCI link, err = %d\n", ret);
+ goto power_off;
+ }
+
+ ret = cnss_pci_call_driver_probe(pci_priv);
+ if (ret)
+ goto suspend_link;
+
+ return 0;
+suspend_link:
+ cnss_suspend_pci_link(pci_priv);
+power_off:
+ cnss_power_off_device(plat_priv);
+out:
+ return ret;
+}
+
+static int cnss_qca6174_shutdown(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
+ cnss_pm_request_resume(pci_priv);
+
+ cnss_pci_call_driver_remove(pci_priv);
+
+ cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev,
+ CNSS_BUS_WIDTH_NONE);
+ cnss_pci_set_monitor_wake_intr(pci_priv, false);
+ cnss_pci_set_auto_suspended(pci_priv, 0);
+
+ ret = cnss_suspend_pci_link(pci_priv);
+ if (ret)
+ cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret);
+
+ cnss_power_off_device(plat_priv);
+
+ clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
+
+ return ret;
+}
+
+static void cnss_qca6174_crash_shutdown(struct cnss_pci_data *pci_priv)
+{
+ if (pci_priv->driver_ops && pci_priv->driver_ops->crash_shutdown)
+ pci_priv->driver_ops->crash_shutdown(pci_priv->pci_dev);
+}
+
+static int cnss_qca6174_ramdump(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+ struct cnss_ramdump_info *ramdump_info;
+ struct ramdump_segment segment;
+
+ ramdump_info = &plat_priv->ramdump_info;
+ if (!ramdump_info->ramdump_size)
+ return -EINVAL;
+
+ memset(&segment, 0, sizeof(segment));
+ segment.v_address = ramdump_info->ramdump_va;
+ segment.size = ramdump_info->ramdump_size;
+ ret = do_ramdump(ramdump_info->ramdump_dev, &segment, 1);
+
+ return ret;
+}
+
+static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+ unsigned int timeout;
+
+ if (plat_priv->ramdump_info_v2.dump_data_valid ||
+ test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
+ cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT);
+ cnss_pci_clear_dump_info(pci_priv);
+ }
+
+ ret = cnss_power_on_device(plat_priv);
+ if (ret) {
+ cnss_pr_err("Failed to power on device, err = %d\n", ret);
+ goto out;
+ }
+
+ ret = cnss_resume_pci_link(pci_priv);
+ if (ret) {
+ cnss_pr_err("Failed to resume PCI link, err = %d\n", ret);
+ goto power_off;
+ }
+
+ timeout = cnss_get_qmi_timeout();
+
+ ret = cnss_pci_start_mhi(pci_priv);
+ if (ret) {
+ cnss_pr_err("Failed to start MHI, err = %d\n", ret);
+ if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) &&
+ !pci_priv->pci_link_down_ind && timeout)
+ mod_timer(&plat_priv->fw_boot_timer,
+ jiffies + msecs_to_jiffies(timeout >> 1));
+ return 0;
+ }
+
+ if (test_bit(USE_CORE_ONLY_FW, cnss_get_debug_quirks())) {
+ clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state);
+ clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
+ return 0;
+ }
+
+ cnss_set_pin_connect_status(plat_priv);
+
+ if (*cnss_get_qmi_bypass()) {
+ ret = cnss_pci_call_driver_probe(pci_priv);
+ if (ret)
+ goto stop_mhi;
+ } else if (timeout) {
+ mod_timer(&plat_priv->fw_boot_timer,
+ jiffies + msecs_to_jiffies(timeout << 1));
+ }
+
+ return 0;
+
+stop_mhi:
+ cnss_pci_stop_mhi(pci_priv);
+ cnss_suspend_pci_link(pci_priv);
+power_off:
+ cnss_power_off_device(plat_priv);
+out:
+ return ret;
+}
+
+static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
+ cnss_pm_request_resume(pci_priv);
+
+ cnss_pci_call_driver_remove(pci_priv);
+
+ cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev,
+ CNSS_BUS_WIDTH_NONE);
+ cnss_pci_set_monitor_wake_intr(pci_priv, false);
+ cnss_pci_set_auto_suspended(pci_priv, 0);
+
+ cnss_pci_stop_mhi(pci_priv);
+
+ ret = cnss_suspend_pci_link(pci_priv);
+ if (ret)
+ cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret);
+
+ cnss_power_off_device(plat_priv);
+
+ clear_bit(CNSS_FW_READY, &plat_priv->driver_state);
+ clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state);
+ clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
+
+ return ret;
+}
+
+static void cnss_qca6290_crash_shutdown(struct cnss_pci_data *pci_priv)
+{
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
+ cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n",
+ plat_priv->driver_state);
+
+ if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
+ cnss_pr_dbg("Ignore crash shutdown\n");
+ return;
+ }
+
+ cnss_pci_collect_dump_info(pci_priv, true);
+}
+
+static int cnss_qca6290_ramdump(struct cnss_pci_data *pci_priv)
+{
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+ struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2;
+ struct cnss_dump_data *dump_data = &info_v2->dump_data;
+ struct cnss_dump_seg *dump_seg = info_v2->dump_data_vaddr;
+ struct ramdump_segment *ramdump_segs, *s;
+ int i, ret = 0;
+
+ if (!info_v2->dump_data_valid ||
+ dump_data->nentries == 0)
+ return 0;
+
+ ramdump_segs = kcalloc(dump_data->nentries,
+ sizeof(*ramdump_segs),
+ GFP_KERNEL);
+ if (!ramdump_segs)
+ return -ENOMEM;
+
+ s = ramdump_segs;
+ for (i = 0; i < dump_data->nentries; i++) {
+ s->address = dump_seg->address;
+ s->v_address = dump_seg->v_address;
+ s->size = dump_seg->size;
+ s++;
+ dump_seg++;
+ }
+
+ ret = do_elf_ramdump(info_v2->ramdump_dev, ramdump_segs,
+ dump_data->nentries);
+ kfree(ramdump_segs);
+
+ cnss_pci_set_mhi_state(plat_priv->bus_priv, CNSS_MHI_DEINIT);
+ cnss_pci_clear_dump_info(plat_priv->bus_priv);
+
+ return ret;
+}
+
+int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (pci_priv->device_id) {
+ case QCA6174_DEVICE_ID:
+ ret = cnss_qca6174_powerup(pci_priv);
+ break;
+ case QCA6290_EMULATION_DEVICE_ID:
+ case QCA6290_DEVICE_ID:
+ ret = cnss_qca6290_powerup(pci_priv);
+ break;
+ default:
+ cnss_pr_err("Unknown device_id found: 0x%x\n",
+ pci_priv->device_id);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (pci_priv->device_id) {
+ case QCA6174_DEVICE_ID:
+ ret = cnss_qca6174_shutdown(pci_priv);
+ break;
+ case QCA6290_EMULATION_DEVICE_ID:
+ case QCA6290_DEVICE_ID:
+ ret = cnss_qca6290_shutdown(pci_priv);
+ break;
+ default:
+ cnss_pr_err("Unknown device_id found: 0x%x\n",
+ pci_priv->device_id);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (pci_priv->device_id) {
+ case QCA6174_DEVICE_ID:
+ cnss_qca6174_crash_shutdown(pci_priv);
+ break;
+ case QCA6290_EMULATION_DEVICE_ID:
+ case QCA6290_DEVICE_ID:
+ cnss_qca6290_crash_shutdown(pci_priv);
+ break;
+ default:
+ cnss_pr_err("Unknown device_id found: 0x%x\n",
+ pci_priv->device_id);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (pci_priv->device_id) {
+ case QCA6174_DEVICE_ID:
+ ret = cnss_qca6174_ramdump(pci_priv);
+ break;
+ case QCA6290_EMULATION_DEVICE_ID:
+ case QCA6290_DEVICE_ID:
+ ret = cnss_qca6290_ramdump(pci_priv);
+ break;
+ default:
+ cnss_pr_err("Unknown device_id found: 0x%x\n",
+ pci_priv->device_id);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+ struct cnss_pci_data *pci_priv;
+
+ if (!plat_priv) {
+ cnss_pr_err("plat_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ pci_priv = plat_priv->bus_priv;
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ if (pci_priv->driver_ops) {
+ cnss_pr_err("Driver has already registered\n");
+ return -EEXIST;
+ }
+
+ ret = cnss_driver_event_post(plat_priv,
+ CNSS_DRIVER_EVENT_REGISTER_DRIVER,
+ CNSS_EVENT_SYNC_UNINTERRUPTIBLE,
+ driver_ops);
+ return ret;
+}
+EXPORT_SYMBOL(cnss_wlan_register_driver);
+
+void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops)
+{
+ struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+
+ if (!plat_priv) {
+ cnss_pr_err("plat_priv is NULL\n");
+ return;
+ }
+
+ cnss_driver_event_post(plat_priv,
+ CNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+ CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(cnss_wlan_unregister_driver);
+
+int cnss_pci_register_driver_hdlr(struct cnss_pci_data *pci_priv,
+ void *data)
+{
+ int ret = 0;
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
+ set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
+ pci_priv->driver_ops = data;
+
+ ret = cnss_pci_dev_powerup(pci_priv);
+ if (ret) {
+ clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state);
+ pci_priv->driver_ops = NULL;
+ }
+
+ return ret;
+}
+
+int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv)
+{
+ struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
+
+ set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
+ cnss_pci_dev_shutdown(pci_priv);
+ pci_priv->driver_ops = NULL;
+
+ return 0;
+}
+
static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv)
{
int ret = 0;
@@ -377,7 +887,6 @@
int ret = 0;
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv;
struct cnss_wlan_driver *driver_ops;
pm_message_t state = { .event = PM_EVENT_SUSPEND };
@@ -385,11 +894,7 @@
if (!pci_priv)
goto out;
- plat_priv = pci_priv->plat_priv;
- if (!plat_priv)
- goto out;
-
- driver_ops = plat_priv->driver_ops;
+ driver_ops = pci_priv->driver_ops;
if (driver_ops && driver_ops->suspend) {
ret = driver_ops->suspend(pci_dev, state);
if (ret) {
@@ -400,7 +905,7 @@
}
}
- if (pci_priv->pci_link_state) {
+ if (pci_priv->pci_link_state == PCI_LINK_UP) {
ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND);
if (ret) {
if (driver_ops && driver_ops->resume)
@@ -432,31 +937,29 @@
int ret = 0;
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv;
struct cnss_wlan_driver *driver_ops;
if (!pci_priv)
goto out;
- plat_priv = pci_priv->plat_priv;
- if (!plat_priv)
- goto out;
-
if (pci_priv->pci_link_down_ind)
goto out;
- ret = pci_enable_device(pci_dev);
- if (ret)
- cnss_pr_err("Failed to enable PCI device, err = %d\n", ret);
+ if (pci_priv->pci_link_state == PCI_LINK_UP) {
+ ret = pci_enable_device(pci_dev);
+ if (ret)
+ cnss_pr_err("Failed to enable PCI device, err = %d\n",
+ ret);
- if (pci_priv->saved_state)
- cnss_set_pci_config_space(pci_priv,
- RESTORE_PCI_CONFIG_SPACE);
+ if (pci_priv->saved_state)
+ cnss_set_pci_config_space(pci_priv,
+ RESTORE_PCI_CONFIG_SPACE);
- pci_set_master(pci_dev);
- cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME);
+ pci_set_master(pci_dev);
+ cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME);
+ }
- driver_ops = plat_priv->driver_ops;
+ driver_ops = pci_priv->driver_ops;
if (driver_ops && driver_ops->resume) {
ret = driver_ops->resume(pci_dev);
if (ret)
@@ -475,20 +978,20 @@
int ret = 0;
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv;
struct cnss_wlan_driver *driver_ops;
if (!pci_priv)
goto out;
- plat_priv = pci_priv->plat_priv;
- if (!plat_priv)
- goto out;
-
- driver_ops = plat_priv->driver_ops;
+ driver_ops = pci_priv->driver_ops;
if (driver_ops && driver_ops->suspend_noirq)
ret = driver_ops->suspend_noirq(pci_dev);
+ ret = cnss_set_pci_link(pci_priv, PCI_LINK_DOWN);
+ if (ret)
+ goto out;
+ pci_priv->pci_link_state = PCI_LINK_DOWN;
+
out:
return ret;
}
@@ -498,17 +1001,17 @@
int ret = 0;
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv;
struct cnss_wlan_driver *driver_ops;
if (!pci_priv)
goto out;
- plat_priv = pci_priv->plat_priv;
- if (!plat_priv)
+ ret = cnss_set_pci_link(pci_priv, PCI_LINK_UP);
+ if (ret)
goto out;
+ pci_priv->pci_link_state = PCI_LINK_UP;
- driver_ops = plat_priv->driver_ops;
+ driver_ops = pci_priv->driver_ops;
if (driver_ops && driver_ops->resume_noirq &&
!pci_priv->pci_link_down_ind)
ret = driver_ops->resume_noirq(pci_dev);
@@ -522,16 +1025,11 @@
int ret = 0;
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv;
struct cnss_wlan_driver *driver_ops;
if (!pci_priv)
return -EAGAIN;
- plat_priv = pci_priv->plat_priv;
- if (!plat_priv)
- return -EAGAIN;
-
if (pci_priv->pci_link_down_ind) {
cnss_pr_dbg("PCI link down recovery is in progress!\n");
return -EAGAIN;
@@ -539,7 +1037,7 @@
cnss_pr_dbg("Runtime suspend start\n");
- driver_ops = plat_priv->driver_ops;
+ driver_ops = pci_priv->driver_ops;
if (driver_ops && driver_ops->runtime_ops &&
driver_ops->runtime_ops->runtime_suspend)
ret = driver_ops->runtime_ops->runtime_suspend(pci_dev);
@@ -554,16 +1052,11 @@
int ret = 0;
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv;
struct cnss_wlan_driver *driver_ops;
if (!pci_priv)
return -EAGAIN;
- plat_priv = pci_priv->plat_priv;
- if (!plat_priv)
- return -EAGAIN;
-
if (pci_priv->pci_link_down_ind) {
cnss_pr_dbg("PCI link down recovery is in progress!\n");
return -EAGAIN;
@@ -571,7 +1064,7 @@
cnss_pr_dbg("Runtime resume start\n");
- driver_ops = plat_priv->driver_ops;
+ driver_ops = pci_priv->driver_ops;
if (driver_ops && driver_ops->runtime_ops &&
driver_ops->runtime_ops->runtime_resume)
ret = driver_ops->runtime_ops->runtime_resume(pci_dev);
@@ -592,18 +1085,7 @@
int cnss_wlan_pm_control(struct device *dev, bool vote)
{
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
- struct cnss_pci_data *pci_priv;
- struct pci_dev *pci_dev;
-
- if (!plat_priv)
- return -ENODEV;
-
- pci_priv = plat_priv->bus_priv;
- if (!pci_priv)
- return -ENODEV;
-
- pci_dev = pci_priv->pci_dev;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
return msm_pcie_pm_control(vote ? MSM_PCIE_DISABLE_PC :
MSM_PCIE_ENABLE_PC,
@@ -615,19 +1097,17 @@
int cnss_auto_suspend(struct device *dev)
{
int ret = 0;
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
- struct pci_dev *pci_dev;
- struct cnss_pci_data *pci_priv;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
+ struct cnss_plat_data *plat_priv;
struct cnss_bus_bw_info *bus_bw_info;
- if (!plat_priv)
- return -ENODEV;
-
- pci_priv = plat_priv->bus_priv;
if (!pci_priv)
return -ENODEV;
- pci_dev = pci_priv->pci_dev;
+ plat_priv = pci_priv->plat_priv;
+ if (!plat_priv)
+ return -ENODEV;
if (pci_priv->pci_link_state) {
if (cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND)) {
@@ -673,19 +1153,18 @@
int cnss_auto_resume(struct device *dev)
{
int ret = 0;
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
- struct pci_dev *pci_dev;
- struct cnss_pci_data *pci_priv;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
+ struct cnss_plat_data *plat_priv;
struct cnss_bus_bw_info *bus_bw_info;
- if (!plat_priv)
- return -ENODEV;
-
- pci_priv = plat_priv->bus_priv;
if (!pci_priv)
return -ENODEV;
- pci_dev = pci_priv->pci_dev;
+ plat_priv = pci_priv->plat_priv;
+ if (!plat_priv)
+ return -ENODEV;
+
if (!pci_priv->pci_link_state) {
cnss_pr_dbg("Resuming PCI link\n");
if (cnss_set_pci_link(pci_priv, PCI_LINK_UP)) {
@@ -732,18 +1211,21 @@
int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv)
{
struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
- struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem;
+ struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
+ int i;
- if (!fw_mem->va && fw_mem->size) {
- fw_mem->va = dma_alloc_coherent(&pci_priv->pci_dev->dev,
- fw_mem->size, &fw_mem->pa,
- GFP_KERNEL);
- if (!fw_mem->va) {
- cnss_pr_err("Failed to allocate memory for FW, size: 0x%zx\n",
- fw_mem->size);
- fw_mem->size = 0;
+ for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
+ if (!fw_mem[i].va && fw_mem[i].size) {
+ fw_mem[i].va =
+ dma_alloc_coherent(&pci_priv->pci_dev->dev,
+ fw_mem[i].size,
+ &fw_mem[i].pa, GFP_KERNEL);
+ if (!fw_mem[i].va) {
+ cnss_pr_err("Failed to allocate memory for FW, size: 0x%zx, type: %u\n",
+ fw_mem[i].size, fw_mem[i].type);
- return -ENOMEM;
+ return -ENOMEM;
+ }
}
}
@@ -753,17 +1235,25 @@
static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv)
{
struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
- struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem;
+ struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
+ int i;
- if (fw_mem->va && fw_mem->size) {
- cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx\n",
- fw_mem->va, &fw_mem->pa, fw_mem->size);
- dma_free_coherent(&pci_priv->pci_dev->dev, fw_mem->size,
- fw_mem->va, fw_mem->pa);
- fw_mem->va = NULL;
- fw_mem->pa = 0;
- fw_mem->size = 0;
+ for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
+ if (fw_mem[i].va && fw_mem[i].size) {
+ cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n",
+ fw_mem[i].va, &fw_mem[i].pa,
+ fw_mem[i].size, fw_mem[i].type);
+ dma_free_coherent(&pci_priv->pci_dev->dev,
+ fw_mem[i].size, fw_mem[i].va,
+ fw_mem[i].pa);
+ fw_mem[i].va = NULL;
+ fw_mem[i].pa = 0;
+ fw_mem[i].size = 0;
+ fw_mem[i].type = 0;
+ }
}
+
+ plat_priv->fw_mem_seg_len = 0;
}
int cnss_pci_load_m3(struct cnss_pci_data *pci_priv)
@@ -819,18 +1309,59 @@
m3_mem->size = 0;
}
-int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va,
- phys_addr_t *pa)
+int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv)
{
+ int ret;
+ struct cnss_plat_data *plat_priv;
+
if (!pci_priv)
return -ENODEV;
- *va = pci_priv->bar;
- *pa = pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM);
+ plat_priv = pci_priv->plat_priv;
+ if (!plat_priv)
+ return -ENODEV;
+
+ ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_TRIGGER_RDDM);
+ if (ret) {
+ cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret);
+ cnss_schedule_recovery(&pci_priv->pci_dev->dev,
+ CNSS_REASON_DEFAULT);
+ return 0;
+ }
+
+ if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) {
+ mod_timer(&plat_priv->fw_boot_timer,
+ jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT));
+ }
return 0;
}
+void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv)
+{
+ if (!pci_priv)
+ return;
+
+ cnss_pr_err("Timeout waiting for FW ready indication\n");
+
+ cnss_schedule_recovery(&pci_priv->pci_dev->dev,
+ CNSS_REASON_TIMEOUT);
+}
+
+int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info)
+{
+ struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev));
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ info->va = pci_priv->bar;
+ info->pa = pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM);
+
+ return 0;
+}
+EXPORT_SYMBOL(cnss_get_soc_info);
+
static struct cnss_msi_config msi_config = {
.total_vectors = 32,
.total_users = 4,
@@ -916,7 +1447,7 @@
int *num_vectors, u32 *user_base_data,
u32 *base_vector)
{
- struct cnss_pci_data *pci_priv = dev_get_drvdata(dev);
+ struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev));
struct cnss_msi_config *msi_config;
int idx;
@@ -975,6 +1506,25 @@
}
EXPORT_SYMBOL(cnss_get_msi_address);
+u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv)
+{
+ int ret, num_vectors;
+ u32 user_base_data, base_vector;
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev,
+ WAKE_MSI_NAME, &num_vectors,
+ &user_base_data, &base_vector);
+ if (ret) {
+ cnss_pr_err("WAKE MSI is not valid\n");
+ return 0;
+ }
+
+ return user_base_data;
+}
+
static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv)
{
int ret = 0;
@@ -1107,7 +1657,7 @@
struct cnss_dump_seg *dump_seg =
plat_priv->ramdump_info_v2.dump_data_vaddr;
struct image_info *fw_image, *rddm_image;
- struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem;
+ struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
int ret, i;
ret = mhi_download_rddm_img(pci_priv->mhi_ctrl, in_panic);
@@ -1152,18 +1702,20 @@
dump_data->nentries += rddm_image->entries;
- if (fw_mem->pa && fw_mem->va && fw_mem->size) {
- cnss_pr_dbg("Collect remote heap dump segment, nentries 1\n");
+ cnss_pr_dbg("Collect remote heap dump segment\n");
- dump_seg->address = fw_mem->pa;
- dump_seg->v_address = fw_mem->va;
- dump_seg->size = fw_mem->size;
- dump_seg->type = CNSS_FW_REMOTE_HEAP;
- cnss_pr_dbg("seg-0: address 0x%lx, v_address %pK, size 0x%lx\n",
- dump_seg->address, dump_seg->v_address,
- dump_seg->size);
- dump_seg++;
- dump_data->nentries++;
+ for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
+ if (fw_mem[i].type == QMI_WLFW_MEM_TYPE_DDR_V01) {
+ dump_seg->address = fw_mem[i].pa;
+ dump_seg->v_address = fw_mem[i].va;
+ dump_seg->size = fw_mem[i].size;
+ dump_seg->type = CNSS_FW_REMOTE_HEAP;
+ cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n",
+ i, dump_seg->address, dump_seg->v_address,
+ dump_seg->size);
+ dump_seg++;
+ dump_data->nentries++;
+ }
}
if (dump_data->nentries > 0)
@@ -1214,9 +1766,9 @@
cnss_pr_dbg("MHI status cb is called with reason %d\n", reason);
- if (plat_priv->driver_ops && plat_priv->driver_ops->update_status)
- plat_priv->driver_ops->update_status(pci_priv->pci_dev,
- CNSS_FW_DOWN);
+ if (pci_priv->driver_ops && pci_priv->driver_ops->update_status)
+ pci_priv->driver_ops->update_status(pci_priv->pci_dev,
+ CNSS_FW_DOWN);
switch (reason) {
case MHI_CB_EE_RDDM:
diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h
index 4b2f6bc..79f66ac 100644
--- a/drivers/net/wireless/cnss2/pci.h
+++ b/drivers/net/wireless/cnss2/pci.h
@@ -21,16 +21,6 @@
#include "main.h"
-#define QCA6174_VENDOR_ID 0x168C
-#define QCA6174_DEVICE_ID 0x003E
-#define QCA6174_REV_ID_OFFSET 0x08
-#define QCA6174_REV3_VERSION 0x5020000
-#define QCA6174_REV3_2_VERSION 0x5030000
-#define QCA6290_VENDOR_ID 0x17CB
-#define QCA6290_DEVICE_ID 0x1100
-#define QCA6290_EMULATION_VENDOR_ID 0x168C
-#define QCA6290_EMULATION_DEVICE_ID 0xABCD
-
enum cnss_mhi_state {
CNSS_MHI_INIT,
CNSS_MHI_DEINIT,
@@ -61,6 +51,7 @@
const struct pci_device_id *pci_device_id;
u32 device_id;
u16 revision_id;
+ struct cnss_wlan_driver *driver_ops;
bool pci_link_state;
bool pci_link_down_ind;
struct pci_saved_state *saved_state;
@@ -130,8 +121,6 @@
void cnss_pci_deinit(struct cnss_plat_data *plat_priv);
int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv);
int cnss_pci_load_m3(struct cnss_pci_data *pci_priv);
-int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va,
- phys_addr_t *pa);
int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
enum cnss_mhi_state state);
int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv);
@@ -139,5 +128,18 @@
void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic);
void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv);
int cnss_pm_request_resume(struct cnss_pci_data *pci_priv);
+u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv);
+int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv);
+void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv);
+int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv);
+int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv);
+int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv);
+int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv);
+int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv);
+int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv);
+int cnss_pci_register_driver_hdlr(struct cnss_pci_data *pci_priv, void *data);
+int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv);
+int cnss_pci_call_driver_modem_status(struct cnss_pci_data *pci_priv,
+ int modem_current_status);
#endif /* _CNSS_PCI_H */
diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c
index f4344ae..222a131 100644
--- a/drivers/net/wireless/cnss2/qmi.c
+++ b/drivers/net/wireless/cnss2/qmi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,8 +15,9 @@
#include <linux/qmi_encdec.h>
#include <soc/qcom/msm_qmi_interface.h>
-#include "main.h"
+#include "bus.h"
#include "debug.h"
+#include "main.h"
#include "qmi.h"
#define WLFW_SERVICE_INS_ID_V01 1
@@ -159,17 +160,29 @@
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
- req.daemon_support_valid = 1;
- req.daemon_support = daemon_support;
+ req.num_clients_valid = 1;
+ req.num_clients = daemon_support ? 2 : 1;
+ cnss_pr_dbg("Number of clients is %d\n", req.num_clients);
- cnss_pr_dbg("daemon_support is %d\n", req.daemon_support);
-
- req.wake_msi = cnss_get_wake_msi(plat_priv);
+ req.wake_msi = cnss_bus_get_wake_irq(plat_priv);
if (req.wake_msi) {
cnss_pr_dbg("WAKE MSI base data is %d\n", req.wake_msi);
req.wake_msi_valid = 1;
}
+ req.bdf_support_valid = 1;
+ req.bdf_support = 1;
+
+ req.m3_support_valid = 1;
+ req.m3_support = 1;
+
+ req.m3_cache_support_valid = 1;
+ req.m3_cache_support = 1;
+
+ req.cal_done_valid = 1;
+ req.cal_done = plat_priv->cal_done;
+ cnss_pr_dbg("Calibration done is %d\n", plat_priv->cal_done);
+
req_desc.max_msg_len = WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN;
req_desc.msg_id = QMI_WLFW_HOST_CAP_REQ_V01;
req_desc.ei_array = wlfw_host_cap_req_msg_v01_ei;
@@ -221,8 +234,8 @@
req.request_mem_enable = 1;
req.fw_mem_ready_enable_valid = 1;
req.fw_mem_ready_enable = 1;
- req.cold_boot_cal_done_enable_valid = 1;
- req.cold_boot_cal_done_enable = 1;
+ req.fw_init_done_enable_valid = 1;
+ req.fw_init_done_enable = 1;
req.pin_connect_result_enable_valid = 1;
req.pin_connect_result_enable = 1;
@@ -260,27 +273,48 @@
void *msg, unsigned int msg_len)
{
struct msg_desc ind_desc;
- struct wlfw_request_mem_ind_msg_v01 ind_msg;
- struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem;
- int ret = 0;
+ struct wlfw_request_mem_ind_msg_v01 *ind_msg;
+ int ret = 0, i;
+
+ ind_msg = kzalloc(sizeof(*ind_msg), GFP_KERNEL);
+ if (!ind_msg)
+ return -ENOMEM;
ind_desc.msg_id = QMI_WLFW_REQUEST_MEM_IND_V01;
ind_desc.max_msg_len = WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN;
ind_desc.ei_array = wlfw_request_mem_ind_msg_v01_ei;
- ret = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
+ ret = qmi_kernel_decode(&ind_desc, ind_msg, msg, msg_len);
if (ret < 0) {
cnss_pr_err("Failed to decode request memory indication, msg_len: %u, err = %d\n",
ret, msg_len);
- return ret;
+ goto out;
}
- fw_mem->size = ind_msg.size;
+ if (ind_msg->mem_seg_len == 0 ||
+ ind_msg->mem_seg_len > QMI_WLFW_MAX_NUM_MEM_SEG_V01) {
+ cnss_pr_err("Invalid memory segment length: %u\n",
+ ind_msg->mem_seg_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cnss_pr_dbg("FW memory segment count is %u\n", ind_msg->mem_seg_len);
+ plat_priv->fw_mem_seg_len = ind_msg->mem_seg_len;
+ for (i = 0; i < plat_priv->fw_mem_seg_len; i++) {
+ plat_priv->fw_mem[i].type = ind_msg->mem_seg[i].type;
+ plat_priv->fw_mem[i].size = ind_msg->mem_seg[i].size;
+ }
cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_REQUEST_MEM,
0, NULL);
+ kfree(ind_msg);
return 0;
+
+out:
+ kfree(ind_msg);
+ return ret;
}
static int cnss_qmi_pin_result_ind_hdlr(struct cnss_plat_data *plat_priv,
@@ -317,30 +351,47 @@
int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv)
{
- struct wlfw_respond_mem_req_msg_v01 req;
- struct wlfw_respond_mem_resp_msg_v01 resp;
+ struct wlfw_respond_mem_req_msg_v01 *req;
+ struct wlfw_respond_mem_resp_msg_v01 *resp;
struct msg_desc req_desc, resp_desc;
- struct cnss_fw_mem *fw_mem = &plat_priv->fw_mem;
- int ret = 0;
+ struct cnss_fw_mem *fw_mem = plat_priv->fw_mem;
+ int ret = 0, i;
cnss_pr_dbg("Sending respond memory message, state: 0x%lx\n",
plat_priv->driver_state);
- if (!fw_mem->pa || !fw_mem->size) {
- cnss_pr_err("Memory for FW is not available!\n");
- ret = -ENOMEM;
- goto out;
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ req->mem_seg_len = plat_priv->fw_mem_seg_len;
+ for (i = 0; i < req->mem_seg_len; i++) {
+ if (!fw_mem[i].pa || !fw_mem[i].size) {
+ if (fw_mem[i].type == 0) {
+ cnss_pr_err("Invalid memory for FW type, segment = %d\n",
+ i);
+ ret = -EINVAL;
+ goto out;
+ }
+ cnss_pr_err("Memory for FW is not available for type: %u\n",
+ fw_mem[i].type);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cnss_pr_dbg("Memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n",
+ fw_mem[i].va, &fw_mem[i].pa,
+ fw_mem[i].size, fw_mem[i].type);
+
+ req->mem_seg[i].addr = fw_mem[i].pa;
+ req->mem_seg[i].size = fw_mem[i].size;
+ req->mem_seg[i].type = fw_mem[i].type;
}
- cnss_pr_dbg("Memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx\n",
- fw_mem->va, &fw_mem->pa, fw_mem->size);
-
- memset(&req, 0, sizeof(req));
- memset(&resp, 0, sizeof(resp));
-
- req.addr = fw_mem->pa;
- req.size = fw_mem->size;
-
req_desc.max_msg_len = WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN;
req_desc.msg_id = QMI_WLFW_RESPOND_MEM_REQ_V01;
req_desc.ei_array = wlfw_respond_mem_req_msg_v01_ei;
@@ -349,8 +400,8 @@
resp_desc.msg_id = QMI_WLFW_RESPOND_MEM_RESP_V01;
resp_desc.ei_array = wlfw_respond_mem_resp_msg_v01_ei;
- ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, &req,
- sizeof(req), &resp_desc, &resp, sizeof(resp),
+ ret = qmi_send_req_wait(plat_priv->qmi_wlfw_clnt, &req_desc, req,
+ sizeof(*req), &resp_desc, resp, sizeof(*resp),
QMI_WLFW_TIMEOUT_MS);
if (ret < 0) {
cnss_pr_err("Failed to send respond memory request, err = %d\n",
@@ -358,16 +409,21 @@
goto out;
}
- if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
cnss_pr_err("Respond memory request failed, result: %d, err: %d\n",
- resp.resp.result, resp.resp.error);
- ret = resp.resp.result;
+ resp->resp.result, resp->resp.error);
+ ret = resp->resp.result;
goto out;
}
+ kfree(req);
+ kfree(resp);
return 0;
+
out:
CNSS_ASSERT(0);
+ kfree(req);
+ kfree(resp);
return ret;
}
@@ -908,12 +964,12 @@
CNSS_DRIVER_EVENT_FW_MEM_READY,
0, NULL);
break;
- case QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01:
+ case QMI_WLFW_FW_READY_IND_V01:
cnss_driver_event_post(plat_priv,
CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE,
0, NULL);
break;
- case QMI_WLFW_FW_READY_IND_V01:
+ case QMI_WLFW_FW_INIT_DONE_IND_V01:
cnss_driver_event_post(plat_priv,
CNSS_DRIVER_EVENT_FW_READY,
0, NULL);
@@ -974,11 +1030,11 @@
cnss_pr_info("QMI WLFW service connected, state: 0x%lx\n",
plat_priv->driver_state);
- ret = cnss_wlfw_host_cap_send_sync(plat_priv);
+ ret = cnss_wlfw_ind_register_send_sync(plat_priv);
if (ret < 0)
goto out;
- ret = cnss_wlfw_ind_register_send_sync(plat_priv);
+ ret = cnss_wlfw_host_cap_send_sync(plat_priv);
if (ret < 0)
goto out;
diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c
index 7d6a771..bbf707b 100644
--- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c
+++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -62,7 +62,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -97,7 +97,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -123,7 +123,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -140,7 +140,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -175,7 +175,131 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_mem_cfg_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u64),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_cfg_s_v01,
+ offset),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_cfg_s_v01,
+ size),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_cfg_s_v01,
+ secure_flag),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_mem_seg_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_s_v01,
+ size),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_mem_type_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_s_v01,
+ type),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_s_v01,
+ mem_cfg_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_MEM_CFG_V01,
+ .elem_size = sizeof(struct wlfw_mem_cfg_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_s_v01,
+ mem_cfg),
+ .ei_array = wlfw_mem_cfg_s_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_mem_seg_resp_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u64),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_resp_s_v01,
+ addr),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_resp_s_v01,
+ size),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_mem_type_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_resp_s_v01,
+ type),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_mem_seg_resp_s_v01,
+ restore),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -201,7 +325,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -218,7 +342,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -235,7 +359,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -261,7 +385,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -418,7 +542,7 @@
.is_array = NO_ARRAY,
.tlv_type = 0x18,
.offset = offsetof(struct wlfw_ind_register_req_msg_v01,
- cold_boot_cal_done_enable_valid),
+ fw_init_done_enable_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
@@ -427,7 +551,7 @@
.is_array = NO_ARRAY,
.tlv_type = 0x18,
.offset = offsetof(struct wlfw_ind_register_req_msg_v01,
- cold_boot_cal_done_enable),
+ fw_init_done_enable),
},
{
.data_type = QMI_OPT_FLAG,
@@ -448,9 +572,45 @@
rejuvenate_enable),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ xo_cal_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ xo_cal_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ cal_done_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ cal_done_enable),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -489,7 +649,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -497,7 +657,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -505,7 +665,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -573,7 +733,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -608,7 +768,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -626,7 +786,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -764,7 +924,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -782,7 +942,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -790,7 +950,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -920,7 +1080,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1054,7 +1214,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1073,7 +1233,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1117,7 +1277,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1135,7 +1295,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1153,7 +1313,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1269,7 +1429,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1288,7 +1448,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1316,7 +1476,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1342,7 +1502,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1459,7 +1619,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1485,7 +1645,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1522,7 +1682,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1530,7 +1690,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1548,7 +1708,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1574,7 +1734,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1592,7 +1752,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1627,7 +1787,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1676,7 +1836,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1724,7 +1884,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1743,7 +1903,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1760,7 +1920,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1778,7 +1938,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1804,7 +1964,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1822,7 +1982,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1834,16 +1994,16 @@
.is_array = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct wlfw_host_cap_req_msg_v01,
- daemon_support_valid),
+ num_clients_valid),
},
{
- .data_type = QMI_UNSIGNED_1_BYTE,
+ .data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
- .elem_size = sizeof(u8),
+ .elem_size = sizeof(u32),
.is_array = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct wlfw_host_cap_req_msg_v01,
- daemon_support),
+ num_clients),
},
{
.data_type = QMI_OPT_FLAG,
@@ -1864,9 +2024,216 @@
wake_msi),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ gpios_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ gpios_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = QMI_WLFW_MAX_NUM_GPIO_V01,
+ .elem_size = sizeof(u32),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ gpios),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ nm_modem_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ nm_modem),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ bdf_support_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ bdf_support),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ bdf_cache_support_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ bdf_cache_support),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ m3_support_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ m3_support),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ m3_cache_support_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ m3_cache_support),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ cal_filesys_support_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ cal_filesys_support),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ cal_cache_support_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ cal_cache_support),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ cal_done_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ cal_done),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ mem_bucket_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ mem_bucket),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1C,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ mem_cfg_mode_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1C,
+ .offset = offsetof(struct wlfw_host_cap_req_msg_v01,
+ mem_cfg_mode),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1884,50 +2251,61 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct elem_info wlfw_request_mem_ind_msg_v01_ei[] = {
{
- .data_type = QMI_UNSIGNED_4_BYTE,
+ .data_type = QMI_DATA_LEN,
.elem_len = 1,
- .elem_size = sizeof(u32),
+ .elem_size = sizeof(u8),
.is_array = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct wlfw_request_mem_ind_msg_v01,
- size),
+ mem_seg_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_MEM_SEG_V01,
+ .elem_size = sizeof(struct wlfw_mem_seg_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_request_mem_ind_msg_v01,
+ mem_seg),
+ .ei_array = wlfw_mem_seg_s_v01_ei,
},
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct elem_info wlfw_respond_mem_req_msg_v01_ei[] = {
{
- .data_type = QMI_UNSIGNED_8_BYTE,
+ .data_type = QMI_DATA_LEN,
.elem_len = 1,
- .elem_size = sizeof(u64),
+ .elem_size = sizeof(u8),
.is_array = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct wlfw_respond_mem_req_msg_v01,
- addr),
+ mem_seg_len),
},
{
- .data_type = QMI_UNSIGNED_4_BYTE,
- .elem_len = 1,
- .elem_size = sizeof(u32),
- .is_array = NO_ARRAY,
- .tlv_type = 0x02,
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_MEM_SEG_V01,
+ .elem_size = sizeof(struct wlfw_mem_seg_resp_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x01,
.offset = offsetof(struct wlfw_respond_mem_req_msg_v01,
- size),
+ mem_seg),
+ .ei_array = wlfw_mem_seg_resp_s_v01_ei,
},
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1945,7 +2323,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -1953,15 +2331,15 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
-struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[] = {
+struct elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2041,7 +2419,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2049,7 +2427,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2068,7 +2446,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2096,7 +2474,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2155,7 +2533,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2181,7 +2559,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2199,7 +2577,7 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
@@ -2216,6 +2594,14 @@
{
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
- .is_array = QMI_COMMON_TLV_TYPE,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_done_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
},
};
diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h
index 9b56eb0..00a873d 100644
--- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h
+++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,10 +23,12 @@
#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025
#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037
#define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A
+#define QMI_WLFW_CAL_DONE_IND_V01 0x003E
#define QMI_WLFW_HOST_CAP_REQ_V01 0x0034
#define QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01 0x003B
#define QMI_WLFW_M3_INFO_REQ_V01 0x003C
#define QMI_WLFW_CAP_REQ_V01 0x0024
+#define QMI_WLFW_FW_INIT_DONE_IND_V01 0x0038
#define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026
#define QMI_WLFW_M3_INFO_RESP_V01 0x003C
#define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029
@@ -42,7 +44,6 @@
#define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022
#define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020
#define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023
-#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0038
#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035
#define QMI_WLFW_REJUVENATE_IND_V01 0x0039
#define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B
@@ -72,13 +73,16 @@
#define QMI_WLFW_IND_REGISTER_RESP_V01 0x0020
#define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2
+#define QMI_WLFW_MAX_NUM_MEM_SEG_V01 32
#define QMI_WLFW_MAX_NUM_CAL_V01 5
#define QMI_WLFW_MAX_DATA_SIZE_V01 6144
#define QMI_WLFW_FUNCTION_NAME_LEN_V01 128
#define QMI_WLFW_MAX_NUM_CE_V01 12
#define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32
#define QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01 6144
+#define QMI_WLFW_MAX_NUM_GPIO_V01 32
#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128
+#define QMI_WLFW_MAX_NUM_MEM_CFG_V01 2
#define QMI_WLFW_MAX_STR_LEN_V01 16
#define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24
#define QMI_WLFW_MAC_ADDR_SIZE_V01 6
@@ -117,6 +121,17 @@
WLFW_PIPEDIR_ENUM_MAX_VAL_V01 = INT_MAX,
};
+enum wlfw_mem_type_enum_v01 {
+ WLFW_MEM_TYPE_ENUM_MIN_VAL_V01 = INT_MIN,
+ QMI_WLFW_MEM_TYPE_MSA_V01 = 0,
+ QMI_WLFW_MEM_TYPE_DDR_V01 = 1,
+ QMI_WLFW_MEM_BDF_V01 = 2,
+ QMI_WLFW_MEM_M3_V01 = 3,
+ QMI_WLFW_MEM_CAL_V01 = 4,
+ QMI_WLFW_MEM_DPD_V01 = 5,
+ WLFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
#define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00)
#define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01)
#define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02)
@@ -128,6 +143,7 @@
#define QMI_WLFW_FW_READY_V01 ((u64)0x02ULL)
#define QMI_WLFW_MSA_READY_V01 ((u64)0x04ULL)
#define QMI_WLFW_FW_MEM_READY_V01 ((u64)0x08ULL)
+#define QMI_WLFW_FW_INIT_DONE_V01 ((u64)0x10ULL)
#define QMI_WLFW_FW_REJUVENATE_V01 ((u64)0x01ULL)
@@ -160,6 +176,26 @@
u8 secure_flag;
};
+struct wlfw_mem_cfg_s_v01 {
+ u64 offset;
+ u32 size;
+ u8 secure_flag;
+};
+
+struct wlfw_mem_seg_s_v01 {
+ u32 size;
+ enum wlfw_mem_type_enum_v01 type;
+ u32 mem_cfg_len;
+ struct wlfw_mem_cfg_s_v01 mem_cfg[QMI_WLFW_MAX_NUM_MEM_CFG_V01];
+};
+
+struct wlfw_mem_seg_resp_s_v01 {
+ u64 addr;
+ u32 size;
+ enum wlfw_mem_type_enum_v01 type;
+ u8 restore;
+};
+
struct wlfw_rf_chip_info_s_v01 {
u32 chip_id;
u32 chip_family;
@@ -195,13 +231,17 @@
u8 request_mem_enable;
u8 fw_mem_ready_enable_valid;
u8 fw_mem_ready_enable;
- u8 cold_boot_cal_done_enable_valid;
- u8 cold_boot_cal_done_enable;
+ u8 fw_init_done_enable_valid;
+ u8 fw_init_done_enable;
u8 rejuvenate_enable_valid;
u32 rejuvenate_enable;
+ u8 xo_cal_enable_valid;
+ u8 xo_cal_enable;
+ u8 cal_done_enable_valid;
+ u8 cal_done_enable;
};
-#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 46
+#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 54
extern struct elem_info wlfw_ind_register_req_msg_v01_ei[];
struct wlfw_ind_register_resp_msg_v01 {
@@ -533,13 +573,36 @@
extern struct elem_info wlfw_mac_addr_resp_msg_v01_ei[];
struct wlfw_host_cap_req_msg_v01 {
- u8 daemon_support_valid;
- u8 daemon_support;
+ u8 num_clients_valid;
+ u32 num_clients;
u8 wake_msi_valid;
u32 wake_msi;
+ u8 gpios_valid;
+ u32 gpios_len;
+ u32 gpios[QMI_WLFW_MAX_NUM_GPIO_V01];
+ u8 nm_modem_valid;
+ u8 nm_modem;
+ u8 bdf_support_valid;
+ u8 bdf_support;
+ u8 bdf_cache_support_valid;
+ u8 bdf_cache_support;
+ u8 m3_support_valid;
+ u8 m3_support;
+ u8 m3_cache_support_valid;
+ u8 m3_cache_support;
+ u8 cal_filesys_support_valid;
+ u8 cal_filesys_support;
+ u8 cal_cache_support_valid;
+ u8 cal_cache_support;
+ u8 cal_done_valid;
+ u8 cal_done;
+ u8 mem_bucket_valid;
+ u32 mem_bucket;
+ u8 mem_cfg_mode_valid;
+ u8 mem_cfg_mode;
};
-#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 11
+#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 189
extern struct elem_info wlfw_host_cap_req_msg_v01_ei[];
struct wlfw_host_cap_resp_msg_v01 {
@@ -550,18 +613,19 @@
extern struct elem_info wlfw_host_cap_resp_msg_v01_ei[];
struct wlfw_request_mem_ind_msg_v01 {
- u32 size;
+ u32 mem_seg_len;
+ struct wlfw_mem_seg_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
};
-#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 7
+#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 1124
extern struct elem_info wlfw_request_mem_ind_msg_v01_ei[];
struct wlfw_respond_mem_req_msg_v01 {
- u64 addr;
- u32 size;
+ u32 mem_seg_len;
+ struct wlfw_mem_seg_resp_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
};
-#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 18
+#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 548
extern struct elem_info wlfw_respond_mem_req_msg_v01_ei[];
struct wlfw_respond_mem_resp_msg_v01 {
@@ -578,12 +642,12 @@
#define WLFW_FW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0
extern struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[];
-struct wlfw_cold_boot_cal_done_ind_msg_v01 {
+struct wlfw_fw_init_done_ind_msg_v01 {
char placeholder;
};
-#define WLFW_COLD_BOOT_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0
-extern struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[];
+#define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_fw_init_done_ind_msg_v01_ei[];
struct wlfw_rejuvenate_ind_msg_v01 {
u8 cause_for_rejuvenation_valid;
@@ -654,4 +718,11 @@
#define WLFW_XO_CAL_IND_MSG_V01_MAX_MSG_LEN 4
extern struct elem_info wlfw_xo_cal_ind_msg_v01_ei[];
+struct wlfw_cal_done_ind_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_cal_done_ind_msg_v01_ei[];
+
#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
index d4b73de..b35adbc 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
@@ -79,8 +79,8 @@
/* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 17
#define IWL7265_UCODE_API_MIN 17
-#define IWL7265D_UCODE_API_MIN 17
-#define IWL3168_UCODE_API_MIN 20
+#define IWL7265D_UCODE_API_MIN 22
+#define IWL3168_UCODE_API_MIN 22
/* NVM versions */
#define IWL7260_NVM_VERSION 0x0a1d
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
index 8d3e53f..20d08dd 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
@@ -74,8 +74,8 @@
#define IWL8265_UCODE_API_MAX 26
/* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN 17
-#define IWL8265_UCODE_API_MIN 20
+#define IWL8000_UCODE_API_MIN 22
+#define IWL8265_UCODE_API_MIN 22
/* NVM versions */
#define IWL8000_NVM_VERSION 0x0a1d
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c
index ea16185..e267377 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c
@@ -65,12 +65,12 @@
#define IWL_A000_TX_POWER_VERSION 0xffff /* meaningless */
/* Memory offsets and lengths */
-#define IWL_A000_DCCM_OFFSET 0x800000
-#define IWL_A000_DCCM_LEN 0x18000
+#define IWL_A000_DCCM_OFFSET 0x800000 /* LMAC1 */
+#define IWL_A000_DCCM_LEN 0x10000 /* LMAC1 */
#define IWL_A000_DCCM2_OFFSET 0x880000
#define IWL_A000_DCCM2_LEN 0x8000
#define IWL_A000_SMEM_OFFSET 0x400000
-#define IWL_A000_SMEM_LEN 0x68000
+#define IWL_A000_SMEM_LEN 0xD0000
#define IWL_A000_FW_PRE "iwlwifi-Qu-a0-jf-b0-"
#define IWL_A000_MODULE_FIRMWARE(api) \
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
index 88f260d..68412ff 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
@@ -76,8 +76,8 @@
}
IWL_EXPORT_SYMBOL(iwl_notification_wait_init);
-void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt)
+bool iwl_notification_wait(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt)
{
bool triggered = false;
@@ -118,13 +118,11 @@
}
}
spin_unlock(¬if_wait->notif_wait_lock);
-
}
- if (triggered)
- wake_up_all(¬if_wait->notif_waitq);
+ return triggered;
}
-IWL_EXPORT_SYMBOL(iwl_notification_wait_notify);
+IWL_EXPORT_SYMBOL(iwl_notification_wait);
void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
index 0f9995e..368884b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -32,6 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -89,10 +90,10 @@
*
* This structure is not used directly, to wait for a
* notification declare it on the stack, and call
- * iwlagn_init_notification_wait() with appropriate
+ * iwl_init_notification_wait() with appropriate
* parameters. Then do whatever will cause the ucode
* to notify the driver, and to wait for that then
- * call iwlagn_wait_notification().
+ * call iwl_wait_notification().
*
* Each notification is one-shot. If at some point we
* need to support multi-shot notifications (which
@@ -114,10 +115,24 @@
/* caller functions */
void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data);
-void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data,
- struct iwl_rx_packet *pkt);
+bool iwl_notification_wait(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt);
void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data);
+static inline void
+iwl_notification_notify(struct iwl_notif_wait_data *notif_data)
+{
+ wake_up_all(¬if_data->notif_waitq);
+}
+
+static inline void
+iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data,
+ struct iwl_rx_packet *pkt)
+{
+ if (iwl_notification_wait(notif_data, pkt))
+ iwl_notification_notify(notif_data);
+}
+
/* user functions */
void __acquires(wait_entry)
iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data,
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
index 406ef30..da8234b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
@@ -369,6 +369,7 @@
#define MON_DMARB_RD_DATA_ADDR (0xa03c5c)
#define DBGC_IN_SAMPLE (0xa03c00)
+#define DBGC_OUT_CTRL (0xa03c0c)
/* enable the ID buf for read */
#define WFPM_PS_CTL_CLR 0xA0300C
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
index 700d244..2642d8e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
@@ -914,14 +914,6 @@
return 0;
}
-static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
-{
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
- iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
- else
- iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
-}
-
int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
{
u8 *ptr;
@@ -935,10 +927,8 @@
/* EARLY START - firmware's configuration is hard coded */
if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
!mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
- conf_id == FW_DBG_START_FROM_ALIVE) {
- iwl_mvm_restart_early_start(mvm);
+ conf_id == FW_DBG_START_FROM_ALIVE)
return 0;
- }
if (!mvm->fw->dbg_conf_tlv[conf_id])
return -EINVAL;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index c60703e..2b1c691 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -1666,8 +1666,11 @@
*/
static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm)
{
+ u32 cmd_queue = iwl_mvm_is_dqa_supported(mvm) ? IWL_MVM_DQA_CMD_QUEUE :
+ IWL_MVM_CMD_QUEUE;
+
return ((BIT(mvm->cfg->base_params->num_of_queues) - 1) &
- ~BIT(IWL_MVM_CMD_QUEUE));
+ ~BIT(cmd_queue));
}
static inline
@@ -1687,6 +1690,7 @@
static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm)
{
mvm->ucode_loaded = false;
+ mvm->fw_dbg_conf = FW_DBG_INVALID;
iwl_trans_stop_device(mvm->trans);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 4d35deb..6d38eec 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -1118,22 +1118,38 @@
mutex_lock(&mvm->mutex);
- /* stop recording */
if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ /* stop recording */
iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+
+ iwl_mvm_fw_error_dump(mvm);
+
+ /* start recording again if the firmware is not crashed */
+ if (!test_bit(STATUS_FW_ERROR, &mvm->trans->status) &&
+ mvm->fw->dbg_dest_tlv)
+ iwl_clear_bits_prph(mvm->trans,
+ MON_BUFF_SAMPLE_CTL, 0x100);
} else {
+ u32 in_sample = iwl_read_prph(mvm->trans, DBGC_IN_SAMPLE);
+ u32 out_ctrl = iwl_read_prph(mvm->trans, DBGC_OUT_CTRL);
+
+ /* stop recording */
iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
- /* wait before we collect the data till the DBGC stop */
udelay(100);
+ iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, 0);
+ /* wait before we collect the data till the DBGC stop */
+ udelay(500);
+
+ iwl_mvm_fw_error_dump(mvm);
+
+ /* start recording again if the firmware is not crashed */
+ if (!test_bit(STATUS_FW_ERROR, &mvm->trans->status) &&
+ mvm->fw->dbg_dest_tlv) {
+ iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, in_sample);
+ iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, out_ctrl);
+ }
}
- iwl_mvm_fw_error_dump(mvm);
-
- /* start recording again if the firmware is not crashed */
- WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) &&
- mvm->fw->dbg_dest_tlv &&
- iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf));
-
mutex_unlock(&mvm->mutex);
iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index 227c5ed..0aea476 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -1867,12 +1867,10 @@
struct rs_rate *rate = &search_tbl->rate;
const struct rs_tx_column *column = &rs_tx_columns[col_id];
const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
- u32 sz = (sizeof(struct iwl_scale_tbl_info) -
- (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
unsigned long rate_mask = 0;
u32 rate_idx = 0;
- memcpy(search_tbl, tbl, sz);
+ memcpy(search_tbl, tbl, offsetof(struct iwl_scale_tbl_info, win));
rate->sgi = column->sgi;
rate->ant = column->ant;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 0e60e38..b78e60e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -104,7 +104,20 @@
u8 crypt_len,
struct iwl_rx_cmd_buffer *rxb)
{
- unsigned int hdrlen, fraglen;
+ unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ unsigned int fraglen;
+
+ /*
+ * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len,
+ * but those are all multiples of 4 long) all goes away, but we
+ * want the *end* of it, which is going to be the start of the IP
+ * header, to be aligned when it gets pulled in.
+ * The beginning of the skb->data is aligned on at least a 4-byte
+ * boundary after allocation. Everything here is aligned at least
+ * on a 2-byte boundary so we can just take hdrlen & 3 and pad by
+ * the result.
+ */
+ skb_reserve(skb, hdrlen & 3);
/* If frame is small enough to fit in skb->head, pull it completely.
* If not, only pull ieee80211_hdr (including crypto if present, and
@@ -118,8 +131,7 @@
* If the latter changes (there are efforts in the standards group
* to do so) we should revisit this and ieee80211_data_to_8023().
*/
- hdrlen = (len <= skb_tailroom(skb)) ? len :
- sizeof(*hdr) + crypt_len + 8;
+ hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8;
memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
fraglen = len - hdrlen;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index bec7d9c..c520356 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -790,11 +790,13 @@
struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
int ret;
- if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR))
- return -EIO;
-
mutex_lock(&mvm->mutex);
+ if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) {
+ ret = -EIO;
+ goto unlock;
+ }
+
if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
ret = -EINVAL;
goto unlock;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index 10ef44e..fe32de2 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -2824,7 +2824,8 @@
#ifdef CONFIG_PM_SLEEP
static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
{
- if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3)
+ if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3 &&
+ (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3))
return iwl_pci_fw_enter_d0i3(trans);
return 0;
@@ -2832,7 +2833,8 @@
static void iwl_trans_pcie_resume(struct iwl_trans *trans)
{
- if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3)
+ if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3 &&
+ (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3))
iwl_pci_fw_exit_d0i3(trans);
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 4b462dc..2681b533 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -552,8 +552,6 @@
/* wmediumd portid responsible for netgroup of this radio */
u32 wmediumd;
- int power_level;
-
/* difference between this hw's clock and the real clock, in usecs */
s64 tsf_offset;
s64 bcn_delta;
@@ -730,16 +728,21 @@
val != PS_MANUAL_POLL)
return -EINVAL;
+ if (val == PS_MANUAL_POLL) {
+ if (data->ps != PS_ENABLED)
+ return -EINVAL;
+ local_bh_disable();
+ ieee80211_iterate_active_interfaces_atomic(
+ data->hw, IEEE80211_IFACE_ITER_NORMAL,
+ hwsim_send_ps_poll, data);
+ local_bh_enable();
+ return 0;
+ }
old_ps = data->ps;
data->ps = val;
local_bh_disable();
- if (val == PS_MANUAL_POLL) {
- ieee80211_iterate_active_interfaces_atomic(
- data->hw, IEEE80211_IFACE_ITER_NORMAL,
- hwsim_send_ps_poll, data);
- data->ps_poll_pending = true;
- } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
+ if (old_ps == PS_DISABLED && val != PS_DISABLED) {
ieee80211_iterate_active_interfaces_atomic(
data->hw, IEEE80211_IFACE_ITER_NORMAL,
hwsim_send_nullfunc_ps, data);
@@ -1208,7 +1211,9 @@
if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
rx_status.flag |= RX_FLAG_SHORT_GI;
/* TODO: simulate real signal strength (and optional packet loss) */
- rx_status.signal = data->power_level - 50;
+ rx_status.signal = -50;
+ if (info->control.vif)
+ rx_status.signal += info->control.vif->bss_conf.txpower;
if (data->ps != PS_DISABLED)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
@@ -1607,7 +1612,6 @@
WARN_ON(data->channel && data->use_chanctx);
- data->power_level = conf->power_level;
if (!data->started || !data->beacon_int)
tasklet_hrtimer_cancel(&data->beacon_timer);
else if (!hrtimer_is_queued(&data->beacon_timer.timer)) {
@@ -2212,7 +2216,6 @@
"d_tx_failed",
"d_ps_mode",
"d_group",
- "d_tx_power",
};
#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats)
@@ -2249,7 +2252,6 @@
data[i++] = ar->tx_failed;
data[i++] = ar->ps;
data[i++] = ar->group;
- data[i++] = ar->power_level;
WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN);
}
@@ -3154,7 +3156,7 @@
if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
continue;
- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!skb) {
res = -ENOMEM;
goto out_err;
@@ -3344,8 +3346,11 @@
continue;
list_del(&data->list);
- INIT_WORK(&data->destroy_work, destroy_radio);
- schedule_work(&data->destroy_work);
+ spin_unlock_bh(&hwsim_radio_lock);
+ mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
+ NULL);
+ spin_lock_bh(&hwsim_radio_lock);
+
}
spin_unlock_bh(&hwsim_radio_lock);
}
diff --git a/drivers/net/wireless/marvell/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c
index c3a53cd..7b4955c 100644
--- a/drivers/net/wireless/marvell/libertas/if_spi.c
+++ b/drivers/net/wireless/marvell/libertas/if_spi.c
@@ -1181,6 +1181,10 @@
/* Initialize interrupt handling stuff. */
card->workqueue = alloc_workqueue("libertas_spi", WQ_MEM_RECLAIM, 0);
+ if (!card->workqueue) {
+ err = -ENOMEM;
+ goto remove_card;
+ }
INIT_WORK(&card->packet_work, if_spi_host_to_card_worker);
INIT_WORK(&card->resume_work, if_spi_resume_worker);
@@ -1209,6 +1213,7 @@
free_irq(spi->irq, card);
terminate_workqueue:
destroy_workqueue(card->workqueue);
+remove_card:
lbs_remove_card(priv); /* will call free_netdev */
free_card:
free_if_spi_card(card);
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 8677a53..48d51be 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -1109,6 +1109,12 @@
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
+ if (priv->scan_request) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "change virtual interface: scan in process\n");
+ return -EBUSY;
+ }
+
switch (curr_iftype) {
case NL80211_IFTYPE_ADHOC:
switch (type) {
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index 2478ccd..1e36f11 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -146,7 +146,6 @@
kfree(adapter->regd);
- vfree(adapter->chan_stats);
kfree(adapter);
return 0;
}
@@ -636,6 +635,7 @@
goto done;
err_add_intf:
+ vfree(adapter->chan_stats);
wiphy_unregister(adapter->wiphy);
wiphy_free(adapter->wiphy);
err_init_fw:
@@ -1429,6 +1429,7 @@
mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
rtnl_unlock();
}
+ vfree(adapter->chan_stats);
up(sem);
exit_sem_err:
@@ -1729,6 +1730,7 @@
mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
rtnl_unlock();
}
+ vfree(adapter->chan_stats);
wiphy_unregister(adapter->wiphy);
wiphy_free(adapter->wiphy);
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index 8d601dc..486b8c7 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -1458,7 +1458,7 @@
}
if (card->mpa_rx.pkt_cnt == 1)
- mport = adapter->ioport + port;
+ mport = adapter->ioport + card->mpa_rx.start_port;
if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
card->mpa_rx.buf_len, mport, 1))
@@ -1891,7 +1891,7 @@
}
if (card->mpa_tx.pkt_cnt == 1)
- mport = adapter->ioport + port;
+ mport = adapter->ioport + card->mpa_tx.start_port;
ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
card->mpa_tx.buf_len, mport);
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c
index dbdfb3f..a9f5f39 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mcu.c
+++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c
@@ -66,8 +66,10 @@
WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */
skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL);
- skb_reserve(skb, MT_DMA_HDR_LEN);
- memcpy(skb_put(skb, len), data, len);
+ if (skb) {
+ skb_reserve(skb, MT_DMA_HDR_LEN);
+ memcpy(skb_put(skb, len), data, len);
+ }
return skb;
}
@@ -170,6 +172,8 @@
};
skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5);
}
@@ -205,6 +209,8 @@
};
skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true);
}
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
index 13da95a..987c7c4 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
@@ -142,15 +142,25 @@
if (!rt2x00dev->ops->hw->set_rts_threshold &&
(tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS |
IEEE80211_TX_RC_USE_CTS_PROTECT))) {
- if (rt2x00queue_available(queue) <= 1)
- goto exit_fail;
+ if (rt2x00queue_available(queue) <= 1) {
+ /*
+ * Recheck for full queue under lock to avoid race
+ * conditions with rt2x00lib_txdone().
+ */
+ spin_lock(&queue->tx_lock);
+ if (rt2x00queue_threshold(queue))
+ rt2x00queue_pause_queue(queue);
+ spin_unlock(&queue->tx_lock);
+
+ goto exit_free_skb;
+ }
if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb))
- goto exit_fail;
+ goto exit_free_skb;
}
if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false)))
- goto exit_fail;
+ goto exit_free_skb;
/*
* Pausing queue has to be serialized with rt2x00lib_txdone(). Note
@@ -164,10 +174,6 @@
return;
- exit_fail:
- spin_lock(&queue->tx_lock);
- rt2x00queue_pause_queue(queue);
- spin_unlock(&queue->tx_lock);
exit_free_skb:
ieee80211_free_txskb(hw, skb);
}
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 0881ba8..c78abfc 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -247,7 +247,10 @@
0x04, 0x08, /* Noise gain, limit offset */
0x28, 0x28, /* det rssi, med busy offsets */
7, /* det sync thresh */
- 0, 2, 2 /* test mode, min, max */
+ 0, 2, 2, /* test mode, min, max */
+ 0, /* rx/tx delay */
+ 0, 0, 0, 0, 0, 0, /* current BSS id */
+ 0 /* hop set */
};
/*===========================================================================*/
@@ -598,7 +601,7 @@
* a_beacon_period = hops a_beacon_period = KuS
*//* 64ms = 010000 */
if (local->fw_ver == 0x55) {
- memcpy((UCHAR *) &local->sparm.b4, b4_default_startup_parms,
+ memcpy(&local->sparm.b4, b4_default_startup_parms,
sizeof(struct b4_startup_params));
/* Translate sane kus input values to old build 4/5 format */
/* i = hop time in uS truncated to 3 bytes */
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
index 231f84d..6113624 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
@@ -1454,6 +1454,7 @@
goto err_free_dev;
}
mutex_init(&priv->io_mutex);
+ mutex_init(&priv->conf_mutex);
SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);
@@ -1627,7 +1628,6 @@
printk(KERN_ERR "rtl8187: Cannot register device\n");
goto err_free_dmabuf;
}
- mutex_init(&priv->conf_mutex);
skb_queue_head_init(&priv->b_tx_status.queue);
wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n",
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 75ffeaa..e15b462 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -1572,7 +1572,14 @@
dev_kfree_skb_irq(skb);
ring->idx = (ring->idx + 1) % ring->entries;
}
+
+ if (rtlpriv->use_new_trx_flow) {
+ rtlpci->tx_ring[i].cur_tx_rp = 0;
+ rtlpci->tx_ring[i].cur_tx_wp = 0;
+ }
+
ring->idx = 0;
+ ring->entries = rtlpci->txringcount[i];
}
}
spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
index aba60c3..618e509 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
@@ -1125,7 +1125,8 @@
/* Configuration Space offset 0x70f BIT7 is used to control L0S */
tmp8 = _rtl8723be_dbi_read(rtlpriv, 0x70f);
- _rtl8723be_dbi_write(rtlpriv, 0x70f, tmp8 | BIT(7));
+ _rtl8723be_dbi_write(rtlpriv, 0x70f, tmp8 | BIT(7) |
+ ASPM_L1_LATENCY << 3);
/* Configuration Space offset 0x719 Bit3 is for L1
* BIT4 is for clock request
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 280196a..ae87b39 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -3434,6 +3434,10 @@
/* because rndis_command() sleeps we need to use workqueue */
priv->workqueue = create_singlethread_workqueue("rndis_wlan");
+ if (!priv->workqueue) {
+ wiphy_free(wiphy);
+ return -ENOMEM;
+ }
INIT_WORK(&priv->work, rndis_wlan_worker);
INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller);
INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results);
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c
index 1c539c8..5e41bf0 100644
--- a/drivers/net/wireless/ti/wl1251/main.c
+++ b/drivers/net/wireless/ti/wl1251/main.c
@@ -1200,8 +1200,7 @@
WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
enable = bss_conf->arp_addr_cnt == 1 && bss_conf->assoc;
- wl1251_acx_arp_ip_filter(wl, enable, addr);
-
+ ret = wl1251_acx_arp_ip_filter(wl, enable, addr);
if (ret < 0)
goto out_sleep;
}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
index c5effd6c..01ca1d5 100644
--- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
@@ -1278,6 +1278,9 @@
u8 bulk_out_ep;
int r;
+ if (iface_desc->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
/* Find bulk out endpoint */
for (r = 1; r >= 0; r--) {
endpoint = &iface_desc->endpoint[r].desc;
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 1a9dadf..1b28786 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1345,6 +1345,7 @@
netif_carrier_off(netdev);
+ xenbus_switch_state(dev, XenbusStateInitialising);
return netdev;
exit:
@@ -2037,7 +2038,10 @@
case XenbusStateInitialised:
case XenbusStateReconfiguring:
case XenbusStateReconfigured:
+ break;
+
case XenbusStateUnknown:
+ wake_up_all(&module_unload_q);
break;
case XenbusStateInitWait:
@@ -2168,7 +2172,9 @@
xenbus_switch_state(dev, XenbusStateClosing);
wait_event(module_unload_q,
xenbus_read_driver_state(dev->otherend) ==
- XenbusStateClosing);
+ XenbusStateClosing ||
+ xenbus_read_driver_state(dev->otherend) ==
+ XenbusStateUnknown);
xenbus_switch_state(dev, XenbusStateClosed);
wait_event(module_unload_q,
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c
index af62c4c..b4f31da 100644
--- a/drivers/nfc/nfcmrvl/fw_dnld.c
+++ b/drivers/nfc/nfcmrvl/fw_dnld.c
@@ -17,7 +17,7 @@
*/
#include <linux/module.h>
-#include <linux/unaligned/access_ok.h>
+#include <asm/unaligned.h>
#include <linux/firmware.h>
#include <linux/nfc.h>
#include <net/nfc/nci.h>
diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c
index a7faa0b..fc8e78a 100644
--- a/drivers/nfc/nfcmrvl/spi.c
+++ b/drivers/nfc/nfcmrvl/spi.c
@@ -96,10 +96,9 @@
/* Send the SPI packet */
err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion,
skb);
- if (err != 0) {
+ if (err)
nfc_err(priv->dev, "spi_send failed %d", err);
- kfree_skb(skb);
- }
+
return err;
}
diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c
index 1dc8924..11d78b4 100644
--- a/drivers/nfc/pn533/i2c.c
+++ b/drivers/nfc/pn533/i2c.c
@@ -242,10 +242,10 @@
dev_dbg(&client->dev, "%s\n", __func__);
- pn533_unregister_device(phy->priv);
-
free_irq(client->irq, phy);
+ pn533_unregister_device(phy->priv);
+
return 0;
}
diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 9faaa96..77db979 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -286,8 +286,6 @@
disk->queue = q;
disk->flags = GENHD_FL_EXT_DEVT;
nvdimm_namespace_disk_name(&nsblk->common, disk->disk_name);
- set_capacity(disk, 0);
- device_add_disk(dev, disk);
if (devm_add_action_or_reset(dev, nd_blk_release_disk, disk))
return -ENOMEM;
@@ -300,6 +298,7 @@
}
set_capacity(disk, available_disk_size >> SECTOR_SHIFT);
+ device_add_disk(dev, disk);
revalidate_disk(disk);
return 0;
}
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 7121453..0c46ada 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1392,8 +1392,6 @@
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, btt->btt_queue);
btt->btt_queue->queuedata = btt;
- set_capacity(btt->btt_disk, 0);
- device_add_disk(&btt->nd_btt->dev, btt->btt_disk);
if (btt_meta_size(btt)) {
int rc = nd_integrity_init(btt->btt_disk, btt_meta_size(btt));
@@ -1405,6 +1403,7 @@
}
}
set_capacity(btt->btt_disk, btt->nlba * btt->sector_size >> 9);
+ device_add_disk(&btt->nd_btt->dev, btt->btt_disk);
btt->nd_btt->size = btt->nlba * (u64)btt->sector_size;
revalidate_disk(btt->btt_disk);
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 0392eb8..8311a93 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -812,16 +812,17 @@
int read_only, unsigned int ioctl_cmd, unsigned long arg)
{
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
- size_t buf_len = 0, in_len = 0, out_len = 0;
static char out_env[ND_CMD_MAX_ENVELOPE];
static char in_env[ND_CMD_MAX_ENVELOPE];
const struct nd_cmd_desc *desc = NULL;
unsigned int cmd = _IOC_NR(ioctl_cmd);
void __user *p = (void __user *) arg;
struct device *dev = &nvdimm_bus->dev;
- struct nd_cmd_pkg pkg;
const char *cmd_name, *dimm_name;
+ u32 in_len = 0, out_len = 0;
unsigned long cmd_mask;
+ struct nd_cmd_pkg pkg;
+ u64 buf_len = 0;
void *buf;
int rc, i;
@@ -882,7 +883,7 @@
}
if (cmd == ND_CMD_CALL) {
- dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n",
+ dev_dbg(dev, "%s:%s, idx: %llu, in: %u, out: %u, len %llu\n",
__func__, dimm_name, pkg.nd_command,
in_len, out_len, buf_len);
@@ -912,9 +913,9 @@
out_len += out_size;
}
- buf_len = out_len + in_len;
+ buf_len = (u64) out_len + (u64) in_len;
if (buf_len > ND_IOCTL_MAX_BUFLEN) {
- dev_dbg(dev, "%s:%s cmd: %s buf_len: %zu > %d\n", __func__,
+ dev_dbg(dev, "%s:%s cmd: %s buf_len: %llu > %d\n", __func__,
dimm_name, cmd_name, buf_len,
ND_IOCTL_MAX_BUFLEN);
return -EINVAL;
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index b8fb1ef..74257ac 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1747,7 +1747,7 @@
}
if (i < nd_region->ndr_mappings) {
- struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]);
+ struct nvdimm *nvdimm = nd_region->mapping[i].nvdimm;
/*
* Give up if we don't find an instance of a uuid at each
@@ -1755,7 +1755,7 @@
* find a dimm with two instances of the same uuid.
*/
dev_err(&nd_region->dev, "%s missing label for %pUb\n",
- dev_name(ndd->dev), nd_label->uuid);
+ nvdimm_name(nvdimm), nd_label->uuid);
rc = -EINVAL;
goto err;
}
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 42abdd2..d6aa59c 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -563,6 +563,12 @@
return altmap;
}
+static u64 phys_pmem_align_down(struct nd_pfn *nd_pfn, u64 phys)
+{
+ return min_t(u64, PHYS_SECTION_ALIGN_DOWN(phys),
+ ALIGN_DOWN(phys, nd_pfn->align));
+}
+
static int nd_pfn_init(struct nd_pfn *nd_pfn)
{
u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0;
@@ -618,13 +624,16 @@
start = nsio->res.start;
size = PHYS_SECTION_ALIGN_UP(start + size) - start;
if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM,
- IORES_DESC_NONE) == REGION_MIXED) {
+ IORES_DESC_NONE) == REGION_MIXED
+ || !IS_ALIGNED(start + resource_size(&nsio->res),
+ nd_pfn->align)) {
size = resource_size(&nsio->res);
- end_trunc = start + size - PHYS_SECTION_ALIGN_DOWN(start + size);
+ end_trunc = start + size - phys_pmem_align_down(nd_pfn,
+ start + size);
}
if (start_pad + end_trunc)
- dev_info(&nd_pfn->dev, "%s section collision, truncate %d bytes\n",
+ dev_info(&nd_pfn->dev, "%s alignment collision, truncate %d bytes\n",
dev_name(&ndns->dev), start_pad + end_trunc);
/*
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 719ee5f..c823e93 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1204,7 +1204,8 @@
blk_queue_max_hw_sectors(q, ctrl->max_hw_sectors);
blk_queue_max_segments(q, min_t(u32, max_segments, USHRT_MAX));
}
- if (ctrl->quirks & NVME_QUIRK_STRIPE_SIZE)
+ if ((ctrl->quirks & NVME_QUIRK_STRIPE_SIZE) &&
+ is_power_of_2(ctrl->max_hw_sectors))
blk_queue_chunk_sectors(q, ctrl->max_hw_sectors);
blk_queue_virt_boundary(q, ctrl->page_size - 1);
if (ctrl->vwc & NVME_CTRL_VWC_PRESENT)
@@ -2039,6 +2040,10 @@
struct nvme_ns *ns;
mutex_lock(&ctrl->namespaces_mutex);
+
+ /* Forcibly start all queues to avoid having stuck requests */
+ blk_mq_start_hw_queues(ctrl->admin_q);
+
list_for_each_entry(ns, &ctrl->namespaces, list) {
/*
* Revalidating a dead namespace sets capacity to 0. This will
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index e48ecb9..8cc856e 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1263,7 +1263,7 @@
bool nssro = dev->subsystem && (csts & NVME_CSTS_NSSRO);
/* If there is a reset ongoing, we shouldn't reset again. */
- if (work_busy(&dev->reset_work))
+ if (dev->ctrl.state == NVME_CTRL_RESETTING)
return false;
/* We shouldn't reset unless the controller is on fatal error state
@@ -1755,7 +1755,7 @@
struct nvme_dev *dev = container_of(work, struct nvme_dev, reset_work);
int result = -ENODEV;
- if (WARN_ON(dev->ctrl.state == NVME_CTRL_RESETTING))
+ if (WARN_ON(dev->ctrl.state != NVME_CTRL_RESETTING))
goto out;
/*
@@ -1765,9 +1765,6 @@
if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
nvme_dev_disable(dev, false);
- if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_RESETTING))
- goto out;
-
result = nvme_pci_enable(dev);
if (result)
goto out;
@@ -1841,8 +1838,8 @@
{
if (!dev->ctrl.admin_q || blk_queue_dying(dev->ctrl.admin_q))
return -ENODEV;
- if (work_busy(&dev->reset_work))
- return -ENODEV;
+ if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_RESETTING))
+ return -EBUSY;
if (!queue_work(nvme_workq, &dev->reset_work))
return -EBUSY;
return 0;
@@ -1944,6 +1941,7 @@
if (result)
goto release_pools;
+ nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_RESETTING);
dev_info(dev->ctrl.device, "pci function %s\n", dev_name(&pdev->dev));
queue_work(nvme_workq, &dev->reset_work);
@@ -1987,6 +1985,7 @@
nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_DELETING);
+ cancel_work_sync(&dev->reset_work);
pci_set_drvdata(pdev, NULL);
if (!pci_device_is_present(pdev)) {
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 52a297d..0a963b1 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -46,10 +46,20 @@
config OF_PROMTREE
bool
+config OF_KOBJ
+ bool "Display devicetree in sysfs"
+ def_bool SYSFS
+ help
+ Some embedded platforms have no need to display the devicetree
+ nodes and properties in sysfs. Disabling this option will save
+ a small amount of memory, as well as decrease boot time. By
+ default this option will be enabled if SYSFS is enabled.
+
# Hardly any platforms need this. It is safe to select, but only do so if you
# need it.
config OF_DYNAMIC
bool "Support for dynamic device trees" if OF_UNITTEST
+ select OF_KOBJ
help
On some platforms, the device tree can be manipulated at runtime.
While this option is selected automatically on such platforms, you
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index b2f474a..760b730 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,5 @@
obj-y = base.o device.o platform.o
+obj-$(CONFIG_OF_KOBJ) += kobj.o
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 23a6d36..9a6fad6 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -98,108 +98,6 @@
}
#endif
-#ifndef CONFIG_OF_DYNAMIC
-static void of_node_release(struct kobject *kobj)
-{
- /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
-}
-#endif /* CONFIG_OF_DYNAMIC */
-
-struct kobj_type of_node_ktype = {
- .release = of_node_release,
-};
-
-static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t offset, size_t count)
-{
- struct property *pp = container_of(bin_attr, struct property, attr);
- return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
-}
-
-/* always return newly allocated name, caller must free after use */
-static const char *safe_name(struct kobject *kobj, const char *orig_name)
-{
- const char *name = orig_name;
- struct kernfs_node *kn;
- int i = 0;
-
- /* don't be a hero. After 16 tries give up */
- while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
- sysfs_put(kn);
- if (name != orig_name)
- kfree(name);
- name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
- }
-
- if (name == orig_name) {
- name = kstrdup(orig_name, GFP_KERNEL);
- } else {
- pr_warn("Duplicate name in %s, renamed to \"%s\"\n",
- kobject_name(kobj), name);
- }
- return name;
-}
-
-int __of_add_property_sysfs(struct device_node *np, struct property *pp)
-{
- int rc;
-
- /* Important: Don't leak passwords */
- bool secure = strncmp(pp->name, "security-", 9) == 0;
-
- if (!IS_ENABLED(CONFIG_SYSFS))
- return 0;
-
- if (!of_kset || !of_node_is_attached(np))
- return 0;
-
- sysfs_bin_attr_init(&pp->attr);
- pp->attr.attr.name = safe_name(&np->kobj, pp->name);
- pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO;
- pp->attr.size = secure ? 0 : pp->length;
- pp->attr.read = of_node_property_read;
-
- rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
- WARN(rc, "error adding attribute %s to node %s\n", pp->name, np->full_name);
- return rc;
-}
-
-int __of_attach_node_sysfs(struct device_node *np)
-{
- const char *name;
- struct kobject *parent;
- struct property *pp;
- int rc;
-
- if (!IS_ENABLED(CONFIG_SYSFS))
- return 0;
-
- if (!of_kset)
- return 0;
-
- np->kobj.kset = of_kset;
- if (!np->parent) {
- /* Nodes without parents are new top level trees */
- name = safe_name(&of_kset->kobj, "base");
- parent = NULL;
- } else {
- name = safe_name(&np->parent->kobj, kbasename(np->full_name));
- parent = &np->parent->kobj;
- }
- if (!name)
- return -ENOMEM;
- rc = kobject_add(&np->kobj, parent, "%s", name);
- kfree(name);
- if (rc)
- return rc;
-
- for_each_property_of_node(np, pp)
- __of_add_property_sysfs(np, pp);
-
- return 0;
-}
-
static struct device_node **phandle_cache;
static u32 phandle_cache_mask;
@@ -2021,22 +1919,6 @@
return 0;
}
-void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
-{
- sysfs_remove_bin_file(&np->kobj, &prop->attr);
- kfree(prop->attr.attr.name);
-}
-
-void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
-{
- if (!IS_ENABLED(CONFIG_SYSFS))
- return;
-
- /* at early boot, bail here and defer setup to of_init() */
- if (of_kset && of_node_is_attached(np))
- __of_sysfs_remove_bin_file(np, prop);
-}
-
/**
* of_remove_property - Remove a property from a node.
*
@@ -2096,21 +1978,6 @@
return 0;
}
-void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
- struct property *oldprop)
-{
- if (!IS_ENABLED(CONFIG_SYSFS))
- return;
-
- /* At early boot, bail out and defer setup to of_init() */
- if (!of_kset)
- return;
-
- if (oldprop)
- __of_sysfs_remove_bin_file(np, oldprop);
- __of_add_property_sysfs(np, newprop);
-}
-
/*
* of_update_property - Update a property in a node, if the property does
* not exist, add it.
@@ -2218,7 +2085,7 @@
continue;
/* Allocate an alias_prop with enough space for the stem */
- ap = dt_alloc(sizeof(*ap) + len + 1, 4);
+ ap = dt_alloc(sizeof(*ap) + len + 1, __alignof__(*ap));
if (!ap)
continue;
memset(ap, 0, sizeof(*ap) + len + 1);
diff --git a/drivers/of/device.c b/drivers/of/device.c
index f7a9701..3cda60c 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -223,7 +223,7 @@
str[i] = '_';
}
- return tsize;
+ return repend;
}
EXPORT_SYMBOL_GPL(of_device_get_modalias);
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 888fdbc..765ba6e 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -16,6 +16,11 @@
#include "of_private.h"
+static struct device_node *kobj_to_device_node(struct kobject *kobj)
+{
+ return container_of(kobj, struct device_node, kobj);
+}
+
/**
* of_node_get() - Increment refcount of a node
* @node: Node to inc refcount, NULL is supported to simplify writing of
@@ -43,28 +48,6 @@
}
EXPORT_SYMBOL(of_node_put);
-void __of_detach_node_sysfs(struct device_node *np)
-{
- struct property *pp;
-
- if (!IS_ENABLED(CONFIG_SYSFS))
- return;
-
- BUG_ON(!of_node_is_initialized(np));
- if (!of_kset)
- return;
-
- /* only remove properties if on sysfs */
- if (of_node_is_attached(np)) {
- for_each_property_of_node(np, pp)
- __of_sysfs_remove_bin_file(np, pp);
- kobject_del(&np->kobj);
- }
-
- /* finally remove the kobj_init ref */
- of_node_put(np);
-}
-
static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
int of_reconfig_notifier_register(struct notifier_block *nb)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 755b386..744f625 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -937,7 +937,7 @@
int offset;
const char *p, *q, *options = NULL;
int l;
- const struct earlycon_id *match;
+ const struct earlycon_id **p_match;
const void *fdt = initial_boot_params;
offset = fdt_path_offset(fdt, "/chosen");
@@ -964,7 +964,10 @@
return 0;
}
- for (match = __earlycon_table; match < __earlycon_table_end; match++) {
+ for (p_match = __earlycon_table; p_match < __earlycon_table_end;
+ p_match++) {
+ const struct earlycon_id *match = *p_match;
+
if (!match->compatible[0])
continue;
diff --git a/drivers/of/kobj.c b/drivers/of/kobj.c
new file mode 100644
index 0000000..662f79e
--- /dev/null
+++ b/drivers/of/kobj.c
@@ -0,0 +1,165 @@
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include "of_private.h"
+
+/* true when node is initialized */
+static int of_node_is_initialized(struct device_node *node)
+{
+ return node && node->kobj.state_initialized;
+}
+
+/* true when node is attached (i.e. present on sysfs) */
+int of_node_is_attached(struct device_node *node)
+{
+ return node && node->kobj.state_in_sysfs;
+}
+
+
+#ifndef CONFIG_OF_DYNAMIC
+static void of_node_release(struct kobject *kobj)
+{
+ /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
+}
+#endif /* CONFIG_OF_DYNAMIC */
+
+struct kobj_type of_node_ktype = {
+ .release = of_node_release,
+};
+
+static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t offset, size_t count)
+{
+ struct property *pp = container_of(bin_attr, struct property, attr);
+ return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
+}
+
+/* always return newly allocated name, caller must free after use */
+static const char *safe_name(struct kobject *kobj, const char *orig_name)
+{
+ const char *name = orig_name;
+ struct kernfs_node *kn;
+ int i = 0;
+
+ /* don't be a hero. After 16 tries give up */
+ while (i < 16 && name && (kn = sysfs_get_dirent(kobj->sd, name))) {
+ sysfs_put(kn);
+ if (name != orig_name)
+ kfree(name);
+ name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
+ }
+
+ if (name == orig_name) {
+ name = kstrdup(orig_name, GFP_KERNEL);
+ } else {
+ pr_warn("Duplicate name in %s, renamed to \"%s\"\n",
+ kobject_name(kobj), name);
+ }
+ return name;
+}
+
+int __of_add_property_sysfs(struct device_node *np, struct property *pp)
+{
+ int rc;
+
+ /* Important: Don't leak passwords */
+ bool secure = strncmp(pp->name, "security-", 9) == 0;
+
+ if (!IS_ENABLED(CONFIG_SYSFS))
+ return 0;
+
+ if (!of_kset || !of_node_is_attached(np))
+ return 0;
+
+ sysfs_bin_attr_init(&pp->attr);
+ pp->attr.attr.name = safe_name(&np->kobj, pp->name);
+ pp->attr.attr.mode = secure ? 0400 : 0444;
+ pp->attr.size = secure ? 0 : pp->length;
+ pp->attr.read = of_node_property_read;
+
+ rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
+ WARN(rc, "error adding attribute %s to node %s\n", pp->name,
+ np->full_name);
+ return rc;
+}
+
+void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
+{
+ if (!IS_ENABLED(CONFIG_SYSFS))
+ return;
+
+ sysfs_remove_bin_file(&np->kobj, &prop->attr);
+ kfree(prop->attr.attr.name);
+}
+
+void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
+{
+ /* at early boot, bail here and defer setup to of_init() */
+ if (of_kset && of_node_is_attached(np))
+ __of_sysfs_remove_bin_file(np, prop);
+}
+
+void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
+ struct property *oldprop)
+{
+ /* At early boot, bail out and defer setup to of_init() */
+ if (!of_kset)
+ return;
+
+ if (oldprop)
+ __of_sysfs_remove_bin_file(np, oldprop);
+ __of_add_property_sysfs(np, newprop);
+}
+
+int __of_attach_node_sysfs(struct device_node *np)
+{
+ const char *name;
+ struct kobject *parent;
+ struct property *pp;
+ int rc;
+
+ if (!of_kset)
+ return 0;
+
+ np->kobj.kset = of_kset;
+ if (!np->parent) {
+ /* Nodes without parents are new top level trees */
+ name = safe_name(&of_kset->kobj, "base");
+ parent = NULL;
+ } else {
+ name = safe_name(&np->parent->kobj, kbasename(np->full_name));
+ parent = &np->parent->kobj;
+ }
+ if (!name)
+ return -ENOMEM;
+ rc = kobject_add(&np->kobj, parent, "%s", name);
+ kfree(name);
+ if (rc)
+ return rc;
+
+ for_each_property_of_node(np, pp)
+ __of_add_property_sysfs(np, pp);
+
+ return 0;
+}
+
+void __of_detach_node_sysfs(struct device_node *np)
+{
+ struct property *pp;
+
+ BUG_ON(!of_node_is_initialized(np));
+ if (!of_kset)
+ return;
+
+ /* only remove properties if on sysfs */
+ if (of_node_is_attached(np)) {
+ for_each_property_of_node(np, pp)
+ __of_sysfs_remove_bin_file(np, pp);
+ kobject_del(&np->kobj);
+ }
+
+ /* finally remove the kobj_init ref */
+ of_node_put(np);
+}
+
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index c4d7fdc..eb811185 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -35,12 +35,6 @@
extern struct list_head aliases_lookup;
extern struct kset *of_kset;
-
-static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
-{
- return container_of(kobj, struct device_node, kobj);
-}
-
#if defined(CONFIG_OF_DYNAMIC)
extern int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop);
@@ -55,6 +49,29 @@
}
#endif /* CONFIG_OF_DYNAMIC */
+#if defined(CONFIG_OF_KOBJ)
+int of_node_is_attached(struct device_node *node);
+int __of_add_property_sysfs(struct device_node *np, struct property *pp);
+void __of_remove_property_sysfs(struct device_node *np, struct property *prop);
+void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
+ struct property *oldprop);
+int __of_attach_node_sysfs(struct device_node *np);
+void __of_detach_node_sysfs(struct device_node *np);
+#else
+static inline int __of_add_property_sysfs(struct device_node *np, struct property *pp)
+{
+ return 0;
+}
+static inline void __of_remove_property_sysfs(struct device_node *np, struct property *prop) {}
+static inline void __of_update_property_sysfs(struct device_node *np,
+ struct property *newprop, struct property *oldprop) {}
+static inline int __of_attach_node_sysfs(struct device_node *np)
+{
+ return 0;
+}
+static inline void __of_detach_node_sysfs(struct device_node *np) {}
+#endif
+
/**
* General utilities for working with live trees.
*
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 4aba42f..8d8e4c5 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -502,7 +502,7 @@
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
-#ifndef CONFIG_PPC
+#if !defined(CONFIG_PPC) && !defined(CONFIG_ARCH_MSM8953_BOOT_ORDERING)
static int __init of_platform_default_populate_init(void)
{
struct device_node *node;
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 78530d1..bdce067 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2646,6 +2646,7 @@
netmos_9901,
netmos_9865,
quatech_sppxp100,
+ wch_ch382l,
};
@@ -2708,6 +2709,7 @@
/* netmos_9901 */ { 1, { { 0, -1 }, } },
/* netmos_9865 */ { 1, { { 0, -1 }, } },
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
+ /* wch_ch382l */ { 1, { { 2, -1 }, } },
};
static const struct pci_device_id parport_pc_pci_tbl[] = {
@@ -2797,6 +2799,8 @@
/* Quatech SPPXP-100 Parallel port PCI ExpressCard */
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
+ /* WCH CH382L PCI-E single parallel port card */
+ { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
{ 0, } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index 4fce494..11bad82 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -32,6 +32,7 @@
#define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5
#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11)
#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12
+#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ 0x2
#define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0
#define PCIE_CORE_LINK_L0S_ENTRY BIT(0)
#define PCIE_CORE_LINK_TRAINING BIT(5)
@@ -175,8 +176,6 @@
#define PCIE_CONFIG_WR_TYPE0 0xa
#define PCIE_CONFIG_WR_TYPE1 0xb
-/* PCI_BDF shifts 8bit, so we need extra 4bit shift */
-#define PCIE_BDF(dev) (dev << 4)
#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20)
#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15)
#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12)
@@ -298,7 +297,8 @@
reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE |
(7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) |
PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE |
- PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT;
+ (PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ <<
+ PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT);
advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
/* Program PCIe Control 2 to disable strict ordering */
@@ -439,7 +439,7 @@
u32 reg;
int ret;
- if (PCI_SLOT(devfn) != 0) {
+ if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) {
*val = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
@@ -458,7 +458,7 @@
advk_writel(pcie, reg, PIO_CTRL);
/* Program the address registers */
- reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where);
+ reg = PCIE_CONF_ADDR(bus->number, devfn, where);
advk_writel(pcie, reg, PIO_ADDR_LS);
advk_writel(pcie, 0, PIO_ADDR_MS);
@@ -493,7 +493,7 @@
int offset;
int ret;
- if (PCI_SLOT(devfn) != 0)
+ if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0)
return PCIBIOS_DEVICE_NOT_FOUND;
if (where % size)
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
index dafb4cd..d392a55 100644
--- a/drivers/pci/host/pci-hyperv.c
+++ b/drivers/pci/host/pci-hyperv.c
@@ -351,6 +351,7 @@
hv_pcibus_init = 0,
hv_pcibus_probed,
hv_pcibus_installed,
+ hv_pcibus_removed,
hv_pcibus_maximum
};
@@ -1205,9 +1206,11 @@
hbus->pci_bus->msi = &hbus->msi_chip;
hbus->pci_bus->msi->dev = &hbus->hdev->device;
+ pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus);
pci_bus_assign_resources(hbus->pci_bus);
pci_bus_add_devices(hbus->pci_bus);
+ pci_unlock_rescan_remove();
hbus->state = hv_pcibus_installed;
return 0;
}
@@ -1489,13 +1492,24 @@
put_pcichild(hpdev, hv_pcidev_ref_initial);
}
- /* Tell the core to rescan bus because there may have been changes. */
- if (hbus->state == hv_pcibus_installed) {
+ switch(hbus->state) {
+ case hv_pcibus_installed:
+ /*
+ * Tell the core to rescan bus
+ * because there may have been changes.
+ */
pci_lock_rescan_remove();
pci_scan_child_bus(hbus->pci_bus);
pci_unlock_rescan_remove();
- } else {
+ break;
+
+ case hv_pcibus_init:
+ case hv_pcibus_probed:
survey_child_resources(hbus);
+ break;
+
+ default:
+ break;
}
up(&hbus->enum_sem);
@@ -1585,8 +1599,10 @@
pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0,
wslot);
if (pdev) {
+ pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(pdev);
pci_dev_put(pdev);
+ pci_unlock_rescan_remove();
}
memset(&ctxt, 0, sizeof(ctxt));
@@ -2170,6 +2186,7 @@
hbus = kzalloc(sizeof(*hbus), GFP_KERNEL);
if (!hbus)
return -ENOMEM;
+ hbus->state = hv_pcibus_init;
/*
* The PCI bus "domain" is what is called "segment" in ACPI and
@@ -2312,6 +2329,7 @@
pci_stop_root_bus(hbus->pci_bus);
pci_remove_root_bus(hbus->pci_bus);
pci_unlock_rescan_remove();
+ hbus->state = hv_pcibus_removed;
}
ret = hv_send_resources_released(hdev);
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index b67a94d..cc68136 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -1457,6 +1457,10 @@
pci_walk_bus(bus,
&msm_pcie_config_l1_enable, dev);
+ /* enable l1 mode, clear bit 5 (REQ_NOT_ENTR_L1) */
+ msm_pcie_write_mask(dev->parf +
+ PCIE20_PARF_PM_CTRL, BIT(5), 0);
+
msm_pcie_config_l1_enable(dev->dev, dev);
}
break;
@@ -6297,7 +6301,7 @@
return ret;
}
-static void msm_pcie_fixup_suspend(struct pci_dev *dev)
+static void msm_pcie_fixup_suspend_late(struct pci_dev *dev)
{
int ret;
struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
@@ -6330,8 +6334,8 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
- msm_pcie_fixup_suspend);
+DECLARE_PCI_FIXUP_SUSPEND_LATE(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
+ msm_pcie_fixup_suspend_late);
/* Resume the PCIe link */
static int msm_pcie_pm_resume(struct pci_dev *dev,
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index af8f6e9..b3a8715 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -861,7 +861,7 @@
/* setup bus numbers */
val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
val &= 0xff000000;
- val |= 0x00010100;
+ val |= 0x00ff0100;
dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
/* setup command register */
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index a46b585..d44b558 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -587,6 +587,7 @@
{
unsigned long long sta = 0;
struct acpiphp_func *func;
+ u32 dvid;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_STA) {
@@ -597,19 +598,27 @@
if (ACPI_SUCCESS(status) && sta)
break;
} else {
- u32 dvid;
-
- pci_bus_read_config_dword(slot->bus,
- PCI_DEVFN(slot->device,
- func->function),
- PCI_VENDOR_ID, &dvid);
- if (dvid != 0xffffffff) {
+ if (pci_bus_read_dev_vendor_id(slot->bus,
+ PCI_DEVFN(slot->device, func->function),
+ &dvid, 0)) {
sta = ACPI_STA_ALL;
break;
}
}
}
+ if (!sta) {
+ /*
+ * Check for the slot itself since it may be that the
+ * ACPI slot is a device below PCIe upstream port so in
+ * that case it may not even be reachable yet.
+ */
+ if (pci_bus_read_dev_vendor_id(slot->bus,
+ PCI_DEVFN(slot->device, 0), &dvid, 0)) {
+ sta = ACPI_STA_ALL;
+ }
+ }
+
return (unsigned int)sta;
}
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 802997e..d81ad84 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -463,8 +463,6 @@
if (drv && drv->shutdown)
drv->shutdown(pci_dev);
- pci_msi_shutdown(pci_dev);
- pci_msix_shutdown(pci_dev);
/*
* If this is a kexec reboot, turn off Bus Master bit on the
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a87c8e1..9c13aee 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3756,27 +3756,49 @@
}
EXPORT_SYMBOL(pci_wait_for_pending_transaction);
-/*
- * We should only need to wait 100ms after FLR, but some devices take longer.
- * Wait for up to 1000ms for config space to return something other than -1.
- * Intel IGD requires this when an LCD panel is attached. We read the 2nd
- * dword because VFs don't implement the 1st dword.
- */
static void pci_flr_wait(struct pci_dev *dev)
{
- int i = 0;
+ int delay = 1, timeout = 60000;
u32 id;
- do {
- msleep(100);
- pci_read_config_dword(dev, PCI_COMMAND, &id);
- } while (i++ < 10 && id == ~0);
+ /*
+ * Per PCIe r3.1, sec 6.6.2, a device must complete an FLR within
+ * 100ms, but may silently discard requests while the FLR is in
+ * progress. Wait 100ms before trying to access the device.
+ */
+ msleep(100);
- if (id == ~0)
- dev_warn(&dev->dev, "Failed to return from FLR\n");
- else if (i > 1)
- dev_info(&dev->dev, "Required additional %dms to return from FLR\n",
- (i - 1) * 100);
+ /*
+ * After 100ms, the device should not silently discard config
+ * requests, but it may still indicate that it needs more time by
+ * responding to them with CRS completions. The Root Port will
+ * generally synthesize ~0 data to complete the read (except when
+ * CRS SV is enabled and the read was for the Vendor ID; in that
+ * case it synthesizes 0x0001 data).
+ *
+ * Wait for the device to return a non-CRS completion. Read the
+ * Command register instead of Vendor ID so we don't have to
+ * contend with the CRS SV value.
+ */
+ pci_read_config_dword(dev, PCI_COMMAND, &id);
+ while (id == ~0) {
+ if (delay > timeout) {
+ dev_warn(&dev->dev, "not ready %dms after FLR; giving up\n",
+ 100 + delay - 1);
+ return;
+ }
+
+ if (delay > 1000)
+ dev_info(&dev->dev, "not ready %dms after FLR; waiting\n",
+ 100 + delay - 1);
+
+ msleep(delay);
+ delay *= 2;
+ pci_read_config_dword(dev, PCI_COMMAND, &id);
+ }
+
+ if (delay > 1000)
+ dev_info(&dev->dev, "ready %dms after FLR\n", 100 + delay - 1);
}
static int pcie_flr(struct pci_dev *dev, int probe)
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index b0916b1..e38bb48 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -526,10 +526,14 @@
/*
* Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
- * hierarchies.
+ * hierarchies. Note that some PCIe host implementations omit
+ * the root ports entirely, in which case a downstream port on
+ * a switch may become the root of the link state chain for all
+ * its subordinate endpoints.
*/
if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
- pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE ||
+ !pdev->bus->parent->self) {
link->root = link;
} else {
struct pcie_link_state *parent;
@@ -782,7 +786,8 @@
}
EXPORT_SYMBOL(pci_disable_link_state);
-static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
+static int pcie_aspm_set_policy(const char *val,
+ const struct kernel_param *kp)
{
int i;
struct pcie_link_state *link;
@@ -809,7 +814,7 @@
return 0;
}
-static int pcie_aspm_get_policy(char *buffer, struct kernel_param *kp)
+static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp)
{
int i, cnt = 0;
for (i = 0; i < ARRAY_SIZE(policy_str); i++)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index a98be6d..56340ab 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -231,7 +231,7 @@
res->flags |= IORESOURCE_ROM_ENABLE;
l64 = l & PCI_ROM_ADDRESS_MASK;
sz64 = sz & PCI_ROM_ADDRESS_MASK;
- mask64 = (u32)PCI_ROM_ADDRESS_MASK;
+ mask64 = PCI_ROM_ADDRESS_MASK;
}
if (res->flags & IORESOURCE_MEM_64) {
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 98eba91..fb177dc 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3369,22 +3369,29 @@
static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
{
- pci_set_vpd_size(dev, 8192);
+ int chip = (dev->device & 0xf000) >> 12;
+ int func = (dev->device & 0x0f00) >> 8;
+ int prod = (dev->device & 0x00ff) >> 0;
+
+ /*
+ * If this is a T3-based adapter, there's a 1KB VPD area at offset
+ * 0xc00 which contains the preferred VPD values. If this is a T4 or
+ * later based adapter, the special VPD is at offset 0x400 for the
+ * Physical Functions (the SR-IOV Virtual Functions have no VPD
+ * Capabilities). The PCI VPD Access core routines will normally
+ * compute the size of the VPD by parsing the VPD Data Structure at
+ * offset 0x000. This will result in silent failures when attempting
+ * to accesses these other VPD areas which are beyond those computed
+ * limits.
+ */
+ if (chip == 0x0 && prod >= 0x20)
+ pci_set_vpd_size(dev, 8192);
+ else if (chip >= 0x4 && func < 0x8)
+ pci_set_vpd_size(dev, 2048);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
+ quirk_chelsio_extend_vpd);
#ifdef CONFIG_ACPI
/*
@@ -3870,6 +3877,8 @@
quirk_dma_func1_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0642,
quirk_dma_func1_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0645,
+ quirk_dma_func1_alias);
/* https://bugs.gentoo.org/show_bug.cgi?id=497630 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON,
PCI_DEVICE_ID_JMICRON_JMB388_ESD,
@@ -4097,6 +4106,9 @@
*/
acs_flags &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_SV | PCI_ACS_UF);
+ if (!((dev->device >= 0xa000) && (dev->device <= 0xa0ff)))
+ return -ENOTTY;
+
return acs_flags ? 0 : 1;
}
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 4bc589e..85774b7 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -63,7 +63,7 @@
mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK;
} else if (resno == PCI_ROM_RESOURCE) {
- mask = (u32)PCI_ROM_ADDRESS_MASK;
+ mask = PCI_ROM_ADDRESS_MASK;
} else {
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index ffcd001..37df1bf 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -325,10 +325,16 @@
return 0;
}
+static struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu)
+{
+ struct platform_device *pdev = armpmu->plat_device;
+
+ return pdev ? dev_get_platdata(&pdev->dev) : NULL;
+}
+
static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
{
struct arm_pmu *armpmu;
- struct platform_device *plat_device;
struct arm_pmu_platdata *plat;
int ret;
u64 start_clock, finish_clock;
@@ -340,8 +346,8 @@
* dereference.
*/
armpmu = *(void **)dev;
- plat_device = armpmu->plat_device;
- plat = dev_get_platdata(&plat_device->dev);
+
+ plat = armpmu_get_platdata(armpmu);
start_clock = sched_clock();
if (plat && plat->handle_irq)
@@ -1108,7 +1114,7 @@
const struct pmu_probe_info *probe_table)
{
const struct of_device_id *of_id;
- const int (*init_fn)(struct arm_pmu *);
+ int (*init_fn)(struct arm_pmu *);
struct device_node *node = pdev->dev.of_node;
struct arm_pmu *pmu;
int ret = -ENODEV;
@@ -1122,6 +1128,7 @@
armpmu_init(pmu);
pmu->plat_device = pdev;
+ platform_set_drvdata(pdev, pmu);
if (node && (of_id = of_match_node(of_table, pdev->dev.of_node))) {
init_fn = of_id->data;
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index fb38e20..735d8f7 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -992,19 +992,16 @@
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);
/**
- * pinctrl_select_state() - select/activate/program a pinctrl state to HW
+ * pinctrl_commit_state() - select/activate/program a pinctrl state to HW
* @p: the pinctrl handle for the device that requests configuration
* @state: the state handle to select/activate/program
*/
-int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
+static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state)
{
struct pinctrl_setting *setting, *setting2;
struct pinctrl_state *old_state = p->state;
int ret;
- if (p->state == state)
- return 0;
-
if (p->state) {
/*
* For each pinmux setting in the old state, forget SW's record
@@ -1068,6 +1065,19 @@
return ret;
}
+
+/**
+ * pinctrl_select_state() - select/activate/program a pinctrl state to HW
+ * @p: the pinctrl handle for the device that requests configuration
+ * @state: the state handle to select/activate/program
+ */
+int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
+{
+ if (p->state == state)
+ return 0;
+
+ return pinctrl_commit_state(p, state);
+}
EXPORT_SYMBOL_GPL(pinctrl_select_state);
static void devm_pinctrl_release(struct device *dev, void *res)
@@ -1236,7 +1246,7 @@
int pinctrl_force_sleep(struct pinctrl_dev *pctldev)
{
if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep))
- return pinctrl_select_state(pctldev->p, pctldev->hog_sleep);
+ return pinctrl_commit_state(pctldev->p, pctldev->hog_sleep);
return 0;
}
EXPORT_SYMBOL_GPL(pinctrl_force_sleep);
@@ -1248,7 +1258,7 @@
int pinctrl_force_default(struct pinctrl_dev *pctldev)
{
if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default))
- return pinctrl_select_state(pctldev->p, pctldev->hog_default);
+ return pinctrl_commit_state(pctldev->p, pctldev->hog_default);
return 0;
}
EXPORT_SYMBOL_GPL(pinctrl_force_default);
diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c
index 0a96502..fc5b18d 100644
--- a/drivers/pinctrl/intel/pinctrl-baytrail.c
+++ b/drivers/pinctrl/intel/pinctrl-baytrail.c
@@ -46,6 +46,9 @@
#define BYT_TRIG_POS BIT(25)
#define BYT_TRIG_LVL BIT(24)
#define BYT_DEBOUNCE_EN BIT(20)
+#define BYT_GLITCH_FILTER_EN BIT(19)
+#define BYT_GLITCH_F_SLOW_CLK BIT(17)
+#define BYT_GLITCH_F_FAST_CLK BIT(16)
#define BYT_PULL_STR_SHIFT 9
#define BYT_PULL_STR_MASK (3 << BYT_PULL_STR_SHIFT)
#define BYT_PULL_STR_2K (0 << BYT_PULL_STR_SHIFT)
@@ -1579,6 +1582,9 @@
*/
value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG |
BYT_TRIG_LVL);
+ /* Enable glitch filtering */
+ value |= BYT_GLITCH_FILTER_EN | BYT_GLITCH_F_SLOW_CLK |
+ BYT_GLITCH_F_FAST_CLK;
writel(value, reg);
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index df63b7d..b40a074 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -368,18 +368,6 @@
writel(value, padcfg0);
}
-static void intel_gpio_set_gpio_mode(void __iomem *padcfg0)
-{
- u32 value;
-
- /* Put the pad into GPIO mode */
- value = readl(padcfg0) & ~PADCFG0_PMODE_MASK;
- /* Disable SCI/SMI/NMI generation */
- value &= ~(PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI);
- value &= ~(PADCFG0_GPIROUTSMI | PADCFG0_GPIROUTNMI);
- writel(value, padcfg0);
-}
-
static int intel_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned pin)
@@ -387,6 +375,7 @@
struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
void __iomem *padcfg0;
unsigned long flags;
+ u32 value;
raw_spin_lock_irqsave(&pctrl->lock, flags);
@@ -396,7 +385,13 @@
}
padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0);
- intel_gpio_set_gpio_mode(padcfg0);
+ /* Put the pad into GPIO mode */
+ value = readl(padcfg0) & ~PADCFG0_PMODE_MASK;
+ /* Disable SCI/SMI/NMI generation */
+ value &= ~(PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI);
+ value &= ~(PADCFG0_GPIROUTSMI | PADCFG0_GPIROUTNMI);
+ writel(value, padcfg0);
+
/* Disable TX buffer and enable RX (this will be input) */
__intel_gpio_set_direction(padcfg0, true);
@@ -775,8 +770,6 @@
raw_spin_lock_irqsave(&pctrl->lock, flags);
- intel_gpio_set_gpio_mode(reg);
-
value = readl(reg);
value &= ~(PADCFG0_RXEVCFG_MASK | PADCFG0_RXINV);
diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
index 7511723..257c1c2 100644
--- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
+++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
@@ -138,7 +138,6 @@
MESON_PIN(GPIOX_19, EE_OFF),
MESON_PIN(GPIOX_20, EE_OFF),
MESON_PIN(GPIOX_21, EE_OFF),
- MESON_PIN(GPIOX_22, EE_OFF),
MESON_PIN(GPIOCLK_0, EE_OFF),
MESON_PIN(GPIOCLK_1, EE_OFF),
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 49bf7dc..f826793 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -1278,8 +1278,16 @@
{
struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
u32 data;
+ int ret;
+ ret = clk_enable(bank->clk);
+ if (ret < 0) {
+ dev_err(bank->drvdata->dev,
+ "failed to enable clock for bank %s\n", bank->name);
+ return ret;
+ }
data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ clk_disable(bank->clk);
return !(data & BIT(offset));
}
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670.c b/drivers/pinctrl/qcom/pinctrl-sdm670.c
index 196559c..e9d7dfc 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm670.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c
@@ -1636,6 +1636,9 @@
{97, 563},
{101, 564},
{103, 565},
+ {108, 567},
+ {112, 568},
+ {113, 569},
{115, 570},
{116, 571},
{117, 572},
@@ -1647,6 +1650,7 @@
{123, 613},
{124, 614},
{125, 615},
+ {126, 616},
{127, 617},
{128, 618},
{129, 619},
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
index 72b730d..bc8ec39 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
@@ -1738,6 +1738,9 @@
{97, 563},
{101, 564},
{103, 565},
+ {108, 567},
+ {112, 568},
+ {113, 569},
{104, 566},
{115, 570},
{116, 571},
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index 0991a99..bdce49b 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -529,7 +529,7 @@
pad->pullup = arg;
break;
case PMIC_GPIO_CONF_STRENGTH:
- if (arg > PMIC_GPIO_STRENGTH_LOW)
+ if (arg > PMIC_GPIO_STRENGTH_HIGH)
return -EINVAL;
pad->strength = arg;
break;
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
index d3f5501d..a562ed7 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2015, Sony Mobile Communications AB.
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -365,7 +365,7 @@
banks |= BIT(0);
break;
case PM8XXX_QCOM_DRIVE_STRENGH:
- if (arg > PMIC_GPIO_STRENGTH_LOW) {
+ if (arg > PM8921_GPIO_STRENGTH_LOW) {
dev_err(pctrl->dev, "invalid drive strength\n");
return -EINVAL;
}
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 04053fe..cfa3e85 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -60,12 +60,14 @@
struct cros_ec_command *msg)
{
int ret;
+ int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg);
if (ec_dev->proto_version > 2)
- ret = ec_dev->pkt_xfer(ec_dev, msg);
+ xfer_fxn = ec_dev->pkt_xfer;
else
- ret = ec_dev->cmd_xfer(ec_dev, msg);
+ xfer_fxn = ec_dev->cmd_xfer;
+ ret = (*xfer_fxn)(ec_dev, msg);
if (msg->result == EC_RES_IN_PROGRESS) {
int i;
struct cros_ec_command *status_msg;
@@ -88,7 +90,7 @@
for (i = 0; i < EC_COMMAND_RETRIES; i++) {
usleep_range(10000, 11000);
- ret = ec_dev->cmd_xfer(ec_dev, status_msg);
+ ret = (*xfer_fxn)(ec_dev, status_msg);
if (ret < 0)
break;
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index f3baf99..24f1630a 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -187,7 +187,7 @@
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: EC error %d\n", msg->result);
else {
- msg->data[sizeof(msg->data) - 1] = '\0';
+ msg->data[EC_HOST_PARAM_SIZE - 1] = '\0';
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: %s\n", msg->data);
}
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h
index 33e0314..36d49e4 100644
--- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h
@@ -359,7 +359,7 @@
ulong global_irq_counter;
bool dump_conf;
-
+ bool config_mmio_init;
bool enumerated;
enum ep_pcie_link_status link_status;
bool perst_deast;
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
index 4558530..0ada0bf 100644
--- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
@@ -503,6 +503,13 @@
"Initial version of MMIO is:0x%x\n",
readl_relaxed(dev->mmio + PCIE20_MHIVER));
+ if (dev->config_mmio_init) {
+ EP_PCIE_DBG(dev,
+ "PCIe V%d: MMIO already initialized, return\n",
+ dev->rev);
+ return;
+ }
+
ep_pcie_write_reg(dev->mmio, PCIE20_MHICFG, 0x02800880);
ep_pcie_write_reg(dev->mmio, PCIE20_BHI_EXECENV, 0x2);
ep_pcie_write_reg(dev->mmio, PCIE20_MHICTRL, 0x0);
@@ -511,6 +518,8 @@
ep_pcie_write_reg(dev->mmio, PCIE20_BHI_VERSION_LOWER, 0x2);
ep_pcie_write_reg(dev->mmio, PCIE20_BHI_VERSION_UPPER, 0x1);
ep_pcie_write_reg(dev->mmio, PCIE20_BHI_INTVEC, 0xffffffff);
+
+ dev->config_mmio_init = true;
}
static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured)
diff --git a/drivers/platform/msm/gsi/Makefile b/drivers/platform/msm/gsi/Makefile
index b350a59..1eed995 100644
--- a/drivers/platform/msm/gsi/Makefile
+++ b/drivers/platform/msm/gsi/Makefile
@@ -1 +1,8 @@
obj-$(CONFIG_GSI) += gsi.o gsi_dbg.o
+
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+obj-$(CONFIG_GSI) += gsi_emulation.o
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index dc445a0..cf30719 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include "gsi.h"
#include "gsi_reg.h"
+#include "gsi_emulation.h"
#define GSI_CMD_TIMEOUT (5*HZ)
#define GSI_STOP_CMD_TIMEOUT_MS 20
@@ -33,6 +34,8 @@
{ },
};
+static bool running_emulation = IPA_EMULATION_COMPILE;
+
struct gsi_ctx *gsi_ctx;
static void __gsi_config_type_irq(int ee, uint32_t mask, uint32_t val)
@@ -577,7 +580,7 @@
if (!type)
break;
- GSIDBG_LOW("type %x\n", type);
+ GSIDBG_LOW("type 0x%x\n", type);
if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK)
gsi_handle_ch_ctrl(ee);
@@ -777,17 +780,57 @@
GSIERR("bad irq specified %u\n", props->irq);
return -GSI_STATUS_INVALID_PARAMS;
}
-
- res = devm_request_irq(gsi_ctx->dev, props->irq,
+ /*
+ * On a real UE, there are two separate interrupt
+ * vectors that get directed toward the GSI/IPA
+ * drivers. They are handled by gsi_isr() and
+ * (ipa_isr() or ipa3_isr()) respectively. In the
+ * emulation environment, this is not the case;
+ * instead, interrupt vectors are routed to the
+ * emualation hardware's interrupt controller, who in
+ * turn, forwards a single interrupt to the GSI/IPA
+ * driver. When the new interrupt vector is received,
+ * the driver needs to probe the interrupt
+ * controller's registers so see if one, the other, or
+ * both interrupts have occurred. Given the above, we
+ * now need to handle both situations, namely: the
+ * emulator's and the real UE.
+ */
+ if (running_emulation) {
+ /*
+ * New scheme involving the emulator's
+ * interrupt controller.
+ */
+ res = devm_request_threaded_irq(
+ gsi_ctx->dev,
+ props->irq,
+ /* top half handler to follow */
+ emulator_hard_irq_isr,
+ /* threaded bottom half handler to follow */
+ emulator_soft_irq_isr,
+ IRQF_SHARED,
+ "emulator_intcntrlr",
+ gsi_ctx);
+ } else {
+ /*
+ * Traditional scheme used on the real UE.
+ */
+ res = devm_request_irq(gsi_ctx->dev, props->irq,
(irq_handler_t) gsi_isr,
props->req_clk_cb ? IRQF_TRIGGER_RISING :
IRQF_TRIGGER_HIGH,
"gsi",
gsi_ctx);
+ }
if (res) {
- GSIERR("failed to register isr for %u\n", props->irq);
+ GSIERR(
+ "failed to register isr for %u\n",
+ props->irq);
return -GSI_STATUS_ERROR;
}
+ GSIDBG(
+ "succeeded to register isr for %u\n",
+ props->irq);
res = enable_irq_wake(props->irq);
if (res)
@@ -808,6 +851,41 @@
return -GSI_STATUS_RES_ALLOC_FAILURE;
}
+ GSIDBG("GSI base(%pa) mapped to (%pK) with len (0x%lx)\n",
+ &(props->phys_addr),
+ gsi_ctx->base,
+ props->size);
+
+ if (running_emulation) {
+ GSIDBG("GSI SW ver register value 0x%x\n",
+ gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_SW_VERSION_OFFS(0)));
+ gsi_ctx->intcntrlr_mem_size =
+ props->emulator_intcntrlr_size;
+ gsi_ctx->intcntrlr_base =
+ devm_ioremap_nocache(
+ gsi_ctx->dev,
+ props->emulator_intcntrlr_addr,
+ props->emulator_intcntrlr_size);
+ if (!gsi_ctx->intcntrlr_base) {
+ GSIERR(
+ "failed to remap emulator's interrupt controller HW\n");
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+ devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
+ return -GSI_STATUS_RES_ALLOC_FAILURE;
+ }
+
+ GSIDBG(
+ "Emulator's interrupt controller base(%pa) mapped to (%pK) with len (0x%lx)\n",
+ &(props->emulator_intcntrlr_addr),
+ gsi_ctx->intcntrlr_base,
+ props->emulator_intcntrlr_size);
+
+ gsi_ctx->intcntrlr_gsi_isr = gsi_isr;
+ gsi_ctx->intcntrlr_client_isr =
+ props->emulator_intcntrlr_client_isr;
+ }
+
gsi_ctx->per = *props;
gsi_ctx->per_registered = true;
mutex_init(&gsi_ctx->mlock);
@@ -816,6 +894,9 @@
gsi_ctx->max_ch = gsi_get_max_channels(gsi_ctx->per.ver);
if (gsi_ctx->max_ch == 0) {
devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+ if (running_emulation)
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+ gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
GSIERR("failed to get max channels\n");
return -GSI_STATUS_ERROR;
@@ -823,6 +904,9 @@
gsi_ctx->max_ev = gsi_get_max_event_rings(gsi_ctx->per.ver);
if (gsi_ctx->max_ev == 0) {
devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+ if (running_emulation)
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+ gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
GSIERR("failed to get max event rings\n");
return -GSI_STATUS_ERROR;
@@ -831,7 +915,9 @@
if (props->mhi_er_id_limits_valid &&
props->mhi_er_id_limits[0] > (gsi_ctx->max_ev - 1)) {
devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
- gsi_ctx->base = NULL;
+ if (running_emulation)
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+ gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
GSIERR("MHI event ring start id %u is beyond max %u\n",
props->mhi_er_id_limits[0], gsi_ctx->max_ev);
@@ -872,6 +958,22 @@
gsi_writel(0, gsi_ctx->base +
GSI_EE_n_ERROR_LOG_OFFS(gsi_ctx->per.ee));
+ if (running_emulation) {
+ /*
+ * Set up the emulator's interrupt controller...
+ */
+ res = setup_emulator_cntrlr(
+ gsi_ctx->intcntrlr_base, gsi_ctx->intcntrlr_mem_size);
+ if (res != 0) {
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+ devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+ gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
+ devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
+ GSIERR("setup_emulator_cntrlr() failed\n");
+ return res;
+ }
+ }
+
*dev_hdl = (uintptr_t)gsi_ctx;
return GSI_STATUS_SUCCESS;
@@ -1572,6 +1674,10 @@
GSI_EE_n_GSI_CH_k_QOS_MAX_PREFETCH_BMSK) |
((props->use_db_eng << GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_SHFT) &
GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_BMSK));
+ if (gsi_ctx->per.ver >= GSI_VER_2_0)
+ val |= ((props->prefetch_mode <<
+ GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_SHFT)
+ & GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_BMSK);
gsi_writel(val, gsi_ctx->base +
GSI_EE_n_GSI_CH_k_QOS_OFFS(props->ch_id, ee));
}
@@ -1770,6 +1876,32 @@
}
EXPORT_SYMBOL(gsi_alloc_channel);
+static void __gsi_read_channel_scratch(unsigned long chan_hdl,
+ union __packed gsi_channel_scratch * val)
+{
+ uint32_t reg;
+
+ reg = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_CH_k_SCRATCH_0_OFFS(chan_hdl,
+ gsi_ctx->per.ee));
+ val->data.word1 = reg;
+
+ reg = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_CH_k_SCRATCH_1_OFFS(chan_hdl,
+ gsi_ctx->per.ee));
+ val->data.word2 = reg;
+
+ reg = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_CH_k_SCRATCH_2_OFFS(chan_hdl,
+ gsi_ctx->per.ee));
+ val->data.word3 = reg;
+
+ reg = gsi_readl(gsi_ctx->base +
+ GSI_EE_n_GSI_CH_k_SCRATCH_3_OFFS(chan_hdl,
+ gsi_ctx->per.ee));
+ val->data.word4 = reg;
+}
+
static void __gsi_write_channel_scratch(unsigned long chan_hdl,
union __packed gsi_channel_scratch val)
{
@@ -1830,6 +1962,40 @@
}
EXPORT_SYMBOL(gsi_write_channel_scratch);
+int gsi_read_channel_scratch(unsigned long chan_hdl,
+ union __packed gsi_channel_scratch * ch_scratch)
+{
+ struct gsi_chan_ctx *ctx;
+
+ if (!gsi_ctx) {
+ pr_err("%s:%d gsi context not allocated\n", __func__, __LINE__);
+ return -GSI_STATUS_NODEV;
+ }
+
+ if (chan_hdl >= gsi_ctx->max_ch) {
+ GSIERR("bad params chan_hdl=%lu\n", chan_hdl);
+ return -GSI_STATUS_INVALID_PARAMS;
+ }
+
+ if (gsi_ctx->chan[chan_hdl].state != GSI_CHAN_STATE_ALLOCATED &&
+ gsi_ctx->chan[chan_hdl].state != GSI_CHAN_STATE_STARTED &&
+ gsi_ctx->chan[chan_hdl].state != GSI_CHAN_STATE_STOPPED) {
+ GSIERR("bad state %d\n",
+ gsi_ctx->chan[chan_hdl].state);
+ return -GSI_STATUS_UNSUPPORTED_OP;
+ }
+
+ ctx = &gsi_ctx->chan[chan_hdl];
+
+ mutex_lock(&ctx->mlock);
+ __gsi_read_channel_scratch(chan_hdl, ch_scratch);
+ ctx->restore_scratch = *ch_scratch;
+ mutex_unlock(&ctx->mlock);
+
+ return GSI_STATUS_SUCCESS;
+}
+EXPORT_SYMBOL(gsi_read_channel_scratch);
+
int gsi_query_channel_db_addr(unsigned long chan_hdl,
uint32_t *db_addr_wp_lsb, uint32_t *db_addr_wp_msb)
{
@@ -2130,6 +2296,10 @@
BUG();
}
+ /* Hardware issue fixed from GSI 2.0 and no need for the WA */
+ if (gsi_ctx->per.ver >= GSI_VER_2_0)
+ reset_done = true;
+
/* workaround: reset GSI producers again */
if (ctx->props.dir == GSI_CHAN_DIR_FROM_GSI && !reset_done) {
usleep_range(GSI_RESET_WA_MIN_SLEEP, GSI_RESET_WA_MAX_SLEEP);
@@ -2508,7 +2678,7 @@
return -GSI_STATUS_UNSUPPORTED_OP;
}
- if (ctx->state != GSI_CHAN_STATE_STARTED) {
+ if (ctx->state == GSI_CHAN_STATE_NOT_ALLOCATED) {
GSIERR("bad state %d\n", ctx->state);
return -GSI_STATUS_UNSUPPORTED_OP;
}
@@ -2617,21 +2787,27 @@
return -GSI_STATUS_UNSUPPORTED_OP;
}
- spin_lock_irqsave(&gsi_ctx->slock, flags);
if (curr == GSI_CHAN_MODE_CALLBACK &&
mode == GSI_CHAN_MODE_POLL) {
+ spin_lock_irqsave(&gsi_ctx->slock, flags);
__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, 0);
+ spin_unlock_irqrestore(&gsi_ctx->slock, flags);
+ spin_lock_irqsave(&ctx->ring.slock, flags);
atomic_set(&ctx->poll_mode, mode);
+ spin_unlock_irqrestore(&ctx->ring.slock, flags);
ctx->stats.callback_to_poll++;
}
if (curr == GSI_CHAN_MODE_POLL &&
mode == GSI_CHAN_MODE_CALLBACK) {
+ spin_lock_irqsave(&ctx->ring.slock, flags);
atomic_set(&ctx->poll_mode, mode);
+ spin_unlock_irqrestore(&ctx->ring.slock, flags);
+ spin_lock_irqsave(&gsi_ctx->slock, flags);
__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, ~0);
+ spin_unlock_irqrestore(&gsi_ctx->slock, flags);
ctx->stats.poll_to_callback++;
}
- spin_unlock_irqrestore(&gsi_ctx->slock, flags);
return GSI_STATUS_SUCCESS;
}
@@ -2726,24 +2902,47 @@
{
void __iomem *gsi_base = (void __iomem *)base;
- gsi_writel(1, gsi_base + GSI_GSI_IRAM_PTR_CH_CMD_OFFS);
- gsi_writel(2, gsi_base + GSI_GSI_IRAM_PTR_CH_DB_OFFS);
- gsi_writel(3, gsi_base + GSI_GSI_IRAM_PTR_CH_DIS_COMP_OFFS);
- gsi_writel(4, gsi_base + GSI_GSI_IRAM_PTR_CH_EMPTY_OFFS);
- gsi_writel(5, gsi_base + GSI_GSI_IRAM_PTR_EE_GENERIC_CMD_OFFS);
- gsi_writel(6, gsi_base + GSI_GSI_IRAM_PTR_EVENT_GEN_COMP_OFFS);
- gsi_writel(7, gsi_base + GSI_GSI_IRAM_PTR_INT_MOD_STOPED_OFFS);
- gsi_writel(8, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_0_OFFS);
- gsi_writel(9, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_2_OFFS);
- gsi_writel(10, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_1_OFFS);
- gsi_writel(11, gsi_base + GSI_GSI_IRAM_PTR_NEW_RE_OFFS);
- gsi_writel(12, gsi_base + GSI_GSI_IRAM_PTR_READ_ENG_COMP_OFFS);
- gsi_writel(13, gsi_base + GSI_GSI_IRAM_PTR_TIMER_EXPIRED_OFFS);
+ gsi_writel(1,
+ gsi_base + GSI_GSI_IRAM_PTR_CH_CMD_OFFS);
+ gsi_writel(2,
+ gsi_base + GSI_GSI_IRAM_PTR_CH_DB_OFFS);
+ gsi_writel(3,
+ gsi_base + GSI_GSI_IRAM_PTR_CH_DIS_COMP_OFFS);
+ gsi_writel(4,
+ gsi_base + GSI_GSI_IRAM_PTR_CH_EMPTY_OFFS);
+ gsi_writel(5,
+ gsi_base + GSI_GSI_IRAM_PTR_EE_GENERIC_CMD_OFFS);
+ gsi_writel(6,
+ gsi_base + GSI_GSI_IRAM_PTR_EVENT_GEN_COMP_OFFS);
+ gsi_writel(7,
+ gsi_base + GSI_GSI_IRAM_PTR_INT_MOD_STOPED_OFFS);
+ gsi_writel(8,
+ gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_0_OFFS);
+ gsi_writel(9,
+ gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_2_OFFS);
+ gsi_writel(10,
+ gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_1_OFFS);
+ gsi_writel(11,
+ gsi_base + GSI_GSI_IRAM_PTR_NEW_RE_OFFS);
+ gsi_writel(12,
+ gsi_base + GSI_GSI_IRAM_PTR_READ_ENG_COMP_OFFS);
+ gsi_writel(13,
+ gsi_base + GSI_GSI_IRAM_PTR_TIMER_EXPIRED_OFFS);
+
+ if (running_emulation) {
+ gsi_writel(14,
+ gsi_base + GSI_GSI_IRAM_PTR_EV_DB_OFFS);
+ gsi_writel(15,
+ gsi_base + GSI_GSI_IRAM_PTR_UC_GP_INT_OFFS);
+ gsi_writel(16,
+ gsi_base + GSI_GSI_IRAM_PTR_WRITE_ENG_COMP_OFFS);
+ }
}
static void gsi_configure_bck_prs_matrix(void *base)
{
void __iomem *gsi_base = (void __iomem *)base;
+
/*
* For now, these are default values. In the future, GSI FW image will
* produce optimized back-pressure values based on the FW image.
@@ -2966,15 +3165,45 @@
},
};
+static struct platform_device *pdev;
+
/**
* Module Init.
*/
static int __init gsi_init(void)
{
+ int ret;
+
pr_debug("gsi_init\n");
- return platform_driver_register(&msm_gsi_driver);
+
+ ret = platform_driver_register(&msm_gsi_driver);
+ if (ret < 0)
+ goto out;
+
+ if (running_emulation) {
+ pdev = platform_device_register_simple("gsi", -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ platform_driver_unregister(&msm_gsi_driver);
+ goto out;
+ }
+ }
+
+out:
+ return ret;
}
+/*
+ * Module exit.
+ */
+static void __exit gsi_exit(void)
+{
+ if (running_emulation && pdev)
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&msm_gsi_driver);
+}
+
+module_exit(gsi_exit);
arch_initcall(gsi_init);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index 7e10405..4a3f4ec 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,8 +18,16 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/msm_gsi.h>
+#include <linux/errno.h>
#include <linux/ipc_logging.h>
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "gsi_emulation_stubs.h"
+#endif
+
#define GSI_CHAN_MAX 31
#define GSI_EVT_RING_MAX 23
#define GSI_NO_EVT_ERINDEX 31
@@ -129,6 +137,7 @@
bool allocated;
atomic_t poll_mode;
union __packed gsi_channel_scratch scratch;
+ union __packed gsi_channel_scratch restore_scratch;
struct gsi_chan_stats stats;
bool enable_dp_stats;
bool print_dp_stats;
@@ -204,6 +213,13 @@
struct completion gen_ee_cmd_compl;
void *ipc_logbuf;
void *ipc_logbuf_low;
+ /*
+ * The following used only on emulation systems.
+ */
+ void __iomem *intcntrlr_base;
+ u32 intcntrlr_mem_size;
+ irq_handler_t intcntrlr_gsi_isr;
+ irq_handler_t intcntrlr_client_isr;
};
enum gsi_re_type {
diff --git a/drivers/platform/msm/gsi/gsi_emulation.c b/drivers/platform/msm/gsi/gsi_emulation.c
new file mode 100644
index 0000000..adaaaaa
--- /dev/null
+++ b/drivers/platform/msm/gsi/gsi_emulation.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "gsi_emulation.h"
+
+/*
+ * *****************************************************************************
+ * The following used to set up the EMULATION interrupt controller...
+ * *****************************************************************************
+ */
+int setup_emulator_cntrlr(
+ void __iomem *intcntrlr_base,
+ u32 intcntrlr_mem_size)
+{
+ uint32_t val, ver, intrCnt, rangeCnt, range;
+
+ val = gsi_emu_readl(intcntrlr_base + GE_INT_CTL_VER_CNT);
+
+ intrCnt = val & 0xFFFF;
+ ver = (val >> 16) & 0xFFFF;
+ rangeCnt = intrCnt / 32;
+
+ GSIDBG(
+ "CTL_VER_CNT reg val(0x%x) intr cnt(%u) cntrlr ver(0x%x) rangeCnt(%u)\n",
+ val, intrCnt, ver, rangeCnt);
+
+ /*
+ * Verify the interrupt controller version
+ */
+ if (ver == 0 || ver == 0xFFFF || ver < DEO_IC_INT_CTL_VER_MIN) {
+ GSIERR(
+ "Error: invalid interrupt controller version 0x%x\n",
+ ver);
+ return -GSI_STATUS_INVALID_PARAMS;
+ }
+
+ /*
+ * Verify the interrupt count
+ *
+ * NOTE: intrCnt must be at least one block and multiple of 32
+ */
+ if ((intrCnt % 32) != 0) {
+ GSIERR(
+ "Invalid interrupt count read from HW 0x%04x\n",
+ intrCnt);
+ return -GSI_STATUS_ERROR;
+ }
+
+ /*
+ * Calculate number of ranges used, each range handles 32 int lines
+ */
+ if (rangeCnt > DEO_IC_MAX_RANGE_CNT) {
+ GSIERR(
+ "SW interrupt limit(%u) passed, increase DEO_IC_MAX_RANGE_CNT(%u)\n",
+ rangeCnt,
+ DEO_IC_MAX_RANGE_CNT);
+ return -GSI_STATUS_ERROR;
+ }
+
+ /*
+ * Let's take the last register offset minus the first
+ * register offset (ie. range) and compare it to the interrupt
+ * controller's dtsi defined memory size. The range better
+ * fit within the size.
+ */
+ val = GE_SOFT_INT_n(rangeCnt-1) - GE_INT_CTL_VER_CNT;
+ if (val > intcntrlr_mem_size) {
+ GSIERR(
+ "Interrupt controller register range (%u) exceeds dtsi provisioned size (%u)\n",
+ val, intcntrlr_mem_size);
+ return -GSI_STATUS_ERROR;
+ }
+
+ /*
+ * The following will disable the emulators interrupt controller,
+ * so that we can config it...
+ */
+ GSIDBG("Writing GE_INT_MASTER_ENABLE\n");
+ gsi_emu_writel(
+ 0x0,
+ intcntrlr_base + GE_INT_MASTER_ENABLE);
+
+ /*
+ * Init register maps of all ranges
+ */
+ for (range = 0; range < rangeCnt; range++) {
+ /*
+ * Disable all int sources by setting all enable clear bits
+ */
+ GSIDBG("Writing GE_INT_ENABLE_CLEAR_n(%u)\n", range);
+ gsi_emu_writel(
+ 0xFFFFFFFF,
+ intcntrlr_base + GE_INT_ENABLE_CLEAR_n(range));
+
+ /*
+ * Clear all raw statuses
+ */
+ GSIDBG("Writing GE_INT_CLEAR_n(%u)\n", range);
+ gsi_emu_writel(
+ 0xFFFFFFFF,
+ intcntrlr_base + GE_INT_CLEAR_n(range));
+
+ /*
+ * Init all int types
+ */
+ GSIDBG("Writing GE_INT_TYPE_n(%u)\n", range);
+ gsi_emu_writel(
+ 0x0,
+ intcntrlr_base + GE_INT_TYPE_n(range));
+ }
+
+ /*
+ * The following tells the interrupt controller to interrupt us
+ * when it sees interupts from ipa and/or gsi.
+ *
+ * Interrupts:
+ * ===================================================================
+ * DUT0 [ 63 : 16 ]
+ * ipa_irq [ 3 : 0 ] <---HERE
+ * ipa_gsi_bam_irq [ 7 : 4 ] <---HERE
+ * ipa_bam_apu_sec_error_irq [ 8 ]
+ * ipa_bam_apu_non_sec_error_irq [ 9 ]
+ * ipa_bam_xpu2_msa_intr [ 10 ]
+ * ipa_vmidmt_nsgcfgirpt [ 11 ]
+ * ipa_vmidmt_nsgirpt [ 12 ]
+ * ipa_vmidmt_gcfgirpt [ 13 ]
+ * ipa_vmidmt_girpt [ 14 ]
+ * bam_xpu3_qad_non_secure_intr_sp [ 15 ]
+ */
+ GSIDBG("Writing GE_INT_ENABLE_n(0)\n");
+ gsi_emu_writel(
+ 0x00FF, /* See <---HERE above */
+ intcntrlr_base + GE_INT_ENABLE_n(0));
+
+ /*
+ * The following will enable the IC post config...
+ */
+ GSIDBG("Writing GE_INT_MASTER_ENABLE\n");
+ gsi_emu_writel(
+ 0x1,
+ intcntrlr_base + GE_INT_MASTER_ENABLE);
+
+ return 0;
+}
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION hard irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_hard_irq_isr(
+ int irq,
+ void *ctxt)
+{
+ struct gsi_ctx *gsi_ctx_ptr = (struct gsi_ctx *) ctxt;
+
+ uint32_t val;
+
+ val = gsi_emu_readl(gsi_ctx_ptr->intcntrlr_base + GE_INT_MASTER_STATUS);
+
+ /*
+ * If bit zero is set, interrupt is for us, hence return IRQ_NONE
+ * when it's not set...
+ */
+ if (!(val & 0x00000001))
+ return IRQ_NONE;
+
+ /*
+ * The following will mask (ie. turn off) future interrupts from
+ * the emulator's interrupt controller. It wil stay this way until
+ * we turn back on...which will be done in the bottom half
+ * (ie. emulator_soft_irq_isr)...
+ */
+ gsi_emu_writel(
+ 0x0,
+ gsi_ctx_ptr->intcntrlr_base + GE_INT_OUT_ENABLE);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION soft irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_soft_irq_isr(
+ int irq,
+ void *ctxt)
+{
+ struct gsi_ctx *gsi_ctx_ptr = (struct gsi_ctx *) ctxt;
+
+ irqreturn_t retVal = IRQ_HANDLED;
+ uint32_t val;
+
+ val = gsi_emu_readl(gsi_ctx_ptr->intcntrlr_base + GE_IRQ_STATUS_n(0));
+
+ GSIDBG("Got irq(%d) with status(0x%08X)\n", irq, val);
+
+ if (val & 0xF0 && gsi_ctx_ptr->intcntrlr_gsi_isr) {
+ GSIDBG("Got gsi interrupt\n");
+ retVal = gsi_ctx_ptr->intcntrlr_gsi_isr(irq, ctxt);
+ }
+
+ if (val & 0x0F && gsi_ctx_ptr->intcntrlr_client_isr) {
+ GSIDBG("Got ipa interrupt\n");
+ retVal = gsi_ctx_ptr->intcntrlr_client_isr(irq, 0);
+ }
+
+ /*
+ * The following will clear the interrupts...
+ */
+ gsi_emu_writel(
+ 0xFFFFFFFF,
+ gsi_ctx_ptr->intcntrlr_base + GE_INT_CLEAR_n(0));
+
+ /*
+ * The following will unmask (ie. turn on) future interrupts from
+ * the emulator's interrupt controller...
+ */
+ gsi_emu_writel(
+ 0x1,
+ gsi_ctx_ptr->intcntrlr_base + GE_INT_OUT_ENABLE);
+
+ return retVal;
+}
diff --git a/drivers/platform/msm/gsi/gsi_emulation.h b/drivers/platform/msm/gsi/gsi_emulation.h
new file mode 100644
index 0000000..246f9a8
--- /dev/null
+++ b/drivers/platform/msm/gsi/gsi_emulation.h
@@ -0,0 +1,192 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#if !defined(_GSI_EMULATION_H_)
+# define _GSI_EMULATION_H_
+
+# include <linux/interrupt.h>
+
+# include "gsi.h"
+# include "gsi_reg.h"
+# include "gsi_emulation_stubs.h"
+
+# define gsi_emu_readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
+# define gsi_emu_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); })
+
+# define CNTRLR_BASE 0
+
+/*
+ * The following file contains definitions and declarations that are
+ * germane only to the IPA emulation system, which is run from an X86
+ * environment. Declaration's for non-X86 (ie. arm) are merely stubs
+ * to facilitate compile and link.
+ *
+ * Interrupt controller registers.
+ * Descriptions taken from the EMULATION interrupt controller SWI.
+ * - There is only one Master Enable register
+ * - Each group of 32 interrupt lines (range) is controlled by 8 registers,
+ * which are consecutive in memory:
+ * GE_INT_ENABLE_n
+ * GE_INT_ENABLE_CLEAR_n
+ * GE_INT_ENABLE_SET_n
+ * GE_INT_TYPE_n
+ * GE_IRQ_STATUS_n
+ * GE_RAW_STATUS_n
+ * GE_INT_CLEAR_n
+ * GE_SOFT_INT_n
+ * - After the above 8 registers, there are the registers of the next
+ * group (range) of 32 interrupt lines, and so on.
+ */
+
+/** @brief The interrupt controller version and interrupt count register.
+ * Specifies interrupt controller version (upper 16 bits) and the
+ * number of interrupt lines supported by HW (lower 16 bits).
+ */
+# define GE_INT_CTL_VER_CNT \
+ (CNTRLR_BASE + 0x0000)
+
+/** @brief Enable or disable physical IRQ output signal to the system,
+ * not affecting any status registers.
+ *
+ * 0x0 : DISABLE IRQ output disabled
+ * 0x1 : ENABLE IRQ output enabled
+ */
+# define GE_INT_OUT_ENABLE \
+ (CNTRLR_BASE + 0x0004)
+
+/** @brief The IRQ master enable register.
+ * Bit #0: IRQ_ENABLE, set 0 to disable, 1 to enable.
+ */
+# define GE_INT_MASTER_ENABLE \
+ (CNTRLR_BASE + 0x0008)
+
+# define GE_INT_MASTER_STATUS \
+ (CNTRLR_BASE + 0x000C)
+
+/** @brief Each bit disables (bit=0, default) or enables (bit=1) the
+ * corresponding interrupt source
+ */
+# define GE_INT_ENABLE_n(n) \
+ (CNTRLR_BASE + 0x0010 + 0x20 * (n))
+
+/** @brief Write bit=1 to clear (to 0) the corresponding bit(s) in INT_ENABLE.
+ * Does nothing for bit=0
+ */
+# define GE_INT_ENABLE_CLEAR_n(n) \
+ (CNTRLR_BASE + 0x0014 + 0x20 * (n))
+
+/** @brief Write bit=1 to set (to 1) the corresponding bit(s) in INT_ENABLE.
+ * Does nothing for bit=0
+ */
+# define GE_INT_ENABLE_SET_n(n) \
+ (CNTRLR_BASE + 0x0018 + 0x20 * (n))
+
+/** @brief Select level (bit=0, default) or edge (bit=1) sensitive input
+ * detection logic for each corresponding interrupt source
+ */
+# define GE_INT_TYPE_n(n) \
+ (CNTRLR_BASE + 0x001C + 0x20 * (n))
+
+/** @brief Shows the interrupt sources captured in RAW_STATUS that have been
+ * steered to irq_n by INT_SELECT. Interrupts must also be enabled by
+ * INT_ENABLE and MASTER_ENABLE. Read only register.
+ * Bit values: 1=active, 0=inactive
+ */
+# define GE_IRQ_STATUS_n(n) \
+ (CNTRLR_BASE + 0x0020 + 0x20 * (n))
+
+/** @brief Shows the interrupt sources that have been latched by the input
+ * logic of the Interrupt Controller. Read only register.
+ * Bit values: 1=active, 0=inactive
+ */
+# define GE_RAW_STATUS_n(n) \
+ (CNTRLR_BASE + 0x0024 + 0x20 * (n))
+
+/** @brief Write bit=1 to clear the corresponding bit(s) in RAW_STATUS.
+ * Does nothing for bit=0
+ */
+# define GE_INT_CLEAR_n(n) \
+ (CNTRLR_BASE + 0x0028 + 0x20 * (n))
+
+/** @brief Write bit=1 to set the corresponding bit(s) in RAW_STATUS.
+ * Does nothing for bit=0.
+ * @note Only functional for edge detected interrupts
+ */
+# define GE_SOFT_INT_n(n) \
+ (CNTRLR_BASE + 0x002C + 0x20 * (n))
+
+/** @brief Maximal number of ranges in SW. Each range supports 32 interrupt
+ * lines. If HW is extended considerably, increase this value
+ */
+# define DEO_IC_MAX_RANGE_CNT 8
+
+/** @brief Size of the registers of one range in memory, in bytes */
+# define DEO_IC_RANGE_MEM_SIZE 32 /* SWI: 8 registers, no gaps */
+
+/** @brief Minimal Interrupt controller HW version */
+# define DEO_IC_INT_CTL_VER_MIN 0x0102
+
+
+#if IPA_EMULATION_COMPILE == 1 /* declarations to follow */
+
+/*
+ * *****************************************************************************
+ * The following used to set up the EMULATION interrupt controller...
+ * *****************************************************************************
+ */
+int setup_emulator_cntrlr(
+ void __iomem *intcntrlr_base,
+ u32 intcntrlr_mem_size);
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION hard irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_hard_irq_isr(
+ int irq,
+ void *ctxt);
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION soft irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_soft_irq_isr(
+ int irq,
+ void *ctxt);
+
+# else /* #if IPA_EMULATION_COMPILE != 1, then definitions to follow */
+
+static inline int setup_emulator_cntrlr(
+ void __iomem *intcntrlr_base,
+ u32 intcntrlr_mem_size)
+{
+ return 0;
+}
+
+static inline irqreturn_t emulator_hard_irq_isr(
+ int irq,
+ void *ctxt)
+{
+ return IRQ_NONE;
+}
+
+static inline irqreturn_t emulator_soft_irq_isr(
+ int irq,
+ void *ctxt)
+{
+ return IRQ_HANDLED;
+}
+
+# endif /* #if IPA_EMULATION_COMPILE == 1 */
+
+#endif /* #if !defined(_GSI_EMULATION_H_) */
diff --git a/drivers/platform/msm/gsi/gsi_emulation_stubs.h b/drivers/platform/msm/gsi/gsi_emulation_stubs.h
new file mode 100644
index 0000000..dd9d0df
--- /dev/null
+++ b/drivers/platform/msm/gsi/gsi_emulation_stubs.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#if !defined(_GSI_EMULATION_STUBS_H_)
+# define _GSI_EMULATION_STUBS_H_
+
+# include <asm/barrier.h>
+# define __iormb() rmb() /* used in gsi.h */
+# define __iowmb() wmb() /* used in gsi.h */
+
+#endif /* #if !defined(_GSI_EMULATION_STUBS_H_) */
diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h
index 7817613..add3876 100644
--- a/drivers/platform/msm/gsi/gsi_reg.h
+++ b/drivers/platform/msm/gsi/gsi_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1124,6 +1124,8 @@
#define GSI_EE_n_GSI_CH_k_QOS_RMSK 0x303
#define GSI_EE_n_GSI_CH_k_QOS_MAXk 30
#define GSI_EE_n_GSI_CH_k_QOS_MAXn 3
+#define GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_BMSK 0x400
+#define GSI_EE_n_GSI_CH_k_QOS_USE_ESCAPE_BUF_ONLY_SHFT 0xa
#define GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_BMSK 0x200
#define GSI_EE_n_GSI_CH_k_QOS_USE_DB_ENG_SHFT 0x9
#define GSI_EE_n_GSI_CH_k_QOS_MAX_PREFETCH_BMSK 0x100
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index 15ed471..857e17b 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,3 +1,9 @@
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
+
obj-$(CONFIG_IPA) += ipa_v2/ ipa_clients/ ipa_common
obj-$(CONFIG_IPA3) += ipa_v3/ ipa_clients/ ipa_common
obj-$(CONFIG_IPA_UT) += test/
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index fdcf44d..3a75bdd 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -19,8 +19,16 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/ipa_uc_offload.h>
+#include <linux/pci.h>
#include "ipa_api.h"
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "ipa_v3/ipa_emulation_stubs.h"
+#endif
+
#define DRV_NAME "ipa"
#define IPA_API_DISPATCH_RETURN(api, p...) \
@@ -96,6 +104,8 @@
} \
} while (0)
+static bool running_emulation = IPA_EMULATION_COMPILE;
+
static enum ipa_hw_type ipa_api_hw_type;
static struct ipa_api_controller *ipa_api_ctrl;
@@ -2916,6 +2926,57 @@
{}
};
+/*********************************************************/
+/* PCIe Version */
+/*********************************************************/
+
+static const struct of_device_id ipa_pci_drv_match[] = {
+ { .compatible = "qcom,ipa", },
+ {}
+};
+
+/*
+ * Forward declarations of static functions required for PCI
+ * registraion
+ *
+ * VENDOR and DEVICE should be defined in pci_ids.h
+ */
+static int ipa_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+static void ipa_pci_remove(struct pci_dev *pdev);
+static void ipa_pci_shutdown(struct pci_dev *pdev);
+static pci_ers_result_t ipa_pci_io_error_detected(struct pci_dev *dev,
+ pci_channel_state_t state);
+static pci_ers_result_t ipa_pci_io_slot_reset(struct pci_dev *dev);
+static void ipa_pci_io_resume(struct pci_dev *dev);
+
+#define LOCAL_VENDOR 0x17CB
+#define LOCAL_DEVICE 0x00ff
+
+static const char ipa_pci_driver_name[] = "qcipav3";
+
+static const struct pci_device_id ipa_pci_tbl[] = {
+ { PCI_DEVICE(LOCAL_VENDOR, LOCAL_DEVICE) },
+ { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ipa_pci_tbl);
+
+/* PCI Error Recovery */
+static const struct pci_error_handlers ipa_pci_err_handler = {
+ .error_detected = ipa_pci_io_error_detected,
+ .slot_reset = ipa_pci_io_slot_reset,
+ .resume = ipa_pci_io_resume,
+};
+
+static struct pci_driver ipa_pci_driver = {
+ .name = ipa_pci_driver_name,
+ .id_table = ipa_pci_tbl,
+ .probe = ipa_pci_probe,
+ .remove = ipa_pci_remove,
+ .shutdown = ipa_pci_shutdown,
+ .err_handler = &ipa_pci_err_handler
+};
+
static int ipa_generic_plat_drv_probe(struct platform_device *pdev_p)
{
int result;
@@ -3364,10 +3425,86 @@
},
};
+/*********************************************************/
+/* PCIe Version */
+/*********************************************************/
+
+static int ipa_pci_probe(
+ struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ int result;
+
+ if (!pci_dev || !ent) {
+ pr_err(
+ "Bad arg: pci_dev (%pK) and/or ent (%pK)\n",
+ pci_dev, ent);
+ return -EOPNOTSUPP;
+ }
+
+ if (!ipa_api_ctrl) {
+ ipa_api_ctrl = kzalloc(sizeof(*ipa_api_ctrl), GFP_KERNEL);
+ if (ipa_api_ctrl == NULL)
+ return -ENOMEM;
+ /* Get IPA HW Version */
+ result = of_property_read_u32(NULL,
+ "qcom,ipa-hw-ver", &ipa_api_hw_type);
+ if (result || ipa_api_hw_type == 0) {
+ pr_err("ipa: get resource failed for ipa-hw-ver!\n");
+ kfree(ipa_api_ctrl);
+ ipa_api_ctrl = NULL;
+ return -ENODEV;
+ }
+ pr_debug("ipa: ipa_api_hw_type = %d", ipa_api_hw_type);
+ }
+
+ /*
+ * Call a reduced version of platform_probe appropriate for PCIe
+ */
+ result = ipa3_pci_drv_probe(pci_dev, ipa_api_ctrl, ipa_pci_drv_match);
+
+ if (result && result != -EPROBE_DEFER)
+ pr_err("ipa: ipa3_pci_drv_probe failed\n");
+
+ if (running_emulation)
+ ipa_ut_module_init();
+
+ return result;
+}
+
+static void ipa_pci_remove(struct pci_dev *pci_dev)
+{
+ if (running_emulation)
+ ipa_ut_module_exit();
+}
+
+static void ipa_pci_shutdown(struct pci_dev *pci_dev)
+{
+}
+
+static pci_ers_result_t ipa_pci_io_error_detected(struct pci_dev *pci_dev,
+ pci_channel_state_t state)
+{
+ return 0;
+}
+
+static pci_ers_result_t ipa_pci_io_slot_reset(struct pci_dev *pci_dev)
+{
+ return 0;
+}
+
+static void ipa_pci_io_resume(struct pci_dev *pci_dev)
+{
+}
+
static int __init ipa_module_init(void)
{
pr_debug("IPA module init\n");
+ if (running_emulation) {
+ /* Register as a PCI device driver */
+ return pci_register_driver(&ipa_pci_driver);
+ }
/* Register as a platform device driver */
return platform_driver_register(&ipa_plat_drv);
}
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index b7edb6f..cbcb0ee 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -452,6 +452,10 @@
int ipa3_plat_drv_probe(struct platform_device *pdev_p,
struct ipa_api_controller *api_ctrl,
const struct of_device_id *pdrv_match);
+int ipa3_pci_drv_probe(
+ struct pci_dev *pci_dev,
+ struct ipa_api_controller *api_ctrl,
+ const struct of_device_id *pdrv_match);
#else
static inline int ipa3_plat_drv_probe(struct platform_device *pdev_p,
struct ipa_api_controller *api_ctrl,
@@ -459,6 +463,13 @@
{
return -ENODEV;
}
+static inline int ipa3_pci_drv_probe(
+ struct pci_dev *pci_dev,
+ struct ipa_api_controller *api_ctrl,
+ const struct of_device_id *pdrv_match)
+{
+ return -ENODEV;
+}
#endif /* (CONFIG_IPA3) */
#endif /* _IPA_API_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_clients/Makefile b/drivers/platform/msm/ipa/ipa_clients/Makefile
index 738d88f..a213130 100644
--- a/drivers/platform/msm/ipa/ipa_clients/Makefile
+++ b/drivers/platform/msm/ipa/ipa_clients/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
+obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o ipa_gsb.o
obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
obj-$(CONFIG_ECM_IPA) += ecm_ipa.o
obj-$(CONFIG_RNDIS_IPA) += rndis_ipa.o
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_gsb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_gsb.c
new file mode 100644
index 0000000..5812d8d
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_gsb.c
@@ -0,0 +1,1084 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/if_ether.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msm_ipa.h>
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/ipv6.h>
+#include <net/addrconf.h>
+#include <linux/ipa.h>
+#include <linux/cdev.h>
+#include <linux/ipa_odu_bridge.h>
+#include "../ipa_common_i.h"
+#ifdef CONFIG_IPA3
+#include "../ipa_v3/ipa_pm.h"
+#endif
+
+#define IPA_GSB_DRV_NAME "ipa_gsb"
+
+#define MAX_SUPPORTED_IFACE 5
+
+#define IPA_GSB_DBG(fmt, args...) \
+ do { \
+ pr_debug(IPA_GSB_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_GSB_DBG_LOW(fmt, args...) \
+ do { \
+ pr_debug(IPA_GSB_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_GSB_ERR(fmt, args...) \
+ do { \
+ pr_err(IPA_GSB_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_GSB_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_GSB_MAX_MSG_LEN 512
+static char dbg_buff[IPA_GSB_MAX_MSG_LEN];
+
+#define IPA_GSB_SKB_HEADROOM 256
+#define IPA_GSB_SKB_DUMMY_HEADER 42
+#define IPA_GSB_AGGR_BYTE_LIMIT 14
+#define IPA_GSB_AGGR_TIME_LIMIT 1
+
+static struct dentry *dent;
+static struct dentry *dfile_stats;
+
+/**
+ * struct stats - driver statistics,
+ * @num_ul_packets: number of uplink packets
+ * @num_dl_packets: number of downlink packets
+ * @num_insufficient_headroom_packets: number of
+ packets with insufficient headroom
+ */
+struct stats {
+ u64 num_ul_packets;
+ u64 num_dl_packets;
+ u64 num_insufficient_headroom_packets;
+};
+
+/**
+ * struct ipa_gsb_mux_hdr - ipa gsb mux header,
+ * @iface_hdl: interface handle
+ * @qmap_id: qmap id
+ * @pkt_size: packet size
+ */
+struct ipa_gsb_mux_hdr {
+ u8 iface_hdl;
+ u8 qmap_id;
+ u16 pkt_size;
+};
+
+/**
+ * struct ipa_gsb_iface_info - GSB interface information
+ * @netdev_name: network interface name
+ * @device_ethaddr: network interface ethernet address
+ * @priv: client's private data. to be used in client's callbacks
+ * @tx_dp_notify: client callback for handling IPA ODU_PROD callback
+ * @send_dl_skb: client callback for sending skb in downlink direction
+ * @iface_stats: statistics, how many packets were transmitted
+ * using the SW bridge.
+ * @partial_hdr_hdl: handle for partial header
+ * @wakeup_request: client callback to wakeup
+ * @is_conencted: is interface connected ?
+ * @is_resumed: is interface resumed ?
+ * @iface_hdl: interface handle
+ */
+struct ipa_gsb_iface_info {
+ char netdev_name[IPA_RESOURCE_NAME_MAX];
+ u8 device_ethaddr[ETH_ALEN];
+ void *priv;
+ ipa_notify_cb tx_dp_notify;
+ int (*send_dl_skb)(void *priv, struct sk_buff *skb);
+ struct stats iface_stats;
+ uint32_t partial_hdr_hdl[IPA_IP_MAX];
+ void (*wakeup_request)(void *);
+ bool is_connected;
+ bool is_resumed;
+ u8 iface_hdl;
+};
+
+/**
+ * struct ipa_gsb_context - GSB driver context information
+ * @logbuf: buffer of ipc logging
+ * @logbuf_low: buffer of ipc logging (low priority)
+ * @lock: mutex lock
+ * @prod_hdl: handle for prod pipe
+ * @cons_hdl: handle for cons pipe
+ * @ipa_sys_desc_size: sys pipe desc size
+ * @num_iface: number of interface
+ * @iface_hdl: interface handles
+ * @num_connected_iface: number of connected interface
+ * @num_resumed_iface: number of resumed interface
+ * @iface: interface information
+ * @pm_hdl: IPA PM handle
+ */
+struct ipa_gsb_context {
+ void *logbuf;
+ void *logbuf_low;
+ struct mutex lock;
+ u32 prod_hdl;
+ u32 cons_hdl;
+ u32 ipa_sys_desc_size;
+ int num_iface;
+ bool iface_hdl[MAX_SUPPORTED_IFACE];
+ int num_connected_iface;
+ int num_resumed_iface;
+ struct ipa_gsb_iface_info *iface[MAX_SUPPORTED_IFACE];
+ u32 pm_hdl;
+};
+
+static struct ipa_gsb_context *ipa_gsb_ctx;
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t ipa_gsb_debugfs_stats(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int i, nbytes = 0;
+
+ for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
+ if (ipa_gsb_ctx->iface[i] != NULL) {
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_GSB_MAX_MSG_LEN - nbytes,
+ "netdev: %s\n",
+ ipa_gsb_ctx->iface[i]->netdev_name);
+
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_GSB_MAX_MSG_LEN - nbytes,
+ "UL packets: %lld\n",
+ ipa_gsb_ctx->iface[i]->
+ iface_stats.num_ul_packets);
+
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_GSB_MAX_MSG_LEN - nbytes,
+ "DL packets: %lld\n",
+ ipa_gsb_ctx->iface[i]->
+ iface_stats.num_dl_packets);
+
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_GSB_MAX_MSG_LEN - nbytes,
+ "packets with insufficient headroom: %lld\n",
+ ipa_gsb_ctx->iface[i]->
+ iface_stats.num_insufficient_headroom_packets);
+ }
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static const struct file_operations ipa_gsb_stats_ops = {
+ .read = ipa_gsb_debugfs_stats,
+};
+
+static void ipa_gsb_debugfs_init(void)
+{
+ const mode_t read_only_mode = 00444;
+
+ dent = debugfs_create_dir("ipa_gsb", NULL);
+ if (IS_ERR(dent)) {
+ IPA_GSB_ERR("fail to create folder ipa_gsb\n");
+ return;
+ }
+
+ dfile_stats =
+ debugfs_create_file("stats", read_only_mode, dent,
+ NULL, &ipa_gsb_stats_ops);
+ if (!dfile_stats || IS_ERR(dfile_stats)) {
+ IPA_GSB_ERR("fail to create file stats\n");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ debugfs_remove_recursive(dent);
+}
+
+static void ipa_gsb_debugfs_destroy(void)
+{
+ debugfs_remove_recursive(dent);
+}
+#else
+static void ipa_gsb_debugfs_init(void)
+{
+}
+
+static void ipa_gsb_debugfs_destroy(void)
+{
+}
+#endif
+
+static int ipa_gsb_driver_init(struct odu_bridge_params *params)
+{
+ if (!ipa_is_ready()) {
+ IPA_GSB_ERR("IPA is not ready\n");
+ return -EFAULT;
+ }
+
+ ipa_gsb_ctx = kzalloc(sizeof(*ipa_gsb_ctx),
+ GFP_KERNEL);
+
+ if (!ipa_gsb_ctx)
+ return -ENOMEM;
+
+ mutex_init(&ipa_gsb_ctx->lock);
+ ipa_gsb_debugfs_init();
+
+ return 0;
+}
+
+static int ipa_gsb_commit_partial_hdr(struct ipa_gsb_iface_info *iface_info)
+{
+ int i;
+ struct ipa_ioc_add_hdr *hdr;
+
+ if (!iface_info) {
+ IPA_GSB_ERR("invalid input\n");
+ return -EINVAL;
+ }
+
+ hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
+ 2 * sizeof(struct ipa_hdr_add), GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ hdr->commit = 1;
+ hdr->num_hdrs = 2;
+
+ snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name),
+ "%s_ipv4", iface_info->netdev_name);
+ snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name),
+ "%s_ipv6", iface_info->netdev_name);
+ /*
+ * partial header:
+ * [hdl][QMAP ID][pkt size][Dummy Header][ETH header]
+ */
+ for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) {
+ /*
+ * Optimization: add dummy header to reserve space
+ * for rndis header, so we can do the skb_clone
+ * instead of deep copy.
+ */
+ hdr->hdr[i].hdr_len = ETH_HLEN +
+ sizeof(struct ipa_gsb_mux_hdr) +
+ IPA_GSB_SKB_DUMMY_HEADER;
+ hdr->hdr[i].type = IPA_HDR_L2_ETHERNET_II;
+ hdr->hdr[i].is_partial = 1;
+ hdr->hdr[i].is_eth2_ofst_valid = 1;
+ hdr->hdr[i].eth2_ofst = sizeof(struct ipa_gsb_mux_hdr) +
+ IPA_GSB_SKB_DUMMY_HEADER;
+ /* populate iface handle */
+ hdr->hdr[i].hdr[0] = iface_info->iface_hdl;
+ /* populate src ETH address */
+ memcpy(&hdr->hdr[i].hdr[10 + IPA_GSB_SKB_DUMMY_HEADER],
+ iface_info->device_ethaddr, 6);
+ /* populate Ethertype */
+ if (i == IPA_IP_v4)
+ *(u16 *)(hdr->hdr[i].hdr + 16 +
+ IPA_GSB_SKB_DUMMY_HEADER) = htons(ETH_P_IP);
+ else
+ *(u16 *)(hdr->hdr[i].hdr + 16 +
+ IPA_GSB_SKB_DUMMY_HEADER) = htons(ETH_P_IPV6);
+ }
+
+ if (ipa_add_hdr(hdr)) {
+ IPA_GSB_ERR("fail to add partial headers\n");
+ kfree(hdr);
+ return -EFAULT;
+ }
+
+ for (i = IPA_IP_v4; i < IPA_IP_MAX; i++)
+ iface_info->partial_hdr_hdl[i] =
+ hdr->hdr[i].hdr_hdl;
+
+ IPA_GSB_DBG("added partial hdr hdl for ipv4: %d\n",
+ iface_info->partial_hdr_hdl[IPA_IP_v4]);
+ IPA_GSB_DBG("added partial hdr hdl for ipv6: %d\n",
+ iface_info->partial_hdr_hdl[IPA_IP_v6]);
+
+ kfree(hdr);
+ return 0;
+}
+
+static void ipa_gsb_delete_partial_hdr(struct ipa_gsb_iface_info *iface_info)
+{
+ struct ipa_ioc_del_hdr *del_hdr;
+
+ del_hdr = kzalloc(sizeof(struct ipa_ioc_del_hdr) +
+ 2 * sizeof(struct ipa_hdr_del), GFP_KERNEL);
+ if (!del_hdr)
+ return;
+
+ del_hdr->commit = 1;
+ del_hdr->num_hdls = 2;
+ del_hdr->hdl[IPA_IP_v4].hdl = iface_info->partial_hdr_hdl[IPA_IP_v4];
+ del_hdr->hdl[IPA_IP_v6].hdl = iface_info->partial_hdr_hdl[IPA_IP_v6];
+
+ if (ipa_del_hdr(del_hdr) != 0)
+ IPA_GSB_ERR("failed to delete partial hdr\n");
+
+ IPA_GSB_DBG("deleted partial hdr hdl for ipv4: %d\n",
+ iface_info->partial_hdr_hdl[IPA_IP_v4]);
+ IPA_GSB_DBG("deleted partial hdr hdl for ipv6: %d\n",
+ iface_info->partial_hdr_hdl[IPA_IP_v6]);
+
+ kfree(del_hdr);
+}
+
+static int ipa_gsb_reg_intf_props(struct ipa_gsb_iface_info *iface_info)
+{
+ struct ipa_tx_intf tx;
+ struct ipa_rx_intf rx;
+ struct ipa_ioc_tx_intf_prop tx_prop[2];
+ struct ipa_ioc_rx_intf_prop rx_prop[2];
+
+ /* populate tx prop */
+ tx.num_props = 2;
+ tx.prop = tx_prop;
+
+ memset(tx_prop, 0, sizeof(tx_prop));
+ tx_prop[0].ip = IPA_IP_v4;
+ tx_prop[0].dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
+ tx_prop[0].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+ snprintf(tx_prop[0].hdr_name, sizeof(tx_prop[0].hdr_name),
+ "%s_ipv4", iface_info->netdev_name);
+
+ tx_prop[1].ip = IPA_IP_v6;
+ tx_prop[1].dst_pipe = IPA_CLIENT_ODU_EMB_CONS;
+ tx_prop[1].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+ snprintf(tx_prop[1].hdr_name, sizeof(tx_prop[1].hdr_name),
+ "%s_ipv6", iface_info->netdev_name);
+
+ /* populate rx prop */
+ rx.num_props = 2;
+ rx.prop = rx_prop;
+
+ memset(rx_prop, 0, sizeof(rx_prop));
+ rx_prop[0].ip = IPA_IP_v4;
+ rx_prop[0].src_pipe = IPA_CLIENT_ODU_PROD;
+ rx_prop[0].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+ rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_prop[0].attrib.meta_data = iface_info->iface_hdl;
+ rx_prop[0].attrib.meta_data_mask = 0xFF;
+
+ rx_prop[1].ip = IPA_IP_v6;
+ rx_prop[1].src_pipe = IPA_CLIENT_ODU_PROD;
+ rx_prop[1].hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+ rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA;
+ rx_prop[1].attrib.meta_data = iface_info->iface_hdl;
+ rx_prop[1].attrib.meta_data_mask = 0xFF;
+
+ if (ipa_register_intf(iface_info->netdev_name, &tx, &rx)) {
+ IPA_GSB_ERR("fail to add interface prop\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static void ipa_gsb_dereg_intf_props(struct ipa_gsb_iface_info *iface_info)
+{
+ if (ipa_deregister_intf(iface_info->netdev_name) != 0)
+ IPA_GSB_ERR("fail to dereg intf props\n");
+
+ IPA_GSB_DBG("deregistered iface props for %s\n",
+ iface_info->netdev_name);
+}
+
+static void ipa_gsb_pm_cb(void *user_data, enum ipa_pm_cb_event event)
+{
+ int i;
+
+ if (event != IPA_PM_REQUEST_WAKEUP) {
+ IPA_GSB_ERR("Unexpected event %d\n", event);
+ WARN_ON(1);
+ return;
+ }
+
+ IPA_GSB_DBG_LOW("wake up clients\n");
+ for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
+ if (ipa_gsb_ctx->iface[i] != NULL)
+ ipa_gsb_ctx->iface[i]->wakeup_request(
+ ipa_gsb_ctx->iface[i]->priv);
+}
+
+static int ipa_gsb_register_pm(void)
+{
+ struct ipa_pm_register_params reg_params;
+ int ret;
+
+ memset(®_params, 0, sizeof(reg_params));
+ reg_params.name = "ipa_gsb";
+ reg_params.callback = ipa_gsb_pm_cb;
+ reg_params.user_data = NULL;
+ reg_params.group = IPA_PM_GROUP_DEFAULT;
+
+ ret = ipa_pm_register(®_params,
+ &ipa_gsb_ctx->pm_hdl);
+ if (ret) {
+ IPA_GSB_ERR("fail to register with PM %d\n", ret);
+ goto fail_pm_reg;
+ }
+ IPA_GSB_DBG("ipa pm hdl: %d\n", ipa_gsb_ctx->pm_hdl);
+
+ ret = ipa_pm_associate_ipa_cons_to_client(ipa_gsb_ctx->pm_hdl,
+ IPA_CLIENT_ODU_EMB_CONS);
+ if (ret) {
+ IPA_GSB_ERR("fail to associate cons with PM %d\n", ret);
+ goto fail_pm_cons;
+ }
+
+ return 0;
+
+fail_pm_cons:
+ ipa_pm_deregister(ipa_gsb_ctx->pm_hdl);
+ ipa_gsb_ctx->pm_hdl = ~0;
+fail_pm_reg:
+ return ret;
+}
+
+int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
+{
+ int i, ret;
+ struct ipa_gsb_iface_info *new_intf;
+
+ if (!params || !params->wakeup_request || !hdl ||
+ !params->info.netdev_name || !params->info.tx_dp_notify ||
+ !params->info.send_dl_skb) {
+ IPA_GSB_ERR("NULL parameters\n");
+ return -EINVAL;
+ }
+
+ IPA_GSB_DBG("netdev_name: %s\n", params->info.netdev_name);
+
+ if (ipa_gsb_ctx == NULL) {
+ ret = ipa_gsb_driver_init(¶ms->info);
+ if (ret) {
+ IPA_GSB_ERR("fail to init ipa gsb driver\n");
+ return -EFAULT;
+ }
+ ipa_gsb_ctx->ipa_sys_desc_size =
+ params->info.ipa_desc_size;
+ IPA_GSB_DBG("desc size: %d\n", ipa_gsb_ctx->ipa_sys_desc_size);
+ }
+
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ if (params->info.ipa_desc_size != ipa_gsb_ctx->ipa_sys_desc_size) {
+ IPA_GSB_ERR("unmatch: orig desc size %d, new desc size %d\n",
+ ipa_gsb_ctx->ipa_sys_desc_size,
+ params->info.ipa_desc_size);
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return -EFAULT;
+ }
+
+ for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
+ if (ipa_gsb_ctx->iface[i] != NULL &&
+ strnlen(ipa_gsb_ctx->iface[i]->netdev_name,
+ IPA_RESOURCE_NAME_MAX) ==
+ strnlen(params->info.netdev_name,
+ IPA_RESOURCE_NAME_MAX) &&
+ strcmp(ipa_gsb_ctx->iface[i]->netdev_name,
+ params->info.netdev_name) == 0) {
+ IPA_GSB_ERR("intf was added before.\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return -EFAULT;
+ }
+
+ if (ipa_gsb_ctx->num_iface == MAX_SUPPORTED_IFACE) {
+ IPA_GSB_ERR("reached maximum supported interfaces");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return -EFAULT;
+ }
+
+ for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
+ if (ipa_gsb_ctx->iface_hdl[i] == false) {
+ ipa_gsb_ctx->iface_hdl[i] = true;
+ *hdl = i;
+ IPA_GSB_DBG("iface hdl: %d\n", *hdl);
+ break;
+ }
+
+ IPA_GSB_DBG("intf was not added before, proceed.\n");
+ new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL);
+ if (new_intf == NULL) {
+ ret = -ENOMEM;
+ goto fail_alloc_mem;
+ }
+
+ strlcpy(new_intf->netdev_name, params->info.netdev_name,
+ sizeof(new_intf->netdev_name));
+ new_intf->wakeup_request = params->wakeup_request;
+ new_intf->priv = params->info.priv;
+ new_intf->tx_dp_notify = params->info.tx_dp_notify;
+ new_intf->send_dl_skb = params->info.send_dl_skb;
+ new_intf->iface_hdl = *hdl;
+ memcpy(new_intf->device_ethaddr, params->info.device_ethaddr,
+ sizeof(new_intf->device_ethaddr));
+
+ if (ipa_gsb_commit_partial_hdr(new_intf) != 0) {
+ IPA_GSB_ERR("fail to commit partial hdrs\n");
+ ret = -EFAULT;
+ goto fail_partial_hdr;
+ }
+
+ if (ipa_gsb_reg_intf_props(new_intf) != 0) {
+ IPA_GSB_ERR("fail to register interface props\n");
+ ret = -EFAULT;
+ goto fail_reg_intf_props;
+ }
+
+ if (ipa_gsb_ctx->num_iface == 0) {
+ ret = ipa_gsb_register_pm();
+ if (ret) {
+ IPA_GSB_ERR("fail to register with IPA PM %d\n", ret);
+ ret = -EFAULT;
+ goto fail_register_pm;
+ }
+ }
+
+ ipa_gsb_ctx->iface[*hdl] = new_intf;
+ ipa_gsb_ctx->num_iface++;
+ IPA_GSB_DBG("num_iface %d\n", ipa_gsb_ctx->num_iface);
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+
+fail_register_pm:
+ ipa_gsb_dereg_intf_props(new_intf);
+fail_reg_intf_props:
+ ipa_gsb_delete_partial_hdr(new_intf);
+fail_partial_hdr:
+ kfree(new_intf);
+fail_alloc_mem:
+ ipa_gsb_ctx->iface_hdl[*hdl] = false;
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+}
+EXPORT_SYMBOL(ipa_bridge_init);
+
+static void ipa_gsb_deregister_pm(void)
+{
+ IPA_GSB_DBG("deregister ipa pm hdl: %d\n", ipa_gsb_ctx->pm_hdl);
+ ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
+ ipa_pm_deregister(ipa_gsb_ctx->pm_hdl);
+ ipa_gsb_ctx->pm_hdl = ~0;
+}
+
+int ipa_bridge_cleanup(u32 hdl)
+{
+ if (!ipa_gsb_ctx) {
+ IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
+ return -EFAULT;
+ }
+
+ if (hdl >= MAX_SUPPORTED_IFACE) {
+ IPA_GSB_ERR("invalid hdl: %d\n", hdl);
+ return -EINVAL;
+ }
+
+ if (ipa_gsb_ctx->iface[hdl] == NULL) {
+ IPA_GSB_ERR("fail to find interface\n");
+ return -EFAULT;
+ }
+
+ IPA_GSB_DBG("client hdl: %d\n", hdl);
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ if (ipa_gsb_ctx->iface[hdl]->is_connected) {
+ IPA_GSB_ERR("cannot cleanup when iface is connected\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return -EFAULT;
+ }
+
+ ipa_gsb_dereg_intf_props(ipa_gsb_ctx->iface[hdl]);
+ ipa_gsb_delete_partial_hdr(ipa_gsb_ctx->iface[hdl]);
+ kfree(ipa_gsb_ctx->iface[hdl]);
+ ipa_gsb_ctx->iface[hdl] = NULL;
+ ipa_gsb_ctx->iface_hdl[hdl] = false;
+ ipa_gsb_ctx->num_iface--;
+ IPA_GSB_DBG("num_iface %d\n", ipa_gsb_ctx->num_iface);
+
+ if (ipa_gsb_ctx->num_iface == 0) {
+ ipa_gsb_deregister_pm();
+ ipa_gsb_debugfs_destroy();
+ ipc_log_context_destroy(ipa_gsb_ctx->logbuf);
+ ipc_log_context_destroy(ipa_gsb_ctx->logbuf_low);
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ kfree(ipa_gsb_ctx);
+ ipa_gsb_ctx = NULL;
+ return 0;
+ }
+
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+}
+EXPORT_SYMBOL(ipa_bridge_cleanup);
+
+static void ipa_gsb_cons_cb(void *priv, enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ struct ipa_gsb_mux_hdr *mux_hdr;
+ u16 pkt_size, pad_byte;
+ u8 hdl;
+
+ if (evt != IPA_RECEIVE) {
+ IPA_GSB_ERR("unexpected event\n");
+ WARN_ON(1);
+ return;
+ }
+
+ skb = (struct sk_buff *)data;
+
+ while (skb->len) {
+ mux_hdr = (struct ipa_gsb_mux_hdr *)skb->data;
+ pkt_size = mux_hdr->pkt_size;
+ /* 4-byte padding */
+ pad_byte = ((pkt_size + sizeof(*mux_hdr) + ETH_HLEN +
+ 3 + IPA_GSB_SKB_DUMMY_HEADER) & ~3) -
+ (pkt_size + sizeof(*mux_hdr) +
+ ETH_HLEN + IPA_GSB_SKB_DUMMY_HEADER);
+ hdl = mux_hdr->iface_hdl;
+ IPA_GSB_DBG_LOW("pkt_size: %d, pad_byte: %d, hdl: %d\n",
+ pkt_size, pad_byte, hdl);
+
+ /* remove 4 byte mux header AND dummy header*/
+ skb_pull(skb, sizeof(*mux_hdr) + IPA_GSB_SKB_DUMMY_HEADER);
+
+ skb2 = skb_clone(skb, GFP_KERNEL);
+ if (!skb2) {
+ IPA_GSB_ERR("skb_clone failed\n");
+ WARN_ON(1);
+ break;
+ }
+ skb_trim(skb2, pkt_size + ETH_HLEN);
+ ipa_gsb_ctx->iface[hdl]->send_dl_skb(
+ ipa_gsb_ctx->iface[hdl]->priv, skb2);
+ ipa_gsb_ctx->iface[hdl]->iface_stats.num_dl_packets++;
+ skb_pull(skb, pkt_size + ETH_HLEN + pad_byte);
+ }
+ if (skb) {
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+ }
+}
+
+static void ipa_gsb_tx_dp_notify(void *priv, enum ipa_dp_evt_type evt,
+ unsigned long data)
+{
+ struct sk_buff *skb;
+ struct ipa_gsb_mux_hdr *mux_hdr;
+ u8 hdl;
+
+ skb = (struct sk_buff *)data;
+
+ if (evt != IPA_WRITE_DONE && evt != IPA_RECEIVE) {
+ IPA_GSB_ERR("unexpected event: %d\n", evt);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ /* fetch iface handle from header */
+ mux_hdr = (struct ipa_gsb_mux_hdr *)skb->data;
+ /* change to host order */
+ *(u32 *)mux_hdr = ntohl(*(u32 *)mux_hdr);
+ hdl = mux_hdr->iface_hdl;
+ IPA_GSB_DBG_LOW("evt: %d, hdl in tx_dp_notify: %d\n", evt, hdl);
+
+ /* remove 4 byte mux header */
+ skb_pull(skb, sizeof(struct ipa_gsb_mux_hdr));
+ ipa_gsb_ctx->iface[hdl]->tx_dp_notify(
+ ipa_gsb_ctx->iface[hdl]->priv, evt,
+ (unsigned long)skb);
+}
+
+static int ipa_gsb_connect_sys_pipe(void)
+{
+ struct ipa_sys_connect_params prod_params;
+ struct ipa_sys_connect_params cons_params;
+ int res;
+
+ memset(&prod_params, 0, sizeof(prod_params));
+ memset(&cons_params, 0, sizeof(cons_params));
+
+ /* configure RX EP */
+ prod_params.client = IPA_CLIENT_ODU_PROD;
+ prod_params.ipa_ep_cfg.hdr.hdr_len =
+ ETH_HLEN + sizeof(struct ipa_gsb_mux_hdr);
+ prod_params.ipa_ep_cfg.nat.nat_en = IPA_SRC_NAT;
+ prod_params.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
+ prod_params.ipa_ep_cfg.hdr.hdr_ofst_metadata = 0;
+ prod_params.desc_fifo_sz = ipa_gsb_ctx->ipa_sys_desc_size;
+ prod_params.priv = NULL;
+ prod_params.notify = ipa_gsb_tx_dp_notify;
+ res = ipa_setup_sys_pipe(&prod_params,
+ &ipa_gsb_ctx->prod_hdl);
+ if (res) {
+ IPA_GSB_ERR("fail to setup prod sys pipe %d\n", res);
+ goto fail_prod;
+ }
+
+ /* configure TX EP */
+ cons_params.client = IPA_CLIENT_ODU_EMB_CONS;
+ cons_params.ipa_ep_cfg.hdr.hdr_len =
+ ETH_HLEN + sizeof(struct ipa_gsb_mux_hdr) +
+ IPA_GSB_SKB_DUMMY_HEADER;
+ cons_params.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
+ cons_params.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 2;
+ cons_params.ipa_ep_cfg.hdr_ext.hdr_pad_to_alignment = 2;
+ cons_params.ipa_ep_cfg.hdr_ext.hdr_little_endian = true;
+ cons_params.ipa_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
+ /* setup aggregation */
+ cons_params.ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR;
+ cons_params.ipa_ep_cfg.aggr.aggr = IPA_GENERIC;
+ cons_params.ipa_ep_cfg.aggr.aggr_time_limit =
+ IPA_GSB_AGGR_TIME_LIMIT;
+ cons_params.ipa_ep_cfg.aggr.aggr_byte_limit =
+ IPA_GSB_AGGR_BYTE_LIMIT;
+ cons_params.desc_fifo_sz = ipa_gsb_ctx->ipa_sys_desc_size;
+ cons_params.priv = NULL;
+ cons_params.notify = ipa_gsb_cons_cb;
+ res = ipa_setup_sys_pipe(&cons_params,
+ &ipa_gsb_ctx->cons_hdl);
+ if (res) {
+ IPA_GSB_ERR("fail to setup cons sys pipe %d\n", res);
+ goto fail_cons;
+ }
+
+ IPA_GSB_DBG("prod_hdl = %d, cons_hdl = %d\n",
+ ipa_gsb_ctx->prod_hdl, ipa_gsb_ctx->cons_hdl);
+
+ return 0;
+
+fail_cons:
+ ipa_teardown_sys_pipe(ipa_gsb_ctx->prod_hdl);
+ ipa_gsb_ctx->prod_hdl = 0;
+fail_prod:
+ return res;
+}
+
+int ipa_bridge_connect(u32 hdl)
+{
+ int ret;
+
+ if (!ipa_gsb_ctx) {
+ IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
+ return -EFAULT;
+ }
+
+ IPA_GSB_DBG("client hdl: %d\n", hdl);
+
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ if (ipa_gsb_ctx->iface[hdl]->is_connected) {
+ IPA_GSB_DBG("iface was already connected\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+ }
+
+ if (ipa_gsb_ctx->num_connected_iface == 0) {
+ ret = ipa_pm_activate_sync(ipa_gsb_ctx->pm_hdl);
+ if (ret) {
+ IPA_GSB_ERR("failed to activate ipa pm\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+ }
+ ret = ipa_gsb_connect_sys_pipe();
+ if (ret) {
+ IPA_GSB_ERR("fail to connect pipe\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+ }
+ }
+
+ /* connect = connect + resume */
+ ipa_gsb_ctx->iface[hdl]->is_connected = true;
+ ipa_gsb_ctx->iface[hdl]->is_resumed = true;
+
+ ipa_gsb_ctx->num_connected_iface++;
+ IPA_GSB_DBG("connected iface: %d\n",
+ ipa_gsb_ctx->num_connected_iface);
+ ipa_gsb_ctx->num_resumed_iface++;
+ IPA_GSB_DBG("num resumed iface: %d\n",
+ ipa_gsb_ctx->num_resumed_iface);
+
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+}
+EXPORT_SYMBOL(ipa_bridge_connect);
+
+static int ipa_gsb_disconnect_sys_pipe(void)
+{
+ int ret;
+
+ IPA_GSB_DBG("prod_hdl = %d, cons_hdl = %d\n",
+ ipa_gsb_ctx->prod_hdl, ipa_gsb_ctx->cons_hdl);
+
+ ret = ipa_teardown_sys_pipe(ipa_gsb_ctx->prod_hdl);
+ if (ret) {
+ IPA_GSB_ERR("failed to tear down prod pipe\n");
+ return -EFAULT;
+ }
+ ipa_gsb_ctx->prod_hdl = 0;
+
+ ret = ipa_teardown_sys_pipe(ipa_gsb_ctx->cons_hdl);
+ if (ret) {
+ IPA_GSB_ERR("failed to tear down cons pipe\n");
+ return -EFAULT;
+ }
+ ipa_gsb_ctx->cons_hdl = 0;
+
+ return 0;
+}
+
+int ipa_bridge_disconnect(u32 hdl)
+{
+ int ret;
+
+ if (!ipa_gsb_ctx) {
+ IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
+ return -EFAULT;
+ }
+
+ IPA_GSB_DBG("client hdl: %d\n", hdl);
+
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
+ IPA_GSB_DBG("iface was not connected\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+ }
+
+ if (ipa_gsb_ctx->num_connected_iface == 1) {
+ ret = ipa_gsb_disconnect_sys_pipe();
+ if (ret) {
+ IPA_GSB_ERR("fail to discon pipes\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return -EFAULT;
+ }
+
+ ret = ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
+ if (ret) {
+ IPA_GSB_ERR("failed to deactivate ipa pm\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return -EFAULT;
+ }
+ }
+
+ /* disconnect = suspend + disconnect */
+ ipa_gsb_ctx->iface[hdl]->is_connected = false;
+ ipa_gsb_ctx->num_connected_iface--;
+ IPA_GSB_DBG("connected iface: %d\n",
+ ipa_gsb_ctx->num_connected_iface);
+
+ if (ipa_gsb_ctx->iface[hdl]->is_resumed) {
+ ipa_gsb_ctx->iface[hdl]->is_resumed = false;
+ ipa_gsb_ctx->num_resumed_iface--;
+ IPA_GSB_DBG("num resumed iface: %d\n",
+ ipa_gsb_ctx->num_resumed_iface);
+ }
+
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+}
+EXPORT_SYMBOL(ipa_bridge_disconnect);
+
+int ipa_bridge_resume(u32 hdl)
+{
+ int ret;
+
+ if (!ipa_gsb_ctx) {
+ IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
+ return -EFAULT;
+ }
+
+ IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);
+
+ if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
+ IPA_GSB_ERR("iface is not connected\n");
+ return -EFAULT;
+ }
+
+ if (ipa_gsb_ctx->iface[hdl]->is_resumed) {
+ IPA_GSB_DBG_LOW("iface was already resumed\n");
+ return 0;
+ }
+
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ if (ipa_gsb_ctx->num_resumed_iface == 0) {
+ ret = ipa_pm_activate_sync(ipa_gsb_ctx->pm_hdl);
+ if (ret) {
+ IPA_GSB_ERR("fail to activate ipa pm\n");
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+ }
+
+ ret = ipa_start_gsi_channel(
+ ipa_gsb_ctx->cons_hdl);
+ if (ret) {
+ IPA_GSB_ERR(
+ "fail to start con ep %d\n",
+ ret);
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+ }
+ }
+
+ ipa_gsb_ctx->iface[hdl]->is_resumed = true;
+ ipa_gsb_ctx->num_resumed_iface++;
+ IPA_GSB_DBG_LOW("num resumed iface: %d\n",
+ ipa_gsb_ctx->num_resumed_iface);
+
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+}
+EXPORT_SYMBOL(ipa_bridge_resume);
+
+int ipa_bridge_suspend(u32 hdl)
+{
+ int ret;
+
+ if (!ipa_gsb_ctx) {
+ IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
+ return -EFAULT;
+ }
+
+ IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);
+
+ if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
+ IPA_GSB_ERR("iface is not connected\n");
+ return -EFAULT;
+ }
+
+ if (!ipa_gsb_ctx->iface[hdl]->is_resumed) {
+ IPA_GSB_DBG_LOW("iface was already suspended\n");
+ return 0;
+ }
+
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ if (ipa_gsb_ctx->num_resumed_iface == 1) {
+ ret = ipa_stop_gsi_channel(
+ ipa_gsb_ctx->cons_hdl);
+ if (ret) {
+ IPA_GSB_ERR(
+ "fail to stop cons ep %d\n",
+ ret);
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+ }
+
+ ret = ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
+ if (ret) {
+ IPA_GSB_ERR("fail to deactivate ipa pm\n");
+ ipa_start_gsi_channel(ipa_gsb_ctx->cons_hdl);
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+ }
+ }
+
+ ipa_gsb_ctx->iface[hdl]->is_resumed = false;
+ ipa_gsb_ctx->num_resumed_iface--;
+ IPA_GSB_DBG_LOW("num resumed iface: %d\n",
+ ipa_gsb_ctx->num_resumed_iface);
+
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return 0;
+}
+EXPORT_SYMBOL(ipa_bridge_suspend);
+
+int ipa_bridge_set_perf_profile(u32 hdl, u32 bandwidth)
+{
+ int ret;
+
+ IPA_GSB_DBG("client hdl: %d, BW: %d\n", hdl, bandwidth);
+
+ mutex_lock(&ipa_gsb_ctx->lock);
+
+ ret = ipa_pm_set_perf_profile(ipa_gsb_ctx->pm_hdl,
+ bandwidth);
+ if (ret)
+ IPA_GSB_ERR("fail to set perf profile\n");
+
+ mutex_unlock(&ipa_gsb_ctx->lock);
+ return ret;
+}
+EXPORT_SYMBOL(ipa_bridge_set_perf_profile);
+
+int ipa_bridge_tx_dp(u32 hdl, struct sk_buff *skb,
+ struct ipa_tx_meta *metadata)
+{
+ struct ipa_gsb_mux_hdr *mux_hdr;
+ struct sk_buff *skb2;
+ int ret;
+
+ IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);
+
+ /* make sure skb has enough headroom */
+ if (unlikely(skb_headroom(skb) < sizeof(struct ipa_gsb_mux_hdr))) {
+ IPA_GSB_DBG_LOW("skb doesn't have enough headroom\n");
+ skb2 = skb_copy_expand(skb, sizeof(struct ipa_gsb_mux_hdr),
+ 0, GFP_ATOMIC);
+ if (!skb2) {
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ ipa_gsb_ctx->iface[hdl]->iface_stats.
+ num_insufficient_headroom_packets++;
+ }
+
+ /* add 4 byte header for mux */
+ mux_hdr = (struct ipa_gsb_mux_hdr *)skb_push(skb,
+ sizeof(struct ipa_gsb_mux_hdr));
+ mux_hdr->iface_hdl = (u8)hdl;
+ /* change to network order */
+ *(u32 *)mux_hdr = htonl(*(u32 *)mux_hdr);
+
+ ret = ipa_tx_dp(IPA_CLIENT_ODU_PROD, skb, metadata);
+ if (ret) {
+ IPA_GSB_ERR("tx dp failed %d\n", ret);
+ return -EFAULT;
+ }
+ ipa_gsb_ctx->iface[hdl]->iface_stats.num_ul_packets++;
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa_bridge_tx_dp);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ipa gsb driver");
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index 906e911..b4af0a09 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,13 +24,39 @@
#endif
#define IPA_MHI_DRV_NAME "ipa_mhi_client"
+
#define IPA_MHI_DBG(fmt, args...) \
- pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args)
+ do { \
+ pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+#define IPA_MHI_DBG_LOW(fmt, args...) \
+ do { \
+ pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
+
#define IPA_MHI_ERR(fmt, args...) \
- pr_err(IPA_MHI_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+ do { \
+ pr_err(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+ } while (0)
+
#define IPA_MHI_FUNC_ENTRY() \
IPA_MHI_DBG("ENTRY\n")
+
#define IPA_MHI_FUNC_EXIT() \
IPA_MHI_DBG("EXIT\n")
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index ebf1d29..8b998fe 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -1336,7 +1336,12 @@
chan_params.chan_params.ring_base_addr =
params->xfer_ring_base_addr_iova;
chan_params.chan_params.ring_base_vaddr = NULL;
- chan_params.chan_params.use_db_eng = GSI_CHAN_DB_MODE;
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+ chan_params.chan_params.use_db_eng = GSI_CHAN_DIRECT_MODE;
+ else
+ chan_params.chan_params.use_db_eng = GSI_CHAN_DB_MODE;
+ chan_params.chan_params.prefetch_mode =
+ ipa_get_ep_prefetch_mode(chan_params.client);
chan_params.chan_params.max_prefetch = GSI_ONE_PREFETCH_SEG;
if (params->dir == GSI_CHAN_DIR_FROM_GSI)
chan_params.chan_params.low_weight =
@@ -1847,12 +1852,15 @@
}
if (ipa_pm_is_used()) {
- result = ipa_pm_set_perf_profile(
- ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl,
- params->max_supported_bandwidth_mbps);
- if (result) {
- IPA_USB_ERR("failed to set perf profile\n");
- return result;
+ /* perf profile is not set on USB DPL pipe */
+ if (ttype != IPA_USB_TRANSPORT_DPL) {
+ result = ipa_pm_set_perf_profile(
+ ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl,
+ params->max_supported_bandwidth_mbps);
+ if (result) {
+ IPA_USB_ERR("failed to set perf profile\n");
+ return result;
+ }
}
result = ipa_pm_activate_sync(
@@ -2150,6 +2158,18 @@
static void ipa_usb_debugfs_remove(void){}
#endif /* CONFIG_DEBUG_FS */
+static int ipa_usb_set_lock_unlock(bool is_lock)
+{
+ IPA_USB_DBG("entry\n");
+ if (is_lock)
+ mutex_lock(&ipa3_usb_ctx->general_mutex);
+ else
+ mutex_unlock(&ipa3_usb_ctx->general_mutex);
+ IPA_USB_DBG("exit\n");
+
+ return 0;
+}
+
int ipa_usb_xdci_connect(struct ipa_usb_xdci_chan_params *ul_chan_params,
@@ -2213,6 +2233,16 @@
goto connect_fail;
}
+ /*
+ * Register for xdci lock/unlock callback with ipa core driver.
+ * As per use case, only register for IPA_CONS end point for now.
+ * If needed we can include the same for IPA_PROD ep.
+ * For IPA_USB_DIAG/DPL config there will not be any UL ep.
+ */
+ if (connect_params->teth_prot != IPA_USB_DIAG)
+ ipa3_register_lock_unlock_callback(&ipa_usb_set_lock_unlock,
+ ul_out_params->clnt_hdl);
+
IPA_USB_DBG_LOW("exit\n");
mutex_unlock(&ipa3_usb_ctx->general_mutex);
return 0;
@@ -2290,6 +2320,15 @@
}
}
+ /*
+ * Deregister for xdci lock/unlock callback from ipa core driver.
+ * As per use case, only deregister for IPA_CONS end point for now.
+ * If needed we can include the same for IPA_PROD ep.
+ * For IPA_USB_DIAG/DPL config there will not be any UL config.
+ */
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype))
+ ipa3_deregister_lock_unlock_callback(ul_clnt_hdl);
+
/* Change state to STOPPED */
if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype))
IPA_USB_ERR("failed to change state to stopped\n");
diff --git a/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c b/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c
index df546cd..3228410 100644
--- a/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1262,385 +1262,5 @@
}
EXPORT_SYMBOL(odu_bridge_cleanup);
-/* IPA Bridge implementation */
-#ifdef CONFIG_IPA3
-
-static void ipa_br_rm_notify(void *user_data, enum ipa_rm_event event,
- unsigned long data)
-{
- if (event == IPA_RM_RESOURCE_GRANTED)
- complete(&odu_bridge_ctx->rm_comp);
-}
-
-static int ipa_br_request_prod(void)
-{
- int res;
-
- ODU_BRIDGE_FUNC_ENTRY();
-
- reinit_completion(&odu_bridge_ctx->rm_comp);
- ODU_BRIDGE_DBG("requesting odu prod\n");
- res = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
- if (res) {
- if (res != -EINPROGRESS) {
- ODU_BRIDGE_ERR("failed to request prod %d\n", res);
- return res;
- }
- wait_for_completion(&odu_bridge_ctx->rm_comp);
- }
-
- ODU_BRIDGE_FUNC_EXIT();
- return 0;
-
-}
-
-static int ipa_br_release_prod(void)
-{
- int res;
-
- ODU_BRIDGE_FUNC_ENTRY();
-
- reinit_completion(&odu_bridge_ctx->rm_comp);
- ODU_BRIDGE_DBG("requesting odu prod\n");
- res = ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
- if (res) {
- ODU_BRIDGE_ERR("failed to release prod %d\n", res);
- return res;
- }
-
- ODU_BRIDGE_FUNC_EXIT();
- return 0;
-
-}
-
-static int ipa_br_cons_request(void)
-{
- ODU_BRIDGE_FUNC_ENTRY();
- if (odu_bridge_ctx->is_suspended)
- odu_bridge_ctx->wakeup_request(odu_bridge_ctx->priv);
- ODU_BRIDGE_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_br_cons_release(void)
-{
- ODU_BRIDGE_FUNC_ENTRY();
- ODU_BRIDGE_FUNC_EXIT();
- return 0;
-}
-
-static void ipa_br_pm_cb(void *p, enum ipa_pm_cb_event event)
-{
- ODU_BRIDGE_FUNC_ENTRY();
- if (event != IPA_PM_REQUEST_WAKEUP) {
- ODU_BRIDGE_ERR("Unexpected event %d\n", event);
- WARN_ON(1);
- return;
- }
-
- if (odu_bridge_ctx->is_suspended)
- odu_bridge_ctx->wakeup_request(odu_bridge_ctx->priv);
- ODU_BRIDGE_FUNC_EXIT();
-}
-
-static int ipa_br_register_pm(void)
-{
- struct ipa_pm_register_params reg_params;
- int ret;
-
- memset(®_params, 0, sizeof(reg_params));
- reg_params.name = "ODU Bridge";
- reg_params.callback = ipa_br_pm_cb;
- reg_params.group = IPA_PM_GROUP_DEFAULT;
-
- ret = ipa_pm_register(®_params,
- &odu_bridge_ctx->pm_hdl);
- if (ret) {
- ODU_BRIDGE_ERR("fail to register with PM %d\n", ret);
- goto fail_pm_reg;
- }
-
- ret = ipa_pm_associate_ipa_cons_to_client(odu_bridge_ctx->pm_hdl,
- IPA_CLIENT_ODU_EMB_CONS);
- if (ret) {
- ODU_BRIDGE_ERR("fail to associate cons with PM %d\n", ret);
- goto fail_pm_cons;
- }
-
- return 0;
-
-fail_pm_cons:
- ipa_pm_deregister(odu_bridge_ctx->pm_hdl);
- odu_bridge_ctx->pm_hdl = ~0;
-fail_pm_reg:
- return ret;
-}
-
-static int ipa_br_create_rm_resources(void)
-{
- int ret;
- struct ipa_rm_create_params create_params;
-
- /* create IPA RM resources for power management */
- init_completion(&odu_bridge_ctx->rm_comp);
- memset(&create_params, 0, sizeof(create_params));
- create_params.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
- create_params.reg_params.user_data = odu_bridge_ctx;
- create_params.reg_params.notify_cb = ipa_br_rm_notify;
- create_params.floor_voltage = IPA_VOLTAGE_SVS;
- ret = ipa_rm_create_resource(&create_params);
- if (ret) {
- ODU_BRIDGE_ERR("failed to create RM prod %d\n", ret);
- goto fail_rm_prod;
- }
-
- ret = ipa_rm_add_dependency_sync(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS);
- if (ret) {
- ODU_BRIDGE_ERR("failed to add ODU->APPS dependency %d\n", ret);
- goto fail_add_dep;
- }
-
- memset(&create_params, 0, sizeof(create_params));
- create_params.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
- create_params.request_resource = ipa_br_cons_request;
- create_params.release_resource = ipa_br_cons_release;
- create_params.floor_voltage = IPA_VOLTAGE_SVS;
- ret = ipa_rm_create_resource(&create_params);
- if (ret) {
- ODU_BRIDGE_ERR("failed to create RM cons %d\n", ret);
- goto fail_rm_cons;
- }
-
- return 0;
-
-fail_rm_cons:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS);
-fail_add_dep:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
-fail_rm_prod:
- return ret;
-}
-
-/* IPA Bridge API is the new API which will replaces old odu_bridge API */
-int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
-{
- int ret;
-
- if (!params || !params->wakeup_request || !hdl) {
- ODU_BRIDGE_ERR("NULL arg\n");
- return -EINVAL;
- }
-
-
- ret = odu_bridge_init(¶ms->info);
- if (ret)
- return ret;
-
- odu_bridge_ctx->wakeup_request = params->wakeup_request;
-
- if (ipa_pm_is_used())
- ret = ipa_br_register_pm();
- else
- ret = ipa_br_create_rm_resources();
- if (ret) {
- ODU_BRIDGE_ERR("fail to register woth RM/PM %d\n", ret);
- goto fail_pm;
- }
-
- /* handle is ignored for now */
- *hdl = 0;
-
- return 0;
-
-fail_pm:
- odu_bridge_cleanup();
- return ret;
-}
-EXPORT_SYMBOL(ipa_bridge_init);
-
-int ipa_bridge_connect(u32 hdl)
-{
- int ret;
-
- if (!odu_bridge_ctx) {
- ODU_BRIDGE_ERR("Not initialized\n");
- return -EFAULT;
- }
-
- if (odu_bridge_ctx->is_connected) {
- ODU_BRIDGE_ERR("already connected\n");
- return -EFAULT;
- }
-
- if (ipa_pm_is_used())
- ret = ipa_pm_activate_sync(odu_bridge_ctx->pm_hdl);
- else
- ret = ipa_br_request_prod();
- if (ret)
- return ret;
-
- return odu_bridge_connect();
-}
-EXPORT_SYMBOL(ipa_bridge_connect);
-
-int ipa_bridge_set_perf_profile(u32 hdl, u32 bandwidth)
-{
- struct ipa_rm_perf_profile profile = {0};
- int ret;
-
- if (ipa_pm_is_used())
- return ipa_pm_set_perf_profile(odu_bridge_ctx->pm_hdl,
- bandwidth);
-
- profile.max_supported_bandwidth_mbps = bandwidth;
- ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_ODU_ADAPT_PROD, &profile);
- if (ret) {
- ODU_BRIDGE_ERR("failed to set perf profile to prod %d\n", ret);
- return ret;
- }
-
- ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_ODU_ADAPT_CONS, &profile);
- if (ret) {
- ODU_BRIDGE_ERR("failed to set perf profile to cons %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(ipa_bridge_set_perf_profile);
-
-int ipa_bridge_disconnect(u32 hdl)
-{
- int ret;
-
- ret = odu_bridge_disconnect();
- if (ret)
- return ret;
-
- if (ipa_pm_is_used())
- ret = ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
- else
- ret = ipa_br_release_prod();
- if (ret)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL(ipa_bridge_disconnect);
-
-int ipa_bridge_suspend(u32 hdl)
-{
- int ret;
-
- if (!odu_bridge_ctx) {
- ODU_BRIDGE_ERR("Not initialized\n");
- return -EFAULT;
- }
-
- if (!odu_bridge_ctx->is_connected) {
- ODU_BRIDGE_ERR("bridge is disconnected\n");
- return -EFAULT;
- }
-
- if (odu_bridge_ctx->is_suspended) {
- ODU_BRIDGE_ERR("bridge is already suspended\n");
- return -EFAULT;
- }
-
- /* stop cons channel to prevent downlink data during suspend */
- ret = ipa_stop_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
- if (ret) {
- ODU_BRIDGE_ERR("failed to stop CONS channel %d\n", ret);
- return ret;
- }
-
- if (ipa_pm_is_used())
- ret = ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
- else
- ret = ipa_br_release_prod();
- if (ret) {
- ODU_BRIDGE_ERR("failed to release prod %d\n", ret);
- ipa_start_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
- return ret;
- }
- odu_bridge_ctx->is_suspended = true;
-
- return 0;
-}
-EXPORT_SYMBOL(ipa_bridge_suspend);
-
-int ipa_bridge_resume(u32 hdl)
-{
- int ret;
-
- if (!odu_bridge_ctx) {
- ODU_BRIDGE_ERR("Not initialized\n");
- return -EFAULT;
- }
-
- if (!odu_bridge_ctx->is_connected) {
- ODU_BRIDGE_ERR("bridge is disconnected\n");
- return -EFAULT;
- }
-
- if (!odu_bridge_ctx->is_suspended) {
- ODU_BRIDGE_ERR("bridge is not suspended\n");
- return -EFAULT;
- }
-
- if (ipa_pm_is_used())
- ret = ipa_pm_activate_sync(odu_bridge_ctx->pm_hdl);
- else
- ret = ipa_br_request_prod();
- if (ret)
- return ret;
-
- ret = ipa_start_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
- if (ret) {
- ODU_BRIDGE_ERR("failed to start CONS channel %d\n", ret);
- return ret;
- }
- odu_bridge_ctx->is_suspended = false;
-
- return 0;
-}
-EXPORT_SYMBOL(ipa_bridge_resume);
-
-int ipa_bridge_tx_dp(u32 hdl, struct sk_buff *skb,
- struct ipa_tx_meta *metadata)
-{
- return odu_bridge_tx_dp(skb, metadata);
-}
-EXPORT_SYMBOL(ipa_bridge_tx_dp);
-
-static void ipa_br_delete_rm_resources(void)
-{
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS);
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
-}
-
-static void ipa_br_deregister_pm(void)
-{
- ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
- ipa_pm_deregister(odu_bridge_ctx->pm_hdl);
- odu_bridge_ctx->pm_hdl = ~0;
-}
-
-int ipa_bridge_cleanup(u32 hdl)
-{
- if (ipa_pm_is_used())
- ipa_br_deregister_pm();
- else
- ipa_br_delete_rm_resources();
- return odu_bridge_cleanup();
-}
-EXPORT_SYMBOL(ipa_bridge_cleanup);
-
-#endif /* CONFIG_IPA3 */
-
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ODU bridge driver");
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index b8a517e..530aa54 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -15,6 +15,7 @@
#ifndef _IPA_COMMON_I_H_
#define _IPA_COMMON_I_H_
+#include <linux/errno.h>
#include <linux/ipc_logging.h>
#include <linux/ipa.h>
#include <linux/ipa_uc_offload.h>
@@ -441,4 +442,7 @@
struct sg_table *in_sgt_ptr);
int ipa_smmu_free_sgt(struct sg_table **out_sgt_ptr);
+int ipa_ut_module_init(void);
+void ipa_ut_module_exit(void);
+
#endif /* _IPA_COMMON_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index f062ed2..c2f7aae 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -89,6 +89,8 @@
__stringify(DEL_L2TP_VLAN_MAPPING),
__stringify(IPA_PER_CLIENT_STATS_CONNECT_EVENT),
__stringify(IPA_PER_CLIENT_STATS_DISCONNECT_EVENT),
+ __stringify(ADD_BRIDGE_VLAN_MAPPING),
+ __stringify(DEL_BRIDGE_VLAN_MAPPING),
};
const char *ipa_hdr_l2_type_name[] = {
@@ -432,6 +434,8 @@
list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
link) {
+ if (entry->cookie != IPA_HDR_COOKIE)
+ continue;
nbytes = scnprintf(
dbg_buff,
IPA_MAX_MSG_LEN,
@@ -606,6 +610,14 @@
if (attrib->protocol_eq_present)
pr_err("protocol:%d ", attrib->protocol_eq);
+ if (attrib->num_ihl_offset_range_16 >
+ IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS) {
+ IPAERR_RL("num_ihl_offset_range_16 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS,
+ attrib->num_ihl_offset_range_16);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_ihl_offset_range_16; i++) {
pr_err(
"(ihl_ofst_range16: ofst:%u lo:%u hi:%u) ",
@@ -614,6 +626,12 @@
attrib->ihl_offset_range_16[i].range_high);
}
+ if (attrib->num_offset_meq_32 > IPA_IPFLTR_NUM_MEQ_32_EQNS) {
+ IPAERR_RL("num_offset_meq_32 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_MEQ_32_EQNS, attrib->num_offset_meq_32);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_offset_meq_32; i++) {
pr_err(
"(ofst_meq32: ofst:%u mask:0x%x val:0x%x) ",
@@ -635,6 +653,12 @@
attrib->ihl_offset_eq_16.value);
}
+ if (attrib->num_ihl_offset_meq_32 > IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS) {
+ IPAERR_RL("num_ihl_offset_meq_32 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS, attrib->num_ihl_offset_meq_32);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_ihl_offset_meq_32; i++) {
pr_err(
"(ihl_ofst_meq32: ofts:%d mask:0x%x val:0x%x) ",
@@ -643,6 +667,12 @@
attrib->ihl_offset_meq_32[i].value);
}
+ if (attrib->num_offset_meq_128 > IPA_IPFLTR_NUM_MEQ_128_EQNS) {
+ IPAERR_RL("num_offset_meq_128 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_MEQ_128_EQNS, attrib->num_offset_meq_128);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_offset_meq_128; i++) {
for (j = 0; j < 16; j++) {
addr[j] = attrib->offset_meq_128[i].value[j];
@@ -812,11 +842,14 @@
u32 rt_tbl_idx;
u32 bitmap;
bool eq;
+ int res = 0;
tbl = &ipa_ctx->glob_flt_tbl[ip];
mutex_lock(&ipa_ctx->lock);
i = 0;
list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+ if (entry->cookie != IPA_FLT_COOKIE)
+ continue;
if (entry->rule.eq_attrib_type) {
rt_tbl_idx = entry->rule.rt_tbl_idx;
bitmap = entry->rule.eq_attrib.rule_eq_bitmap;
@@ -835,10 +868,14 @@
i, entry->rule.action, rt_tbl_idx);
pr_err("attrib_mask:%08x retain_hdr:%d eq:%d ",
bitmap, entry->rule.retain_hdr, eq);
- if (eq)
- ipa_attrib_dump_eq(
+ if (eq) {
+ res = ipa_attrib_dump_eq(
&entry->rule.eq_attrib);
- else
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
+ } else
ipa_attrib_dump(
&entry->rule.attrib, ip);
i++;
@@ -848,6 +885,8 @@
tbl = &ipa_ctx->flt_tbl[j][ip];
i = 0;
list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+ if (entry->cookie != IPA_FLT_COOKIE)
+ continue;
if (entry->rule.eq_attrib_type) {
rt_tbl_idx = entry->rule.rt_tbl_idx;
bitmap = entry->rule.eq_attrib.rule_eq_bitmap;
@@ -867,18 +906,23 @@
pr_err("attrib_mask:%08x retain_hdr:%d ",
bitmap, entry->rule.retain_hdr);
pr_err("eq:%d ", eq);
- if (eq)
- ipa_attrib_dump_eq(
- &entry->rule.eq_attrib);
- else
+ if (eq) {
+ res = ipa_attrib_dump_eq(
+ &entry->rule.eq_attrib);
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
+ } else
ipa_attrib_dump(
&entry->rule.attrib, ip);
i++;
}
}
+bail:
mutex_unlock(&ipa_ctx->lock);
- return 0;
+ return res;
}
static ssize_t ipa_read_stats(struct file *file, char __user *ubuf,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 9f71d7b..ba98228 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2115,8 +2115,10 @@
goto fail_dma_mapping;
}
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2130,8 +2132,10 @@
return;
fail_sps_transfer:
+ spin_lock_bh(&sys->spinlock);
list_del(&rx_pkt->link);
rx_len_cached = --sys->len;
+ spin_unlock_bh(&sys->spinlock);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
@@ -2171,8 +2175,10 @@
goto fail_dma_mapping;
}
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2185,9 +2191,11 @@
return;
fail_sps_transfer:
+ spin_lock_bh(&sys->spinlock);
rx_len_cached = --sys->len;
list_del(&rx_pkt->link);
INIT_LIST_HEAD(&rx_pkt->link);
+ spin_unlock_bh(&sys->spinlock);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
@@ -2219,7 +2227,9 @@
}
rx_pkt = sys->repl.cache[curr];
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
+ spin_unlock_bh(&sys->spinlock);
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
@@ -2278,6 +2288,7 @@
u32 head;
u32 tail;
+ spin_lock_bh(&sys->spinlock);
list_for_each_entry_safe(rx_pkt, r,
&sys->head_desc_list, link) {
list_del(&rx_pkt->link);
@@ -2295,6 +2306,7 @@
sys->free_skb(rx_pkt->data.skb);
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
}
+ spin_unlock_bh(&sys->spinlock);
if (sys->repl.cache) {
head = atomic_read(&sys->repl.head_idx);
@@ -2976,8 +2988,10 @@
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
+ spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
return;
}
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
@@ -2985,6 +2999,7 @@
link);
list_del(&rx_pkt_expected->link);
sys->len--;
+ spin_unlock_bh(&sys->spinlock);
if (size)
rx_pkt_expected->len = size;
rx_skb = rx_pkt_expected->data.skb;
@@ -3005,8 +3020,10 @@
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
+ spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
return;
}
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
@@ -3014,6 +3031,7 @@
link);
list_del(&rx_pkt_expected->link);
sys->len--;
+ spin_unlock_bh(&sys->spinlock);
if (size)
rx_pkt_expected->len = size;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index 84dce6f..50fe2a1 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
@@ -1495,8 +1495,16 @@
}
}
}
- mutex_unlock(&ipa_ctx->lock);
+ /* commit the change to IPA-HW */
+ if (ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4) ||
+ ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6)) {
+ IPAERR_RL("fail to commit flt-rule\n");
+ WARN_ON_RATELIMIT_IPA(1);
+ mutex_unlock(&ipa_ctx->lock);
+ return -EPERM;
+ }
+ mutex_unlock(&ipa_ctx->lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index b38cde5..61c3a71 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -1249,6 +1249,8 @@
struct ipa_hdr_offset_entry *off_next;
struct ipa_hdr_proc_ctx_offset_entry *ctx_off_entry;
struct ipa_hdr_proc_ctx_offset_entry *ctx_off_next;
+ struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl;
+ struct ipa_hdr_proc_ctx_tbl *htbl_proc = &ipa_ctx->hdr_proc_ctx_tbl;
int i;
/*
@@ -1294,8 +1296,15 @@
entry->hdr_len,
DMA_TO_DEVICE);
entry->proc_ctx = NULL;
+ } else {
+ /* move the offset entry to free list */
+ entry->offset_entry->ipacm_installed = 0;
+ list_move(&entry->offset_entry->link,
+ &htbl->head_free_offset_list[
+ entry->offset_entry->bin]);
}
list_del(&entry->link);
+ htbl->hdr_cnt--;
entry->ref_cnt = 0;
entry->cookie = 0;
@@ -1304,40 +1313,35 @@
kmem_cache_free(ipa_ctx->hdr_cache, entry);
}
}
- for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
- list_for_each_entry_safe(off_entry, off_next,
- &ipa_ctx->hdr_tbl.head_offset_list[i],
- link) {
- /*
- * do not remove the default exception header which is
- * at offset 0
- */
- if (off_entry->offset == 0)
- continue;
-
- if (!user_only ||
- off_entry->ipacm_installed) {
+ /* only clean up offset_list and free_offset_list on global reset */
+ if (!user_only) {
+ for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
+ list_for_each_entry_safe(off_entry, off_next,
+ &ipa_ctx->hdr_tbl.head_offset_list[i],
+ link) {
+ /**
+ * do not remove the default exception
+ * header which is at offset 0
+ */
+ if (off_entry->offset == 0)
+ continue;
list_del(&off_entry->link);
kmem_cache_free(ipa_ctx->hdr_offset_cache,
off_entry);
}
- }
- list_for_each_entry_safe(off_entry, off_next,
+ list_for_each_entry_safe(off_entry, off_next,
&ipa_ctx->hdr_tbl.head_free_offset_list[i],
link) {
-
- if (!user_only ||
- off_entry->ipacm_installed) {
list_del(&off_entry->link);
kmem_cache_free(ipa_ctx->hdr_offset_cache,
off_entry);
}
}
+ /* there is one header of size 8 */
+ ipa_ctx->hdr_tbl.end = 8;
+ ipa_ctx->hdr_tbl.hdr_cnt = 1;
}
- /* there is one header of size 8 */
- ipa_ctx->hdr_tbl.end = 8;
- ipa_ctx->hdr_tbl.hdr_cnt = 1;
IPADBG("reset hdr proc ctx\n");
list_for_each_entry_safe(
@@ -1348,13 +1352,18 @@
if (ipa_id_find(ctx_entry->id) == NULL) {
mutex_unlock(&ipa_ctx->lock);
- WARN_ON(1);
+ WARN_ON_RATELIMIT_IPA(1);
return -EFAULT;
}
if (!user_only ||
ctx_entry->ipacm_installed) {
+ /* move the offset entry to appropriate free list */
+ list_move(&ctx_entry->offset_entry->link,
+ &htbl_proc->head_free_offset_list[
+ ctx_entry->offset_entry->bin]);
list_del(&ctx_entry->link);
+ htbl_proc->proc_ctx_cnt--;
ctx_entry->ref_cnt = 0;
ctx_entry->cookie = 0;
@@ -1364,36 +1373,39 @@
ctx_entry);
}
}
- for (i = 0; i < IPA_HDR_PROC_CTX_BIN_MAX; i++) {
- list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
+ /* only clean up offset_list and free_offset_list on global reset */
+ if (!user_only) {
+ for (i = 0; i < IPA_HDR_PROC_CTX_BIN_MAX; i++) {
+ list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
&ipa_ctx->hdr_proc_ctx_tbl.head_offset_list[i],
link) {
-
- if (!user_only ||
- ctx_off_entry->ipacm_installed) {
+ list_del(&ctx_off_entry->link);
+ kmem_cache_free(
+ ipa_ctx->hdr_proc_ctx_offset_cache,
+ ctx_off_entry);
+ }
+ list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
+ &ipa_ctx->hdr_proc_ctx_tbl.
+ head_free_offset_list[i], link) {
list_del(&ctx_off_entry->link);
kmem_cache_free(
ipa_ctx->hdr_proc_ctx_offset_cache,
ctx_off_entry);
}
}
- list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
- &ipa_ctx->hdr_proc_ctx_tbl.head_free_offset_list[i],
- link) {
-
- if (!user_only ||
- ctx_off_entry->ipacm_installed) {
- list_del(&ctx_off_entry->link);
- kmem_cache_free(
- ipa_ctx->hdr_proc_ctx_offset_cache,
- ctx_off_entry);
- }
- }
+ ipa_ctx->hdr_proc_ctx_tbl.end = 0;
+ ipa_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt = 0;
}
- ipa_ctx->hdr_proc_ctx_tbl.end = 0;
- ipa_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt = 0;
- mutex_unlock(&ipa_ctx->lock);
+ /* commit the change to IPA-HW */
+ if (ipa_ctx->ctrl->ipa_commit_hdr()) {
+ IPAERR_RL("fail to commit hdr\n");
+ WARN_ON_RATELIMIT_IPA(1);
+ mutex_unlock(&ipa_ctx->lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&ipa_ctx->lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
index c043bad..15be5d6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
@@ -327,6 +327,11 @@
size_t tmp;
gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
+ if (!ipa_ctx->nat_mem.is_dev_init) {
+ IPAERR_RL("Nat table not initialized\n");
+ return -EPERM;
+ }
+
IPADBG("\n");
if (init->table_entries == 0) {
IPADBG("Table entries is zero\n");
@@ -576,6 +581,11 @@
int ret = 0;
gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
+ if (!ipa_ctx->nat_mem.is_dev_init) {
+ IPAERR_RL("Nat table not initialized\n");
+ return -EPERM;
+ }
+
IPADBG("\n");
if (dma->entries <= 0) {
IPAERR_RL("Invalid number of commands %d\n",
@@ -762,6 +772,16 @@
int result;
gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
+ if (!ipa_ctx->nat_mem.is_dev_init) {
+ IPAERR_RL("Nat table not initialized\n");
+ return -EPERM;
+ }
+
+ if (ipa_ctx->nat_mem.public_ip_addr) {
+ IPAERR_RL("Public IP addr not assigned and trying to delete\n");
+ return -EPERM;
+ }
+
IPADBG("\n");
if (ipa_ctx->nat_mem.is_tmp_mem) {
IPAERR("using temp memory during nat del\n");
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
index 1f5d619..98f5574 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -281,7 +281,7 @@
{
}
-static int rmnet_ipa_reset_tethering_stats
+static inline int rmnet_ipa_reset_tethering_stats
(
struct wan_ioctl_reset_tether_stats *data
)
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 7710279..073409b 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -1445,6 +1445,15 @@
}
}
}
+
+ /* commit the change to IPA-HW */
+ if (ipa_ctx->ctrl->ipa_commit_rt(IPA_IP_v4) ||
+ ipa_ctx->ctrl->ipa_commit_rt(IPA_IP_v6)) {
+ IPAERR("fail to commit rt-rule\n");
+ WARN_ON_RATELIMIT_IPA(1);
+ mutex_unlock(&ipa_ctx->lock);
+ return -EPERM;
+ }
mutex_unlock(&ipa_ctx->lock);
return 0;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index d76c208..681b009 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -72,6 +72,9 @@
#define INVALID_EP_MAPPING_INDEX (-1)
+#define IPA_IS_RAN_OUT_OF_EQ(__eq_array, __eq_index) \
+ (ARRAY_SIZE(__eq_array) <= (__eq_index))
+
struct ipa_ep_confing {
bool valid;
int pipe_num;
@@ -119,6 +122,7 @@
[IPA_2_0][IPA_CLIENT_MHI_PROD] = {true, 18},
[IPA_2_0][IPA_CLIENT_Q6_LAN_PROD] = {true, 6},
[IPA_2_0][IPA_CLIENT_Q6_CMD_PROD] = {true, 7},
+
[IPA_2_0][IPA_CLIENT_MEMCPY_DMA_SYNC_PROD]
= {true, 12},
[IPA_2_0][IPA_CLIENT_MEMCPY_DMA_ASYNC_PROD]
@@ -1409,6 +1413,11 @@
}
if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) {
+ if (IPA_IS_RAN_OUT_OF_EQ(ipa_ihl_ofst_meq32,
+ ihl_ofst_meq32)) {
+ IPAERR("ran out of ihl_meq32 eq\n");
+ return -EPERM;
+ }
if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
IPAERR("ran out of ihl_meq32 eq\n");
return -EPERM;
@@ -1426,6 +1435,11 @@
}
if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) {
+ if (IPA_IS_RAN_OUT_OF_EQ(ipa_ihl_ofst_meq32,
+ ihl_ofst_meq32)) {
+ IPAERR("ran out of ihl_meq32 eq\n");
+ return -EPERM;
+ }
if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
IPAERR("ran out of ihl_meq32 eq\n");
return -EPERM;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index c86b0df..130afc7 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1433,6 +1433,8 @@
/* Extended IOCTLs */
case RMNET_IOCTL_EXTENDED:
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
IPAWANDBG("get ioctl: RMNET_IOCTL_EXTENDED\n");
if (copy_from_user(&extend_ioctl_data,
(u8 *)ifr->ifr_ifru.ifru_data,
diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile
index ae4dccf..ed78342 100644
--- a/drivers/platform/msm/ipa/ipa_v3/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/Makefile
@@ -1,9 +1,19 @@
obj-$(CONFIG_IPA3) += ipahal/
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
+
obj-$(CONFIG_IPA3) += ipat.o
ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \
ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \
ipa_hw_stats.o ipa_pm.o ipa_wdi3_i.o
+ifdef CONFIG_X86
+ipat-y += ipa_dtsi_replacement.o
+endif
+
obj-$(CONFIG_RMNET_IPA3) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 465b66b..4641d8d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -35,6 +35,7 @@
#include <linux/time.h>
#include <linux/hashtable.h>
#include <linux/jhash.h>
+#include <linux/pci.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/smem.h>
#include <soc/qcom/scm.h>
@@ -57,169 +58,14 @@
#define CREATE_TRACE_POINTS
#include "ipa_trace.h"
-#define IPA_GPIO_IN_QUERY_CLK_IDX 0
-#define IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX 0
-#define IPA_GPIO_OUT_CLK_VOTE_IDX 1
-
-#define IPA_SUMMING_THRESHOLD (0x10)
-#define IPA_PIPE_MEM_START_OFST (0x0)
-#define IPA_PIPE_MEM_SIZE (0x0)
-#define IPA_MOBILE_AP_MODE(x) (x == IPA_MODE_MOBILE_AP_ETH || \
- x == IPA_MODE_MOBILE_AP_WAN || \
- x == IPA_MODE_MOBILE_AP_WLAN)
-#define IPA_CNOC_CLK_RATE (75 * 1000 * 1000UL)
-#define IPA_A5_MUX_HEADER_LENGTH (8)
-
-#define IPA_AGGR_MAX_STR_LENGTH (10)
-
-#define CLEANUP_TAG_PROCESS_TIMEOUT 500
-
-#define IPA_AGGR_STR_IN_BYTES(str) \
- (strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
-
-#define IPA_TRANSPORT_PROD_TIMEOUT_MSEC 100
-
-#define IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE 2048
-
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_EP 0
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SIMPLE 1
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
-
-#define IPA_MHI_GSI_EVENT_RING_ID_START 10
-#define IPA_MHI_GSI_EVENT_RING_ID_END 12
-
-#define IPA_SMEM_SIZE (8 * 1024)
-
-#define IPA_GSI_CHANNEL_HALT_MIN_SLEEP 5000
-#define IPA_GSI_CHANNEL_HALT_MAX_SLEEP 10000
-#define IPA_GSI_CHANNEL_HALT_MAX_TRY 10
-
-/* round addresses for closes page per SMMU requirements */
-#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
- do { \
- (iova_p) = rounddown((iova), PAGE_SIZE); \
- (pa_p) = rounddown((pa), PAGE_SIZE); \
- (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
- } while (0)
-
-
-/* The relative location in /lib/firmware where the FWs will reside */
-#define IPA_FWS_PATH "ipa/ipa_fws.elf"
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "ipa_emulation_stubs.h"
+#endif
#ifdef CONFIG_COMPAT
-#define IPA_IOC_ADD_HDR32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ADD_HDR, \
- compat_uptr_t)
-#define IPA_IOC_DEL_HDR32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_DEL_HDR, \
- compat_uptr_t)
-#define IPA_IOC_ADD_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ADD_RT_RULE, \
- compat_uptr_t)
-#define IPA_IOC_DEL_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_DEL_RT_RULE, \
- compat_uptr_t)
-#define IPA_IOC_ADD_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ADD_FLT_RULE, \
- compat_uptr_t)
-#define IPA_IOC_DEL_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_DEL_FLT_RULE, \
- compat_uptr_t)
-#define IPA_IOC_GET_RT_TBL32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_GET_RT_TBL, \
- compat_uptr_t)
-#define IPA_IOC_COPY_HDR32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_COPY_HDR, \
- compat_uptr_t)
-#define IPA_IOC_QUERY_INTF32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_QUERY_INTF, \
- compat_uptr_t)
-#define IPA_IOC_QUERY_INTF_TX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_QUERY_INTF_TX_PROPS, \
- compat_uptr_t)
-#define IPA_IOC_QUERY_INTF_RX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_QUERY_INTF_RX_PROPS, \
- compat_uptr_t)
-#define IPA_IOC_QUERY_INTF_EXT_PROPS32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_QUERY_INTF_EXT_PROPS, \
- compat_uptr_t)
-#define IPA_IOC_GET_HDR32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_GET_HDR, \
- compat_uptr_t)
-#define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ALLOC_NAT_MEM, \
- compat_uptr_t)
-#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ALLOC_NAT_TABLE, \
- compat_uptr_t)
-#define IPA_IOC_ALLOC_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
- compat_uptr_t)
-#define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_V4_INIT_NAT, \
- compat_uptr_t)
-#define IPA_IOC_INIT_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_INIT_IPV6CT_TABLE, \
- compat_uptr_t)
-#define IPA_IOC_TABLE_DMA_CMD32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_TABLE_DMA_CMD, \
- compat_uptr_t)
-#define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_V4_DEL_NAT, \
- compat_uptr_t)
-#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_DEL_NAT_TABLE, \
- compat_uptr_t)
-#define IPA_IOC_DEL_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_DEL_IPV6CT_TABLE, \
- compat_uptr_t)
-#define IPA_IOC_NAT_MODIFY_PDN32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_NAT_MODIFY_PDN, \
- compat_uptr_t)
-#define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_GET_NAT_OFFSET, \
- compat_uptr_t)
-#define IPA_IOC_PULL_MSG32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_PULL_MSG, \
- compat_uptr_t)
-#define IPA_IOC_RM_ADD_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_RM_ADD_DEPENDENCY, \
- compat_uptr_t)
-#define IPA_IOC_RM_DEL_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_RM_DEL_DEPENDENCY, \
- compat_uptr_t)
-#define IPA_IOC_GENERATE_FLT_EQ32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_GENERATE_FLT_EQ, \
- compat_uptr_t)
-#define IPA_IOC_QUERY_RT_TBL_INDEX32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_QUERY_RT_TBL_INDEX, \
- compat_uptr_t)
-#define IPA_IOC_WRITE_QMAPID32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_WRITE_QMAPID, \
- compat_uptr_t)
-#define IPA_IOC_MDFY_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_MDFY_FLT_RULE, \
- compat_uptr_t)
-#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD, \
- compat_uptr_t)
-#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL, \
- compat_uptr_t)
-#define IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED, \
- compat_uptr_t)
-#define IPA_IOC_ADD_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_ADD_HDR_PROC_CTX, \
- compat_uptr_t)
-#define IPA_IOC_DEL_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_DEL_HDR_PROC_CTX, \
- compat_uptr_t)
-#define IPA_IOC_MDFY_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
- IPA_IOCTL_MDFY_RT_RULE, \
- compat_uptr_t)
-
/**
* struct ipa3_ioc_nat_alloc_mem32 - nat table memory allocation
* properties
@@ -243,8 +89,7 @@
compat_size_t size;
compat_off_t offset;
};
-
-#endif
+#endif /* #ifdef CONFIG_COMPAT */
#define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
#define TZ_MEM_PROTECT_REGION_ID 0x10
@@ -284,6 +129,7 @@
static struct clk *ipa3_clk;
struct ipa3_context *ipa3_ctx;
+
static struct {
bool present[IPA_SMMU_CB_MAX];
bool arm_smmu;
@@ -653,10 +499,15 @@
return;
}
- if (type != ADD_VLAN_IFACE &&
- type != DEL_VLAN_IFACE &&
- type != ADD_L2TP_VLAN_MAPPING &&
- type != DEL_L2TP_VLAN_MAPPING) {
+ switch (type) {
+ case ADD_VLAN_IFACE:
+ case DEL_VLAN_IFACE:
+ case ADD_L2TP_VLAN_MAPPING:
+ case DEL_L2TP_VLAN_MAPPING:
+ case ADD_BRIDGE_VLAN_MAPPING:
+ case DEL_BRIDGE_VLAN_MAPPING:
+ break;
+ default:
IPAERR("Wrong type given. buff %pK type %d\n", buff, type);
return;
}
@@ -669,10 +520,17 @@
int retval;
struct ipa_ioc_vlan_iface_info *vlan_info;
struct ipa_ioc_l2tp_vlan_mapping_info *mapping_info;
+ struct ipa_ioc_bridge_vlan_mapping_info *bridge_vlan_info;
struct ipa_msg_meta msg_meta;
+ void *buff;
- if (msg_type == ADD_VLAN_IFACE ||
- msg_type == DEL_VLAN_IFACE) {
+ IPADBG("type %d\n", msg_type);
+
+ memset(&msg_meta, 0, sizeof(msg_meta));
+ msg_meta.msg_type = msg_type;
+
+ if ((msg_type == ADD_VLAN_IFACE) ||
+ (msg_type == DEL_VLAN_IFACE)) {
vlan_info = kzalloc(sizeof(struct ipa_ioc_vlan_iface_info),
GFP_KERNEL);
if (!vlan_info) {
@@ -686,18 +544,10 @@
return -EFAULT;
}
- memset(&msg_meta, 0, sizeof(msg_meta));
- msg_meta.msg_type = msg_type;
msg_meta.msg_len = sizeof(struct ipa_ioc_vlan_iface_info);
- retval = ipa3_send_msg(&msg_meta, vlan_info,
- ipa3_vlan_l2tp_msg_free_cb);
- if (retval) {
- IPAERR("ipa3_send_msg failed: %d\n", retval);
- kfree(vlan_info);
- return retval;
- }
- } else if (msg_type == ADD_L2TP_VLAN_MAPPING ||
- msg_type == DEL_L2TP_VLAN_MAPPING) {
+ buff = vlan_info;
+ } else if ((msg_type == ADD_L2TP_VLAN_MAPPING) ||
+ (msg_type == DEL_L2TP_VLAN_MAPPING)) {
mapping_info = kzalloc(sizeof(struct
ipa_ioc_l2tp_vlan_mapping_info), GFP_KERNEL);
if (!mapping_info) {
@@ -712,22 +562,46 @@
return -EFAULT;
}
- memset(&msg_meta, 0, sizeof(msg_meta));
- msg_meta.msg_type = msg_type;
msg_meta.msg_len = sizeof(struct
ipa_ioc_l2tp_vlan_mapping_info);
- retval = ipa3_send_msg(&msg_meta, mapping_info,
- ipa3_vlan_l2tp_msg_free_cb);
- if (retval) {
- IPAERR("ipa3_send_msg failed: %d\n", retval);
- kfree(mapping_info);
- return retval;
+ buff = mapping_info;
+ } else if ((msg_type == ADD_BRIDGE_VLAN_MAPPING) ||
+ (msg_type == DEL_BRIDGE_VLAN_MAPPING)) {
+ bridge_vlan_info = kzalloc(
+ sizeof(struct ipa_ioc_bridge_vlan_mapping_info),
+ GFP_KERNEL);
+ if (!bridge_vlan_info) {
+ IPAERR("no memory\n");
+ return -ENOMEM;
}
+
+ if (copy_from_user((u8 *)bridge_vlan_info,
+ (void __user *)usr_param,
+ sizeof(struct ipa_ioc_bridge_vlan_mapping_info))) {
+ kfree(bridge_vlan_info);
+ IPAERR("copy from user failed\n");
+ return -EFAULT;
+ }
+
+ msg_meta.msg_len = sizeof(struct
+ ipa_ioc_bridge_vlan_mapping_info);
+ buff = bridge_vlan_info;
} else {
IPAERR("Unexpected event\n");
return -EFAULT;
}
+ retval = ipa3_send_msg(&msg_meta, buff,
+ ipa3_vlan_l2tp_msg_free_cb);
+ if (retval) {
+ IPAERR("ipa3_send_msg failed: %d, msg_type %d\n",
+ retval,
+ msg_type);
+ kfree(buff);
+ return retval;
+ }
+ IPADBG("exit\n");
+
return 0;
}
@@ -737,6 +611,7 @@
u32 pyld_sz;
u8 header[128] = { 0 };
u8 *param = NULL;
+ bool is_vlan_mode;
struct ipa_ioc_nat_alloc_mem nat_mem;
struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
struct ipa_ioc_v4_nat_init nat_init;
@@ -746,6 +621,7 @@
struct ipa_ioc_nat_pdn_entry mdfy_pdn;
struct ipa_ioc_rm_dependency rm_depend;
struct ipa_ioc_nat_dma_cmd *table_dma_cmd;
+ struct ipa_ioc_get_vlan_mode vlan_mode;
size_t sz;
int pre_entry;
@@ -1843,6 +1719,28 @@
}
break;
+ case IPA_IOC_GET_VLAN_MODE:
+ if (copy_from_user(&vlan_mode, (const void __user *)arg,
+ sizeof(struct ipa_ioc_get_vlan_mode))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipa3_is_vlan_mode(
+ vlan_mode.iface,
+ &is_vlan_mode);
+ if (retval)
+ break;
+
+ vlan_mode.is_vlan_mode = is_vlan_mode;
+
+ if (copy_to_user((void __user *)arg,
+ &vlan_mode,
+ sizeof(struct ipa_ioc_get_vlan_mode))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
case IPA_IOC_ADD_VLAN_IFACE:
if (ipa3_send_vlan_l2tp_msg(arg, ADD_VLAN_IFACE)) {
retval = -EFAULT;
@@ -1856,7 +1754,18 @@
break;
}
break;
-
+ case IPA_IOC_ADD_BRIDGE_VLAN_MAPPING:
+ if (ipa3_send_vlan_l2tp_msg(arg, ADD_BRIDGE_VLAN_MAPPING)) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+ case IPA_IOC_DEL_BRIDGE_VLAN_MAPPING:
+ if (ipa3_send_vlan_l2tp_msg(arg, DEL_BRIDGE_VLAN_MAPPING)) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
case IPA_IOC_ADD_L2TP_VLAN_MAPPING:
if (ipa3_send_vlan_l2tp_msg(arg, ADD_L2TP_VLAN_MAPPING)) {
retval = -EFAULT;
@@ -2637,6 +2546,8 @@
*/
ipa3_q6_pipe_delay(false);
+ ipa3_set_usb_prod_pipe_delay();
+
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
IPADBG_LOW("Exit with success\n");
}
@@ -2711,6 +2622,16 @@
u32 *ipa_sram_mmio;
unsigned long phys_addr;
+ IPADBG(
+ "ipa_wrapper_base(0x%08X) ipa_reg_base_ofst(0x%08X) IPA_SRAM_DIRECT_ACCESS_n(0x%08X) smem_restricted_bytes(0x%08X) smem_sz(0x%08X)\n",
+ ipa3_ctx->ipa_wrapper_base,
+ ipa3_ctx->ctrl->ipa_reg_base_ofst,
+ ipahal_get_reg_n_ofst(
+ IPA_SRAM_DIRECT_ACCESS_n,
+ ipa3_ctx->smem_restricted_bytes / 4),
+ ipa3_ctx->smem_restricted_bytes,
+ ipa3_ctx->smem_sz);
+
phys_addr = ipa3_ctx->ipa_wrapper_base +
ipa3_ctx->ctrl->ipa_reg_base_ofst +
ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n,
@@ -3183,21 +3104,27 @@
}
IPADBG("Apps to IPA cmd pipe is connected\n");
+ IPADBG("Will initialize SRAM\n");
ipa3_ctx->ctrl->ipa_init_sram();
IPADBG("SRAM initialized\n");
+ IPADBG("Will initialize HDR\n");
ipa3_ctx->ctrl->ipa_init_hdr();
IPADBG("HDR initialized\n");
+ IPADBG("Will initialize V4 RT\n");
ipa3_ctx->ctrl->ipa_init_rt4();
IPADBG("V4 RT initialized\n");
+ IPADBG("Will initialize V6 RT\n");
ipa3_ctx->ctrl->ipa_init_rt6();
IPADBG("V6 RT initialized\n");
+ IPADBG("Will initialize V4 FLT\n");
ipa3_ctx->ctrl->ipa_init_flt4();
IPADBG("V4 FLT initialized\n");
+ IPADBG("Will initialize V6 FLT\n");
ipa3_ctx->ctrl->ipa_init_flt6();
IPADBG("V6 FLT initialized\n");
@@ -3923,16 +3850,18 @@
{
u32 clk_rate;
- if (!ipa3_ctx->enable_clock_scaling)
+ IPADBG_LOW("idx = %d\n", idx);
+
+ if (!ipa3_ctx->enable_clock_scaling) {
+ ipa3_ctx->ipa3_active_clients.bus_vote_idx = idx;
return 0;
+ }
if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_NORMAL) {
IPAERR("not supported in this mode\n");
return 0;
}
- IPADBG_LOW("idx = %d\n", idx);
-
if (idx <= 0 || idx >= ipa3_ctx->ctrl->msm_bus_data_ptr->num_usecases) {
IPAERR("bad voltage\n");
return -EINVAL;
@@ -4350,7 +4279,7 @@
IPAERR("uC panic handler failed %d\n", res);
if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) != 0)
- ipahal_print_all_regs();
+ ipahal_print_all_regs(false);
return NOTIFY_DONE;
}
@@ -4386,8 +4315,8 @@
int result;
result = gsi_configure_regs(ipa3_res.transport_mem_base,
- ipa3_res.transport_mem_size,
- ipa3_res.ipa_mem_base);
+ ipa3_res.transport_mem_size,
+ ipa3_res.ipa_mem_base);
if (result) {
IPAERR("Failed to configure GSI registers\n");
return -EINVAL;
@@ -4480,12 +4409,19 @@
if (ipa3_ctx->ipa_hw_type != IPA_HW_v4_0)
ipa3_proxy_clk_vote();
- /* SMMU was already attached if used, safe to do allocations */
- if (ipahal_init(ipa3_ctx->ipa_hw_type, ipa3_ctx->mmio,
- ipa3_ctx->pdev)) {
- IPAERR("fail to init ipahal\n");
- result = -EFAULT;
- goto fail_ipahal;
+ /*
+ * SMMU was already attached if used, safe to do allocations
+ *
+ * NOTE WELL: On an emulation system, this allocation is done
+ * in ipa3_pre_init()
+ */
+ if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION) {
+ if (ipahal_init(ipa3_ctx->ipa_hw_type, ipa3_ctx->mmio,
+ ipa3_ctx->pdev)) {
+ IPAERR("fail to init ipahal\n");
+ result = -EFAULT;
+ goto fail_ipahal;
+ }
}
result = ipa3_init_hw();
@@ -4620,9 +4556,18 @@
gsi_props.ver = ipa3_get_gsi_ver(resource_p->ipa_hw_type);
gsi_props.ee = resource_p->ee;
gsi_props.intr = GSI_INTR_IRQ;
- gsi_props.irq = resource_p->transport_irq;
gsi_props.phys_addr = resource_p->transport_mem_base;
gsi_props.size = resource_p->transport_mem_size;
+ if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+ gsi_props.irq = resource_p->emulator_irq;
+ gsi_props.emulator_intcntrlr_client_isr = ipa3_get_isr();
+ gsi_props.emulator_intcntrlr_addr =
+ resource_p->emulator_intcntrlr_mem_base;
+ gsi_props.emulator_intcntrlr_size =
+ resource_p->emulator_intcntrlr_mem_size;
+ } else {
+ gsi_props.irq = resource_p->transport_irq;
+ }
gsi_props.notify_cb = ipa_gsi_notify_cb;
gsi_props.req_clk_cb = NULL;
gsi_props.rel_clk_cb = NULL;
@@ -4690,6 +4635,8 @@
ipa3_register_panic_hdlr();
+ ipa3_debugfs_init();
+
mutex_lock(&ipa3_ctx->lock);
ipa3_ctx->ipa_initialization_complete = true;
mutex_unlock(&ipa3_ctx->lock);
@@ -4698,8 +4645,6 @@
complete_all(&ipa3_ctx->init_completion_obj);
pr_info("IPA driver initialization was successful.\n");
- ipa3_debugfs_init();
-
return 0;
fail_teth_bridge_driver_init:
@@ -4708,15 +4653,16 @@
gsi_deregister_device(ipa3_ctx->gsi_dev_hdl, false);
fail_register_device:
ipa3_destroy_flt_tbl_idrs();
- ipa3_proxy_clk_unvote();
fail_allok_pkt_init:
ipa3_nat_ipv6ct_destroy_devices();
fail_nat_ipv6ct_init_dev:
ipa3_free_dma_task_for_gsi();
fail_dma_task:
fail_init_hw:
- ipahal_destroy();
+ if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION)
+ ipahal_destroy();
fail_ipahal:
+ ipa3_proxy_clk_unvote();
return result;
}
@@ -4725,10 +4671,24 @@
{
int result;
const struct firmware *fw;
+ const char *path = IPA_FWS_PATH;
- IPADBG("Manual FW loading process initiated\n");
+ if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+ switch (ipa3_get_emulation_type()) {
+ case IPA_HW_v3_5_1:
+ path = IPA_FWS_PATH_3_5_1;
+ break;
+ case IPA_HW_v4_0:
+ path = IPA_FWS_PATH_4_0;
+ break;
+ default:
+ break;
+ }
+ }
- result = request_firmware(&fw, IPA_FWS_PATH, ipa3_ctx->cdev.dev);
+ IPADBG("Manual FW loading (%s) process initiated\n", path);
+
+ result = request_firmware(&fw, path, ipa3_ctx->cdev.dev);
if (result < 0) {
IPAERR("request_firmware failed, error %d\n", result);
return result;
@@ -4740,7 +4700,13 @@
IPADBG("FWs are available for loading\n");
- result = ipa3_load_fws(fw, ipa3_res.transport_mem_base);
+ if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+ result = emulator_load_fws(fw,
+ ipa3_res.transport_mem_base,
+ ipa3_res.transport_mem_size);
+ } else {
+ result = ipa3_load_fws(fw, ipa3_res.transport_mem_base);
+ }
if (result) {
IPAERR("Manual IPA FWs loading has failed\n");
release_firmware(fw);
@@ -4759,6 +4725,7 @@
release_firmware(fw);
IPADBG("Manual FW loading process is complete\n");
+
return 0;
}
@@ -4792,7 +4759,8 @@
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- if (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5))
+ if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION &&
+ (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5)))
result = ipa3_pil_load_ipa_fws();
else
result = ipa3_manual_load_ipa_fws();
@@ -5071,6 +5039,7 @@
ipa3_ctx->gsi_ch20_wa = resource_p->gsi_ch20_wa;
ipa3_ctx->use_ipa_pm = resource_p->use_ipa_pm;
ipa3_ctx->ipa3_active_clients_logging.log_rdy = false;
+ ipa3_ctx->ipa_config_is_mhi = resource_p->ipa_mhi_dynamic_config;
ipa3_ctx->mhi_evid_limits[0] = resource_p->mhi_evid_limits[0];
ipa3_ctx->mhi_evid_limits[1] = resource_p->mhi_evid_limits[1];
@@ -5128,7 +5097,8 @@
goto fail_init_mem_partition;
}
- if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_VIRTUAL) {
+ if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_VIRTUAL &&
+ ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION) {
ipa3_ctx->ctrl->msm_bus_data_ptr =
msm_bus_cl_get_pdata(ipa3_ctx->master_pdev);
if (ipa3_ctx->ctrl->msm_bus_data_ptr == NULL) {
@@ -5178,6 +5148,28 @@
goto fail_remap;
}
+ IPADBG(
+ "base(0x%x)+offset(0x%x)=(0x%x) mapped to (%pK) with len (0x%x)\n",
+ resource_p->ipa_mem_base,
+ ipa3_ctx->ctrl->ipa_reg_base_ofst,
+ resource_p->ipa_mem_base + ipa3_ctx->ctrl->ipa_reg_base_ofst,
+ ipa3_ctx->mmio,
+ resource_p->ipa_mem_size);
+
+ /*
+ * Emulation requires ipahal be initialized early...for FW
+ * download, hence...
+ */
+ if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+ if (ipahal_init(ipa3_ctx->ipa_hw_type,
+ ipa3_ctx->mmio,
+ &(ipa3_ctx->master_pdev->dev))) {
+ IPAERR("fail to init ipahal\n");
+ result = -EFAULT;
+ goto fail_ipahal_init;
+ }
+ }
+
mutex_init(&ipa3_ctx->ipa3_active_clients.mutex);
IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE");
@@ -5386,10 +5378,13 @@
* We can't register the GSI driver yet, as it expects
* the GSI FW to be up and running before the registration.
*
- * For IPA3.0, the GSI configuration is done by the GSI driver.
+ * For IPA3.0 and the emulation system, the GSI configuration
+ * is done by the GSI driver.
+ *
* For IPA3.1 (and on), the GSI configuration is done by TZ.
*/
- if (ipa3_ctx->ipa_hw_type == IPA_HW_v3_0) {
+ if (ipa3_ctx->ipa_hw_type == IPA_HW_v3_0 ||
+ ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
result = ipa3_gsi_pre_fw_load_init();
if (result) {
IPAERR("gsi pre FW loading config failed\n");
@@ -5467,6 +5462,9 @@
fail_create_transport_wq:
destroy_workqueue(ipa3_ctx->power_mgmt_wq);
fail_init_hw:
+ if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION)
+ ipahal_destroy();
+fail_ipahal_init:
iounmap(ipa3_ctx->mmio);
fail_remap:
ipa3_disable_clks();
@@ -5601,6 +5599,7 @@
ipa_drv_res->ipa3_hw_mode = 0;
ipa_drv_res->modem_cfg_emb_pipe_flt = false;
ipa_drv_res->ipa_wdi2 = false;
+ ipa_drv_res->ipa_mhi_dynamic_config = false;
ipa_drv_res->use_64_bit_dma_mask = false;
ipa_drv_res->use_bw_vote = false;
ipa_drv_res->wan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ;
@@ -5663,6 +5662,13 @@
ipa_drv_res->use_ipa_teth_bridge
? "True" : "False");
+ ipa_drv_res->ipa_mhi_dynamic_config =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,use-ipa-in-mhi-mode");
+ IPADBG(": ipa_mhi_dynamic_config (%s)\n",
+ ipa_drv_res->ipa_mhi_dynamic_config
+ ? "True" : "False");
+
ipa_drv_res->modem_cfg_emb_pipe_flt =
of_property_read_bool(pdev->dev.of_node,
"qcom,modem-cfg-emb-pipe-flt");
@@ -5771,6 +5777,7 @@
&ipa_drv_res->ee);
if (result)
ipa_drv_res->ee = 0;
+ IPADBG(":ee = %u\n", ipa_drv_res->ee);
ipa_drv_res->apply_rg10_wa =
of_property_read_bool(pdev->dev.of_node,
@@ -5783,7 +5790,7 @@
of_property_read_bool(pdev->dev.of_node,
"qcom,do-not-use-ch-gsi-20");
IPADBG(": GSI CH 20 WA is = %s\n",
- ipa_drv_res->apply_rg10_wa
+ ipa_drv_res->gsi_ch20_wa
? "Needed" : "Not needed");
elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
@@ -5862,6 +5869,26 @@
return result;
}
+ /*
+ * If we're on emulator, get its interrupt controller's mem
+ * start and size
+ */
+ if (ipa_drv_res->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+ resource = platform_get_resource_byname(
+ pdev, IORESOURCE_MEM, "intctrl-base");
+ if (!resource) {
+ IPAERR(":Can't find intctrl-base resource\n");
+ return -ENODEV;
+ }
+ ipa_drv_res->emulator_intcntrlr_mem_base =
+ resource->start;
+ ipa_drv_res->emulator_intcntrlr_mem_size =
+ resource_size(resource);
+ IPADBG(":using intctrl-base at 0x%x of size 0x%x\n",
+ ipa_drv_res->emulator_intcntrlr_mem_base,
+ ipa_drv_res->emulator_intcntrlr_mem_size);
+ }
+
return 0;
}
@@ -6243,7 +6270,7 @@
iova_p, &pa_p, size_p);
ipa3_iommu_map(cb->mapping->domain,
iova_p, pa_p, size_p,
- IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO);
+ IOMMU_READ | IOMMU_WRITE);
}
@@ -6656,5 +6683,216 @@
return 0;
}
+/**************************************************************
+ * PCIe Version
+ *************************************************************/
+
+int ipa3_pci_drv_probe(
+ struct pci_dev *pci_dev,
+ struct ipa_api_controller *api_ctrl,
+ const struct of_device_id *pdrv_match)
+{
+ int result;
+ struct ipa3_plat_drv_res *ipa_drv_res;
+ u32 bar0_offset;
+ u32 mem_start;
+ u32 mem_end;
+ uint32_t bits;
+ uint32_t ipa_start, gsi_start, intctrl_start;
+ struct device *dev;
+ static struct platform_device platform_dev;
+
+ if (!pci_dev || !api_ctrl || !pdrv_match) {
+ IPAERR(
+ "Bad arg: pci_dev (%pK) and/or api_ctrl (%pK) and/or pdrv_match (%pK)\n",
+ pci_dev, api_ctrl, pdrv_match);
+ return -EOPNOTSUPP;
+ }
+
+ dev = &(pci_dev->dev);
+
+ IPADBG("IPA PCI driver probing started\n");
+
+ /*
+ * Follow PCI driver flow here.
+ * pci_enable_device: Enables device and assigns resources
+ * pci_request_region: Makes BAR0 address region usable
+ */
+ result = pci_enable_device(pci_dev);
+ if (result < 0) {
+ IPAERR("pci_enable_device() failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ result = pci_request_region(pci_dev, 0, "IPA Memory");
+ if (result < 0) {
+ IPAERR("pci_request_region() failed\n");
+ pci_disable_device(pci_dev);
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * When in the PCI/emulation environment, &platform_dev is
+ * passed to get_ipa_dts_configuration(), but is unused, since
+ * all usages of it in the function are replaced by CPP
+ * relative to definitions in ipa_emulation_stubs.h. Passing
+ * &platform_dev makes code validity tools happy.
+ */
+ if (get_ipa_dts_configuration(&platform_dev, &ipa3_res) != 0) {
+ IPAERR("get_ipa_dts_configuration() failed\n");
+ pci_release_region(pci_dev, 0);
+ pci_disable_device(pci_dev);
+ return -EOPNOTSUPP;
+ }
+
+ ipa_drv_res = &ipa3_res;
+
+ result =
+ of_property_read_u32(NULL, "emulator-bar0-offset",
+ &bar0_offset);
+ if (result) {
+ IPAERR(":get resource failed for emulator-bar0-offset!\n");
+ pci_release_region(pci_dev, 0);
+ pci_disable_device(pci_dev);
+ return -ENODEV;
+ }
+ IPADBG(":using emulator-bar0-offset 0x%08X\n", bar0_offset);
+
+ ipa_start = ipa_drv_res->ipa_mem_base;
+ gsi_start = ipa_drv_res->transport_mem_base;
+ intctrl_start = ipa_drv_res->emulator_intcntrlr_mem_base;
+
+ /*
+ * Where will we be inerrupted at?
+ */
+ ipa_drv_res->emulator_irq = pci_dev->irq;
+ IPADBG(
+ "EMULATION PCI_INTERRUPT_PIN(%u)\n",
+ ipa_drv_res->emulator_irq);
+
+ /*
+ * Set the ipa_mem_base to the PCI base address of BAR0
+ */
+ mem_start = pci_resource_start(pci_dev, 0);
+ mem_end = pci_resource_end(pci_dev, 0);
+
+ IPADBG("PCI START = 0x%x\n", mem_start);
+ IPADBG("PCI END = 0x%x\n", mem_end);
+
+ ipa_drv_res->ipa_mem_base = mem_start + bar0_offset;
+
+ smmu_info.ipa_base = ipa_drv_res->ipa_mem_base;
+ smmu_info.ipa_size = ipa_drv_res->ipa_mem_size;
+
+ ipa_drv_res->transport_mem_base =
+ ipa_drv_res->ipa_mem_base + (gsi_start - ipa_start);
+
+ ipa_drv_res->emulator_intcntrlr_mem_base =
+ ipa_drv_res->ipa_mem_base + (intctrl_start - ipa_start);
+
+ IPADBG("ipa_mem_base = 0x%x\n",
+ ipa_drv_res->ipa_mem_base);
+ IPADBG("ipa_mem_size = 0x%x\n",
+ ipa_drv_res->ipa_mem_size);
+
+ IPADBG("transport_mem_base = 0x%x\n",
+ ipa_drv_res->transport_mem_base);
+ IPADBG("transport_mem_size = 0x%x\n",
+ ipa_drv_res->transport_mem_size);
+
+ IPADBG("emulator_intcntrlr_mem_base = 0x%x\n",
+ ipa_drv_res->emulator_intcntrlr_mem_base);
+ IPADBG("emulator_intcntrlr_mem_size = 0x%x\n",
+ ipa_drv_res->emulator_intcntrlr_mem_size);
+
+ result = ipa3_bind_api_controller(ipa_drv_res->ipa_hw_type, api_ctrl);
+ if (result != 0) {
+ IPAERR("ipa3_bind_api_controller() failed\n");
+ pci_release_region(pci_dev, 0);
+ pci_disable_device(pci_dev);
+ return result;
+ }
+
+ bits = (ipa_drv_res->use_64_bit_dma_mask) ? 64 : 32;
+
+ if (dma_set_mask(dev, DMA_BIT_MASK(bits)) != 0) {
+ IPAERR("dma_set_mask(%pK, %u) failed\n", dev, bits);
+ pci_release_region(pci_dev, 0);
+ pci_disable_device(pci_dev);
+ return -EOPNOTSUPP;
+ }
+
+ if (dma_set_coherent_mask(dev, DMA_BIT_MASK(bits)) != 0) {
+ IPAERR("dma_set_coherent_mask(%pK, %u) failed\n", dev, bits);
+ pci_release_region(pci_dev, 0);
+ pci_disable_device(pci_dev);
+ return -EOPNOTSUPP;
+ }
+
+ pci_set_master(pci_dev);
+
+ memset(&platform_dev, 0, sizeof(platform_dev));
+ platform_dev.dev = *dev;
+
+ /* Proceed to real initialization */
+ result = ipa3_pre_init(&ipa3_res, &platform_dev);
+ if (result) {
+ IPAERR("ipa3_init failed\n");
+ pci_clear_master(pci_dev);
+ pci_release_region(pci_dev, 0);
+ pci_disable_device(pci_dev);
+ return result;
+ }
+
+ return result;
+}
+
+/*
+ * The following returns transport register memory location and
+ * size...
+ */
+int ipa3_get_transport_info(
+ phys_addr_t *phys_addr_ptr,
+ unsigned long *size_ptr)
+{
+ if (!phys_addr_ptr || !size_ptr) {
+ IPAERR("Bad arg: phys_addr_ptr(%pK) and/or size_ptr(%pK)\n",
+ phys_addr_ptr, size_ptr);
+ return -EINVAL;
+ }
+
+ *phys_addr_ptr = ipa3_res.transport_mem_base;
+ *size_ptr = ipa3_res.transport_mem_size;
+
+ return 0;
+}
+EXPORT_SYMBOL(ipa3_get_transport_info);
+
+static uint emulation_type = IPA_HW_v4_0;
+
+/*
+ * The following returns emulation type...
+ */
+uint ipa3_get_emulation_type(void)
+{
+ return emulation_type;
+}
+
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("IPA HW device driver");
+
+/*
+ * Module parameter. Invoke as follows:
+ * insmod ipat.ko emulation_type=[13|14|...|N]
+ * Examples:
+ * insmod ipat.ko emulation_type=13 # for IPA 3.5.1
+ * insmod ipat.ko emulation_type=14 # for IPA 4.0
+ *
+ * NOTE: The emulation_type values need to come from: enum ipa_hw_type
+ *
+ */
+
+module_param(emulation_type, uint, 0000);
+MODULE_PARM_DESC(
+ emulation_type,
+ "IPA emulation type (Use 13 for IPA 3.5.1, 14 for IPA 4.0)");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index fe39440..5bcd49e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -635,6 +635,69 @@
return 0;
}
+void ipa3_register_lock_unlock_callback(int (*client_cb)(bool is_lock),
+ u32 ipa_ep_idx)
+{
+ struct ipa3_ep_context *ep;
+
+ IPADBG("entry\n");
+
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+
+ if (!ep->valid) {
+ IPAERR("Invalid EP\n");
+ return;
+ }
+
+ if (client_cb == NULL) {
+ IPAERR("Bad Param");
+ return;
+ }
+
+ ep->client_lock_unlock = client_cb;
+ IPADBG("exit\n");
+}
+
+void ipa3_deregister_lock_unlock_callback(u32 ipa_ep_idx)
+{
+ struct ipa3_ep_context *ep;
+
+ IPADBG("entry\n");
+
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+
+ if (!ep->valid) {
+ IPAERR("Invalid EP\n");
+ return;
+ }
+
+ if (ep->client_lock_unlock == NULL) {
+ IPAERR("client_lock_unlock is already NULL");
+ return;
+ }
+
+ ep->client_lock_unlock = NULL;
+ IPADBG("exit\n");
+}
+
+static void client_lock_unlock_cb(u32 ipa_ep_idx, bool is_lock)
+{
+ struct ipa3_ep_context *ep;
+
+ IPADBG("entry\n");
+
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+
+ if (!ep->valid) {
+ IPAERR("Invalid EP\n");
+ return;
+ }
+
+ if (ep->client_lock_unlock)
+ ep->client_lock_unlock(is_lock);
+
+ IPADBG("exit\n");
+}
int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params,
struct ipa_req_chan_out_params *out_params)
@@ -1259,6 +1322,46 @@
return result;
}
+/*
+ * Set USB PROD pipe delay for MBIM/RMNET config
+ * Clocks, should be voted before calling this API
+ * locks should be taken before calling this API
+ */
+
+void ipa3_set_usb_prod_pipe_delay(void)
+{
+ int result;
+ int pipe_idx;
+ struct ipa3_ep_context *ep;
+ struct ipa_ep_cfg_ctrl ep_ctrl;
+
+ memset(&ep_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+ ep_ctrl.ipa_ep_delay = true;
+
+
+ pipe_idx = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD);
+
+ if (pipe_idx == IPA_EP_NOT_ALLOCATED) {
+ IPAERR("client (%d) not valid\n", IPA_CLIENT_USB_PROD);
+ return;
+ }
+
+ ep = &ipa3_ctx->ep[pipe_idx];
+
+ /* Setting delay on USB_PROD with skip_ep_cfg */
+ client_lock_unlock_cb(pipe_idx, true);
+ if (ep->valid && ep->skip_ep_cfg) {
+ ep->ep_delay_set = ep_ctrl.ipa_ep_delay;
+ result = ipa3_cfg_ep_ctrl(pipe_idx, &ep_ctrl);
+ if (result)
+ IPAERR("client (ep: %d) failed result=%d\n",
+ pipe_idx, result);
+ else
+ IPADBG("client (ep: %d) success\n", pipe_idx);
+ }
+ client_lock_unlock_cb(pipe_idx, false);
+}
+
void ipa3_xdci_ep_delay_rm(u32 clnt_hdl)
{
struct ipa3_ep_context *ep;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index ee9c49c..10abdcf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -73,6 +73,8 @@
__stringify(DEL_L2TP_VLAN_MAPPING),
__stringify(IPA_PER_CLIENT_STATS_CONNECT_EVENT),
__stringify(IPA_PER_CLIENT_STATS_DISCONNECT_EVENT),
+ __stringify(ADD_BRIDGE_VLAN_MAPPING),
+ __stringify(DEL_BRIDGE_VLAN_MAPPING),
};
const char *ipa3_hdr_l2_type_name[] = {
@@ -372,6 +374,8 @@
list_for_each_entry(entry, &ipa3_ctx->hdr_tbl.head_hdr_entry_list,
link) {
+ if (entry->cookie != IPA_HDR_COOKIE)
+ continue;
nbytes = scnprintf(
dbg_buff,
IPA_MAX_MSG_LEN,
@@ -556,6 +560,12 @@
if (attrib->tc_eq_present)
pr_err("tc:%d ", attrib->tc_eq);
+ if (attrib->num_offset_meq_128 > IPA_IPFLTR_NUM_MEQ_128_EQNS) {
+ IPAERR_RL("num_offset_meq_128 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_MEQ_128_EQNS, attrib->num_offset_meq_128);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_offset_meq_128; i++) {
for (j = 0; j < 16; j++) {
addr[j] = attrib->offset_meq_128[i].value[j];
@@ -567,6 +577,12 @@
mask, addr);
}
+ if (attrib->num_offset_meq_32 > IPA_IPFLTR_NUM_MEQ_32_EQNS) {
+ IPAERR_RL("num_offset_meq_32 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_MEQ_32_EQNS, attrib->num_offset_meq_32);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_offset_meq_32; i++)
pr_err(
"(ofst_meq32: ofst:%u mask:0x%x val:0x%x) ",
@@ -574,6 +590,12 @@
attrib->offset_meq_32[i].mask,
attrib->offset_meq_32[i].value);
+ if (attrib->num_ihl_offset_meq_32 > IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS) {
+ IPAERR_RL("num_ihl_offset_meq_32 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS, attrib->num_ihl_offset_meq_32);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_ihl_offset_meq_32; i++)
pr_err(
"(ihl_ofst_meq32: ofts:%d mask:0x%x val:0x%x) ",
@@ -588,6 +610,14 @@
attrib->metadata_meq32.mask,
attrib->metadata_meq32.value);
+ if (attrib->num_ihl_offset_range_16 >
+ IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS) {
+ IPAERR_RL("num_ihl_offset_range_16 Max %d passed value %d\n",
+ IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS,
+ attrib->num_ihl_offset_range_16);
+ return -EPERM;
+ }
+
for (i = 0; i < attrib->num_ihl_offset_range_16; i++)
pr_err(
"(ihl_ofst_range16: ofst:%u lo:%u hi:%u) ",
@@ -780,7 +810,11 @@
pr_err("rule_id:%u prio:%u retain_hdr:%u ",
rules[rl].id, rules[rl].priority,
rules[rl].retain_hdr);
- ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+ res = ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
}
pr_err("=== Routing Table %d = Non-Hashable Rules ===\n", tbl);
@@ -811,7 +845,11 @@
pr_err("rule_id:%u prio:%u retain_hdr:%u\n",
rules[rl].id, rules[rl].priority,
rules[rl].retain_hdr);
- ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+ res = ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
}
pr_err("\n");
}
@@ -885,6 +923,7 @@
u32 rt_tbl_idx;
u32 bitmap;
bool eq;
+ int res = 0;
mutex_lock(&ipa3_ctx->lock);
@@ -894,6 +933,8 @@
tbl = &ipa3_ctx->flt_tbl[j][ip];
i = 0;
list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+ if (entry->cookie != IPA_FLT_COOKIE)
+ continue;
if (entry->rule.eq_attrib_type) {
rt_tbl_idx = entry->rule.rt_tbl_idx;
bitmap = entry->rule.eq_attrib.rule_eq_bitmap;
@@ -919,18 +960,23 @@
pr_err("pdn index %d, set metadata %d ",
entry->rule.pdn_idx,
entry->rule.set_metadata);
- if (eq)
- ipa3_attrib_dump_eq(
- &entry->rule.eq_attrib);
- else
+ if (eq) {
+ res = ipa3_attrib_dump_eq(
+ &entry->rule.eq_attrib);
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
+ } else
ipa3_attrib_dump(
&entry->rule.attrib, ip);
i++;
}
}
+bail:
mutex_unlock(&ipa3_ctx->lock);
- return 0;
+ return res;
}
static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf,
@@ -985,7 +1031,11 @@
pr_err("pdn: %u, set_metadata: %u ",
rules[rl].rule.pdn_idx,
rules[rl].rule.set_metadata);
- ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+ res = ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
}
pr_err("=== Filtering Table ep:%d = Non-Hashable Rules ===\n",
@@ -1013,7 +1063,11 @@
pr_err("pdn: %u, set_metadata: %u ",
rules[rl].rule.pdn_idx,
rules[rl].rule.set_metadata);
- ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+ res = ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+ if (res) {
+ IPAERR_RL("failed read attrib eq\n");
+ goto bail;
+ }
}
pr_err("\n");
}
@@ -1898,7 +1952,7 @@
size_t count, loff_t *ppos)
{
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- ipahal_print_all_regs();
+ ipahal_print_all_regs(true);
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return 0;
@@ -2239,6 +2293,13 @@
goto fail;
}
+ file = debugfs_create_u32("clk_rate", IPA_READ_ONLY_MODE,
+ dent, &ipa3_ctx->curr_ipa_clk_rate);
+ if (!file) {
+ IPAERR("could not create clk_rate file\n");
+ goto fail;
+ }
+
ipa_debugfs_init_stats(dent);
return;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 9a0f44a..d3ed36a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -35,6 +35,9 @@
#define IPA_GENERIC_AGGR_TIME_LIMIT 1
#define IPA_GENERIC_AGGR_PKT_LIMIT 0
+#define IPA_GSB_AGGR_BYTE_LIMIT 14
+#define IPA_GSB_RX_BUFF_BASE_SZ 16384
+
#define IPA_GENERIC_RX_BUFF_BASE_SZ 8192
#define IPA_REAL_GENERIC_RX_BUFF_SZ(X) (SKB_DATA_ALIGN(\
(X) + NET_SKB_PAD) +\
@@ -646,7 +649,6 @@
atomic_set(&comp->cnt, 2);
sys = ipa3_ctx->ep[ep_idx].sys;
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
if (num_desc == 1) {
if (descr->callback || descr->user1)
@@ -685,7 +687,6 @@
kfree(comp);
bail:
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return result;
}
@@ -1112,11 +1113,6 @@
IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
ipa3_disable_data_path(clnt_hdl);
- if (ep->napi_enabled) {
- do {
- usleep_range(95, 105);
- } while (atomic_read(&ep->sys->curr_polling_state));
- }
if (IPA_CLIENT_IS_PROD(ep->client)) {
do {
@@ -1130,9 +1126,6 @@
} while (1);
}
- if (IPA_CLIENT_IS_CONS(ep->client))
- cancel_delayed_work_sync(&ep->sys->replenish_rx_work);
- flush_workqueue(ep->sys->wq);
/* channel stop might fail on timeout if IPA is busy */
for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
result = ipa3_stop_gsi_channel(clnt_hdl);
@@ -1140,7 +1133,7 @@
break;
if (result != -GSI_STATUS_AGAIN &&
- result != -GSI_STATUS_TIMED_OUT)
+ result != -GSI_STATUS_TIMED_OUT)
break;
}
@@ -1149,6 +1142,17 @@
ipa_assert();
return result;
}
+
+ if (ep->napi_enabled) {
+ do {
+ usleep_range(95, 105);
+ } while (atomic_read(&ep->sys->curr_polling_state));
+ }
+
+ if (IPA_CLIENT_IS_CONS(ep->client))
+ cancel_delayed_work_sync(&ep->sys->replenish_rx_work);
+ flush_workqueue(ep->sys->wq);
+
result = ipa3_reset_gsi_channel(clnt_hdl);
if (result != GSI_STATUS_SUCCESS) {
IPAERR("Failed to reset chan: %d.\n", result);
@@ -2935,7 +2939,6 @@
INIT_DELAYED_WORK(&sys->replenish_rx_work,
ipa3_replenish_rx_work_func);
atomic_set(&sys->curr_polling_state, 0);
- sys->rx_buff_sz = IPA_ODU_RX_BUFF_SZ;
sys->rx_pool_sz = in->desc_fifo_sz /
IPA_FIFO_ELEMENT_SIZE - 1;
if (sys->rx_pool_sz > IPA_ODU_RX_POOL_SZ)
@@ -2943,8 +2946,23 @@
sys->pyld_hdlr = ipa3_odu_rx_pyld_hdlr;
sys->get_skb = ipa3_get_skb_ipa_rx;
sys->free_skb = ipa3_free_skb_rx;
- sys->free_rx_wrapper = ipa3_free_rx_wrapper;
- sys->repl_hdlr = ipa3_replenish_rx_cache;
+ /* recycle skb for GSB use case */
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+ sys->free_rx_wrapper =
+ ipa3_free_rx_wrapper;
+ sys->repl_hdlr =
+ ipa3_replenish_rx_cache;
+ /* Overwrite buffer size & aggr limit for GSB */
+ sys->rx_buff_sz = IPA_GENERIC_RX_BUFF_SZ(
+ IPA_GSB_RX_BUFF_BASE_SZ);
+ in->ipa_ep_cfg.aggr.aggr_byte_limit =
+ IPA_GSB_AGGR_BYTE_LIMIT;
+ } else {
+ sys->free_rx_wrapper =
+ ipa3_free_rx_wrapper;
+ sys->repl_hdlr = ipa3_replenish_rx_cache;
+ sys->rx_buff_sz = IPA_ODU_RX_BUFF_SZ;
+ }
} else if (in->client ==
IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS) {
IPADBG("assigning policy to client:%d",
@@ -3683,8 +3701,13 @@
ep->gsi_mem_info.chan_ring_base_vaddr =
gsi_channel_props.ring_base_vaddr;
- gsi_channel_props.use_db_eng = GSI_CHAN_DB_MODE;
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+ gsi_channel_props.use_db_eng = GSI_CHAN_DIRECT_MODE;
+ else
+ gsi_channel_props.use_db_eng = GSI_CHAN_DB_MODE;
gsi_channel_props.max_prefetch = GSI_ONE_PREFETCH_SEG;
+ gsi_channel_props.prefetch_mode =
+ ipa_get_ep_prefetch_mode(ep->client);
if (ep->client == IPA_CLIENT_APPS_CMD_PROD)
gsi_channel_props.low_weight = IPA_GSI_MAX_CH_LOW_WEIGHT;
else
@@ -3923,7 +3946,12 @@
dma_alloc_coherent(ipa3_ctx->pdev, gsi_channel_props.ring_len,
&dma_addr, 0);
gsi_channel_props.ring_base_addr = dma_addr;
- gsi_channel_props.use_db_eng = GSI_CHAN_DB_MODE;
+
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+ gsi_channel_props.use_db_eng = GSI_CHAN_DIRECT_MODE;
+ else
+ gsi_channel_props.use_db_eng = GSI_CHAN_DB_MODE;
+
gsi_channel_props.max_prefetch = GSI_ONE_PREFETCH_SEG;
gsi_channel_props.low_weight = 1;
gsi_channel_props.err_cb = ipa_gsi_chan_err_cb;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dtsi_replacement.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dtsi_replacement.c
new file mode 100644
index 0000000..5fe2294
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dtsi_replacement.c
@@ -0,0 +1,765 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/msm_ipa.h>
+#include "ipa_i.h"
+#include "ipa_emulation_stubs.h"
+
+# undef strsame
+# define strsame(x, y) \
+ (!strcmp((x), (y)))
+
+/*
+ * The following enum values used to index tables below.
+ */
+enum dtsi_index_e {
+ DTSI_INDEX_3_5_1 = 0,
+ DTSI_INDEX_4_0 = 1,
+};
+
+struct dtsi_replacement_u32 {
+ char *key;
+ u32 value;
+};
+
+struct dtsi_replacement_u32_table {
+ struct dtsi_replacement_u32 *p_table;
+ u32 num_entries;
+};
+
+struct dtsi_replacement_bool {
+ char *key;
+ bool value;
+};
+
+struct dtsi_replacement_bool_table {
+ struct dtsi_replacement_bool *p_table;
+ u32 num_entries;
+};
+
+struct dtsi_replacement_u32_array {
+ char *key;
+ u32 *p_value;
+ u32 num_elements;
+};
+
+struct dtsi_replacement_u32_array_table {
+ struct dtsi_replacement_u32_array *p_table;
+ u32 num_entries;
+};
+
+struct dtsi_replacement_resource_table {
+ struct resource *p_table;
+ u32 num_entries;
+};
+
+/*
+ * Any of the data below with _4_0 in the name represent data taken
+ * from the 4.0 dtsi file.
+ *
+ * Any of the data below with _3_5_1 in the name represent data taken
+ * from the 3.5.1 dtsi file.
+ */
+static struct dtsi_replacement_bool ipa3_plat_drv_bool_4_0[] = {
+ {"qcom,use-ipa-tethering-bridge", true},
+ {"qcom,modem-cfg-emb-pipe-flt", true},
+ {"qcom,ipa-wdi2", true},
+ {"qcom,use-64-bit-dma-mask", false},
+ {"qcom,bandwidth-vote-for-ipa", false},
+ {"qcom,skip-uc-pipe-reset", false},
+ {"qcom,tethered-flow-control", true},
+ {"qcom,use-rg10-limitation-mitigation", false},
+ {"qcom,do-not-use-ch-gsi-20", false},
+ {"qcom,use-ipa-pm", false},
+};
+
+static struct dtsi_replacement_bool ipa3_plat_drv_bool_3_5_1[] = {
+ {"qcom,use-ipa-tethering-bridge", true},
+ {"qcom,modem-cfg-emb-pipe-flt", true},
+ {"qcom,ipa-wdi2", true},
+ {"qcom,use-64-bit-dma-mask", false},
+ {"qcom,bandwidth-vote-for-ipa", true},
+ {"qcom,skip-uc-pipe-reset", false},
+ {"qcom,tethered-flow-control", false},
+ {"qcom,use-rg10-limitation-mitigation", false},
+ {"qcom,do-not-use-ch-gsi-20", false},
+ {"qcom,use-ipa-pm", false},
+};
+
+static struct dtsi_replacement_bool_table
+ipa3_plat_drv_bool_table[] = {
+ { ipa3_plat_drv_bool_3_5_1,
+ ARRAY_SIZE(ipa3_plat_drv_bool_3_5_1) },
+ { ipa3_plat_drv_bool_4_0,
+ ARRAY_SIZE(ipa3_plat_drv_bool_4_0) },
+};
+
+static struct dtsi_replacement_u32 ipa3_plat_drv_u32_4_0[] = {
+ {"qcom,ipa-hw-ver", IPA_HW_v4_0},
+ {"qcom,ipa-hw-mode", 3},
+ {"qcom,wan-rx-ring-size", 192},
+ {"qcom,lan-rx-ring-size", 192},
+ {"qcom,ee", 0},
+ {"emulator-bar0-offset", 0x01C00000},
+};
+
+static struct dtsi_replacement_u32 ipa3_plat_drv_u32_3_5_1[] = {
+ {"qcom,ipa-hw-ver", IPA_HW_v3_5_1},
+ {"qcom,ipa-hw-mode", 3},
+ {"qcom,wan-rx-ring-size", 192},
+ {"qcom,lan-rx-ring-size", 192},
+ {"qcom,ee", 0},
+ {"emulator-bar0-offset", 0x01C00000},
+};
+
+static struct dtsi_replacement_u32_table ipa3_plat_drv_u32_table[] = {
+ { ipa3_plat_drv_u32_3_5_1,
+ ARRAY_SIZE(ipa3_plat_drv_u32_3_5_1) },
+ { ipa3_plat_drv_u32_4_0,
+ ARRAY_SIZE(ipa3_plat_drv_u32_4_0) },
+};
+
+static u32 mhi_event_ring_id_limits_array_4_0[] = {
+ 9, 10
+};
+
+static u32 mhi_event_ring_id_limits_array_3_5_1[] = {
+ IPA_MHI_GSI_EVENT_RING_ID_START, IPA_MHI_GSI_EVENT_RING_ID_END
+};
+
+static u32 ipa_tz_unlock_reg_array_4_0[] = {
+ 0x04043583c, 0x00001000
+};
+
+static u32 ipa_tz_unlock_reg_array_3_5_1[] = {
+ 0x04043583c, 0x00001000
+};
+
+static u32 ipa_ram_mmap_array_4_0[] = {
+ 0x00000280, 0x00000000, 0x00000000, 0x00000288, 0x00000078,
+ 0x00004000, 0x00000308, 0x00000078, 0x00004000, 0x00000388,
+ 0x00000078, 0x00004000, 0x00000408, 0x00000078, 0x00004000,
+ 0x0000000F, 0x00000000, 0x00000007, 0x00000008, 0x0000000E,
+ 0x00000488, 0x00000078, 0x00004000, 0x00000508, 0x00000078,
+ 0x00004000, 0x0000000F, 0x00000000, 0x00000007, 0x00000008,
+ 0x0000000E, 0x00000588, 0x00000078, 0x00004000, 0x00000608,
+ 0x00000078, 0x00004000, 0x00000688, 0x00000140, 0x000007C8,
+ 0x00000000, 0x00000800, 0x000007D0, 0x00000200, 0x000009D0,
+ 0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x000013F0,
+ 0x0000100C, 0x000023FC, 0x00000000, 0x000023FC, 0x00000000,
+ 0x000023FC, 0x00000000, 0x000023FC, 0x00000000, 0x00000080,
+ 0x00000200, 0x00002800, 0x000023FC, 0x00000000, 0x000023FC,
+ 0x00000000, 0x000023FC, 0x00000000, 0x000023FC, 0x00000000,
+ 0x00002400, 0x00000400, 0x00000BD8, 0x00000050, 0x00000C30,
+ 0x00000060, 0x00000C90, 0x00000140, 0x00000DD0, 0x00000180,
+ 0x00000F50, 0x00000180, 0x000010D0, 0x00000180, 0x00001250,
+ 0x00000180, 0x000013D0, 0x00000020
+};
+
+static u32 ipa_ram_mmap_array_3_5_1[] = {
+ 0x00000280, 0x00000000, 0x00000000, 0x00000288, 0x00000078,
+ 0x00004000, 0x00000308, 0x00000078, 0x00004000, 0x00000388,
+ 0x00000078, 0x00004000, 0x00000408, 0x00000078, 0x00004000,
+ 0x0000000F, 0x00000000, 0x00000007, 0x00000008, 0x0000000E,
+ 0x00000488, 0x00000078, 0x00004000, 0x00000508, 0x00000078,
+ 0x00004000, 0x0000000F, 0x00000000, 0x00000007, 0x00000008,
+ 0x0000000E, 0x00000588, 0x00000078, 0x00004000, 0x00000608,
+ 0x00000078, 0x00004000, 0x00000688, 0x00000140, 0x000007C8,
+ 0x00000000, 0x00000800, 0x000007D0, 0x00000200, 0x000009D0,
+ 0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x00000BD8,
+ 0x00001024, 0x00002000, 0x00000000, 0x00002000, 0x00000000,
+ 0x00002000, 0x00000000, 0x00002000, 0x00000000, 0x00000080,
+ 0x00000200, 0x00002000, 0x00002000, 0x00000000, 0x00002000,
+ 0x00000000, 0x00002000, 0x00000000, 0x00002000, 0x00000000,
+ 0x00001C00, 0x00000400
+};
+
+struct dtsi_replacement_u32_array ipa3_plat_drv_u32_array_4_0[] = {
+ {"qcom,mhi-event-ring-id-limits",
+ mhi_event_ring_id_limits_array_4_0,
+ ARRAY_SIZE(mhi_event_ring_id_limits_array_4_0) },
+ {"qcom,ipa-tz-unlock-reg",
+ ipa_tz_unlock_reg_array_4_0,
+ ARRAY_SIZE(ipa_tz_unlock_reg_array_4_0) },
+ {"qcom,ipa-ram-mmap",
+ ipa_ram_mmap_array_4_0,
+ ARRAY_SIZE(ipa_ram_mmap_array_4_0) },
+};
+
+struct dtsi_replacement_u32_array ipa3_plat_drv_u32_array_3_5_1[] = {
+ {"qcom,mhi-event-ring-id-limits",
+ mhi_event_ring_id_limits_array_3_5_1,
+ ARRAY_SIZE(mhi_event_ring_id_limits_array_3_5_1) },
+ {"qcom,ipa-tz-unlock-reg",
+ ipa_tz_unlock_reg_array_3_5_1,
+ ARRAY_SIZE(ipa_tz_unlock_reg_array_3_5_1) },
+ {"qcom,ipa-ram-mmap",
+ ipa_ram_mmap_array_3_5_1,
+ ARRAY_SIZE(ipa_ram_mmap_array_3_5_1) },
+};
+
+struct dtsi_replacement_u32_array_table
+ipa3_plat_drv_u32_array_table[] = {
+ { ipa3_plat_drv_u32_array_3_5_1,
+ ARRAY_SIZE(ipa3_plat_drv_u32_array_3_5_1) },
+ { ipa3_plat_drv_u32_array_4_0,
+ ARRAY_SIZE(ipa3_plat_drv_u32_array_4_0) },
+};
+
+#define INTCTRL_OFFSET 0x083C0000
+#define INTCTRL_SIZE 0x00000110
+
+#define IPA_BASE_OFFSET_4_0 0x01e00000
+#define IPA_BASE_SIZE_4_0 0x00034000
+#define GSI_BASE_OFFSET_4_0 0x01e04000
+#define GSI_BASE_SIZE_4_0 0x00028000
+
+struct resource ipa3_plat_drv_resource_4_0[] = {
+ /*
+ * PLEASE NOTE WELL: The following offset values below
+ * ("ipa-base", "gsi-base", and "intctrl-base") are used to
+ * calculate offsets relative to the PCI BAR0 address provided
+ * by the PCI probe. After their use to calculate the
+ * offsets, they are not used again, since PCI ultimately
+ * dictates where things live.
+ */
+ {
+ IPA_BASE_OFFSET_4_0,
+ (IPA_BASE_OFFSET_4_0 + IPA_BASE_SIZE_4_0),
+ "ipa-base",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ GSI_BASE_OFFSET_4_0,
+ (GSI_BASE_OFFSET_4_0 + GSI_BASE_SIZE_4_0),
+ "gsi-base",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ /*
+ * The following entry is germane only to the emulator
+ * environment. It is needed to locate the emulator's PCI
+ * interrupt controller...
+ */
+ {
+ INTCTRL_OFFSET,
+ (INTCTRL_OFFSET + INTCTRL_SIZE),
+ "intctrl-base",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ IPA_PIPE_MEM_START_OFST,
+ (IPA_PIPE_MEM_START_OFST + IPA_PIPE_MEM_SIZE),
+ "ipa-pipe-mem",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ 0,
+ 0,
+ "gsi-irq",
+ IORESOURCE_IRQ,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ 0,
+ 0,
+ "ipa-irq",
+ IORESOURCE_IRQ,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+};
+
+#define IPA_BASE_OFFSET_3_5_1 0x01e00000
+#define IPA_BASE_SIZE_3_5_1 0x00034000
+#define GSI_BASE_OFFSET_3_5_1 0x01e04000
+#define GSI_BASE_SIZE_3_5_1 0x0002c000
+
+struct resource ipa3_plat_drv_resource_3_5_1[] = {
+ /*
+ * PLEASE NOTE WELL: The following offset values below
+ * ("ipa-base", "gsi-base", and "intctrl-base") are used to
+ * calculate offsets relative to the PCI BAR0 address provided
+ * by the PCI probe. After their use to calculate the
+ * offsets, they are not used again, since PCI ultimately
+ * dictates where things live.
+ */
+ {
+ IPA_BASE_OFFSET_3_5_1,
+ (IPA_BASE_OFFSET_3_5_1 + IPA_BASE_SIZE_3_5_1),
+ "ipa-base",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ GSI_BASE_OFFSET_3_5_1,
+ (GSI_BASE_OFFSET_3_5_1 + GSI_BASE_SIZE_3_5_1),
+ "gsi-base",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ /*
+ * The following entry is germane only to the emulator
+ * environment. It is needed to locate the emulator's PCI
+ * interrupt controller...
+ */
+ {
+ INTCTRL_OFFSET,
+ (INTCTRL_OFFSET + INTCTRL_SIZE),
+ "intctrl-base",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ IPA_PIPE_MEM_START_OFST,
+ (IPA_PIPE_MEM_START_OFST + IPA_PIPE_MEM_SIZE),
+ "ipa-pipe-mem",
+ IORESOURCE_MEM,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ 0,
+ 0,
+ "gsi-irq",
+ IORESOURCE_IRQ,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+
+ {
+ 0,
+ 0,
+ "ipa-irq",
+ IORESOURCE_IRQ,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+};
+
+struct dtsi_replacement_resource_table
+ipa3_plat_drv_resource_table[] = {
+ { ipa3_plat_drv_resource_3_5_1,
+ ARRAY_SIZE(ipa3_plat_drv_resource_3_5_1) },
+ { ipa3_plat_drv_resource_4_0,
+ ARRAY_SIZE(ipa3_plat_drv_resource_4_0) },
+};
+
+/*
+ * The following code uses the data above...
+ */
+static u32 emulator_type_to_index(void)
+{
+ /*
+ * Use the input parameter to the IPA driver loadable module,
+ * which specifies the type of hardware the driver is running
+ * on.
+ */
+ u32 index = DTSI_INDEX_4_0;
+ uint emulation_type = ipa3_get_emulation_type();
+
+ switch (emulation_type) {
+ case IPA_HW_v3_5_1:
+ index = DTSI_INDEX_3_5_1;
+ break;
+ case IPA_HW_v4_0:
+ index = DTSI_INDEX_4_0;
+ break;
+ default:
+ break;
+ }
+
+ IPADBG("emulation_type(%u) emulation_index(%u)\n",
+ emulation_type, index);
+
+ return index;
+}
+
+/* From include/linux/of.h */
+/**
+ * emulator_of_property_read_bool - Find from a property
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device node.
+ * Returns true if the property exists false otherwise.
+ */
+bool emulator_of_property_read_bool(
+ const struct device_node *np,
+ const char *propname)
+{
+ u16 i;
+ u32 index;
+ struct dtsi_replacement_bool *ipa3_plat_drv_boolP;
+
+ /*
+ * Get the index for the type of hardware we're running on.
+ * This is used as a table index.
+ */
+ index = emulator_type_to_index();
+ if (index >= ARRAY_SIZE(ipa3_plat_drv_bool_table)) {
+ IPADBG(
+ "Did not find ipa3_plat_drv_bool_table for index %u\n",
+ index);
+ return false;
+ }
+
+ ipa3_plat_drv_boolP =
+ ipa3_plat_drv_bool_table[index].p_table;
+
+ for (i = 0;
+ i < ipa3_plat_drv_bool_table[index].num_entries;
+ i++) {
+ if (strsame(ipa3_plat_drv_boolP[i].key, propname)) {
+ IPADBG(
+ "Found value %u for propname %s index %u\n",
+ ipa3_plat_drv_boolP[i].value,
+ propname,
+ index);
+ return ipa3_plat_drv_boolP[i].value;
+ }
+ }
+
+ IPADBG("Did not find match for propname %s index %u\n",
+ propname,
+ index);
+
+ return false;
+}
+
+/* From include/linux/of.h */
+int emulator_of_property_read_u32(
+ const struct device_node *np,
+ const char *propname,
+ u32 *out_value)
+{
+ u16 i;
+ u32 index;
+ struct dtsi_replacement_u32 *ipa3_plat_drv_u32P;
+
+ /*
+ * Get the index for the type of hardware we're running on.
+ * This is used as a table index.
+ */
+ index = emulator_type_to_index();
+ if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_table)) {
+ IPADBG(
+ "Did not find ipa3_plat_drv_u32_table for index %u\n",
+ index);
+ return false;
+ }
+
+ ipa3_plat_drv_u32P =
+ ipa3_plat_drv_u32_table[index].p_table;
+
+ for (i = 0;
+ i < ipa3_plat_drv_u32_table[index].num_entries;
+ i++) {
+ if (strsame(ipa3_plat_drv_u32P[i].key, propname)) {
+ *out_value = ipa3_plat_drv_u32P[i].value;
+ IPADBG(
+ "Found value %u for propname %s index %u\n",
+ ipa3_plat_drv_u32P[i].value,
+ propname,
+ index);
+ return 0;
+ }
+ }
+
+ IPADBG("Did not find match for propname %s index %u\n",
+ propname,
+ index);
+
+ return -EINVAL;
+}
+
+/* From include/linux/of.h */
+/**
+ * emulator_of_property_read_u32_array - Find and read an array of 32
+ * bit integers from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int emulator_of_property_read_u32_array(
+ const struct device_node *np,
+ const char *propname,
+ u32 *out_values,
+ size_t sz)
+{
+ u16 i;
+ u32 index;
+ struct dtsi_replacement_u32_array *u32_arrayP;
+
+ /*
+ * Get the index for the type of hardware we're running on.
+ * This is used as a table index.
+ */
+ index = emulator_type_to_index();
+ if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_array_table)) {
+ IPADBG(
+ "Did not find ipa3_plat_drv_u32_array_table for index %u\n",
+ index);
+ return false;
+ }
+
+ u32_arrayP =
+ ipa3_plat_drv_u32_array_table[index].p_table;
+ for (i = 0;
+ i < ipa3_plat_drv_u32_array_table[index].num_entries;
+ i++) {
+ if (strsame(
+ u32_arrayP[i].key, propname)) {
+ u32 num_elements =
+ u32_arrayP[i].num_elements;
+ u32 *p_element =
+ &u32_arrayP[i].p_value[0];
+ size_t j = 0;
+
+ if (num_elements > sz) {
+ IPAERR(
+ "Found array of %u values for propname %s; only room for %u elements in copy buffer\n",
+ num_elements,
+ propname,
+ (unsigned int) sz);
+ return -EOVERFLOW;
+ }
+
+ while (j++ < num_elements)
+ *out_values++ = *p_element++;
+
+ IPADBG(
+ "Found array of values starting with %u for propname %s index %u\n",
+ u32_arrayP[i].p_value[0],
+ propname,
+ index);
+
+ return 0;
+ }
+ }
+
+ IPADBG("Did not find match for propname %s index %u\n",
+ propname,
+ index);
+
+ return -EINVAL;
+}
+
+/* From drivers/base/platform.c */
+/**
+ * emulator_platform_get_resource_byname - get a resource for a device by name
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
+ */
+struct resource *emulator_platform_get_resource_byname(
+ struct platform_device *dev,
+ unsigned int type,
+ const char *name)
+{
+ u16 i;
+ u32 index;
+ struct resource *ipa3_plat_drv_resourceP;
+
+ /*
+ * Get the index for the type of hardware we're running on.
+ * This is used as a table index.
+ */
+ index = emulator_type_to_index();
+ if (index >= ARRAY_SIZE(ipa3_plat_drv_resource_table)) {
+ IPADBG(
+ "Did not find ipa3_plat_drv_resource_table for index %u\n",
+ index);
+ return false;
+ }
+
+ ipa3_plat_drv_resourceP =
+ ipa3_plat_drv_resource_table[index].p_table;
+ for (i = 0;
+ i < ipa3_plat_drv_resource_table[index].num_entries;
+ i++) {
+ struct resource *r = &ipa3_plat_drv_resourceP[i];
+
+ if (type == resource_type(r) && strsame(r->name, name)) {
+ IPADBG(
+ "Found start 0x%x size %u for name %s index %u\n",
+ (unsigned int) (r->start),
+ (unsigned int) (resource_size(r)),
+ name,
+ index);
+ return r;
+ }
+ }
+
+ IPADBG("Did not find match for name %s index %u\n",
+ name,
+ index);
+
+ return NULL;
+}
+
+/* From drivers/of/base.c */
+/**
+ * emulator_of_property_count_elems_of_size - Count the number of
+ * elements in a property
+ *
+ * @np: device node from which the property value is to
+ * be read. Not used.
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property and count the number of elements of size
+ * elem_size in it. Returns number of elements on success, -EINVAL if
+ * the property does not exist or its length does not match a multiple
+ * of elem_size and -ENODATA if the property does not have a value.
+ */
+int emulator_of_property_count_elems_of_size(
+ const struct device_node *np,
+ const char *propname,
+ int elem_size)
+{
+ u32 index;
+
+ /*
+ * Get the index for the type of hardware we're running on.
+ * This is used as a table index.
+ */
+ index = emulator_type_to_index();
+
+ /*
+ * Use elem_size to determine which table to search for the
+ * specified property name
+ */
+ if (elem_size == sizeof(u32)) {
+ u16 i;
+ struct dtsi_replacement_u32_array *u32_arrayP;
+
+ if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_array_table)) {
+ IPADBG(
+ "Did not find ipa3_plat_drv_u32_array_table for index %u\n",
+ index);
+ return false;
+ }
+
+ u32_arrayP =
+ ipa3_plat_drv_u32_array_table[index].p_table;
+
+ for (i = 0;
+ i < ipa3_plat_drv_u32_array_table[index].num_entries;
+ i++) {
+ if (strsame(u32_arrayP[i].key, propname)) {
+ if (u32_arrayP[i].p_value == NULL) {
+ IPADBG(
+ "Found no elements for propname %s index %u\n",
+ propname,
+ index);
+ return -ENODATA;
+ }
+
+ IPADBG(
+ "Found %u elements for propname %s index %u\n",
+ u32_arrayP[i].num_elements,
+ propname,
+ index);
+
+ return u32_arrayP[i].num_elements;
+ }
+ }
+
+ IPADBG(
+ "Found no match in table with elem_size %d for propname %s index %u\n",
+ elem_size,
+ propname,
+ index);
+
+ return -EINVAL;
+ }
+
+ IPAERR(
+ "Found no tables with element size %u to search for propname %s index %u\n",
+ elem_size,
+ propname,
+ index);
+
+ return -EINVAL;
+}
+
+int emulator_of_property_read_variable_u32_array(
+ const struct device_node *np,
+ const char *propname,
+ u32 *out_values,
+ size_t sz_min,
+ size_t sz_max)
+{
+ return emulator_of_property_read_u32_array(
+ np, propname, out_values, sz_max);
+}
+
+resource_size_t emulator_resource_size(const struct resource *res)
+{
+ return res->end - res->start;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_emulation_stubs.h b/drivers/platform/msm/ipa/ipa_v3/ipa_emulation_stubs.h
new file mode 100644
index 0000000..cf4c7c9
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_emulation_stubs.h
@@ -0,0 +1,125 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#if !defined(_IPA_EMULATION_STUBS_H_)
+# define _IPA_EMULATION_STUBS_H_
+
+# define clk_get(x, y) ((struct clk *) -(MAX_ERRNO+1))
+# define clk_put(x) do { } while (0)
+# define clk_prepare(x) do { } while (0)
+# define clk_enable(x) do { } while (0)
+# define clk_set_rate(x, y) do { } while (0)
+# define clk_disable_unprepare(x) do { } while (0)
+
+# define outer_flush_range(x, y)
+# define __flush_dcache_area(x, y)
+# define __cpuc_flush_dcache_area(x, y) __flush_dcache_area(x, y)
+
+/* Point several API calls to these new EMULATION functions */
+# define of_property_read_bool(np, propname) \
+ emulator_of_property_read_bool(NULL, propname)
+# define of_property_read_u32(np, propname, out_value) \
+ emulator_of_property_read_u32(NULL, propname, out_value)
+# define of_property_read_u32_array(np, propname, out_values, sz) \
+ emulator_of_property_read_u32_array(NULL, propname, out_values, sz)
+# define platform_get_resource_byname(dev, type, name) \
+ emulator_platform_get_resource_byname(NULL, type, name)
+# define of_property_count_elems_of_size(np, propname, elem_size) \
+ emulator_of_property_count_elems_of_size(NULL, propname, elem_size)
+# define of_property_read_variable_u32_array( \
+ np, propname, out_values, sz_min, sz_max) \
+ emulator_of_property_read_variable_u32_array( \
+ NULL, propname, out_values, sz_min, sz_max)
+# define resource_size(res) \
+ emulator_resource_size(res)
+
+/**
+ * emulator_of_property_read_bool - Findfrom a property
+ * @np: device node used to find the property value. (not used)
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device node.
+ * Returns true if the property exists false otherwise.
+ */
+bool emulator_of_property_read_bool(
+ const struct device_node *np,
+ const char *propname);
+
+int emulator_of_property_read_u32(
+ const struct device_node *np,
+ const char *propname,
+ u32 *out_value);
+
+/**
+ * emulator_of_property_read_u32_array - Find and read an array of 32
+ * bit integers from a property.
+ *
+ * @np: device node used to find the property value. (not used)
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int emulator_of_property_read_u32_array(
+ const struct device_node *np,
+ const char *propname,
+ u32 *out_values,
+ size_t sz);
+
+/**
+ * emulator_platform_get_resource_byname - get a resource for a device
+ * by name
+ *
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
+ */
+struct resource *emulator_platform_get_resource_byname(
+ struct platform_device *dev,
+ unsigned int type,
+ const char *name);
+
+/**
+ * emulator_of_property_count_elems_of_size - Count the number of
+ * elements in a property
+ *
+ * @np: device node used to find the property value. (not used)
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property and count the number of elements of size
+ * elem_size in it. Returns number of elements on success, -EINVAL if
+ * the property does not exist or its length does not match a multiple
+ * of elem_size and -ENODATA if the property does not have a value.
+ */
+int emulator_of_property_count_elems_of_size(
+ const struct device_node *np,
+ const char *propname,
+ int elem_size);
+
+int emulator_of_property_read_variable_u32_array(
+ const struct device_node *np,
+ const char *propname,
+ u32 *out_values,
+ size_t sz_min,
+ size_t sz_max);
+
+resource_size_t emulator_resource_size(
+ const struct resource *res);
+
+#endif /* #if !defined(_IPA_EMULATION_STUBS_H_) */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index 29fd547..b090151 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -61,8 +61,10 @@
gen_params.rule = (const struct ipa_flt_rule *)&entry->rule;
res = ipahal_flt_generate_hw_rule(&gen_params, &entry->hw_len, buf);
- if (res)
+ if (res) {
IPAERR_RL("failed to generate flt h/w rule\n");
+ return res;
+ }
return 0;
}
@@ -1012,41 +1014,12 @@
goto error;
}
+ if (__ipa_validate_flt_rule(&frule->rule, &rt_tbl, ip))
+ goto error;
+
if (entry->rt_tbl)
entry->rt_tbl->ref_cnt--;
- if (frule->rule.action != IPA_PASS_TO_EXCEPTION) {
- if (!frule->rule.eq_attrib_type) {
- if (!frule->rule.rt_tbl_hdl) {
- IPAERR_RL("invalid RT tbl\n");
- goto error;
- }
-
- rt_tbl = ipa3_id_find(frule->rule.rt_tbl_hdl);
- if (rt_tbl == NULL) {
- IPAERR_RL("RT tbl not found\n");
- goto error;
- }
-
- if (rt_tbl->cookie != IPA_RT_TBL_COOKIE) {
- IPAERR_RL("RT table cookie is invalid\n");
- goto error;
- }
- } else {
- if (frule->rule.rt_tbl_idx > ((ip == IPA_IP_v4) ?
- IPA_MEM_PART(v4_modem_rt_index_hi) :
- IPA_MEM_PART(v6_modem_rt_index_hi))) {
- IPAERR_RL("invalid RT tbl\n");
- goto error;
- }
- }
- } else {
- if (frule->rule.rt_tbl_idx > 0) {
- IPAERR_RL("invalid RT tbl\n");
- goto error;
- }
- }
-
entry->rule = frule->rule;
entry->rt_tbl = rt_tbl;
if (entry->rt_tbl)
@@ -1445,8 +1418,16 @@
}
}
}
- mutex_unlock(&ipa3_ctx->lock);
+ /* commit the change to IPA-HW */
+ if (ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v4) ||
+ ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v6)) {
+ IPAERR("fail to commit flt-rule\n");
+ WARN_ON_RATELIMIT_IPA(1);
+ mutex_unlock(&ipa3_ctx->lock);
+ return -EPERM;
+ }
+ mutex_unlock(&ipa3_ctx->lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 757f420..4eb8edb 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -657,7 +657,6 @@
return 0;
}
-
int __ipa3_del_hdr(u32 hdr_hdl, bool by_user)
{
struct ipa3_hdr_entry *entry;
@@ -997,6 +996,8 @@
struct ipa_hdr_offset_entry *off_next;
struct ipa3_hdr_proc_ctx_offset_entry *ctx_off_entry;
struct ipa3_hdr_proc_ctx_offset_entry *ctx_off_next;
+ struct ipa3_hdr_tbl *htbl = &ipa3_ctx->hdr_tbl;
+ struct ipa3_hdr_proc_ctx_tbl *htbl_proc = &ipa3_ctx->hdr_proc_ctx_tbl;
int i;
/*
@@ -1042,8 +1043,15 @@
entry->hdr_len,
DMA_TO_DEVICE);
entry->proc_ctx = NULL;
+ } else {
+ /* move the offset entry to free list */
+ entry->offset_entry->ipacm_installed = 0;
+ list_move(&entry->offset_entry->link,
+ &htbl->head_free_offset_list[
+ entry->offset_entry->bin]);
}
list_del(&entry->link);
+ htbl->hdr_cnt--;
entry->ref_cnt = 0;
entry->cookie = 0;
@@ -1052,40 +1060,35 @@
kmem_cache_free(ipa3_ctx->hdr_cache, entry);
}
}
- for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
- list_for_each_entry_safe(off_entry, off_next,
+
+ /* only clean up offset_list and free_offset_list on global reset */
+ if (!user_only) {
+ for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
+ list_for_each_entry_safe(off_entry, off_next,
&ipa3_ctx->hdr_tbl.head_offset_list[i],
link) {
-
- /*
- * do not remove the default exception header which is
- * at offset 0
- */
- if (off_entry->offset == 0)
- continue;
-
- if (!user_only ||
- off_entry->ipacm_installed) {
+ /**
+ * do not remove the default exception
+ * header which is at offset 0
+ */
+ if (off_entry->offset == 0)
+ continue;
list_del(&off_entry->link);
kmem_cache_free(ipa3_ctx->hdr_offset_cache,
off_entry);
}
- }
- list_for_each_entry_safe(off_entry, off_next,
+ list_for_each_entry_safe(off_entry, off_next,
&ipa3_ctx->hdr_tbl.head_free_offset_list[i],
link) {
-
- if (!user_only ||
- off_entry->ipacm_installed) {
list_del(&off_entry->link);
kmem_cache_free(ipa3_ctx->hdr_offset_cache,
off_entry);
}
}
+ /* there is one header of size 8 */
+ ipa3_ctx->hdr_tbl.end = 8;
+ ipa3_ctx->hdr_tbl.hdr_cnt = 1;
}
- /* there is one header of size 8 */
- ipa3_ctx->hdr_tbl.end = 8;
- ipa3_ctx->hdr_tbl.hdr_cnt = 1;
IPADBG("reset hdr proc ctx\n");
list_for_each_entry_safe(
@@ -1102,7 +1105,12 @@
if (!user_only ||
ctx_entry->ipacm_installed) {
+ /* move the offset entry to appropriate free list */
+ list_move(&ctx_entry->offset_entry->link,
+ &htbl_proc->head_free_offset_list[
+ ctx_entry->offset_entry->bin]);
list_del(&ctx_entry->link);
+ htbl_proc->proc_ctx_cnt--;
ctx_entry->ref_cnt = 0;
ctx_entry->cookie = 0;
@@ -1112,36 +1120,39 @@
ctx_entry);
}
}
- for (i = 0; i < IPA_HDR_PROC_CTX_BIN_MAX; i++) {
- list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
+ /* only clean up offset_list and free_offset_list on global reset */
+ if (!user_only) {
+ for (i = 0; i < IPA_HDR_PROC_CTX_BIN_MAX; i++) {
+ list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
&ipa3_ctx->hdr_proc_ctx_tbl.head_offset_list[i],
link) {
-
- if (!user_only ||
- ctx_off_entry->ipacm_installed) {
+ list_del(&ctx_off_entry->link);
+ kmem_cache_free(
+ ipa3_ctx->hdr_proc_ctx_offset_cache,
+ ctx_off_entry);
+ }
+ list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
+ &ipa3_ctx->hdr_proc_ctx_tbl.
+ head_free_offset_list[i], link) {
list_del(&ctx_off_entry->link);
kmem_cache_free(
ipa3_ctx->hdr_proc_ctx_offset_cache,
ctx_off_entry);
}
}
- list_for_each_entry_safe(ctx_off_entry, ctx_off_next,
- &ipa3_ctx->hdr_proc_ctx_tbl.head_free_offset_list[i],
- link) {
-
- if (!user_only ||
- ctx_off_entry->ipacm_installed) {
- list_del(&ctx_off_entry->link);
- kmem_cache_free(
- ipa3_ctx->hdr_proc_ctx_offset_cache,
- ctx_off_entry);
- }
- }
+ ipa3_ctx->hdr_proc_ctx_tbl.end = 0;
+ ipa3_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt = 0;
}
- ipa3_ctx->hdr_proc_ctx_tbl.end = 0;
- ipa3_ctx->hdr_proc_ctx_tbl.proc_ctx_cnt = 0;
- mutex_unlock(&ipa3_ctx->lock);
+ /* commit the change to IPA-HW */
+ if (ipa3_ctx->ctrl->ipa3_commit_hdr()) {
+ IPAERR("fail to commit hdr\n");
+ WARN_ON_RATELIMIT_IPA(1);
+ mutex_unlock(&ipa3_ctx->lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&ipa3_ctx->lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 08263b5..c860bd0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -203,6 +203,188 @@
#define IPA_WDI_TX_DB_RES 7
#define IPA_WDI_MAX_RES 8
+#ifdef CONFIG_ARM64
+/* Outer caches unsupported on ARM64 platforms */
+# define outer_flush_range(x, y)
+# define __cpuc_flush_dcache_area __flush_dcache_area
+#endif
+
+#define IPA_GPIO_IN_QUERY_CLK_IDX 0
+#define IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX 0
+#define IPA_GPIO_OUT_CLK_VOTE_IDX 1
+
+#define IPA_SUMMING_THRESHOLD (0x10)
+#define IPA_PIPE_MEM_START_OFST (0x0)
+#define IPA_PIPE_MEM_SIZE (0x0)
+#define IPA_MOBILE_AP_MODE(x) (x == IPA_MODE_MOBILE_AP_ETH || \
+ x == IPA_MODE_MOBILE_AP_WAN || \
+ x == IPA_MODE_MOBILE_AP_WLAN)
+#define IPA_CNOC_CLK_RATE (75 * 1000 * 1000UL)
+#define IPA_A5_MUX_HEADER_LENGTH (8)
+
+#define IPA_AGGR_MAX_STR_LENGTH (10)
+
+#define CLEANUP_TAG_PROCESS_TIMEOUT 500
+
+#define IPA_AGGR_STR_IN_BYTES(str) \
+ (strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
+
+#define IPA_TRANSPORT_PROD_TIMEOUT_MSEC 100
+
+#define IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE 2048
+
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_EP 0
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SIMPLE 1
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
+
+#define IPA_MHI_GSI_EVENT_RING_ID_START 10
+#define IPA_MHI_GSI_EVENT_RING_ID_END 12
+
+#define IPA_SMEM_SIZE (8 * 1024)
+
+#define IPA_GSI_CHANNEL_HALT_MIN_SLEEP 5000
+#define IPA_GSI_CHANNEL_HALT_MAX_SLEEP 10000
+#define IPA_GSI_CHANNEL_HALT_MAX_TRY 10
+
+/* round addresses for closes page per SMMU requirements */
+#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
+ do { \
+ (iova_p) = rounddown((iova), PAGE_SIZE); \
+ (pa_p) = rounddown((pa), PAGE_SIZE); \
+ (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
+ } while (0)
+
+
+/* The relative location in /lib/firmware where the FWs will reside */
+#define IPA_FWS_PATH "ipa/ipa_fws.elf"
+/*
+ * The following paths below are used when building the system for the
+ * emulation environment or when IPA_EMULATION_COMPILE == 1.
+ *
+ * As new hardware platforms are added into the emulation environment,
+ * please add the appropriate paths here for their firmwares.
+ */
+#define IPA_FWS_PATH_4_0 "ipa/4.0/ipa_fws.elf"
+#define IPA_FWS_PATH_3_5_1 "ipa/3.5.1/ipa_fws.elf"
+
+#ifdef CONFIG_COMPAT
+#define IPA_IOC_ADD_HDR32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ADD_HDR, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_HDR32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_HDR, \
+ compat_uptr_t)
+#define IPA_IOC_ADD_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ADD_RT_RULE, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_RT_RULE, \
+ compat_uptr_t)
+#define IPA_IOC_ADD_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ADD_FLT_RULE, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_FLT_RULE, \
+ compat_uptr_t)
+#define IPA_IOC_GET_RT_TBL32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GET_RT_TBL, \
+ compat_uptr_t)
+#define IPA_IOC_COPY_HDR32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_COPY_HDR, \
+ compat_uptr_t)
+#define IPA_IOC_QUERY_INTF32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_QUERY_INTF, \
+ compat_uptr_t)
+#define IPA_IOC_QUERY_INTF_TX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_QUERY_INTF_TX_PROPS, \
+ compat_uptr_t)
+#define IPA_IOC_QUERY_INTF_RX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_QUERY_INTF_RX_PROPS, \
+ compat_uptr_t)
+#define IPA_IOC_QUERY_INTF_EXT_PROPS32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_QUERY_INTF_EXT_PROPS, \
+ compat_uptr_t)
+#define IPA_IOC_GET_HDR32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GET_HDR, \
+ compat_uptr_t)
+#define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_NAT_MEM, \
+ compat_uptr_t)
+#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_NAT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_ALLOC_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_V4_INIT_NAT, \
+ compat_uptr_t)
+#define IPA_IOC_INIT_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_INIT_IPV6CT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_TABLE_DMA_CMD32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_TABLE_DMA_CMD, \
+ compat_uptr_t)
+#define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_V4_DEL_NAT, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_NAT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_IPV6CT_TABLE, \
+ compat_uptr_t)
+#define IPA_IOC_NAT_MODIFY_PDN32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_NAT_MODIFY_PDN, \
+ compat_uptr_t)
+#define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GET_NAT_OFFSET, \
+ compat_uptr_t)
+#define IPA_IOC_PULL_MSG32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_PULL_MSG, \
+ compat_uptr_t)
+#define IPA_IOC_RM_ADD_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_RM_ADD_DEPENDENCY, \
+ compat_uptr_t)
+#define IPA_IOC_RM_DEL_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_RM_DEL_DEPENDENCY, \
+ compat_uptr_t)
+#define IPA_IOC_GENERATE_FLT_EQ32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GENERATE_FLT_EQ, \
+ compat_uptr_t)
+#define IPA_IOC_QUERY_RT_TBL_INDEX32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_QUERY_RT_TBL_INDEX, \
+ compat_uptr_t)
+#define IPA_IOC_WRITE_QMAPID32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_WRITE_QMAPID, \
+ compat_uptr_t)
+#define IPA_IOC_MDFY_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_MDFY_FLT_RULE, \
+ compat_uptr_t)
+#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD, \
+ compat_uptr_t)
+#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL, \
+ compat_uptr_t)
+#define IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED, \
+ compat_uptr_t)
+#define IPA_IOC_ADD_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ADD_HDR_PROC_CTX, \
+ compat_uptr_t)
+#define IPA_IOC_DEL_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_HDR_PROC_CTX, \
+ compat_uptr_t)
+#define IPA_IOC_MDFY_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_MDFY_RT_RULE, \
+ compat_uptr_t)
+#endif /* #ifdef CONFIG_COMPAT */
+
+#define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
+#define TZ_MEM_PROTECT_REGION_ID 0x10
+
struct ipa3_active_client_htable_entry {
struct hlist_node list;
char id_string[IPA3_ACTIVE_CLIENTS_LOG_NAME_LEN];
@@ -558,6 +740,8 @@
* @qmi_request_sent: Indicates whether QMI request to enable clear data path
* request is sent or not.
* @napi_enabled: when true, IPA call client callback to start polling
+ * @client_lock_unlock: callback function to take mutex lock/unlock for USB
+ * clients
*/
struct ipa3_ep_context {
int valid;
@@ -590,6 +774,8 @@
u32 eot_in_poll_err;
bool ep_delay_set;
+ int (*client_lock_unlock)(bool is_lock);
+
/* sys MUST be the last element of this struct */
struct ipa3_sys_context *sys;
};
@@ -912,11 +1098,13 @@
* @IPA_HW_Normal: Regular IPA hardware
* @IPA_HW_Virtual: IPA hardware supporting virtual memory allocation
* @IPA_HW_PCIE: IPA hardware supporting memory allocation over PCIE Bridge
+ * @IPA_HW_Emulation: IPA emulation hardware
*/
enum ipa3_hw_mode {
- IPA_HW_MODE_NORMAL = 0,
- IPA_HW_MODE_VIRTUAL = 1,
- IPA_HW_MODE_PCIE = 2
+ IPA_HW_MODE_NORMAL = 0,
+ IPA_HW_MODE_VIRTUAL = 1,
+ IPA_HW_MODE_PCIE = 2,
+ IPA_HW_MODE_EMULATION = 3,
};
enum ipa3_config_this_ep {
@@ -1207,6 +1395,7 @@
* @mode: IPA operating mode
* @mmio: iomem
* @ipa_wrapper_base: IPA wrapper base address
+ * @ipa_wrapper_size: size of the memory pointed to by ipa_wrapper_base
* @hdr_tbl: IPA header table
* @hdr_proc_ctx_tbl: IPA processing context table
* @rt_tbl_set: list of routing tables each of which is a list of rules
@@ -1423,6 +1612,9 @@
u32 ipa_mem_size;
u32 transport_mem_base;
u32 transport_mem_size;
+ u32 emulator_intcntrlr_mem_base;
+ u32 emulator_intcntrlr_mem_size;
+ u32 emulator_irq;
u32 ipa_irq;
u32 transport_irq;
u32 ipa_pipe_mem_start_ofst;
@@ -1441,6 +1633,7 @@
bool gsi_ch20_wa;
bool tethered_flow_control;
u32 mhi_evid_limits[2]; /* start and end values */
+ bool ipa_mhi_dynamic_config;
u32 ipa_tz_unlock_reg_num;
struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
bool use_ipa_pm;
@@ -1725,6 +1918,9 @@
int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id);
void ipa3_xdci_ep_delay_rm(u32 clnt_hdl);
+void ipa3_register_lock_unlock_callback(int (*client_cb)(bool), u32 ipa_ep_idx);
+void ipa3_deregister_lock_unlock_callback(u32 ipa_ep_idx);
+void ipa3_set_usb_prod_pipe_delay(void);
int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
bool should_force_clear, u32 qmi_req_id, bool is_dpl);
@@ -2062,6 +2258,8 @@
*/
int ipa3_get_ep_mapping(enum ipa_client_type client);
+enum gsi_prefetch_mode ipa_get_ep_prefetch_mode(enum ipa_client_type client);
+
bool ipa3_is_ready(void);
void ipa3_proxy_clk_vote(void);
@@ -2354,6 +2552,10 @@
void ipa3_inc_acquire_wakelock(void);
void ipa3_dec_release_wakelock(void);
int ipa3_load_fws(const struct firmware *firmware, phys_addr_t gsi_mem_base);
+int emulator_load_fws(
+ const struct firmware *firmware,
+ u32 transport_mem_base,
+ u32 transport_mem_size);
int ipa3_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data);
const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
int ipa_gsi_ch20_wa(void);
@@ -2379,4 +2581,10 @@
int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
struct ipahal_imm_cmd_pyld *cmd_pyld);
+int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res);
+uint ipa3_get_emulation_type(void);
+int ipa3_get_transport_info(
+ phys_addr_t *phys_addr_ptr,
+ unsigned long *size_ptr);
+irq_handler_t ipa3_get_isr(void);
#endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
index d69d6ae..46b7434 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -339,6 +339,12 @@
ipa3_dec_client_disable_clks_no_block(&log_info);
return IRQ_HANDLED;
}
+
+irq_handler_t ipa3_get_isr(void)
+{
+ return ipa3_isr;
+}
+
/**
* ipa3_add_interrupt_handler() - Adds handler to an interrupt type
* @interrupt: Interrupt type
@@ -486,21 +492,35 @@
return -ENOMEM;
}
- res = request_irq(ipa_irq, (irq_handler_t) ipa3_isr,
- IRQF_TRIGGER_RISING, "ipa", ipa_dev);
- if (res) {
- IPAERR("fail to register IPA IRQ handler irq=%d\n", ipa_irq);
- return -ENODEV;
+ /*
+ * NOTE:
+ *
+ * We'll only register an isr on non-emulator (ie. real UE)
+ * systems.
+ *
+ * On the emulator, emulator_soft_irq_isr() will be calling
+ * ipa3_isr, so hence, no isr registration here, and instead,
+ * we'll pass the address of ipa3_isr to the gsi layer where
+ * emulator interrupts are handled...
+ */
+ if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION) {
+ res = request_irq(ipa_irq, (irq_handler_t) ipa3_isr,
+ IRQF_TRIGGER_RISING, "ipa", ipa_dev);
+ if (res) {
+ IPAERR(
+ "fail to register IPA IRQ handler irq=%d\n",
+ ipa_irq);
+ return -ENODEV;
+ }
+ IPADBG("IPA IRQ handler irq=%d registered\n", ipa_irq);
+
+ res = enable_irq_wake(ipa_irq);
+ if (res)
+ IPAERR("fail to enable IPA IRQ wakeup irq=%d res=%d\n",
+ ipa_irq, res);
+ else
+ IPADBG("IPA IRQ wakeup enabled irq=%d\n", ipa_irq);
}
- IPADBG("IPA IRQ handler irq=%d registered\n", ipa_irq);
-
- res = enable_irq_wake(ipa_irq);
- if (res)
- IPAERR("fail to enable IPA IRQ wakeup irq=%d res=%d\n",
- ipa_irq, res);
- else
- IPADBG("IPA IRQ wakeup enabled irq=%d\n", ipa_irq);
-
spin_lock_init(&suspend_wa_lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 82cd8187..dd97038 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -56,9 +56,9 @@
#define IPA_MHI_FUNC_ENTRY() \
- IPA_MHI_DBG_LOW("ENTRY\n")
+ IPA_MHI_DBG("ENTRY\n")
#define IPA_MHI_FUNC_EXIT() \
- IPA_MHI_DBG_LOW("EXIT\n")
+ IPA_MHI_DBG("EXIT\n")
#define IPA_MHI_MAX_UL_CHANNELS 1
#define IPA_MHI_MAX_DL_CHANNELS 1
@@ -198,6 +198,7 @@
union __packed gsi_channel_scratch ch_scratch;
struct ipa3_ep_context *ep;
const struct ipa_gsi_ep_config *ep_cfg;
+ bool burst_mode_enabled = false;
IPA_MHI_FUNC_ENTRY();
@@ -280,8 +281,21 @@
ch_props.ring_len = params->ch_ctx_host->rlen;
ch_props.ring_base_addr = IPA_MHI_HOST_ADDR_COND(
params->ch_ctx_host->rbase);
- ch_props.use_db_eng = GSI_CHAN_DB_MODE;
+
+ if (params->ch_ctx_host->brstmode == IPA_MHI_BURST_MODE_DEFAULT ||
+ params->ch_ctx_host->brstmode == IPA_MHI_BURST_MODE_ENABLE) {
+ burst_mode_enabled = true;
+ }
+
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0 &&
+ !burst_mode_enabled)
+ ch_props.use_db_eng = GSI_CHAN_DIRECT_MODE;
+ else
+ ch_props.use_db_eng = GSI_CHAN_DB_MODE;
+
ch_props.max_prefetch = GSI_ONE_PREFETCH_SEG;
+ ch_props.prefetch_mode =
+ ipa_get_ep_prefetch_mode(client);
ch_props.low_weight = 1;
ch_props.err_cb = params->ch_err_cb;
ch_props.chan_user_data = params->channel;
@@ -307,9 +321,9 @@
ch_scratch.mhi.outstanding_threshold = 0;
}
ch_scratch.mhi.oob_mod_threshold = 4;
- if (params->ch_ctx_host->brstmode == IPA_MHI_BURST_MODE_DEFAULT ||
- params->ch_ctx_host->brstmode == IPA_MHI_BURST_MODE_ENABLE) {
- ch_scratch.mhi.burst_mode_enabled = true;
+
+ if (burst_mode_enabled) {
+ ch_scratch.mhi.burst_mode_enabled = burst_mode_enabled;
ch_scratch.mhi.polling_configuration =
ipa3_mhi_get_ch_poll_cfg(client, params->ch_ctx_host,
(ch_props.ring_len / ch_props.re_size));
@@ -549,6 +563,7 @@
int res;
int ipa_ep_idx;
struct ipa3_ep_context *ep;
+ union __packed gsi_channel_scratch gsi_ch_scratch;
IPA_MHI_FUNC_ENTRY();
@@ -563,11 +578,34 @@
/*
* set polling mode bit to DB mode before
* resuming the channel
+ *
+ * For MHI-->IPA pipes:
+ * when resuming due to transition to M0,
+ * set the polling mode bit to 0.
+ * In other cases, restore it's value form
+ * when you stopped the channel.
+ * Here, after successful resume client move to M0 state.
+ * So, by default setting polling mode bit to 0.
+ *
+ * For IPA-->MHI pipe:
+ * always restore the polling mode bit.
*/
- res = gsi_write_channel_scratch(
- ep->gsi_chan_hdl, ch_scratch);
+
+ res = gsi_read_channel_scratch(
+ ep->gsi_chan_hdl, &gsi_ch_scratch);
if (res) {
- IPA_MHI_ERR("write ch scratch fail %d\n"
+ IPA_MHI_ERR("Read ch scratch fail %d\n"
+ , res);
+ return res;
+ }
+
+ if (IPA_CLIENT_IS_PROD(client))
+ gsi_ch_scratch.mhi.polling_mode = false;
+
+ res = gsi_write_channel_scratch(
+ ep->gsi_chan_hdl, gsi_ch_scratch);
+ if (res) {
+ IPA_MHI_ERR("Write ch scratch fail %d\n"
, res);
return res;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index b48f2c4..7065e2c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -1258,6 +1258,11 @@
goto bail;
}
+ if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+ IPAERR_RL("NAT hasn't been initialized\n");
+ return -EPERM;
+ }
+
for (cnt = 0; cnt < dma->entries; ++cnt) {
result = ipa3_table_validate_table_dma_one(&dma->dma[cnt]);
if (result) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
index 0772dde..47a03f9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
@@ -16,7 +16,7 @@
#include <linux/msm_ipa.h>
/* internal to ipa */
-#define IPA_PM_MAX_CLIENTS 12 /* actual max is value -1 since we start from 1*/
+#define IPA_PM_MAX_CLIENTS 32 /* actual max is value -1 since we start from 1*/
#define IPA_PM_MAX_EX_CL 64
#define IPA_PM_THRESHOLD_MAX 5
#define IPA_PM_EXCEPTION_MAX 2
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index 821abe2..3aa8a01 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -531,6 +531,20 @@
req.v6_hash_filter_tbl_start_addr =
IPA_MEM_PART(v6_flt_hash_ofst) + smem_restr_bytes;
+ req.hw_stats_quota_base_addr_valid = true;
+ req.hw_stats_quota_base_addr =
+ IPA_MEM_PART(stats_quota_ofst) + smem_restr_bytes;
+
+ req.hw_stats_quota_size_valid = true;
+ req.hw_stats_quota_size = IPA_MEM_PART(stats_quota_size);
+
+ req.hw_drop_stats_base_addr_valid = true;
+ req.hw_drop_stats_base_addr =
+ IPA_MEM_PART(stats_drop_ofst) + smem_restr_bytes;
+
+ req.hw_drop_stats_table_size_valid = true;
+ req.hw_drop_stats_table_size = IPA_MEM_PART(stats_drop_size);
+
if (!ipa3_uc_loaded_check()) { /* First time boot */
req.is_ssr_bootup_valid = false;
req.is_ssr_bootup = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
index 3351a33..3210a70 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -399,7 +399,7 @@
static inline void ipa3_q6_handshake_complete(bool ssr_bootup) { }
-static inline int ipa3_wwan_set_modem_perf_profile(int throughput)
+static inline int ipa3_wwan_set_modem_perf_profile(int throughput);
static inline int ipa3_qmi_enable_per_client_stats(
struct ipa_enable_per_client_stats_req_msg_v01 *req,
struct ipa_enable_per_client_stats_resp_msg_v01 *resp)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
index 703acd7..aa56311 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1094,10 +1094,91 @@
v6_hash_filter_tbl_start_addr),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1F,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_stats_quota_base_addr_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x1F,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_stats_quota_base_addr),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x20,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_stats_quota_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x20,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_stats_quota_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x21,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_drop_stats_base_addr_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x21,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_drop_stats_base_addr),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x22,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_drop_stats_table_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x22,
+ .offset = offsetof(
+ struct ipa_init_modem_driver_req_msg_v01,
+ hw_drop_stats_table_size),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
+
};
struct elem_info ipa3_init_modem_driver_resp_msg_data_v01_ei[] = {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 7861896..736c0fb 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -1577,6 +1577,15 @@
}
}
}
+
+ /* commit the change to IPA-HW */
+ if (ipa3_ctx->ctrl->ipa3_commit_rt(IPA_IP_v4) ||
+ ipa3_ctx->ctrl->ipa3_commit_rt(IPA_IP_v6)) {
+ IPAERR("fail to commit rt-rule\n");
+ WARN_ON_RATELIMIT_IPA(1);
+ mutex_unlock(&ipa3_ctx->lock);
+ return -EPERM;
+ }
mutex_unlock(&ipa3_ctx->lock);
return 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
index f5ef141..e746229 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -818,6 +818,11 @@
{
u32 opcode;
+ if (ipa3_ctx->ipa_hw_type > IPA_HW_v4_0) {
+ IPADBG_LOW("not supported past IPA v4.0\n");
+ return 0;
+ }
+
/*
* If the uC interface has not been initialized yet,
* don't notify the uC on the enable/disable
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 80155f9..73fb499 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -24,6 +24,13 @@
#include "ipahal/ipahal_hw_stats.h"
#include "../ipa_rm_i.h"
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "ipa_emulation_stubs.h"
+#endif
+
#define IPA_V3_0_CLK_RATE_SVS2 (37.5 * 1000 * 1000UL)
#define IPA_V3_0_CLK_RATE_SVS (75 * 1000 * 1000UL)
#define IPA_V3_0_CLK_RATE_NOMINAL (150 * 1000 * 1000UL)
@@ -964,12 +971,12 @@
/* IPA_3_5_1 */
[IPA_3_5_1][IPA_CLIENT_WLAN1_PROD] = {
true, IPA_v3_5_GROUP_UL_DL, true,
- IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
{ 7, 1, 8, 16, IPA_EE_UC } },
[IPA_3_5_1][IPA_CLIENT_USB_PROD] = {
true, IPA_v3_5_GROUP_UL_DL, true,
- IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
{ 0, 0, 8, 16, IPA_EE_AP } },
[IPA_3_5_1][IPA_CLIENT_APPS_LAN_PROD] = {
@@ -979,7 +986,7 @@
{ 8, 7, 8, 16, IPA_EE_AP } },
[IPA_3_5_1][IPA_CLIENT_APPS_WAN_PROD] = {
true, IPA_v3_5_GROUP_UL_DL, true,
- IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+ IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
{ 2, 3, 16, 32, IPA_EE_AP } },
[IPA_3_5_1][IPA_CLIENT_APPS_CMD_PROD] = {
@@ -1119,86 +1126,86 @@
true,
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
- { 6, 2, 8, 16, IPA_EE_UC } },
+ { 6, 2, 8, 16, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_USB_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
- { 0, 8, 8, 16, IPA_EE_AP } },
+ { 0, 8, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_APPS_LAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 8, 10, 8, 16, IPA_EE_AP } },
+ { 8, 10, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_APPS_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
- { 2, 3, 16, 32, IPA_EE_AP } },
+ { 2, 3, 16, 32, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_APPS_CMD_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_DMA_ONLY,
QMB_MASTER_SELECT_DDR,
- { 5, 4, 20, 24, IPA_EE_AP } },
+ { 5, 4, 20, 24, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_ODU_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
- { 1, 0, 8, 16, IPA_EE_AP } },
+ { 1, 0, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_ETHERNET_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
QMB_MASTER_SELECT_DDR,
- { 9, 0, 8, 16, IPA_EE_UC } },
+ { 9, 0, 8, 16, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 3, 0, 16, 32, IPA_EE_Q6 } },
+ { 3, 0, 16, 32, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_Q6_CMD_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 4, 1, 20, 24, IPA_EE_Q6 } },
+ { 4, 1, 20, 24, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
/* Only for test purpose */
[IPA_4_0][IPA_CLIENT_TEST_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {0, 8, 8, 16, IPA_EE_AP } },
+ {0, 8, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST1_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {0, 8, 8, 16, IPA_EE_AP } },
+ {0, 8, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST2_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 1, 0, 8, 16, IPA_EE_AP } },
+ { 1, 0, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST3_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 7, 9, 8, 16, IPA_EE_AP } },
+ { 7, 9, 8, 16, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_TEST4_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {8, 10, 8, 16, IPA_EE_AP } },
+ {8, 10, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_WLAN1_CONS] = {
@@ -1206,112 +1213,112 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 18, 3, 6, 9, IPA_EE_UC } },
+ { 18, 3, 6, 9, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_WLAN2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ { 20, 13, 9, 9, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_WLAN3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 21, 14, 9, 9, IPA_EE_AP } },
+ { 21, 14, 9, 9, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_USB_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 19, 12, 9, 9, IPA_EE_AP } },
+ { 19, 12, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_USB_DPL_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 15, 7, 5, 5, IPA_EE_AP } },
+ { 15, 7, 5, 5, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_APPS_LAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 10, 5, 9, 9, IPA_EE_AP } },
+ { 10, 5, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_APPS_WAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 11, 6, 9, 9, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_ODU_EMB_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 17, 1, 17, 17, IPA_EE_AP } },
+ { 17, 1, 17, 17, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_ETHERNET_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 22, 1, 17, 17, IPA_EE_UC } },
+ { 22, 1, 17, 17, IPA_EE_UC, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_Q6_LAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 14, 4, 9, 9, IPA_EE_Q6 } },
+ { 14, 4, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_Q6_WAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 13, 3, 9, 9, IPA_EE_Q6 } },
+ { 13, 3, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0][IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 16, 5, 9, 9, IPA_EE_Q6 } },
+ { 16, 5, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
/* Only for test purpose */
/* MBIM aggregation test pipes should have the same QMB as USB_CONS */
[IPA_4_0][IPA_CLIENT_TEST_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 11, 6, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 11, 6, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 11, 6, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 11, 6, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 12, 2, 5, 5, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 19, 12, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 19, 12, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 21, 14, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 21, 14, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
/* Dummy consumer (pipe 31) is used in L2TP rt rule */
[IPA_4_0][IPA_CLIENT_DUMMY_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 31, 31, 8, 8, IPA_EE_AP } },
+ { 31, 31, 8, 8, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
/* IPA_4_0_MHI */
[IPA_4_0_MHI][IPA_CLIENT_APPS_WAN_PROD] = {
@@ -1319,74 +1326,74 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 2, 3, 16, 32, IPA_EE_AP } },
+ { 2, 3, 16, 32, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_APPS_CMD_PROD] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_DMA_ONLY,
QMB_MASTER_SELECT_DDR,
- { 5, 4, 20, 24, IPA_EE_AP } },
+ { 5, 4, 20, 24, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_MHI_PROD] = {
true, IPA_v4_0_MHI_GROUP_PCIE,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_PCIE,
- { 1, 0, 8, 16, IPA_EE_AP } },
+ { 1, 0, 8, 16, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 3, 0, 16, 32, IPA_EE_Q6 } },
+ { 3, 0, 16, 32, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_CMD_PROD] = {
true, IPA_v4_0_MHI_GROUP_PCIE,
false,
IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 4, 1, 20, 24, IPA_EE_Q6 } },
+ { 4, 1, 20, 24, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_MEMCPY_DMA_SYNC_PROD] = {
true, IPA_v4_0_MHI_GROUP_DMA,
false,
IPA_DPS_HPS_SEQ_TYPE_DMA_ONLY,
QMB_MASTER_SELECT_DDR,
- { 7, 9, 8, 16, IPA_EE_AP } },
+ { 7, 9, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_MEMCPY_DMA_ASYNC_PROD] = {
true, IPA_v4_0_MHI_GROUP_DMA,
false,
IPA_DPS_HPS_SEQ_TYPE_DMA_ONLY,
QMB_MASTER_SELECT_DDR,
- { 8, 10, 8, 16, IPA_EE_AP } },
+ { 8, 10, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
/* Only for test purpose */
[IPA_4_0_MHI][IPA_CLIENT_TEST_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {0, 8, 8, 16, IPA_EE_AP } },
- [IPA_4_0][IPA_CLIENT_TEST1_PROD] = {
+ {0, 8, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
+ [IPA_4_0_MHI][IPA_CLIENT_TEST1_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {0, 8, 8, 16, IPA_EE_AP } },
+ {0, 8, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_TEST2_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 1, 0, 8, 16, IPA_EE_AP } },
+ { 1, 0, 8, 16, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_TEST3_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {7, 9, 8, 16, IPA_EE_AP } },
+ {7, 9, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_TEST4_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 8, 10, 8, 16, IPA_EE_AP } },
+ { 8, 10, 8, 16, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_APPS_LAN_CONS] = {
@@ -1394,87 +1401,87 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 10, 5, 9, 9, IPA_EE_AP } },
+ { 10, 5, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_APPS_WAN_CONS] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 11, 6, 9, 9, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_MHI_CONS] = {
true, IPA_v4_0_MHI_GROUP_PCIE,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 17, 1, 17, 17, IPA_EE_AP } },
+ { 17, 1, 17, 17, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_LAN_CONS] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 14, 4, 9, 9, IPA_EE_Q6 } },
+ { 14, 4, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_WAN_CONS] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 13, 3, 9, 9, IPA_EE_Q6 } },
+ { 13, 3, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
[IPA_4_0_MHI][IPA_CLIENT_MEMCPY_DMA_SYNC_CONS] = {
true, IPA_v4_0_MHI_GROUP_DMA,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ { 20, 13, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS] = {
true, IPA_v4_0_MHI_GROUP_DMA,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 21, 14, 9, 9, IPA_EE_AP } },
+ { 21, 14, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_LTE_WIFI_AGGR_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 16, 5, 9, 9, IPA_EE_Q6 } },
+ { 16, 5, 9, 9, IPA_EE_Q6, GSI_USE_PREFETCH_BUFS } },
/* Only for test purpose */
[IPA_4_0_MHI][IPA_CLIENT_TEST_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 11, 6, 9, 9, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 11, 6, 9, 9, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 12, 2, 5, 5, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 19, 12, 9, 9, IPA_EE_AP } },
+ { 19, 12, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
[IPA_4_0_MHI][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 21, 14, 9, 9, IPA_EE_AP } },
+ { 21, 14, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY } },
/* Dummy consumer (pipe 31) is used in L2TP rt rule */
[IPA_4_0_MHI][IPA_CLIENT_DUMMY_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 31, 31, 8, 8, IPA_EE_AP } },
+ { 31, 31, 8, 8, IPA_EE_AP, GSI_USE_PREFETCH_BUFS } },
};
/**
@@ -1984,10 +1991,13 @@
max_writes.qmb_1_max_writes = 2;
if (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5) {
- max_writes.qmb_1_max_writes = 4;
max_reads.qmb_1_max_reads = 12;
+ max_writes.qmb_1_max_writes = 4;
}
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+ max_reads.qmb_0_max_reads = 12;
+
ipahal_write_reg_fields(IPA_QSB_MAX_WRITES, &max_writes);
ipahal_write_reg_fields(IPA_QSB_MAX_READS, &max_reads);
}
@@ -2123,6 +2133,28 @@
return ipa_ep_idx;
}
+
+/**
+ * ipa_get_ep_prefetch_mode() - provide prefetch_mode endpoint
+ * @client: client type
+ *
+ * Return value: prefetch_mode
+ */
+enum gsi_prefetch_mode ipa_get_ep_prefetch_mode(enum ipa_client_type client)
+{
+ enum gsi_prefetch_mode prefetch_mode;
+
+ if (client >= IPA_CLIENT_MAX || client < 0) {
+ IPAERR_RL("Bad client number: client =%d\n", client);
+ return -IPA_EP_NOT_ALLOCATED;
+ }
+
+ prefetch_mode = ipa3_ep_mapping[ipa3_get_hw_type_index()][client].
+ ipa_gsi_ep_info.prefetch_mode;
+
+ return prefetch_mode;
+}
+
/**
* ipa3_get_gsi_ep_info() - provide gsi ep information
* @client: IPA client value
@@ -2322,6 +2354,32 @@
}
/**
+ * ipa3_get_client_by_pipe() - return client type relative to pipe
+ * index
+ * @pipe_idx: IPA end-point number
+ *
+ * Return value: client type
+ */
+static enum ipa_client_type ipa3_get_client_by_pipe(int pipe_idx)
+{
+ int j = 0;
+
+ for (j = 0; j < IPA_CLIENT_MAX; j++) {
+ const struct ipa_ep_configuration *iec_ptr =
+ &(ipa3_ep_mapping[ipa3_get_hw_type_index()][j]);
+ if (iec_ptr->valid &&
+ iec_ptr->ipa_gsi_ep_info.ipa_ep_num == pipe_idx)
+ break;
+ }
+
+ if (j == IPA_CLIENT_MAX)
+ IPADBG("ipa3_get_client_by_pipe(%d) can't find client\n",
+ pipe_idx);
+
+ return j;
+}
+
+/**
* ipa_init_ep_flt_bitmap() - Initialize the bitmap
* that represents the End-points that supports filtering
*/
@@ -4326,7 +4384,7 @@
*
* Returns: 0 on success, negative on failure
*/
-static int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
+int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
{
if (!res) {
IPAERR("NULL out param\n");
@@ -4870,7 +4928,8 @@
}
/* move resource group configuration from HLOS to TZ */
- if (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_1) {
+ if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION &&
+ ipa3_ctx->ipa_hw_type >= IPA_HW_v3_1) {
IPAERR("skip configuring ipa_rx_hps_clients from HLOS\n");
return;
}
@@ -5179,6 +5238,77 @@
}
/**
+ * emulator_load_single_fw() - load firmware into emulator's memory
+ *
+ * @firmware: Structure which contains the FW data from the user space.
+ * @phdr: ELF program header
+ * @fw_base: memory location to which firmware should get loaded
+ * @offset_from_base: offset to start relative to fw_base
+ *
+ * Return value: 0 on success, negative otherwise
+ */
+static int emulator_load_single_fw(
+ const struct firmware *firmware,
+ const struct elf32_phdr *phdr,
+ void __iomem *fw_base,
+ uint32_t offset_from_base)
+{
+ int index;
+ uint32_t ofb;
+ const uint32_t *elf_data_ptr;
+
+ IPADBG("firmware(%pK) phdr(%pK) fw_base(%pK) offset_from_base(0x%x)\n",
+ firmware, phdr, fw_base, offset_from_base);
+
+ if (phdr->p_offset > firmware->size) {
+ IPAERR("Invalid ELF: offset=%u is beyond elf_size=%zu\n",
+ phdr->p_offset, firmware->size);
+ return -EINVAL;
+ }
+ if ((firmware->size - phdr->p_offset) < phdr->p_filesz) {
+ IPAERR("Invalid ELF: offset=%u filesz=%u elf_size=%zu\n",
+ phdr->p_offset, phdr->p_filesz, firmware->size);
+ return -EINVAL;
+ }
+
+ if (phdr->p_memsz % sizeof(uint32_t)) {
+ IPAERR("FW mem size %u doesn't align to 32bit\n",
+ phdr->p_memsz);
+ return -EFAULT;
+ }
+
+ if (phdr->p_filesz > phdr->p_memsz) {
+ IPAERR("FW image too big src_size=%u dst_size=%u\n",
+ phdr->p_filesz, phdr->p_memsz);
+ return -EFAULT;
+ }
+
+ IPADBG("ELF: p_memsz(0x%x) p_filesz(0x%x) p_filesz/4(0x%x)\n",
+ (uint32_t) phdr->p_memsz,
+ (uint32_t) phdr->p_filesz,
+ (uint32_t) (phdr->p_filesz/sizeof(uint32_t)));
+
+ /* Set the entire region to 0s */
+ ofb = offset_from_base;
+ for (index = 0; index < phdr->p_memsz/sizeof(uint32_t); index++) {
+ writel_relaxed(0, fw_base + ofb);
+ ofb += sizeof(uint32_t);
+ }
+
+ elf_data_ptr = (uint32_t *)(firmware->data + phdr->p_offset);
+
+ /* Write the FW */
+ ofb = offset_from_base;
+ for (index = 0; index < phdr->p_filesz/sizeof(uint32_t); index++) {
+ writel_relaxed(*elf_data_ptr, fw_base + ofb);
+ elf_data_ptr++;
+ ofb += sizeof(uint32_t);
+ }
+
+ return 0;
+}
+
+/**
* ipa3_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
*
* @firmware: Structure which contains the FW data from the user space.
@@ -5286,6 +5416,240 @@
return 0;
}
+/*
+ * The following needed for the EMULATION system. On a non-emulation
+ * system (ie. the real UE), this is functionality is done in the
+ * TZ...
+ */
+#define IPA_SPARE_REG_1_VAL (0xC0000805)
+
+static void ipa_gsi_setup_reg(void)
+{
+ u32 reg_val, start;
+ int i;
+ const struct ipa_gsi_ep_config *gsi_ep_info_cfg;
+ enum ipa_client_type type;
+
+ IPADBG("Setting up registers in preparation for firmware download\n");
+
+ /* enable GSI interface */
+ ipahal_write_reg(IPA_GSI_CONF, 1);
+
+ /*
+ * Before configuring the FIFOs need to unset bit 30 in the
+ * spare register
+ */
+ ipahal_write_reg(IPA_SPARE_REG_1,
+ (IPA_SPARE_REG_1_VAL & (~(1 << 30))));
+
+ /* setup IPA_ENDP_GSI_CFG_TLV_n reg */
+ start = 0;
+ ipa3_ctx->ipa_num_pipes = ipa3_get_num_pipes();
+ IPADBG("ipa_num_pipes=%u\n", ipa3_ctx->ipa_num_pipes);
+
+ for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+ type = ipa3_get_client_by_pipe(i);
+ gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+ IPADBG("for ep %d client is %d\n", i, type);
+ if (!gsi_ep_info_cfg)
+ continue;
+ IPADBG("Config is true");
+ reg_val = (gsi_ep_info_cfg->ipa_if_tlv << 16) + start;
+ start += gsi_ep_info_cfg->ipa_if_tlv;
+ ipahal_write_reg_n(IPA_ENDP_GSI_CFG_TLV_n, i, reg_val);
+ }
+
+ /* setup IPA_ENDP_GSI_CFG_AOS_n reg */
+ start = 0;
+ for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+ type = ipa3_get_client_by_pipe(i);
+ gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+ if (!gsi_ep_info_cfg)
+ continue;
+ reg_val = (gsi_ep_info_cfg->ipa_if_aos << 16) + start;
+ start += gsi_ep_info_cfg->ipa_if_aos;
+ ipahal_write_reg_n(IPA_ENDP_GSI_CFG_AOS_n, i, reg_val);
+ }
+
+ /* setup IPA_ENDP_GSI_CFG1_n reg */
+ for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+ type = ipa3_get_client_by_pipe(i);
+ gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+ if (!gsi_ep_info_cfg)
+ continue;
+ reg_val = (1 << 16) +
+ ((u32)gsi_ep_info_cfg->ipa_gsi_chan_num << 8) +
+ gsi_ep_info_cfg->ee;
+ ipahal_write_reg_n(IPA_ENDP_GSI_CFG1_n, i, reg_val);
+ }
+
+ /*
+ * Setup IPA_ENDP_GSI_CFG2_n reg: this register must be setup
+ * as last one
+ */
+ for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+ type = ipa3_get_client_by_pipe(i);
+ gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+ if (!gsi_ep_info_cfg)
+ continue;
+ reg_val = 1 << 31;
+ ipahal_write_reg_n(IPA_ENDP_GSI_CFG2_n, i, reg_val);
+ reg_val = 0;
+ ipahal_write_reg_n(IPA_ENDP_GSI_CFG2_n, i, reg_val);
+ }
+
+ /*
+ * After configuring the FIFOs need to set bit 30 in the spare
+ * register
+ */
+ ipahal_write_reg(IPA_SPARE_REG_1,
+ (IPA_SPARE_REG_1_VAL | (1 << 30)));
+}
+
+/**
+ * emulator_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
+ *
+ * @firmware: Structure which contains the FW data from the user space.
+ *
+ * Return value: 0 on success, negative otherwise
+ *
+ */
+int emulator_load_fws(
+ const struct firmware *firmware,
+ u32 transport_mem_base,
+ u32 transport_mem_size)
+{
+ const struct elf32_hdr *ehdr;
+ const struct elf32_phdr *phdr;
+ void __iomem *gsi_base;
+ uint32_t hps_seq_offs, dps_seq_offs;
+ unsigned long gsi_offset;
+ int rc;
+
+ IPADBG("Loading firmware(%pK)\n", firmware);
+
+ if (!firmware) {
+ IPAERR("firmware pointer passed to function is NULL\n");
+ return -EINVAL;
+ }
+
+ /* One program header per FW image: GSI, DPS and HPS */
+ if (firmware->size < (sizeof(*ehdr) + 3 * sizeof(*phdr))) {
+ IPAERR(
+ "Missing ELF and Program headers firmware size=%zu\n",
+ firmware->size);
+ return -EINVAL;
+ }
+
+ ehdr = (struct elf32_hdr *) firmware->data;
+
+ ipa_assert_on(!ehdr);
+
+ if (ehdr->e_phnum != 3) {
+ IPAERR("Unexpected number of ELF program headers\n");
+ return -EINVAL;
+ }
+
+ hps_seq_offs = ipahal_get_reg_ofst(IPA_HPS_SEQUENCER_FIRST);
+ dps_seq_offs = ipahal_get_reg_ofst(IPA_DPS_SEQUENCER_FIRST);
+
+ /*
+ * Each ELF program header represents a FW image and contains:
+ * p_vaddr : The starting address to which the FW needs to loaded.
+ * p_memsz : The size of the IRAM (where the image loaded)
+ * p_filesz: The size of the FW image embedded inside the ELF
+ * p_offset: Absolute offset to the image from the head of the ELF
+ *
+ * NOTE WELL: On the emulation platform, the p_vaddr address
+ * is not relevant and is unused. This is because
+ * on the emulation platform, the registers'
+ * address location is mutable, since it's mapped
+ * in via a PCIe probe. Given this, it is the
+ * mapped address info that's used while p_vaddr is
+ * ignored.
+ */
+ phdr = (struct elf32_phdr *)(firmware->data + sizeof(*ehdr));
+
+ phdr += 2;
+
+ /*
+ * Attempt to load IPA HPS FW image
+ */
+ if (phdr->p_memsz > ipahal_get_hps_img_mem_size()) {
+ IPAERR("Invalid IPA HPS img size memsz=%d dps_mem_size=%u\n",
+ phdr->p_memsz, ipahal_get_hps_img_mem_size());
+ return -EINVAL;
+ }
+ IPADBG("Loading HPS FW\n");
+ rc = emulator_load_single_fw(
+ firmware, phdr, ipa3_ctx->mmio, hps_seq_offs);
+ if (rc)
+ return rc;
+ IPADBG("Loading HPS FW complete\n");
+
+ --phdr;
+
+ /*
+ * Attempt to load IPA DPS FW image
+ */
+ if (phdr->p_memsz > ipahal_get_dps_img_mem_size()) {
+ IPAERR("Invalid IPA DPS img size memsz=%d dps_mem_size=%u\n",
+ phdr->p_memsz, ipahal_get_dps_img_mem_size());
+ return -EINVAL;
+ }
+ IPADBG("Loading DPS FW\n");
+ rc = emulator_load_single_fw(
+ firmware, phdr, ipa3_ctx->mmio, dps_seq_offs);
+ if (rc)
+ return rc;
+ IPADBG("Loading DPS FW complete\n");
+
+ /*
+ * Run gsi register setup which is normally done in TZ on
+ * non-EMULATION systems...
+ */
+ ipa_gsi_setup_reg();
+
+ /*
+ * Map to the GSI base...
+ */
+ gsi_base = ioremap_nocache(transport_mem_base, transport_mem_size);
+
+ IPADBG("GSI base(0x%x) mapped to (%pK) with len (0x%x)\n",
+ transport_mem_base,
+ gsi_base,
+ transport_mem_size);
+
+ if (!gsi_base) {
+ IPAERR("ioremap_nocache failed\n");
+ return -EFAULT;
+ }
+
+ --phdr;
+
+ /*
+ * Attempt to load GSI FW image
+ */
+ if (phdr->p_memsz > transport_mem_size) {
+ IPAERR(
+ "Invalid GSI FW img size memsz=%d transport_mem_size=%u\n",
+ phdr->p_memsz, transport_mem_size);
+ return -EINVAL;
+ }
+ IPADBG("Loading GSI FW\n");
+ gsi_get_inst_ram_offset_and_size(&gsi_offset, NULL);
+ rc = emulator_load_single_fw(
+ firmware, phdr, gsi_base, (uint32_t) gsi_offset);
+ iounmap(gsi_base);
+ if (rc)
+ return rc;
+ IPADBG("Loading GSI FW complete\n");
+
+ IPADBG("IPA FWs (GSI FW, DPS and HPS) loaded successfully\n");
+
+ return 0;
+}
+
/**
* ipa3_is_msm_device() - Is the running device a MSM or MDM?
* Determine according to IPA version
@@ -5379,4 +5743,3 @@
desc->len = cmd_pyld->len;
desc->type = IPA_IMM_CMD_DESC;
}
-
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index 1254fe3..530adef 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/errno.h>
#include <linux/ipc_logging.h>
#include <linux/debugfs.h>
#include <linux/ipa.h>
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 26b7f0f..d46f13c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -55,6 +55,14 @@
" %s:%d " fmt, ## args); \
} while (0)
+#define IPAHAL_DBG_REG_IPC_ONLY(fmt, args...) \
+ do { \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+ " %s:%d " fmt, ## args); \
+ IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+ " %s:%d " fmt, ## args); \
+ } while (0)
+
#define IPAHAL_ERR_RL(fmt, args...) \
do { \
pr_err_ratelimited_ipa(IPAHAL_DRV_NAME " %s:%d " fmt, \
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index ce59488..2dbea95 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -133,6 +133,11 @@
__stringify(IPA_FEC_ATTR_EE_n),
__stringify(IPA_MBIM_DEAGGR_FEC_ATTR_EE_n),
__stringify(IPA_GEN_DEAGGR_FEC_ATTR_EE_n),
+ __stringify(IPA_GSI_CONF),
+ __stringify(IPA_ENDP_GSI_CFG1_n),
+ __stringify(IPA_ENDP_GSI_CFG2_n),
+ __stringify(IPA_ENDP_GSI_CFG_AOS_n),
+ __stringify(IPA_ENDP_GSI_CFG_TLV_n),
};
static void ipareg_construct_dummy(enum ipahal_reg_name reg,
@@ -1971,6 +1976,21 @@
[IPA_HW_v3_5][IPA_HPS_FTCH_ARB_QUEUE_WEIGHT] = {
ipareg_construct_hps_queue_weights,
ipareg_parse_hps_queue_weights, 0x000005a4, 0, 0, 0, 0},
+ [IPA_HW_v3_5][IPA_GSI_CONF] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00002790, 0x0, 0, 0, 0 },
+ [IPA_HW_v3_5][IPA_ENDP_GSI_CFG1_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00002794, 0x4, 0, 0, 0 },
+ [IPA_HW_v3_5][IPA_ENDP_GSI_CFG2_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00002A2C, 0x4, 0, 0, 0 },
+ [IPA_HW_v3_5][IPA_ENDP_GSI_CFG_AOS_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x000029A8, 0x4, 0, 0, 0 },
+ [IPA_HW_v3_5][IPA_ENDP_GSI_CFG_TLV_n] = {
+ ipareg_construct_dummy, ipareg_parse_dummy,
+ 0x00002924, 0x4, 0, 0, 0 },
/* IPAv4.0 */
[IPA_HW_v4_0][IPA_IRQ_SUSPEND_INFO_EE_n] = {
@@ -2217,7 +2237,7 @@
0x00000CC0, 0x70, 10, 23, 1},
};
-int ipahal_print_all_regs(void)
+int ipahal_print_all_regs(bool print_to_dmesg)
{
int i, j;
@@ -2236,9 +2256,16 @@
j = ipahal_reg_objs[ipahal_ctx->hw_type][i].n_start;
- if (j == ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end)
- IPAHAL_DBG_REG("%s=0x%x\n", ipahal_reg_name_str(i),
- ipahal_read_reg_n(i, j));
+ if (j == ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end) {
+ if (print_to_dmesg)
+ IPAHAL_DBG_REG("%s=0x%x\n",
+ ipahal_reg_name_str(i),
+ ipahal_read_reg_n(i, j));
+ else
+ IPAHAL_DBG_REG_IPC_ONLY("%s=0x%x\n",
+ ipahal_reg_name_str(i),
+ ipahal_read_reg_n(i, j));
+ }
for (; j < ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end; j++)
IPAHAL_DBG_REG("%s_%u=0x%x\n", ipahal_reg_name_str(i),
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 7e8e8ba..fdf4fd1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -134,6 +134,11 @@
IPA_FEC_ATTR_EE_n,
IPA_MBIM_DEAGGR_FEC_ATTR_EE_n,
IPA_GEN_DEAGGR_FEC_ATTR_EE_n,
+ IPA_GSI_CONF,
+ IPA_ENDP_GSI_CFG1_n,
+ IPA_ENDP_GSI_CFG2_n,
+ IPA_ENDP_GSI_CFG_AOS_n,
+ IPA_ENDP_GSI_CFG_TLV_n,
IPA_REG_MAX,
};
@@ -519,7 +524,7 @@
};
-int ipahal_print_all_regs(void);
+int ipahal_print_all_regs(bool print_to_dmesg);
/*
* ipahal_reg_name_str() - returns string that represent the register
@@ -641,4 +646,3 @@
struct ipahal_reg_valmask *valmask);
#endif /* _IPAHAL_REG_H_ */
-
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 561c533..f1e2e6d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1565,6 +1565,8 @@
/* Extended IOCTLs */
case RMNET_IOCTL_EXTENDED:
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
IPAWANDBG("get ioctl: RMNET_IOCTL_EXTENDED\n");
if (copy_from_user(&extend_ioctl_data,
(u8 *)ifr->ifr_ifru.ifru_data,
diff --git a/drivers/platform/msm/ipa/test/Makefile b/drivers/platform/msm/ipa/test/Makefile
index 82bee5d..e18e534 100644
--- a/drivers/platform/msm/ipa/test/Makefile
+++ b/drivers/platform/msm/ipa/test/Makefile
@@ -1,2 +1,8 @@
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
+
obj-$(CONFIG_IPA_UT) += ipa_ut_mod.o
ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o ipa_test_mhi.o ipa_test_dma.o ipa_test_hw_stats.o ipa_pm_ut.o
diff --git a/drivers/platform/msm/ipa/test/ipa_pm_ut.c b/drivers/platform/msm/ipa/test/ipa_pm_ut.c
index e07040a..83b705d 100644
--- a/drivers/platform/msm/ipa/test/ipa_pm_ut.c
+++ b/drivers/platform/msm/ipa/test/ipa_pm_ut.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,18 +23,32 @@
static int ipa_pm_ut_setup(void **ppriv)
{
+ int i;
IPA_UT_DBG("Start Setup\n");
/* decrement UT vote */
IPA_ACTIVE_CLIENTS_DEC_SPECIAL("IPA_UT");
+ /*decouple PM from RPM */
+ ipa3_ctx->enable_clock_scaling = false;
+
+ if (ipa3_ctx->use_ipa_pm) {
+ for (i = 0; i < IPA_PM_MAX_CLIENTS; i++) {
+ ipa_pm_deactivate_sync(i);
+ ipa_pm_deregister(i);
+ }
+
+ ipa_pm_destroy();
+ }
+
return 0;
}
static int ipa_pm_ut_teardown(void *priv)
{
IPA_UT_DBG("Start Teardown\n");
+ IPA_UT_ERR("WARNING: IPA_PM HAS BEEN DESTROYED, REBOOT TO RE_INIT\n");
/* undo UT vote */
IPA_ACTIVE_CLIENTS_INC_SPECIAL("IPA_UT");
@@ -106,7 +120,7 @@
struct callback_param user_data;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -223,7 +237,7 @@
struct callback_param user_data;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -327,7 +341,7 @@
struct callback_param user_data;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -436,7 +450,7 @@
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -648,7 +662,7 @@
struct callback_param user_data;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -797,7 +811,7 @@
struct callback_param user_data;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -885,7 +899,7 @@
unsigned long flags;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -957,7 +971,7 @@
int i, hdl_USB, hdl_WLAN, vote;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -1095,7 +1109,7 @@
int hdl_USB, hdl_WLAN, vote, idx;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -1210,7 +1224,7 @@
int hdl_USB, hdl_WLAN, hdl_MODEM, vote, idx;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -1377,7 +1391,7 @@
int hdl_USB, hdl_WLAN, hdl_MODEM, vote, idx;
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000}
};
@@ -1542,7 +1556,7 @@
};
struct ipa_pm_init_params init_params = {
- .threshold_size = IPA_PM_THRESHOLD_MAX,
+ .threshold_size = 2,
.default_threshold = {600, 1000},
.exception_size = 1,
.exceptions[0] = exceptions,
diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
index 212557c..7f65956 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
@@ -327,6 +327,8 @@
u32 prod_hdl;
u32 cons_hdl;
u32 test_prod_hdl;
+ phys_addr_t transport_phys_addr;
+ unsigned long transport_size;
};
static struct ipa_test_mhi_context *test_mhi_ctx;
@@ -780,11 +782,6 @@
IPA_UT_DBG("Start Setup\n");
- if (!gsi_ctx) {
- IPA_UT_ERR("No GSI ctx\n");
- return -EINVAL;
- }
-
if (!ipa3_ctx) {
IPA_UT_ERR("No IPA ctx\n");
return -EINVAL;
@@ -797,11 +794,20 @@
return -ENOMEM;
}
- test_mhi_ctx->gsi_mmio = ioremap_nocache(gsi_ctx->per.phys_addr,
- gsi_ctx->per.size);
- if (!test_mhi_ctx) {
+ rc = ipa3_get_transport_info(&test_mhi_ctx->transport_phys_addr,
+ &test_mhi_ctx->transport_size);
+ if (rc != 0) {
+ IPA_UT_ERR("ipa3_get_transport_info() failed\n");
+ rc = -EFAULT;
+ goto fail_free_ctx;
+ }
+
+ test_mhi_ctx->gsi_mmio =
+ ioremap_nocache(test_mhi_ctx->transport_phys_addr,
+ test_mhi_ctx->transport_size);
+ if (!test_mhi_ctx->gsi_mmio) {
IPA_UT_ERR("failed to remap GSI HW size=%lu\n",
- gsi_ctx->per.size);
+ test_mhi_ctx->transport_size);
rc = -EFAULT;
goto fail_free_ctx;
}
@@ -1385,7 +1391,7 @@
/* write value to event ring doorbell */
IPA_UT_LOG("DB to event 0x%llx: base %pa ofst 0x%x\n",
p_events[event_ring_index].wp,
- &(gsi_ctx->per.phys_addr),
+ &(test_mhi_ctx->transport_phys_addr),
GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
event_ring_index + ipa3_ctx->mhi_evid_limits[0], 0));
iowrite32(p_events[event_ring_index].wp,
@@ -1432,7 +1438,7 @@
IPA_UT_LOG(
"DB to channel 0x%llx: base %pa ofst 0x%x\n"
, p_channels[channel_idx].wp
- , &(gsi_ctx->per.phys_addr)
+ , &(test_mhi_ctx->transport_phys_addr)
, GSI_EE_n_GSI_CH_k_DOORBELL_0_OFFS(
channel_idx, 0));
iowrite32(p_channels[channel_idx].wp,
@@ -3324,4 +3330,3 @@
ipa_mhi_test_in_loop_channel_reset_ipa_holb,
true, IPA_HW_v3_0, IPA_HW_MAX),
} IPA_UT_DEFINE_SUITE_END(mhi);
-
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
index bcbcd87..dad3ec9 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -1036,9 +1036,10 @@
* If IPA driver already ready, continue initialization immediately.
* if not, wait for IPA ready notification by IPA driver context
*/
-static int __init ipa_ut_module_init(void)
+int __init ipa_ut_module_init(void)
{
- int ret;
+ int ret = 0;
+ bool init_framewok = true;
IPA_UT_INFO("Loading IPA test module...\n");
@@ -1050,14 +1051,34 @@
mutex_init(&ipa_ut_ctx->lock);
if (!ipa_is_ready()) {
+ init_framewok = false;
+
IPA_UT_DBG("IPA driver not ready, registering callback\n");
+
ret = ipa_register_ipa_ready_cb(ipa_ut_ipa_ready_cb, NULL);
/*
- * If we received -EEXIST, IPA has initialized. So we need
- * to continue the initing process.
+ * If the call to ipa_register_ipa_ready_cb() above
+ * returns 0, this means that we've succeeded in
+ * queuing up a future call to ipa_ut_framework_init()
+ * and that the call to it will be made once the IPA
+ * becomes ready. If this is the case, the call to
+ * ipa_ut_framework_init() below need not be made.
+ *
+ * If the call to ipa_register_ipa_ready_cb() above
+ * returns -EEXIST, it means that during the call to
+ * ipa_register_ipa_ready_cb(), the IPA has become
+ * ready, and hence, no indirect call to
+ * ipa_ut_framework_init() will be made, so we need to
+ * call it ourselves below.
+ *
+ * If the call to ipa_register_ipa_ready_cb() above
+ * return something other than 0 or -EEXIST, that's a
+ * hard error.
*/
- if (ret != -EEXIST) {
+ if (ret == -EEXIST) {
+ init_framewok = true;
+ } else {
if (ret) {
IPA_UT_ERR("IPA CB reg failed - %d\n", ret);
kfree(ipa_ut_ctx);
@@ -1067,12 +1088,15 @@
}
}
- ret = ipa_ut_framework_init();
- if (ret) {
- IPA_UT_ERR("framework init failed\n");
- kfree(ipa_ut_ctx);
- ipa_ut_ctx = NULL;
+ if (init_framewok) {
+ ret = ipa_ut_framework_init();
+ if (ret) {
+ IPA_UT_ERR("framework init failed\n");
+ kfree(ipa_ut_ctx);
+ ipa_ut_ctx = NULL;
+ }
}
+
return ret;
}
@@ -1081,7 +1105,7 @@
*
* Destroys the Framework and removes its context
*/
-static void ipa_ut_module_exit(void)
+void ipa_ut_module_exit(void)
{
IPA_UT_DBG("Entry\n");
@@ -1093,8 +1117,9 @@
ipa_ut_ctx = NULL;
}
+#if IPA_EMULATION_COMPILE == 0 /* On real UE, we have a module */
module_init(ipa_ut_module_init);
module_exit(ipa_ut_module_exit);
-
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("IPA Unit Test module");
+#endif
diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c
index 1c16e5a..3e577cf 100644
--- a/drivers/platform/msm/mhi_dev/mhi.c
+++ b/drivers/platform/msm/mhi_dev/mhi.c
@@ -824,13 +824,13 @@
}
EXPORT_SYMBOL(mhi_dev_send_ee_event);
-static void mhi_dev_trigger_cb(void)
+static void mhi_dev_trigger_cb(enum mhi_client_channel ch_id)
{
struct mhi_dev_ready_cb_info *info;
enum mhi_ctrl_info state_data;
list_for_each_entry(info, &mhi_ctx->client_cb_list, list)
- if (info->cb) {
+ if (info->cb && info->cb_data.channel == ch_id) {
mhi_ctrl_state_info(info->cb_data.channel, &state_data);
info->cb_data.ctrl_info = state_data;
info->cb(&info->cb_data);
@@ -1021,10 +1021,9 @@
if (rc)
pr_err("Error sending command completion event\n");
- /* Trigger callback to clients */
- mhi_dev_trigger_cb();
-
mhi_update_state_info(ch_id, MHI_STATE_CONNECTED);
+ /* Trigger callback to clients */
+ mhi_dev_trigger_cb(ch_id);
if (ch_id == MHI_CLIENT_MBIM_OUT)
kobject_uevent_env(&mhi_ctx->dev->kobj,
KOBJ_CHANGE, connected);
@@ -1418,16 +1417,20 @@
union mhi_dev_ring_element_type *el;
int rc = 0;
struct mhi_req *req = (struct mhi_req *)mreq;
- struct mhi_req *local_req = NULL;
union mhi_dev_ring_element_type *compl_ev = NULL;
struct mhi_dev *mhi = NULL;
unsigned long flags;
+ size_t transfer_len;
+ u32 snd_cmpl;
+ uint32_t rd_offset;
client = req->client;
ch = client->channel;
mhi = ch->ring->mhi_dev;
el = req->el;
- local_req = req;
+ transfer_len = req->len;
+ snd_cmpl = req->snd_cmpl;
+ rd_offset = req->rd_offset;
ch->curr_ereq->context = ch;
dma_unmap_single(&mhi_ctx->pdev->dev, req->dma,
@@ -1441,14 +1444,13 @@
compl_ev->evt_tr_comp.chid = ch->ch_id;
compl_ev->evt_tr_comp.type =
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT;
- compl_ev->evt_tr_comp.len = el->tre.len;
+ compl_ev->evt_tr_comp.len = transfer_len;
compl_ev->evt_tr_comp.code = MHI_CMD_COMPL_CODE_EOT;
compl_ev->evt_tr_comp.ptr = ch->ring->ring_ctx->generic.rbase +
- local_req->rd_offset * TR_RING_ELEMENT_SZ;
+ rd_offset * TR_RING_ELEMENT_SZ;
ch->curr_ereq->num_events++;
- if (ch->curr_ereq->num_events >= MAX_TR_EVENTS ||
- local_req->snd_cmpl){
+ if (ch->curr_ereq->num_events >= MAX_TR_EVENTS || snd_cmpl) {
mhi_log(MHI_MSG_VERBOSE,
"num of tr events %d for ch %d\n",
ch->curr_ereq->num_events, ch->ch_id);
@@ -1570,6 +1572,12 @@
{
struct mhi_dev *mhi = dev_id;
+ if (!atomic_read(&mhi->mhi_dev_wake)) {
+ pm_stay_awake(mhi->dev);
+ atomic_set(&mhi->mhi_dev_wake, 1);
+ mhi_log(MHI_MSG_VERBOSE, "acquiring mhi wakelock in ISR\n");
+ }
+
disable_irq_nosync(mhi->mhi_irq);
schedule_work(&mhi->chdb_ctrl_work);
mhi_log(MHI_MSG_VERBOSE, "mhi irq triggered\n");
@@ -2401,8 +2409,10 @@
return;
}
- if (mhi_ctx->config_iatu || mhi_ctx->mhi_int)
+ if (mhi_ctx->config_iatu || mhi_ctx->mhi_int) {
+ mhi_ctx->mhi_int_en = true;
enable_irq(mhi_ctx->mhi_irq);
+ }
mhi_update_state_info(MHI_DEV_UEVENT_CTRL, MHI_STATE_CONFIGURED);
}
@@ -2452,8 +2462,10 @@
* If channel is open during registration, no callback is issued.
* Instead return -EEXIST to notify the client. Clients request
* is added to the list to notify future state change notification.
+ * Channel struct may not be allocated yet if this function is called
+ * early during boot - add an explicit check for non-null "ch".
*/
- if (mhi_ctx->ch[channel].state == MHI_DEV_CH_STARTED) {
+ if (mhi_ctx->ch && (mhi_ctx->ch[channel].state == MHI_DEV_CH_STARTED)) {
mutex_unlock(&mhi_ctx->mhi_lock);
return -EEXIST;
}
@@ -2832,8 +2844,6 @@
INIT_LIST_HEAD(&mhi_ctx->event_ring_list);
INIT_LIST_HEAD(&mhi_ctx->process_ring_list);
- INIT_LIST_HEAD(&mhi_ctx->client_cb_list);
- mutex_init(&mhi_ctx->mhi_lock);
mutex_init(&mhi_ctx->mhi_event_lock);
mutex_init(&mhi_ctx->mhi_write_test);
@@ -2983,6 +2993,14 @@
dev_err(&pdev->dev,
"Failed to create IPC logging context\n");
}
+ /*
+ * The below list and mutex should be initialized
+ * before calling mhi_uci_init to avoid crash in
+ * mhi_register_state_cb when accessing these.
+ */
+ INIT_LIST_HEAD(&mhi_ctx->client_cb_list);
+ mutex_init(&mhi_ctx->mhi_lock);
+
mhi_uci_init();
mhi_update_state_info(MHI_DEV_UEVENT_CTRL,
MHI_STATE_CONFIGURED);
diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h
index b9120fc..2cc7809 100644
--- a/drivers/platform/msm/mhi_dev/mhi.h
+++ b/drivers/platform/msm/mhi_dev/mhi.h
@@ -605,6 +605,8 @@
/*Register for interrupt */
bool mhi_int;
+ bool mhi_int_en;
+
/* Registered client callback list */
struct list_head client_cb_list;
@@ -712,6 +714,7 @@
MHI_CLIENT_RESERVED_2_LOWER = 102,
MHI_CLIENT_RESERVED_2_UPPER = 127,
MHI_MAX_CHANNELS = 102,
+ MHI_CLIENT_INVALID = 0xFFFFFFFF
};
/* Use ID 0 for legacy /dev/mhi_ctrl. Channel 0 is used for internal only */
diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c
index 1200a36..af05c9e 100644
--- a/drivers/platform/msm/mhi_dev/mhi_sm.c
+++ b/drivers/platform/msm/mhi_dev/mhi_sm.c
@@ -19,6 +19,7 @@
#include <linux/ipa_mhi.h>
#include "mhi_hwio.h"
#include "mhi_sm.h"
+#include <linux/interrupt.h>
#define MHI_SM_DBG(fmt, args...) \
mhi_log(MHI_MSG_DBG, fmt, ##args)
@@ -775,6 +776,7 @@
struct mhi_sm_ep_pcie_event *chg_event = container_of(work,
struct mhi_sm_ep_pcie_event, work);
enum ep_pcie_event pcie_event = chg_event->event;
+ unsigned long flags;
MHI_SM_FUNC_ENTRY();
@@ -846,6 +848,15 @@
mhi_dev_restore_mmio(mhi_sm_ctx->mhi_dev);
+ spin_lock_irqsave(&mhi_sm_ctx->mhi_dev->lock, flags);
+ if ((mhi_sm_ctx->mhi_dev->mhi_int) &&
+ (!mhi_sm_ctx->mhi_dev->mhi_int_en)) {
+ enable_irq(mhi_sm_ctx->mhi_dev->mhi_irq);
+ mhi_sm_ctx->mhi_dev->mhi_int_en = true;
+ MHI_SM_DBG("Enable MHI IRQ during PCIe DEAST");
+ }
+ spin_unlock_irqrestore(&mhi_sm_ctx->mhi_dev->lock, flags);
+
res = ep_pcie_enable_endpoint(mhi_sm_ctx->mhi_dev->phandle,
EP_PCIE_OPT_ENUM);
if (res) {
@@ -1141,6 +1152,7 @@
{
struct mhi_sm_ep_pcie_event *dstate_change_evt;
enum ep_pcie_event event;
+ unsigned long flags;
MHI_SM_FUNC_ENTRY();
@@ -1173,6 +1185,16 @@
break;
case EP_PCIE_EVENT_PM_D3_HOT:
mhi_sm_ctx->stats.d3_hot_event_cnt++;
+
+ spin_lock_irqsave(&mhi_sm_ctx->mhi_dev->lock, flags);
+ if ((mhi_sm_ctx->mhi_dev->mhi_int) &&
+ (mhi_sm_ctx->mhi_dev->mhi_int_en)) {
+ disable_irq(mhi_sm_ctx->mhi_dev->mhi_irq);
+ mhi_sm_ctx->mhi_dev->mhi_int_en = false;
+ MHI_SM_DBG("Disable MHI IRQ during D3 HOT");
+ }
+ spin_unlock_irqrestore(&mhi_sm_ctx->mhi_dev->lock, flags);
+
mhi_dev_backup_mmio(mhi_sm_ctx->mhi_dev);
break;
case EP_PCIE_EVENT_PM_RST_DEAST:
@@ -1180,6 +1202,15 @@
break;
case EP_PCIE_EVENT_PM_D0:
mhi_sm_ctx->stats.d0_event_cnt++;
+
+ spin_lock_irqsave(&mhi_sm_ctx->mhi_dev->lock, flags);
+ if ((mhi_sm_ctx->mhi_dev->mhi_int) &&
+ (!mhi_sm_ctx->mhi_dev->mhi_int_en)) {
+ enable_irq(mhi_sm_ctx->mhi_dev->mhi_irq);
+ mhi_sm_ctx->mhi_dev->mhi_int_en = true;
+ MHI_SM_DBG("Enable MHI IRQ during D0");
+ }
+ spin_unlock_irqrestore(&mhi_sm_ctx->mhi_dev->lock, flags);
break;
case EP_PCIE_EVENT_LINKDOWN:
mhi_sm_ctx->stats.linkdown_event_cnt++;
diff --git a/drivers/platform/msm/mhi_dev/mhi_uci.c b/drivers/platform/msm/mhi_dev/mhi_uci.c
index ed02d0d..febc867 100644
--- a/drivers/platform/msm/mhi_dev/mhi_uci.c
+++ b/drivers/platform/msm/mhi_dev/mhi_uci.c
@@ -35,7 +35,7 @@
#define MHI_SOFTWARE_CLIENT_LIMIT (MHI_MAX_SOFTWARE_CHANNELS/2)
#define MHI_UCI_IPC_LOG_PAGES (100)
-#define MAX_NR_TRBS_PER_CHAN 1
+#define MAX_NR_TRBS_PER_CHAN 9
#define MHI_QTI_IFACE_ID 4
#define DEVICE_NAME "mhi"
@@ -70,7 +70,131 @@
u32 nr_trbs;
/* direction of the channel, see enum mhi_chan_dir */
enum mhi_chan_dir dir;
- u32 uci_ownership;
+ /* need to register mhi channel state change callback */
+ bool register_cb;
+};
+
+/* UCI channel attributes table */
+static const struct chan_attr uci_chan_attr_table[] = {
+ {
+ MHI_CLIENT_LOOPBACK_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_LOOPBACK_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_SAHARA_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_SAHARA_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_EFS_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_EFS_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_MBIM_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_MBIM_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_QMI_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_QMI_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_IP_CTRL_0_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_IP_CTRL_0_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_IP_CTRL_1_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_IP_CTRL_1_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ {
+ MHI_CLIENT_DUN_OUT,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_OUT,
+ false
+ },
+ {
+ MHI_CLIENT_DUN_IN,
+ TRB_MAX_DATA_SIZE,
+ MAX_NR_TRBS_PER_CHAN,
+ MHI_DIR_IN,
+ false
+ },
+ { /* Must be the last */
+ MHI_CLIENT_INVALID,
+ 0,
+ 0,
+ MHI_DIR_INVALID,
+ false
+ },
};
struct uci_ctrl {
@@ -87,6 +211,8 @@
u32 in_chan;
struct mhi_dev_client *out_handle;
struct mhi_dev_client *in_handle;
+ const struct chan_attr *in_chan_attr;
+ const struct chan_attr *out_chan_attr;
wait_queue_head_t read_wq;
wait_queue_head_t write_wq;
atomic_t read_data_ready;
@@ -104,7 +230,6 @@
};
struct mhi_uci_ctxt_t {
- struct chan_attr chan_attrib[MHI_MAX_SOFTWARE_CHANNELS];
struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT];
struct uci_ctrl ctrl_handle;
void (*event_notifier)(struct mhi_dev_client_cb_reason *cb);
@@ -119,6 +244,7 @@
};
#define CHAN_TO_CLIENT(_CHAN_NR) (_CHAN_NR / 2)
+#define CLIENT_TO_CHAN(_CLIENT_NR) (_CLIENT_NR * 2)
#define uci_log(_msg_lvl, _msg, ...) do { \
if (_msg_lvl >= mhi_uci_msg_lvl) { \
@@ -156,7 +282,7 @@
{
int rc = 0;
u32 i, j;
- struct chan_attr *chan_attributes;
+ const struct chan_attr *in_chan_attr;
size_t buf_size;
void *data_loc;
@@ -169,10 +295,15 @@
return -EINVAL;
}
- chan_attributes = &uci_ctxt.chan_attrib[chan];
- buf_size = chan_attributes->max_packet_size;
+ in_chan_attr = client_handle->in_chan_attr;
+ if (!in_chan_attr) {
+ uci_log(UCI_DBG_ERROR, "Null channel attributes for chan %d\n",
+ client_handle->in_chan);
+ return -EINVAL;
+ }
+ buf_size = in_chan_attr->max_packet_size;
- for (i = 0; i < (chan_attributes->nr_trbs); i++) {
+ for (i = 0; i < (in_chan_attr->nr_trbs); i++) {
data_loc = kmalloc(buf_size, GFP_KERNEL);
if (!data_loc) {
rc = -ENOMEM;
@@ -397,20 +528,11 @@
struct file *file_handle)
{
struct uci_client *uci_handle = file_handle->private_data;
- struct mhi_uci_ctxt_t *uci_ctxt;
- u32 nr_in_bufs = 0;
int rc = 0;
- int in_chan = 0;
- u32 buf_size = 0;
if (!uci_handle)
return -EINVAL;
- uci_ctxt = uci_handle->uci_ctxt;
- in_chan = iminor(mhi_inode) + 1;
- nr_in_bufs = uci_ctxt->chan_attrib[in_chan].nr_trbs;
- buf_size = uci_ctxt->chan_attrib[in_chan].max_packet_size;
-
if (atomic_sub_return(1, &uci_handle->ref_count) == 0) {
uci_log(UCI_DBG_DBG,
"Last client left, closing channel 0x%x\n",
@@ -750,54 +872,25 @@
static int uci_init_client_attributes(struct mhi_uci_ctxt_t *uci_ctxt)
{
- u32 i = 0;
- u32 data_size = TRB_MAX_DATA_SIZE;
- u32 index = 0;
+ u32 i;
+ u32 index;
struct uci_client *client;
- struct chan_attr *chan_attrib = NULL;
+ const struct chan_attr *chan_attrib;
- for (i = 0; i < ARRAY_SIZE(uci_ctxt->chan_attrib); i++) {
- chan_attrib = &uci_ctxt->chan_attrib[i];
- switch (i) {
- case MHI_CLIENT_LOOPBACK_OUT:
- case MHI_CLIENT_LOOPBACK_IN:
- case MHI_CLIENT_SAHARA_OUT:
- case MHI_CLIENT_SAHARA_IN:
- case MHI_CLIENT_EFS_OUT:
- case MHI_CLIENT_EFS_IN:
- case MHI_CLIENT_MBIM_OUT:
- case MHI_CLIENT_MBIM_IN:
- case MHI_CLIENT_QMI_OUT:
- case MHI_CLIENT_QMI_IN:
- case MHI_CLIENT_IP_CTRL_0_OUT:
- case MHI_CLIENT_IP_CTRL_0_IN:
- case MHI_CLIENT_IP_CTRL_1_OUT:
- case MHI_CLIENT_IP_CTRL_1_IN:
- case MHI_CLIENT_DUN_OUT:
- case MHI_CLIENT_DUN_IN:
- chan_attrib->uci_ownership = 1;
+ for (i = 0; i < ARRAY_SIZE(uci_chan_attr_table); i += 2) {
+ chan_attrib = &uci_chan_attr_table[i];
+ if (chan_attrib->chan_id == MHI_CLIENT_INVALID)
break;
- default:
- chan_attrib->uci_ownership = 0;
- break;
- }
- if (chan_attrib->uci_ownership) {
- chan_attrib->chan_id = i;
- chan_attrib->max_packet_size = data_size;
- index = CHAN_TO_CLIENT(i);
- client = &uci_ctxt->client_handles[index];
- chan_attrib->nr_trbs = 9;
- client->in_buf_list =
- kmalloc(sizeof(struct mhi_dev_iov) *
- chan_attrib->nr_trbs,
+ index = CHAN_TO_CLIENT(i);
+ client = &uci_ctxt->client_handles[index];
+ client->out_chan_attr = chan_attrib;
+ client->in_chan_attr = ++chan_attrib;
+ client->in_buf_list =
+ kcalloc(chan_attrib->nr_trbs,
+ sizeof(struct mhi_dev_iov),
GFP_KERNEL);
- if (client->in_buf_list == NULL)
- return -ENOMEM;
- }
- if (i % 2 == 0)
- chan_attrib->dir = MHI_DIR_OUT;
- else
- chan_attrib->dir = MHI_DIR_IN;
+ if (!client->in_buf_list)
+ return -ENOMEM;
}
return 0;
}
@@ -949,16 +1042,14 @@
uci_log(UCI_DBG_INFO, "Registering for MHI events.\n");
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++) {
- if (uci_ctxt.chan_attrib[i * 2].uci_ownership) {
- mhi_client = &uci_ctxt.client_handles[i];
-
- r = mhi_register_client(mhi_client, i);
-
- if (r) {
- uci_log(UCI_DBG_CRITICAL,
- "Failed to reg client %d ret %d\n",
- r, i);
- }
+ mhi_client = &uci_ctxt.client_handles[i];
+ if (!mhi_client->in_chan_attr)
+ continue;
+ r = mhi_register_client(mhi_client, i);
+ if (r) {
+ uci_log(UCI_DBG_CRITICAL,
+ "Failed to reg client %d ret %d\n",
+ r, i);
}
}
@@ -992,29 +1083,30 @@
uci_log(UCI_DBG_INFO, "Setting up device nodes.\n");
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++) {
- if (uci_ctxt.chan_attrib[i*2].uci_ownership) {
- cdev_init(&uci_ctxt.cdev[i], &mhi_uci_client_fops);
- uci_ctxt.cdev[i].owner = THIS_MODULE;
- r = cdev_add(&uci_ctxt.cdev[i],
- uci_ctxt.start_ctrl_nr + i, 1);
- if (IS_ERR_VALUE(r)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to add cdev %d, ret 0x%x\n",
- i, r);
- goto failed_char_add;
- }
+ mhi_client = &uci_ctxt.client_handles[i];
+ if (!mhi_client->in_chan_attr)
+ continue;
+ cdev_init(&uci_ctxt.cdev[i], &mhi_uci_client_fops);
+ uci_ctxt.cdev[i].owner = THIS_MODULE;
+ r = cdev_add(&uci_ctxt.cdev[i],
+ uci_ctxt.start_ctrl_nr + i, 1);
+ if (IS_ERR_VALUE(r)) {
+ uci_log(UCI_DBG_ERROR,
+ "Failed to add cdev %d, ret 0x%x\n",
+ i, r);
+ goto failed_char_add;
+ }
- uci_ctxt.client_handles[i].dev =
- device_create(uci_ctxt.mhi_uci_class, NULL,
- uci_ctxt.start_ctrl_nr + i,
- NULL, DEVICE_NAME "_pipe_%d",
- i * 2);
- if (IS_ERR(uci_ctxt.client_handles[i].dev)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to add cdev %d\n", i);
- cdev_del(&uci_ctxt.cdev[i]);
- goto failed_device_create;
- }
+ uci_ctxt.client_handles[i].dev =
+ device_create(uci_ctxt.mhi_uci_class, NULL,
+ uci_ctxt.start_ctrl_nr + i,
+ NULL, DEVICE_NAME "_pipe_%d",
+ i * 2);
+ if (IS_ERR(uci_ctxt.client_handles[i].dev)) {
+ uci_log(UCI_DBG_ERROR,
+ "Failed to add cdev %d\n", i);
+ cdev_del(&uci_ctxt.cdev[i]);
+ goto failed_device_create;
}
}
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 6ce2161..ed4b837 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -317,7 +317,9 @@
static int geni_se_select_dma_mode(void __iomem *base)
{
+ int proto = get_se_proto(base);
unsigned int geni_dma_mode = 0;
+ unsigned int common_geni_m_irq_en;
geni_write_reg(0, base, SE_GSI_EVENT_EN);
geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
@@ -326,6 +328,12 @@
geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
+ common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
+ if (proto != UART)
+ common_geni_m_irq_en &=
+ ~(M_TX_FIFO_WATERMARK_EN | M_RX_FIFO_WATERMARK_EN);
+
+ geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
geni_dma_mode |= GENI_DMA_MODE_EN;
geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 9374bc8..aafb5c8 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -1119,7 +1119,7 @@
&ctx->usb_bam_connections[idx];
unsigned long peer_bam_handle;
- ret = sps_phy2h(pipe_connect->dst_phy_addr, &peer_bam_handle);
+ ret = sps_phy2h(pipe_connect->src_phy_addr, &peer_bam_handle);
if (ret) {
log_event_err("%s: sps_phy2h failed (src BAM) %d\n",
__func__, ret);
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 6eb2837..687cc5b 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -120,6 +120,10 @@
.xusb2pr = 0x01D9,
};
+static struct quirk_entry quirk_asus_ux330uak = {
+ .wmi_force_als_set = true,
+};
+
static int dmi_matched(const struct dmi_system_id *dmi)
{
quirks = dmi->driver_data;
@@ -152,6 +156,15 @@
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X302UA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X302UA"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X401U",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
@@ -413,6 +426,15 @@
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. UX330UAK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UX330UAK"),
+ },
+ .driver_data = &quirk_asus_ux330uak,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X550LB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c
index 1871602..c098328 100644
--- a/drivers/platform/x86/asus-wireless.c
+++ b/drivers/platform/x86/asus-wireless.c
@@ -145,8 +145,10 @@
{
struct asus_wireless_data *data = acpi_driver_data(adev);
- if (data->wq)
+ if (data->wq) {
+ devm_led_classdev_unregister(&adev->dev, &data->led);
destroy_workqueue(data->wq);
+ }
return 0;
}
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 8499d3a..8a1bfd4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1109,6 +1109,15 @@
}
/*
+ * Some devices dont support or have borcken get_als method
+ * but still support set method.
+ */
+static void asus_wmi_set_als(void)
+{
+ asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
+}
+
+/*
* Hwmon device
*/
static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
@@ -2120,6 +2129,9 @@
goto fail_rfkill;
}
+ if (asus->driver->quirks->wmi_force_als_set)
+ asus_wmi_set_als();
+
/* Some Asus desktop boards export an acpi-video backlight interface,
stop this from showing up */
chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index fdff626..5db052d 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -45,6 +45,7 @@
bool store_backlight_power;
bool wmi_backlight_power;
bool wmi_backlight_native;
+ bool wmi_force_als_set;
int wapf;
/*
* For machines with AMD graphic chips, it will send out WMI event
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
index 7808076..a74340d 100644
--- a/drivers/platform/x86/intel-vbtn.c
+++ b/drivers/platform/x86/intel-vbtn.c
@@ -37,6 +37,10 @@
static const struct key_entry intel_vbtn_keymap[] = {
{ KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
{ KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
+ { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* volume-up key press */
+ { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */
+ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */
+ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
{ KE_END },
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index b65ce75..60ee94e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -9526,7 +9526,7 @@
},
};
-static int __init set_ibm_param(const char *val, struct kernel_param *kp)
+static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
{
unsigned int i;
struct ibm_struct *ibm;
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 5b31889..1a28e56 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -76,7 +76,7 @@
#endif
static bool scm_dload_supported;
-static int dload_set(const char *val, struct kernel_param *kp);
+static int dload_set(const char *val, const struct kernel_param *kp);
/* interface for exporting attributes */
struct reset_attribute {
struct attribute attr;
@@ -179,7 +179,7 @@
pr_err("Failed to set secure EDLOAD mode: %d\n", ret);
}
-static int dload_set(const char *val, struct kernel_param *kp)
+static int dload_set(const char *val, const struct kernel_param *kp)
{
int ret;
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index 5cee9aa..48a11fd 100644
--- a/drivers/power/supply/ab8500_charger.c
+++ b/drivers/power/supply/ab8500_charger.c
@@ -3218,11 +3218,13 @@
}
/* Enable backup battery charging */
- abx500_mask_and_set_register_interruptible(di->dev,
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
AB8500_RTC, AB8500_RTC_CTRL_REG,
RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
- if (ret < 0)
+ if (ret < 0) {
dev_err(di->dev, "%s mask and set failed\n", __func__);
+ goto out;
+ }
if (is_ab8540(di->parent)) {
ret = abx500_mask_and_set_register_interruptible(di->dev,
diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c
index 73e2f0b..c4770a9 100644
--- a/drivers/power/supply/bq2415x_charger.c
+++ b/drivers/power/supply/bq2415x_charger.c
@@ -1569,6 +1569,11 @@
acpi_id =
acpi_match_device(client->dev.driver->acpi_match_table,
&client->dev);
+ if (!acpi_id) {
+ dev_err(&client->dev, "failed to match device name\n");
+ ret = -ENODEV;
+ goto error_1;
+ }
name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
}
if (!name) {
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
index 50171fd..8ed2782 100644
--- a/drivers/power/supply/bq24190_charger.c
+++ b/drivers/power/supply/bq24190_charger.c
@@ -506,6 +506,9 @@
int ret, limit = 100;
u8 v;
+ if (device_property_read_bool(bdi->dev, "disable-reset"))
+ return 0;
+
/* Reset the registers */
ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
BQ24190_REG_POC_RESET_MASK,
@@ -1184,8 +1187,13 @@
}
} while (f_reg && ++i < 2);
+ /* ignore over/under voltage fault after disconnect */
+ if (f_reg == (1 << BQ24190_REG_F_CHRG_FAULT_SHIFT) &&
+ !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK))
+ f_reg = 0;
+
if (f_reg != bdi->f_reg) {
- dev_info(bdi->dev,
+ dev_warn(bdi->dev,
"Fault: boost %d, charge %d, battery %d, ntc %d\n",
!!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK),
!!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK),
diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c
index 4cd6899..95af5f3 100644
--- a/drivers/power/supply/isp1704_charger.c
+++ b/drivers/power/supply/isp1704_charger.c
@@ -418,6 +418,10 @@
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct isp1704_charger_data), GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto fail0;
+ }
pdata->enable_gpio = gpio;
dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
diff --git a/drivers/power/supply/pda_power.c b/drivers/power/supply/pda_power.c
index dfe1ee8..922a867 100644
--- a/drivers/power/supply/pda_power.c
+++ b/drivers/power/supply/pda_power.c
@@ -30,9 +30,9 @@
static struct device *dev;
static struct pda_power_pdata *pdata;
static struct resource *ac_irq, *usb_irq;
-static struct timer_list charger_timer;
-static struct timer_list supply_timer;
-static struct timer_list polling_timer;
+static struct delayed_work charger_work;
+static struct delayed_work polling_work;
+static struct delayed_work supply_work;
static int polling;
static struct power_supply *pda_psy_ac, *pda_psy_usb;
@@ -140,7 +140,7 @@
}
}
-static void supply_timer_func(unsigned long unused)
+static void supply_work_func(struct work_struct *work)
{
if (ac_status == PDA_PSY_TO_CHANGE) {
ac_status = new_ac_status;
@@ -161,11 +161,12 @@
* Okay, charger set. Now wait a bit before notifying supplicants,
* charge power should stabilize.
*/
- mod_timer(&supply_timer,
- jiffies + msecs_to_jiffies(pdata->wait_for_charger));
+ cancel_delayed_work(&supply_work);
+ schedule_delayed_work(&supply_work,
+ msecs_to_jiffies(pdata->wait_for_charger));
}
-static void charger_timer_func(unsigned long unused)
+static void charger_work_func(struct work_struct *work)
{
update_status();
psy_changed();
@@ -184,13 +185,14 @@
* Wait a bit before reading ac/usb line status and setting charger,
* because ac/usb status readings may lag from irq.
*/
- mod_timer(&charger_timer,
- jiffies + msecs_to_jiffies(pdata->wait_for_status));
+ cancel_delayed_work(&charger_work);
+ schedule_delayed_work(&charger_work,
+ msecs_to_jiffies(pdata->wait_for_status));
return IRQ_HANDLED;
}
-static void polling_timer_func(unsigned long unused)
+static void polling_work_func(struct work_struct *work)
{
int changed = 0;
@@ -211,8 +213,9 @@
if (changed)
psy_changed();
- mod_timer(&polling_timer,
- jiffies + msecs_to_jiffies(pdata->polling_interval));
+ cancel_delayed_work(&polling_work);
+ schedule_delayed_work(&polling_work,
+ msecs_to_jiffies(pdata->polling_interval));
}
#if IS_ENABLED(CONFIG_USB_PHY)
@@ -250,8 +253,9 @@
* Wait a bit before reading ac/usb line status and setting charger,
* because ac/usb status readings may lag from irq.
*/
- mod_timer(&charger_timer,
- jiffies + msecs_to_jiffies(pdata->wait_for_status));
+ cancel_delayed_work(&charger_work);
+ schedule_delayed_work(&charger_work,
+ msecs_to_jiffies(pdata->wait_for_status));
return NOTIFY_OK;
}
@@ -300,8 +304,8 @@
if (!pdata->ac_max_uA)
pdata->ac_max_uA = 500000;
- setup_timer(&charger_timer, charger_timer_func, 0);
- setup_timer(&supply_timer, supply_timer_func, 0);
+ INIT_DELAYED_WORK(&charger_work, charger_work_func);
+ INIT_DELAYED_WORK(&supply_work, supply_work_func);
ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
@@ -385,9 +389,10 @@
if (polling) {
dev_dbg(dev, "will poll for status\n");
- setup_timer(&polling_timer, polling_timer_func, 0);
- mod_timer(&polling_timer,
- jiffies + msecs_to_jiffies(pdata->polling_interval));
+ INIT_DELAYED_WORK(&polling_work, polling_work_func);
+ cancel_delayed_work(&polling_work);
+ schedule_delayed_work(&polling_work,
+ msecs_to_jiffies(pdata->polling_interval));
}
if (ac_irq || usb_irq)
@@ -433,9 +438,9 @@
free_irq(ac_irq->start, pda_psy_ac);
if (polling)
- del_timer_sync(&polling_timer);
- del_timer_sync(&charger_timer);
- del_timer_sync(&supply_timer);
+ cancel_delayed_work_sync(&polling_work);
+ cancel_delayed_work_sync(&charger_work);
+ cancel_delayed_work_sync(&supply_work);
if (pdata->is_usb_online)
power_supply_unregister(pda_psy_usb);
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index 3dd1722..cd76a09 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -331,6 +331,13 @@
POWER_SUPPLY_ATTR(allow_hvdcp3),
POWER_SUPPLY_ATTR(hvdcp_opti_allowed),
POWER_SUPPLY_ATTR(max_pulse_allowed),
+ POWER_SUPPLY_ATTR(ignore_false_negative_isense),
+ POWER_SUPPLY_ATTR(battery_info),
+ POWER_SUPPLY_ATTR(battery_info_id),
+ POWER_SUPPLY_ATTR(enable_jeita_detection),
+ POWER_SUPPLY_ATTR(esr_actual),
+ POWER_SUPPLY_ATTR(esr_nominal),
+ POWER_SUPPLY_ATTR(soh),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index e571eb4..1c8f116 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -29,6 +29,17 @@
The driver reports the charger status via the power supply framework.
A charger status change triggers an IRQ via the device STAT pin.
+config SMB1360_CHARGER_FG
+ tristate "SMB1360 Charger and Fuel Gauge"
+ depends on I2C
+ help
+ Say Y to include support for SMB1360 Charger and Fuel Gauge.
+ SMB1360 is a single path switching mode charger capable of charging
+ the battery with 1.5Amps of current. It supports a fuel gauge which
+ uses voltage and coloumb counting for state of charge reporting.
+ The driver reports the status via the power supply framework.
+ A status change triggers an IRQ via the device STAT pin.
+
config SMB1355_SLAVE_CHARGER
tristate "SMB1355 Slave Battery Charger"
depends on MFD_I2C_PMIC
@@ -104,6 +115,26 @@
module. It also allows userspace code to read diagnostics of voltage
and current measured during certain phases of the pulses.
+config QPNP_VM_BMS
+ tristate "QPNP Voltage-Mode Battery Monitoring System driver"
+ depends on MFD_SPMI_PMIC
+ help
+ Say Y here to enable support for QPNP chip vm-bms device.
+ The voltage-mode (vm) BMS driver uses periodic VBATT
+ readings from the battery to calculate the State of
+ Charge.
+
+config QPNP_LINEAR_CHARGER
+ tristate "QPNP Linear Charger driver"
+ depends on MFD_SPMI_PMIC
+ depends on THERMAL_QPNP_ADC_TM
+ help
+ Say Y here to enable the Linear battery charger which supports USB
+ detection and charging. The driver also offers relevant information
+ to userspace via the power supply framework.
+ The power supply framework is used to communicate battery and
+ usb properties to userspace and other driver consumers like USB.
+
config QPNP_TYPEC
tristate "QPNP Type-C driver"
depends on MFD_SPMI_PMIC
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index de76a5b..ffc1ce3 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -2,12 +2,15 @@
obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o pmic-voter.o
obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o
+obj-$(CONFIG_SMB1360_CHARGER_FG) += smb1360-charger-fg.o
obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_SMB138X_CHARGER) += step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
-obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o
+obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o
obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
obj-$(CONFIG_QPNP_TYPEC) += qpnp-typec.o
obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o
obj-$(CONFIG_SMB1390_CHARGE_PUMP) += smb1390-charger.o pmic-voter.o
+obj-$(CONFIG_QPNP_VM_BMS) += qpnp-vm-bms.o batterydata-lib.o batterydata-interface.o
+obj-$(CONFIG_QPNP_LINEAR_CHARGER) += qpnp-linear-charger.o
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 275b982..e8d91ae 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -78,6 +78,7 @@
struct class qcom_batt_class;
struct wakeup_source *pl_ws;
struct notifier_block nb;
+ bool pl_disable;
};
struct pl_data *the_chip;
@@ -88,6 +89,7 @@
enum {
AICL_RERUN_WA_BIT = BIT(0),
+ FORCE_INOV_DISABLE_BIT = BIT(1),
};
static int debug_mask;
@@ -848,6 +850,12 @@
chip->pl_settled_ua = 0;
}
+ /* notify parallel state change */
+ if (chip->pl_psy && (chip->pl_disable != pl_disable)) {
+ power_supply_changed(chip->pl_psy);
+ chip->pl_disable = (bool)pl_disable;
+ }
+
pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n",
pl_disable ? "disabled" : "enabled");
@@ -916,7 +924,8 @@
chip->pl_mode = pval.intval;
/* Disable autonomous votage increments for USBIN-USBIN */
- if (IS_USBIN(chip->pl_mode)) {
+ if (IS_USBIN(chip->pl_mode)
+ && (chip->wa_flags & FORCE_INOV_DISABLE_BIT)) {
if (!chip->hvdcp_hw_inov_dis_votable)
chip->hvdcp_hw_inov_dis_votable =
find_votable("HVDCP_HW_INOV_DIS");
@@ -1041,7 +1050,6 @@
else
vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, true, 0);
- rerun_election(chip->fcc_votable);
if (IS_USBIN(chip->pl_mode)) {
/*
@@ -1200,7 +1208,9 @@
switch (smb_version) {
case PMI8998_SUBTYPE:
case PM660_SUBTYPE:
- chip->wa_flags = AICL_RERUN_WA_BIT;
+ chip->wa_flags = AICL_RERUN_WA_BIT | FORCE_INOV_DISABLE_BIT;
+ break;
+ case PMI632_SUBTYPE:
break;
default:
break;
@@ -1301,6 +1311,7 @@
goto unreg_notifier;
}
+ chip->pl_disable = true;
chip->qcom_batt_class.name = "qcom-battery",
chip->qcom_batt_class.owner = THIS_MODULE,
chip->qcom_batt_class.class_attrs = pl_attributes;
diff --git a/drivers/power/supply/qcom/batterydata-interface.c b/drivers/power/supply/qcom/batterydata-interface.c
new file mode 100644
index 0000000..0187827
--- /dev/null
+++ b/drivers/power/supply/qcom/batterydata-interface.c
@@ -0,0 +1,218 @@
+/* Copyright (c) 2014, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "BATTERY: %s: " fmt, __func__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+#include <linux/batterydata-lib.h>
+#include <linux/batterydata-interface.h>
+
+struct battery_data {
+ dev_t dev_no;
+ struct class *battery_class;
+ struct device *battery_device;
+ struct cdev battery_cdev;
+ struct bms_battery_data *profile;
+};
+static struct battery_data *the_battery;
+
+static int battery_data_open(struct inode *inode, struct file *file)
+{
+ struct battery_data *battery = container_of(inode->i_cdev,
+ struct battery_data, battery_cdev);
+
+ pr_debug("battery_data device opened\n");
+
+ file->private_data = battery;
+
+ return 0;
+}
+
+static long battery_data_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct battery_data *battery = file->private_data;
+ struct battery_params __user *bp_user =
+ (struct battery_params __user *)arg;
+ struct battery_params bp;
+ int soc, rbatt_sf, slope, fcc_mah;
+ int rc = 0;
+
+ if (!battery->profile) {
+ pr_err("Battery data not set!\n");
+ return -EINVAL;
+ }
+ if (copy_from_user(&bp, bp_user, sizeof(bp))) {
+ pr_err("copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case BPIOCXSOC:
+ soc = interpolate_pc(battery->profile->pc_temp_ocv_lut,
+ bp.batt_temp, bp.ocv_uv / 1000);
+ rc = put_user(soc, &bp_user->soc);
+ if (rc) {
+ pr_err("BPIOCXSOC: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXSOC: ocv=%d batt_temp=%d soc=%d\n",
+ bp.ocv_uv / 1000, bp.batt_temp, soc);
+ break;
+ case BPIOCXRBATT:
+ rbatt_sf = interpolate_scalingfactor(
+ battery->profile->rbatt_sf_lut,
+ bp.batt_temp, bp.soc);
+ rc = put_user(rbatt_sf, &bp_user->rbatt_sf);
+ if (rc) {
+ pr_err("BPIOCXRBATT: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXRBATT: soc=%d batt_temp=%d rbatt_sf=%d\n",
+ bp.soc, bp.batt_temp, rbatt_sf);
+ break;
+ case BPIOCXSLOPE:
+ slope = interpolate_slope(battery->profile->pc_temp_ocv_lut,
+ bp.batt_temp, bp.soc);
+ rc = put_user(slope, &bp_user->slope);
+ if (rc) {
+ pr_err("BPIOCXSLOPE: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXSLOPE: soc=%d batt_temp=%d slope=%d\n",
+ bp.soc, bp.batt_temp, slope);
+ break;
+ case BPIOCXFCC:
+ fcc_mah = interpolate_fcc(battery->profile->fcc_temp_lut,
+ bp.batt_temp);
+ rc = put_user(fcc_mah, &bp_user->fcc_mah);
+ if (rc) {
+ pr_err("BPIOCXFCC: Failed to 'put_user' rc=%d\n", rc);
+ goto ret_err;
+ }
+ pr_debug("BPIOCXFCC: batt_temp=%d fcc_mah=%d\n",
+ bp.batt_temp, fcc_mah);
+ break;
+ default:
+ pr_err("IOCTL %d not supported\n", cmd);
+ rc = -EINVAL;
+
+ }
+ret_err:
+ return rc;
+}
+
+static int battery_data_release(struct inode *inode, struct file *file)
+{
+ pr_debug("battery_data device closed\n");
+
+ return 0;
+}
+
+static const struct file_operations battery_data_fops = {
+ .owner = THIS_MODULE,
+ .open = battery_data_open,
+ .unlocked_ioctl = battery_data_ioctl,
+ .compat_ioctl = battery_data_ioctl,
+ .release = battery_data_release,
+};
+
+int config_battery_data(struct bms_battery_data *profile)
+{
+ if (!the_battery) {
+ pr_err("Battery data not initialized\n");
+ return -ENODEV;
+ }
+
+ the_battery->profile = profile;
+
+ pr_debug("Battery profile set - %s\n",
+ the_battery->profile->battery_type);
+
+ return 0;
+}
+
+static int batterydata_init(void)
+{
+ int rc;
+ struct battery_data *battery;
+
+ battery = kzalloc(sizeof(*battery), GFP_KERNEL);
+ if (!battery)
+ return -ENOMEM;
+
+ /* character device to access the battery-data from userspace */
+ rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "battery_data");
+ if (rc) {
+ pr_err("Unable to allocate chrdev rc=%d\n", rc);
+ return rc;
+ }
+ cdev_init(&battery->battery_cdev, &battery_data_fops);
+ rc = cdev_add(&battery->battery_cdev, battery->dev_no, 1);
+ if (rc) {
+ pr_err("Unable to add battery_cdev rc=%d\n", rc);
+ goto unregister_chrdev;
+ }
+
+ battery->battery_class = class_create(THIS_MODULE, "battery_data");
+ if (IS_ERR_OR_NULL(battery->battery_class)) {
+ pr_err("Fail to create battery class\n");
+ rc = -ENODEV;
+ goto delete_cdev;
+ }
+
+ battery->battery_device = device_create(battery->battery_class,
+ NULL, battery->dev_no,
+ NULL, "battery_data");
+ if (IS_ERR(battery->battery_device)) {
+ pr_err("Fail to create battery_device device\n");
+ rc = -ENODEV;
+ goto delete_cdev;
+ }
+
+ the_battery = battery;
+
+ pr_info("Battery-data device created!\n");
+
+ return 0;
+
+delete_cdev:
+ cdev_del(&battery->battery_cdev);
+unregister_chrdev:
+ unregister_chrdev_region(battery->dev_no, 1);
+ the_battery = NULL;
+ return rc;
+}
+subsys_initcall(batterydata_init);
+
+static void batterydata_exit(void)
+{
+ if (the_battery) {
+ device_destroy(the_battery->battery_class, the_battery->dev_no);
+ cdev_del(&the_battery->battery_cdev);
+ unregister_chrdev_region(the_battery->dev_no, 1);
+ }
+ kfree(the_battery);
+ the_battery = NULL;
+}
+module_exit(batterydata_exit);
+
+MODULE_DESCRIPTION("Battery-data Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/qcom/batterydata-lib.c b/drivers/power/supply/qcom/batterydata-lib.c
new file mode 100644
index 0000000..fae5658
--- /dev/null
+++ b/drivers/power/supply/qcom/batterydata-lib.c
@@ -0,0 +1,493 @@
+/* Copyright (c) 2012-2014, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/batterydata-lib.h>
+
+int linear_interpolate(int y0, int x0, int y1, int x1, int x)
+{
+ if (y0 == y1 || x == x0)
+ return y0;
+ if (x1 == x0 || x == x1)
+ return y1;
+
+ return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
+}
+
+static int interpolate_single_lut_scaled(struct single_row_lut *lut,
+ int x, int scale)
+{
+ int i, result;
+
+ if (x < lut->x[0] * scale) {
+ pr_debug("x %d less than known range return y = %d lut = %pS\n",
+ x, lut->y[0], lut);
+ return lut->y[0];
+ }
+ if (x > lut->x[lut->cols - 1] * scale) {
+ pr_debug("x %d more than known range return y = %d lut = %pS\n",
+ x, lut->y[lut->cols - 1], lut);
+ return lut->y[lut->cols - 1];
+ }
+
+ for (i = 0; i < lut->cols; i++)
+ if (x <= lut->x[i] * scale)
+ break;
+ if (x == lut->x[i] * scale) {
+ result = lut->y[i];
+ } else {
+ result = linear_interpolate(
+ lut->y[i - 1],
+ lut->x[i - 1] * scale,
+ lut->y[i],
+ lut->x[i] * scale,
+ x);
+ }
+ return result;
+}
+
+int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp)
+{
+ return interpolate_single_lut_scaled(fcc_temp_lut,
+ batt_temp,
+ DEGC_SCALE);
+}
+
+int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut,
+ int cycles)
+{
+ /*
+ * sf table could be null when no battery aging data is available, in
+ * that case return 100%
+ */
+ if (fcc_sf_lut)
+ return interpolate_single_lut_scaled(fcc_sf_lut, cycles, 1);
+ else
+ return 100;
+}
+
+int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc)
+{
+ int i, scalefactorrow1, scalefactorrow2, scalefactor, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+
+ /*
+ * sf table could be null when no battery aging data is available, in
+ * that case return 100%
+ */
+ if (!sf_lut)
+ return 100;
+
+ rows = sf_lut->rows;
+ cols = sf_lut->cols;
+ if (pc > sf_lut->percent[0]) {
+ pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
+ row1 = 0;
+ row2 = 0;
+ } else if (pc < sf_lut->percent[rows - 1]) {
+ pr_debug("pc %d less than known pc ranges for sf\n", pc);
+ row1 = rows - 1;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == sf_lut->percent[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (pc > sf_lut->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (row_entry < sf_lut->row_entries[0] * DEGC_SCALE)
+ row_entry = sf_lut->row_entries[0] * DEGC_SCALE;
+ if (row_entry > sf_lut->row_entries[cols - 1] * DEGC_SCALE)
+ row_entry = sf_lut->row_entries[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (row_entry <= sf_lut->row_entries[i] * DEGC_SCALE)
+ break;
+ if (row_entry == sf_lut->row_entries[i] * DEGC_SCALE) {
+ scalefactor = linear_interpolate(
+ sf_lut->sf[row1][i],
+ sf_lut->percent[row1],
+ sf_lut->sf[row2][i],
+ sf_lut->percent[row2],
+ pc);
+ return scalefactor;
+ }
+
+ scalefactorrow1 = linear_interpolate(
+ sf_lut->sf[row1][i - 1],
+ sf_lut->row_entries[i - 1] * DEGC_SCALE,
+ sf_lut->sf[row1][i],
+ sf_lut->row_entries[i] * DEGC_SCALE,
+ row_entry);
+
+ scalefactorrow2 = linear_interpolate(
+ sf_lut->sf[row2][i - 1],
+ sf_lut->row_entries[i - 1] * DEGC_SCALE,
+ sf_lut->sf[row2][i],
+ sf_lut->row_entries[i] * DEGC_SCALE,
+ row_entry);
+
+ scalefactor = linear_interpolate(
+ scalefactorrow1,
+ sf_lut->percent[row1],
+ scalefactorrow2,
+ sf_lut->percent[row2],
+ pc);
+
+ return scalefactor;
+}
+
+/* get ocv given a soc -- reverse lookup */
+int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
+ int batt_temp, int pc)
+{
+ int i, ocvrow1, ocvrow2, ocv, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+
+ rows = pc_temp_ocv->rows;
+ cols = pc_temp_ocv->cols;
+ if (pc > pc_temp_ocv->percent[0]) {
+ pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
+ row1 = 0;
+ row2 = 0;
+ } else if (pc < pc_temp_ocv->percent[rows - 1]) {
+ pr_debug("pc %d less than known pc ranges for sf\n", pc);
+ row1 = rows - 1;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == pc_temp_ocv->percent[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (pc > pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
+ break;
+ if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
+ ocv = linear_interpolate(
+ pc_temp_ocv->ocv[row1][i],
+ pc_temp_ocv->percent[row1],
+ pc_temp_ocv->ocv[row2][i],
+ pc_temp_ocv->percent[row2],
+ pc);
+ return ocv;
+ }
+
+ ocvrow1 = linear_interpolate(
+ pc_temp_ocv->ocv[row1][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row1][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocvrow2 = linear_interpolate(
+ pc_temp_ocv->ocv[row2][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row2][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocv = linear_interpolate(
+ ocvrow1,
+ pc_temp_ocv->percent[row1],
+ ocvrow2,
+ pc_temp_ocv->percent[row2],
+ pc);
+
+ return ocv;
+}
+
+int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
+ int batt_temp, int ocv)
+{
+ int i, j, pcj, pcj_minus_one, pc;
+ int rows = pc_temp_ocv->rows;
+ int cols = pc_temp_ocv->cols;
+
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE) {
+ pr_debug("batt_temp %d < known temp range\n", batt_temp);
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ }
+
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE) {
+ pr_debug("batt_temp %d > known temp range\n", batt_temp);
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
+ }
+
+ for (j = 0; j < cols; j++)
+ if (batt_temp <= pc_temp_ocv->temp[j] * DEGC_SCALE)
+ break;
+ if (batt_temp == pc_temp_ocv->temp[j] * DEGC_SCALE) {
+ /* found an exact match for temp in the table */
+ if (ocv >= pc_temp_ocv->ocv[0][j])
+ return pc_temp_ocv->percent[0];
+ if (ocv <= pc_temp_ocv->ocv[rows - 1][j])
+ return pc_temp_ocv->percent[rows - 1];
+ for (i = 0; i < rows; i++) {
+ if (ocv >= pc_temp_ocv->ocv[i][j]) {
+ if (ocv == pc_temp_ocv->ocv[i][j])
+ return pc_temp_ocv->percent[i];
+ pc = linear_interpolate(
+ pc_temp_ocv->percent[i],
+ pc_temp_ocv->ocv[i][j],
+ pc_temp_ocv->percent[i - 1],
+ pc_temp_ocv->ocv[i - 1][j],
+ ocv);
+ return pc;
+ }
+ }
+ }
+
+ /*
+ * batt_temp is within temperature for
+ * column j-1 and j
+ */
+ if (ocv >= pc_temp_ocv->ocv[0][j])
+ return pc_temp_ocv->percent[0];
+ if (ocv <= pc_temp_ocv->ocv[rows - 1][j - 1])
+ return pc_temp_ocv->percent[rows - 1];
+
+ pcj_minus_one = 0;
+ pcj = 0;
+ for (i = 0; i < rows-1; i++) {
+ if (pcj == 0
+ && is_between(pc_temp_ocv->ocv[i][j],
+ pc_temp_ocv->ocv[i+1][j], ocv)) {
+ pcj = linear_interpolate(
+ pc_temp_ocv->percent[i],
+ pc_temp_ocv->ocv[i][j],
+ pc_temp_ocv->percent[i + 1],
+ pc_temp_ocv->ocv[i+1][j],
+ ocv);
+ }
+
+ if (pcj_minus_one == 0
+ && is_between(pc_temp_ocv->ocv[i][j-1],
+ pc_temp_ocv->ocv[i+1][j-1], ocv)) {
+ pcj_minus_one = linear_interpolate(
+ pc_temp_ocv->percent[i],
+ pc_temp_ocv->ocv[i][j-1],
+ pc_temp_ocv->percent[i + 1],
+ pc_temp_ocv->ocv[i+1][j-1],
+ ocv);
+ }
+
+ if (pcj && pcj_minus_one) {
+ pc = linear_interpolate(
+ pcj_minus_one,
+ pc_temp_ocv->temp[j-1] * DEGC_SCALE,
+ pcj,
+ pc_temp_ocv->temp[j] * DEGC_SCALE,
+ batt_temp);
+ return pc;
+ }
+ }
+
+ if (pcj)
+ return pcj;
+
+ if (pcj_minus_one)
+ return pcj_minus_one;
+
+ pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%\n",
+ ocv, batt_temp);
+ return 100;
+}
+
+int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv,
+ int batt_temp, int pc)
+{
+ int i, ocvrow1, ocvrow2, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+ int slope;
+
+ rows = pc_temp_ocv->rows;
+ cols = pc_temp_ocv->cols;
+ if (pc >= pc_temp_ocv->percent[0]) {
+ pr_debug("pc %d >= max pc range - use the slope at pc=%d\n",
+ pc, pc_temp_ocv->percent[0]);
+ row1 = 0;
+ row2 = 1;
+ } else if (pc <= pc_temp_ocv->percent[rows - 1]) {
+ pr_debug("pc %d is <= min pc range - use the slope at pc=%d\n",
+ pc, pc_temp_ocv->percent[rows - 1]);
+ row1 = rows - 2;
+ row2 = rows - 1;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ if (pc > pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
+ break;
+
+ if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
+ slope = (pc_temp_ocv->ocv[row1][i] -
+ pc_temp_ocv->ocv[row2][i]);
+ if (slope <= 0) {
+ pr_warn("Slope=%d for pc=%d, using 1\n", slope, pc);
+ slope = 1;
+ }
+ slope *= 1000;
+ slope /= (pc_temp_ocv->percent[row1] -
+ pc_temp_ocv->percent[row2]);
+ return slope;
+ }
+ ocvrow1 = linear_interpolate(
+ pc_temp_ocv->ocv[row1][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row1][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ ocvrow2 = linear_interpolate(
+ pc_temp_ocv->ocv[row2][i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
+ pc_temp_ocv->ocv[row2][i],
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ slope = (ocvrow1 - ocvrow2);
+ if (slope <= 0) {
+ pr_warn("Slope=%d for pc=%d, using 1\n", slope, pc);
+ slope = 1;
+ }
+ slope *= 1000;
+ slope /= (pc_temp_ocv->percent[row1] - pc_temp_ocv->percent[row2]);
+
+ return slope;
+}
+
+
+int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut,
+ int batt_temp, int ibat)
+{
+ int i, accrow1, accrow2, rows, cols;
+ int row1 = 0;
+ int row2 = 0;
+ int acc;
+
+ rows = ibat_acc_lut->rows;
+ cols = ibat_acc_lut->cols;
+
+ if (ibat > ibat_acc_lut->ibat[rows - 1]) {
+ pr_debug("ibatt(%d) > max range(%d)\n", ibat,
+ ibat_acc_lut->ibat[rows - 1]);
+ row1 = rows - 1;
+ row2 = rows - 2;
+ } else if (ibat < ibat_acc_lut->ibat[0]) {
+ pr_debug("ibatt(%d) < max range(%d)\n", ibat,
+ ibat_acc_lut->ibat[0]);
+ row1 = 0;
+ row2 = 0;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (ibat == ibat_acc_lut->ibat[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (ibat < ibat_acc_lut->ibat[i]) {
+ row1 = i;
+ row2 = i - 1;
+ break;
+ }
+ }
+ }
+
+ if (batt_temp < ibat_acc_lut->temp[0] * DEGC_SCALE)
+ batt_temp = ibat_acc_lut->temp[0] * DEGC_SCALE;
+ if (batt_temp > ibat_acc_lut->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = ibat_acc_lut->temp[cols - 1] * DEGC_SCALE;
+
+ for (i = 0; i < cols; i++)
+ if (batt_temp <= ibat_acc_lut->temp[i] * DEGC_SCALE)
+ break;
+
+ if (batt_temp == (ibat_acc_lut->temp[i] * DEGC_SCALE)) {
+ acc = linear_interpolate(
+ ibat_acc_lut->acc[row1][i],
+ ibat_acc_lut->ibat[row1],
+ ibat_acc_lut->acc[row2][i],
+ ibat_acc_lut->ibat[row2],
+ ibat);
+ return acc;
+ }
+
+ accrow1 = linear_interpolate(
+ ibat_acc_lut->acc[row1][i - 1],
+ ibat_acc_lut->temp[i - 1] * DEGC_SCALE,
+ ibat_acc_lut->acc[row1][i],
+ ibat_acc_lut->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ accrow2 = linear_interpolate(
+ ibat_acc_lut->acc[row2][i - 1],
+ ibat_acc_lut->temp[i - 1] * DEGC_SCALE,
+ ibat_acc_lut->acc[row2][i],
+ ibat_acc_lut->temp[i] * DEGC_SCALE,
+ batt_temp);
+
+ acc = linear_interpolate(accrow1,
+ ibat_acc_lut->ibat[row1],
+ accrow2,
+ ibat_acc_lut->ibat[row2],
+ ibat);
+
+ if (acc < 0)
+ acc = 0;
+
+ return acc;
+}
diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c
index 3d74f7e..4003679 100644
--- a/drivers/power/supply/qcom/fg-alg.c
+++ b/drivers/power/supply/qcom/fg-alg.c
@@ -16,11 +16,38 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
#include "fg-alg.h"
#define FULL_SOC_RAW 255
+#define FULL_BATT_SOC GENMASK(31, 0)
#define CAPACITY_DELTA_DECIPCT 500
+#define CENTI_ICORRECT_C0 105
+#define CENTI_ICORRECT_C1 20
+
+#define HOURS_TO_SECONDS 3600
+#define OCV_SLOPE_UV 10869
+#define MILLI_UNIT 1000
+#define MICRO_UNIT 1000000
+#define NANO_UNIT 1000000000
+
+#define DEFAULT_TTF_RUN_PERIOD_MS 10000
+#define DEFAULT_TTF_ITERM_DELTA_MA 200
+
+static const struct ttf_pt ttf_ln_table[] = {
+ { 1000, 0 },
+ { 2000, 693 },
+ { 4000, 1386 },
+ { 6000, 1792 },
+ { 8000, 2079 },
+ { 16000, 2773 },
+ { 32000, 3466 },
+ { 64000, 4159 },
+ { 128000, 4852 },
+};
+
/* Cycle counter APIs */
/**
@@ -164,13 +191,13 @@
}
/**
- * get_cycle_count -
+ * get_bucket_cycle_count -
* @counter: Cycle counter object
*
* Returns the cycle counter for a SOC bucket.
*
*/
-int get_cycle_count(struct cycle_counter *counter)
+static int get_bucket_cycle_count(struct cycle_counter *counter)
{
int count;
@@ -187,6 +214,68 @@
}
/**
+ * get_cycle_counts -
+ * @counter: Cycle counter object
+ * @buf: Bucket cycle counts formatted in a string returned to the caller
+ *
+ * Get cycle count for all buckets in a string format
+ */
+int get_cycle_counts(struct cycle_counter *counter, const char **buf)
+{
+ int i, rc, len = 0;
+
+ for (i = 1; i <= BUCKET_COUNT; i++) {
+ counter->id = i;
+ rc = get_bucket_cycle_count(counter);
+ if (rc < 0) {
+ pr_err("Couldn't get cycle count rc=%d\n", rc);
+ return rc;
+ }
+
+ if (sizeof(counter->str_buf) - len < 8) {
+ pr_err("Invalid length %d\n", len);
+ return -EINVAL;
+ }
+
+ len += snprintf(counter->str_buf+len, 8, "%d ", rc);
+ }
+
+ counter->str_buf[len] = '\0';
+ *buf = counter->str_buf;
+ return 0;
+}
+
+/**
+ * get_cycle_count -
+ * @counter: Cycle counter object
+ * @count: Average cycle count returned to the caller
+ *
+ * Get average cycle count for all buckets
+ */
+int get_cycle_count(struct cycle_counter *counter, int *count)
+{
+ int i, rc, temp = 0;
+
+ for (i = 1; i <= BUCKET_COUNT; i++) {
+ counter->id = i;
+ rc = get_bucket_cycle_count(counter);
+ if (rc < 0) {
+ pr_err("Couldn't get cycle count rc=%d\n", rc);
+ return rc;
+ }
+
+ temp += rc;
+ }
+
+ /*
+ * Normalize the counter across each bucket so that we can get
+ * the overall charge cycle count.
+ */
+ *count = temp / BUCKET_COUNT;
+ return 0;
+}
+
+/**
* cycle_count_init -
* @counter: Cycle counter object
*
@@ -289,7 +378,7 @@
*/
static int cap_learning_process_full_data(struct cap_learning *cl)
{
- int rc, cc_soc_sw, cc_soc_delta_pct;
+ int rc, cc_soc_sw, cc_soc_delta_centi_pct;
int64_t delta_cap_uah;
rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
@@ -298,20 +387,21 @@
return rc;
}
- cc_soc_delta_pct =
- div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 100,
+ cc_soc_delta_centi_pct =
+ div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 10000,
cl->cc_soc_max);
/* If the delta is < 50%, then skip processing full data */
- if (cc_soc_delta_pct < 50) {
- pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct);
+ if (cc_soc_delta_centi_pct < 5000) {
+ pr_err("cc_soc_delta_centi_pct: %d\n", cc_soc_delta_centi_pct);
return -ERANGE;
}
- delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_pct, 100);
+ delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_centi_pct,
+ 10000);
cl->final_cap_uah = cl->init_cap_uah + delta_cap_uah;
- pr_debug("Current cc_soc=%d cc_soc_delta_pct=%d total_cap_uah=%lld\n",
- cc_soc_sw, cc_soc_delta_pct, cl->final_cap_uah);
+ pr_debug("Current cc_soc=%d cc_soc_delta_centi_pct=%d total_cap_uah=%lld\n",
+ cc_soc_sw, cc_soc_delta_centi_pct, cl->final_cap_uah);
return 0;
}
@@ -327,18 +417,20 @@
*/
static int cap_learning_begin(struct cap_learning *cl, u32 batt_soc)
{
- int rc, cc_soc_sw, batt_soc_msb;
+ int rc, cc_soc_sw, batt_soc_msb, batt_soc_pct;
batt_soc_msb = batt_soc >> 24;
- if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
- cl->dt.start_soc) {
- pr_debug("Battery SOC %d is high!, not starting\n",
- batt_soc_msb);
+ batt_soc_pct = DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW);
+
+ if (batt_soc_pct > cl->dt.max_start_soc ||
+ batt_soc_pct < cl->dt.min_start_soc) {
+ pr_debug("Battery SOC %d is high/low, not starting\n",
+ batt_soc_pct);
return -EINVAL;
}
- cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc_msb,
- FULL_SOC_RAW);
+ cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc,
+ FULL_BATT_SOC);
if (cl->prime_cc_soc) {
/*
@@ -604,3 +696,508 @@
mutex_init(&cl->lock);
return 0;
}
+
+/* Time to full/empty algorithm helper functions */
+
+static void ttf_circ_buf_add(struct ttf_circ_buf *buf, int val)
+{
+ buf->arr[buf->head] = val;
+ buf->head = (buf->head + 1) % ARRAY_SIZE(buf->arr);
+ buf->size = min(++buf->size, (int)ARRAY_SIZE(buf->arr));
+}
+
+static void ttf_circ_buf_clr(struct ttf_circ_buf *buf)
+{
+ buf->size = 0;
+ buf->head = 0;
+ memset(buf->arr, 0, sizeof(buf->arr));
+}
+
+static int cmp_int(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static int ttf_circ_buf_median(struct ttf_circ_buf *buf, int *median)
+{
+ int *temp;
+
+ if (buf->size == 0)
+ return -ENODATA;
+
+ if (buf->size == 1) {
+ *median = buf->arr[0];
+ return 0;
+ }
+
+ temp = kmalloc_array(buf->size, sizeof(*temp), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ memcpy(temp, buf->arr, buf->size * sizeof(*temp));
+ sort(temp, buf->size, sizeof(*temp), cmp_int, NULL);
+
+ if (buf->size % 2)
+ *median = temp[buf->size / 2];
+ else
+ *median = (temp[buf->size / 2 - 1] + temp[buf->size / 2]) / 2;
+
+ kfree(temp);
+ return 0;
+}
+
+static int ttf_lerp(const struct ttf_pt *pts, size_t tablesize,
+ s32 input, s32 *output)
+{
+ int i;
+ s64 temp;
+
+ if (pts == NULL) {
+ pr_err("Table is NULL\n");
+ return -EINVAL;
+ }
+
+ if (tablesize < 1) {
+ pr_err("Table has no entries\n");
+ return -ENOENT;
+ }
+
+ if (tablesize == 1) {
+ *output = pts[0].y;
+ return 0;
+ }
+
+ if (pts[0].x > pts[1].x) {
+ pr_err("Table is not in acending order\n");
+ return -EINVAL;
+ }
+
+ if (input <= pts[0].x) {
+ *output = pts[0].y;
+ return 0;
+ }
+
+ if (input >= pts[tablesize - 1].x) {
+ *output = pts[tablesize - 1].y;
+ return 0;
+ }
+
+ for (i = 1; i < tablesize; i++) {
+ if (input >= pts[i].x)
+ continue;
+
+ temp = ((s64)pts[i].y - pts[i - 1].y) *
+ ((s64)input - pts[i - 1].x);
+ temp = div_s64(temp, pts[i].x - pts[i - 1].x);
+ *output = temp + pts[i - 1].y;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int get_time_to_full_locked(struct ttf *ttf, int *val)
+{
+ int rc, ibatt_avg, vbatt_avg, rbatt = 0, msoc = 0, act_cap_mah = 0,
+ i_cc2cv = 0, soc_cc2cv, tau, divisor, iterm = 0, ttf_mode = 0,
+ i, soc_per_step, msoc_this_step, msoc_next_step,
+ ibatt_this_step, t_predicted_this_step, ttf_slope,
+ t_predicted_cv, t_predicted = 0, charge_type = 0,
+ float_volt_uv = 0;
+ s64 delta_ms;
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_MSOC, &msoc);
+ if (rc < 0) {
+ pr_err("failed to get msoc rc=%d\n", rc);
+ return rc;
+ }
+ pr_debug("TTF: msoc=%d\n", msoc);
+
+ /* the battery is considered full if the SOC is 100% */
+ if (msoc >= 100) {
+ *val = 0;
+ return 0;
+ }
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_MODE, &ttf_mode);
+
+ /* when switching TTF algorithms the TTF needs to be reset */
+ if (ttf->mode != ttf_mode) {
+ ttf_circ_buf_clr(&ttf->ibatt);
+ ttf_circ_buf_clr(&ttf->vbatt);
+ ttf->last_ttf = 0;
+ ttf->last_ms = 0;
+ ttf->mode = ttf_mode;
+ }
+
+ /* at least 10 samples are required to produce a stable IBATT */
+ if (ttf->ibatt.size < MAX_TTF_SAMPLES) {
+ *val = -1;
+ return 0;
+ }
+
+ rc = ttf_circ_buf_median(&ttf->ibatt, &ibatt_avg);
+ if (rc < 0) {
+ pr_err("failed to get IBATT AVG rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = ttf_circ_buf_median(&ttf->vbatt, &vbatt_avg);
+ if (rc < 0) {
+ pr_err("failed to get VBATT AVG rc=%d\n", rc);
+ return rc;
+ }
+
+ ibatt_avg = -ibatt_avg / MILLI_UNIT;
+ vbatt_avg /= MILLI_UNIT;
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_ITERM, &iterm);
+ if (rc < 0) {
+ pr_err("failed to get iterm rc=%d\n", rc);
+ return rc;
+ }
+ /* clamp ibatt_avg to iterm */
+ if (ibatt_avg < abs(iterm))
+ ibatt_avg = abs(iterm);
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_RBATT, &rbatt);
+ if (rc < 0) {
+ pr_err("failed to get battery resistance rc=%d\n", rc);
+ return rc;
+ }
+ rbatt /= MILLI_UNIT;
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_FCC, &act_cap_mah);
+ if (rc < 0) {
+ pr_err("failed to get ACT_BATT_CAP rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug(" TTF: ibatt_avg=%d vbatt_avg=%d rbatt=%d act_cap_mah=%d\n",
+ ibatt_avg, vbatt_avg, rbatt, act_cap_mah);
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_VFLOAT, &float_volt_uv);
+ if (rc < 0) {
+ pr_err("failed to get float_volt_uv rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_CHG_TYPE, &charge_type);
+ if (rc < 0) {
+ pr_err("failed to get charge_type rc=%d\n", rc);
+ return rc;
+ }
+ /* estimated battery current at the CC to CV transition */
+ switch (ttf->mode) {
+ case TTF_MODE_NORMAL:
+ i_cc2cv = ibatt_avg * vbatt_avg /
+ max(MILLI_UNIT, float_volt_uv / MILLI_UNIT);
+ break;
+ case TTF_MODE_QNOVO:
+ i_cc2cv = min(
+ ttf->cc_step.arr[MAX_CC_STEPS - 1] / MILLI_UNIT,
+ ibatt_avg * vbatt_avg /
+ max(MILLI_UNIT, float_volt_uv / MILLI_UNIT));
+ break;
+ default:
+ pr_err("TTF mode %d is not supported\n", ttf->mode);
+ break;
+ }
+ pr_debug("TTF: i_cc2cv=%d\n", i_cc2cv);
+
+ /* if we are already in CV state then we can skip estimating CC */
+ if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
+ goto cv_estimate;
+
+ /* estimated SOC at the CC to CV transition */
+ soc_cc2cv = DIV_ROUND_CLOSEST(rbatt * i_cc2cv, OCV_SLOPE_UV);
+ soc_cc2cv = 100 - soc_cc2cv;
+ pr_debug("TTF: soc_cc2cv=%d\n", soc_cc2cv);
+
+ switch (ttf->mode) {
+ case TTF_MODE_NORMAL:
+ if (soc_cc2cv - msoc <= 0)
+ goto cv_estimate;
+
+ divisor = max(100, (ibatt_avg + i_cc2cv) / 2 * 100);
+ t_predicted = div_s64((s64)act_cap_mah * (soc_cc2cv - msoc) *
+ HOURS_TO_SECONDS, divisor);
+ break;
+ case TTF_MODE_QNOVO:
+ soc_per_step = 100 / MAX_CC_STEPS;
+ for (i = msoc / soc_per_step; i < MAX_CC_STEPS - 1; ++i) {
+ msoc_next_step = (i + 1) * soc_per_step;
+ if (i == msoc / soc_per_step)
+ msoc_this_step = msoc;
+ else
+ msoc_this_step = i * soc_per_step;
+
+ /* scale ibatt by 85% to account for discharge pulses */
+ ibatt_this_step = min(
+ ttf->cc_step.arr[i] / MILLI_UNIT,
+ ibatt_avg) * 85 / 100;
+ divisor = max(100, ibatt_this_step * 100);
+ t_predicted_this_step = div_s64((s64)act_cap_mah *
+ (msoc_next_step - msoc_this_step) *
+ HOURS_TO_SECONDS, divisor);
+ t_predicted += t_predicted_this_step;
+ pr_debug("TTF: [%d, %d] ma=%d t=%d\n",
+ msoc_this_step, msoc_next_step,
+ ibatt_this_step, t_predicted_this_step);
+ }
+ break;
+ default:
+ pr_err("TTF mode %d is not supported\n", ttf->mode);
+ break;
+ }
+
+cv_estimate:
+ pr_debug("TTF: t_predicted_cc=%d\n", t_predicted);
+
+ iterm = max(100, abs(iterm) + ttf->iterm_delta);
+ pr_debug("TTF: iterm=%d\n", iterm);
+
+ if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
+ tau = max(MILLI_UNIT, ibatt_avg * MILLI_UNIT / iterm);
+ else
+ tau = max(MILLI_UNIT, i_cc2cv * MILLI_UNIT / iterm);
+
+ rc = ttf_lerp(ttf_ln_table, ARRAY_SIZE(ttf_ln_table), tau, &tau);
+ if (rc < 0) {
+ pr_err("failed to interpolate tau rc=%d\n", rc);
+ return rc;
+ }
+
+ /* tau is scaled linearly from 95% to 100% SOC */
+ if (msoc >= 95)
+ tau = tau * 2 * (100 - msoc) / 10;
+
+ pr_debug("TTF: tau=%d\n", tau);
+ t_predicted_cv = div_s64((s64)act_cap_mah * rbatt * tau *
+ HOURS_TO_SECONDS, NANO_UNIT);
+ pr_debug("TTF: t_predicted_cv=%d\n", t_predicted_cv);
+ t_predicted += t_predicted_cv;
+
+ pr_debug("TTF: t_predicted_prefilter=%d\n", t_predicted);
+ if (ttf->last_ms != 0) {
+ delta_ms = ktime_ms_delta(ktime_get_boottime(),
+ ms_to_ktime(ttf->last_ms));
+ if (delta_ms > 10000) {
+ ttf_slope = div64_s64(
+ ((s64)t_predicted - ttf->last_ttf) *
+ MICRO_UNIT, delta_ms);
+ if (ttf_slope > -100)
+ ttf_slope = -100;
+ else if (ttf_slope < -2000)
+ ttf_slope = -2000;
+
+ t_predicted = div_s64(
+ (s64)ttf_slope * delta_ms, MICRO_UNIT) +
+ ttf->last_ttf;
+ pr_debug("TTF: ttf_slope=%d\n", ttf_slope);
+ } else {
+ t_predicted = ttf->last_ttf;
+ }
+ }
+
+ /* clamp the ttf to 0 */
+ if (t_predicted < 0)
+ t_predicted = 0;
+
+ pr_debug("TTF: t_predicted_postfilter=%d\n", t_predicted);
+ *val = t_predicted;
+ return 0;
+}
+
+/**
+ * ttf_get_time_to_full -
+ * @ttf: ttf object
+ * @val: Average time to full returned to the caller
+ *
+ * Get Average time to full the battery based on current soc, rbatt
+ * battery voltage and charge current etc.
+ */
+int ttf_get_time_to_full(struct ttf *ttf, int *val)
+{
+ int rc;
+
+ mutex_lock(&ttf->lock);
+ rc = get_time_to_full_locked(ttf, val);
+ mutex_unlock(&ttf->lock);
+
+ return rc;
+}
+
+static void ttf_work(struct work_struct *work)
+{
+ struct ttf *ttf = container_of(work,
+ struct ttf, ttf_work.work);
+ int rc, ibatt_now, vbatt_now, ttf_now, charge_status;
+ ktime_t ktime_now;
+
+ mutex_lock(&ttf->lock);
+ rc = ttf->get_ttf_param(ttf->data, TTF_CHG_STATUS, &charge_status);
+ if (rc < 0) {
+ pr_err("failed to get charge_status rc=%d\n", rc);
+ goto end_work;
+ }
+ if (charge_status != POWER_SUPPLY_STATUS_CHARGING &&
+ charge_status != POWER_SUPPLY_STATUS_DISCHARGING)
+ goto end_work;
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_IBAT, &ibatt_now);
+ if (rc < 0) {
+ pr_err("failed to get battery current, rc=%d\n", rc);
+ goto end_work;
+ }
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_VBAT, &vbatt_now);
+ if (rc < 0) {
+ pr_err("failed to get battery voltage, rc=%d\n", rc);
+ goto end_work;
+ }
+
+ ttf_circ_buf_add(&ttf->ibatt, ibatt_now);
+ ttf_circ_buf_add(&ttf->vbatt, vbatt_now);
+
+ if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+ rc = get_time_to_full_locked(ttf, &ttf_now);
+ if (rc < 0) {
+ pr_err("failed to get ttf, rc=%d\n", rc);
+ goto end_work;
+ }
+
+ /* keep the wake lock and prime the IBATT and VBATT buffers */
+ if (ttf_now < 0) {
+ /* delay for one FG cycle */
+ schedule_delayed_work(&ttf->ttf_work,
+ msecs_to_jiffies(1000));
+ mutex_unlock(&ttf->lock);
+ return;
+ }
+
+ /* update the TTF reference point every minute */
+ ktime_now = ktime_get_boottime();
+ if (ktime_ms_delta(ktime_now,
+ ms_to_ktime(ttf->last_ms)) > 60000 ||
+ ttf->last_ms == 0) {
+ ttf->last_ttf = ttf_now;
+ ttf->last_ms = ktime_to_ms(ktime_now);
+ }
+ }
+
+ /* recurse every 10 seconds */
+ schedule_delayed_work(&ttf->ttf_work, msecs_to_jiffies(ttf->period_ms));
+end_work:
+ ttf->awake_voter(ttf->data, false);
+ mutex_unlock(&ttf->lock);
+}
+
+/**
+ * ttf_get_time_to_empty -
+ * @ttf: ttf object
+ * @val: Average time to empty returned to the caller
+ *
+ * Get Average time to empty the battery based on current soc
+ * and average battery current.
+ */
+int ttf_get_time_to_empty(struct ttf *ttf, int *val)
+{
+ int rc, ibatt_avg, msoc, act_cap_mah, divisor;
+
+ rc = ttf_circ_buf_median(&ttf->ibatt, &ibatt_avg);
+ if (rc < 0) {
+ /* try to get instantaneous current */
+ rc = ttf->get_ttf_param(ttf->data, TTF_IBAT, &ibatt_avg);
+ if (rc < 0) {
+ pr_err("failed to get battery current, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ ibatt_avg /= MILLI_UNIT;
+ /* clamp ibatt_avg to 100mA */
+ if (ibatt_avg < 100)
+ ibatt_avg = 100;
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_MSOC, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting capacity, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = ttf->get_ttf_param(ttf->data, TTF_FCC, &act_cap_mah);
+ if (rc < 0) {
+ pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc);
+ return rc;
+ }
+
+ divisor = CENTI_ICORRECT_C0 * 100 + CENTI_ICORRECT_C1 * msoc;
+ divisor = ibatt_avg * divisor / 100;
+ divisor = max(100, divisor);
+ *val = act_cap_mah * msoc * HOURS_TO_SECONDS / divisor;
+ return 0;
+}
+
+/**
+ * ttf_update -
+ * @ttf: ttf object
+ * @input_present: Indicator for input presence
+ *
+ * Called by FG/QG driver when there is a state change (Charging status, SOC)
+ *
+ */
+void ttf_update(struct ttf *ttf, bool input_present)
+{
+ int delay_ms;
+
+ if (ttf->input_present == input_present)
+ return;
+
+ ttf->input_present = input_present;
+ if (input_present)
+ /* wait 35 seconds for the input to settle */
+ delay_ms = 35000;
+ else
+ /* wait 5 seconds for current to settle during discharge */
+ delay_ms = 5000;
+
+ ttf->awake_voter(ttf->data, true);
+ cancel_delayed_work_sync(&ttf->ttf_work);
+ mutex_lock(&ttf->lock);
+ ttf_circ_buf_clr(&ttf->ibatt);
+ ttf_circ_buf_clr(&ttf->vbatt);
+ ttf->last_ttf = 0;
+ ttf->last_ms = 0;
+ mutex_unlock(&ttf->lock);
+ schedule_delayed_work(&ttf->ttf_work, msecs_to_jiffies(delay_ms));
+}
+
+/**
+ * ttf_tte_init -
+ * @ttf: Time to full object
+ *
+ * FG/QG have to call this during driver probe to validate the required
+ * parameters after allocating ttf object.
+ *
+ */
+int ttf_tte_init(struct ttf *ttf)
+{
+ if (!ttf)
+ return -ENODEV;
+
+ if (!ttf->awake_voter || !ttf->get_ttf_param) {
+ pr_err("Insufficient functions for supporting ttf\n");
+ return -EINVAL;
+ }
+
+ if (!ttf->iterm_delta)
+ ttf->iterm_delta = DEFAULT_TTF_ITERM_DELTA_MA;
+ if (!ttf->period_ms)
+ ttf->period_ms = DEFAULT_TTF_RUN_PERIOD_MS;
+
+ mutex_init(&ttf->lock);
+ INIT_DELAYED_WORK(&ttf->ttf_work, ttf_work);
+
+ return 0;
+}
diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h
index ff5bece..70183ba 100644
--- a/drivers/power/supply/qcom/fg-alg.h
+++ b/drivers/power/supply/qcom/fg-alg.h
@@ -15,12 +15,15 @@
#define BUCKET_COUNT 8
#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
+#define MAX_CC_STEPS 20
+#define MAX_TTF_SAMPLES 10
struct cycle_counter {
void *data;
bool started[BUCKET_COUNT];
u16 count[BUCKET_COUNT];
u8 last_soc[BUCKET_COUNT];
+ char str_buf[BUCKET_COUNT * 8];
int id;
int last_bucket;
struct mutex lock;
@@ -29,7 +32,8 @@
};
struct cl_params {
- int start_soc;
+ int min_start_soc;
+ int max_start_soc;
int max_temp;
int min_temp;
int max_cap_inc;
@@ -56,11 +60,63 @@
int (*prime_cc_soc)(void *data, u32 cc_soc_sw);
};
+enum ttf_mode {
+ TTF_MODE_NORMAL = 0,
+ TTF_MODE_QNOVO,
+};
+
+enum ttf_param {
+ TTF_MSOC = 0,
+ TTF_VBAT,
+ TTF_IBAT,
+ TTF_FCC,
+ TTF_MODE,
+ TTF_ITERM,
+ TTF_RBATT,
+ TTF_VFLOAT,
+ TTF_CHG_TYPE,
+ TTF_CHG_STATUS,
+};
+
+struct ttf_circ_buf {
+ int arr[MAX_TTF_SAMPLES];
+ int size;
+ int head;
+};
+
+struct ttf_cc_step_data {
+ int arr[MAX_CC_STEPS];
+ int sel;
+};
+
+struct ttf_pt {
+ s32 x;
+ s32 y;
+};
+
+struct ttf {
+ void *data;
+ struct ttf_circ_buf ibatt;
+ struct ttf_circ_buf vbatt;
+ struct ttf_cc_step_data cc_step;
+ struct mutex lock;
+ int mode;
+ int last_ttf;
+ int input_present;
+ int iterm_delta;
+ int period_ms;
+ s64 last_ms;
+ struct delayed_work ttf_work;
+ int (*get_ttf_param)(void *data, enum ttf_param, int *val);
+ int (*awake_voter)(void *data, bool vote);
+};
+
int restore_cycle_count(struct cycle_counter *counter);
void clear_cycle_count(struct cycle_counter *counter);
void cycle_count_update(struct cycle_counter *counter, int batt_soc,
int charge_status, bool charge_done, bool input_present);
-int get_cycle_count(struct cycle_counter *counter);
+int get_cycle_count(struct cycle_counter *counter, int *count);
+int get_cycle_counts(struct cycle_counter *counter, const char **buf);
int cycle_count_init(struct cycle_counter *counter);
void cap_learning_abort(struct cap_learning *cl);
void cap_learning_update(struct cap_learning *cl, int batt_temp,
@@ -69,5 +125,9 @@
int cap_learning_init(struct cap_learning *cl);
int cap_learning_post_profile_init(struct cap_learning *cl,
int64_t nom_cap_uah);
+void ttf_update(struct ttf *ttf, bool input_present);
+int ttf_get_time_to_empty(struct ttf *ttf, int *val);
+int ttf_get_time_to_full(struct ttf *ttf, int *val);
+int ttf_tte_init(struct ttf *ttf);
#endif
diff --git a/drivers/power/supply/qcom/qg-battery-profile.c b/drivers/power/supply/qcom/qg-battery-profile.c
index 441c759..00a4533 100644
--- a/drivers/power/supply/qcom/qg-battery-profile.c
+++ b/drivers/power/supply/qcom/qg-battery-profile.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <uapi/linux/qg-profile.h>
+#include "qg-battery-profile.h"
#include "qg-profile-lib.h"
#include "qg-defs.h"
@@ -397,10 +398,8 @@
{
u8 table_index = charging ? TABLE_SOC_OCV1 : TABLE_SOC_OCV2;
- if (!the_battery || !the_battery->profile) {
- pr_err("Battery profile not loaded\n");
+ if (!the_battery || !the_battery->profile)
return -ENODEV;
- }
*soc = interpolate_soc(&the_battery->profile[table_index],
batt_temp, UV_TO_DECIUV(ocv_uv));
@@ -410,6 +409,24 @@
return 0;
}
+int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging)
+{
+ u8 table_index = charging ? TABLE_FCC1 : TABLE_FCC2;
+ u32 fcc_mah;
+
+ if (!the_battery || !the_battery->profile)
+ return -ENODEV;
+
+ fcc_mah = interpolate_single_row_lut(
+ &the_battery->profile[table_index],
+ batt_temp, DEGC_SCALE);
+ fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah);
+
+ *nom_cap_uah = fcc_mah * 1000;
+
+ return 0;
+}
+
int qg_batterydata_init(struct device_node *profile_node)
{
int rc = 0;
diff --git a/drivers/power/supply/qcom/qg-battery-profile.h b/drivers/power/supply/qcom/qg-battery-profile.h
index 143a4f2..1b06277 100644
--- a/drivers/power/supply/qcom/qg-battery-profile.h
+++ b/drivers/power/supply/qcom/qg-battery-profile.h
@@ -15,5 +15,6 @@
int qg_batterydata_init(struct device_node *node);
void qg_batterydata_exit(void);
int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging);
+int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging);
#endif /* __QG_BATTERY_PROFILE_H__ */
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 6e44f33..f21b2a8 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -12,6 +12,10 @@
#ifndef __QG_CORE_H__
#define __QG_CORE_H__
+#include <linux/kernel.h>
+#include "fg-alg.h"
+#include "qg-defs.h"
+
struct qg_batt_props {
const char *batt_type_str;
int float_volt_uv;
@@ -47,8 +51,25 @@
int rbat_conn_mohm;
int ignore_shutdown_soc_secs;
int cold_temp_threshold;
+ int esr_qual_i_ua;
+ int esr_qual_v_uv;
+ int esr_disable_soc;
bool hold_soc_while_full;
bool linearize_soc;
+ bool cl_disable;
+ bool cl_feedback_on;
+ bool esr_disable;
+ bool esr_discharge_enable;
+ bool qg_ext_sense;
+};
+
+struct qg_esr_data {
+ u32 pre_esr_v;
+ u32 pre_esr_i;
+ u32 post_esr_v;
+ u32 post_esr_i;
+ u32 esr;
+ bool valid;
};
struct qpnp_qg {
@@ -82,6 +103,7 @@
struct power_supply *batt_psy;
struct power_supply *usb_psy;
struct power_supply *parallel_psy;
+ struct qg_esr_data esr_data[QG_MAX_ESR_COUNT];
/* status variable */
u32 *debug_mask;
@@ -97,10 +119,18 @@
bool charge_full;
int charge_status;
int charge_type;
+ int chg_iterm_ma;
int next_wakeup_ms;
+ int esr_actual;
+ int esr_nominal;
+ int soh;
+ int soc_reporting_ready;
+ u32 fifo_done_count;
u32 wa_flags;
u32 seq_no;
u32 charge_counter_uah;
+ u32 esr_avg;
+ u32 esr_last;
ktime_t last_user_update_time;
ktime_t last_fifo_update_time;
@@ -109,12 +139,27 @@
int maint_soc;
int msoc;
int pon_soc;
+ int batt_soc;
+ int cc_soc;
+ int full_soc;
struct alarm alarm_timer;
u32 sdam_data[SDAM_MAX];
/* DT */
struct qg_dt dt;
struct qg_batt_props bp;
+ /* capacity learning */
+ struct cap_learning *cl;
+ /* charge counter */
+ struct cycle_counter *counter;
+ /* ttf */
+ struct ttf *ttf;
+};
+
+struct ocv_all {
+ u32 ocv_uv;
+ u32 ocv_raw;
+ char ocv_type[20];
};
enum ocv_type {
@@ -122,6 +167,7 @@
S3_GOOD_OCV,
S3_LAST_OCV,
SDAM_PON_OCV,
+ PON_OCV_MAX,
};
enum debug_mask {
@@ -135,6 +181,8 @@
QG_DEBUG_PM = BIT(7),
QG_DEBUG_BUS_READ = BIT(8),
QG_DEBUG_BUS_WRITE = BIT(9),
+ QG_DEBUG_ALG_CL = BIT(10),
+ QG_DEBUG_ESR = BIT(11),
};
enum qg_irq {
diff --git a/drivers/power/supply/qcom/qg-defs.h b/drivers/power/supply/qcom/qg-defs.h
index 6ae9aa2..997ff70 100644
--- a/drivers/power/supply/qcom/qg-defs.h
+++ b/drivers/power/supply/qcom/qg-defs.h
@@ -34,6 +34,7 @@
#define GOOD_OCV_VOTER "GOOD_OCV_VOTER"
#define PROFILE_IRQ_DISABLE "NO_PROFILE_IRQ_DISABLE"
#define QG_INIT_STATE_IRQ_DISABLE "QG_INIT_STATE_IRQ_DISABLE"
+#define TTF_AWAKE_VOTER "TTF_AWAKE_VOTER"
#define V_RAW_TO_UV(V_RAW) div_u64(194637ULL * (u64)V_RAW, 1000)
#define I_RAW_TO_UA(I_RAW) div_s64(152588LL * (s64)I_RAW, 1000)
@@ -44,7 +45,13 @@
#define UV_TO_DECIUV(a) (a / 100)
#define DECIUV_TO_UV(a) (a * 100)
+#define QG_MAX_ESR_COUNT 10
+#define QG_MIN_ESR_COUNT 2
+
#define CAP(min, max, value) \
((min > value) ? min : ((value > max) ? max : value))
+#define QG_SOC_FULL 10000
+#define BATT_SOC_32BIT GENMASK(31, 0)
+
#endif /* __QG_DEFS_H__ */
diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h
index 96533d4..e0f400d 100644
--- a/drivers/power/supply/qcom/qg-reg.h
+++ b/drivers/power/supply/qcom/qg-reg.h
@@ -17,7 +17,9 @@
#define QG_TYPE 0x0D
#define QG_STATUS1_REG 0x08
+#define QG_OK_BIT BIT(7)
#define BATTERY_PRESENT_BIT BIT(0)
+#define ESR_MEAS_DONE_BIT BIT(4)
#define QG_STATUS2_REG 0x09
#define GOOD_OCV_BIT BIT(1)
@@ -25,9 +27,13 @@
#define QG_STATUS3_REG 0x0A
#define COUNT_FIFO_RT_MASK GENMASK(3, 0)
+#define QG_STATUS4_REG 0x0B
+#define ESR_MEAS_IN_PROGRESS_BIT BIT(4)
+
#define QG_INT_RT_STS_REG 0x10
#define FIFO_UPDATE_DONE_RT_STS_BIT BIT(3)
#define VBAT_LOW_INT_RT_STS_BIT BIT(1)
+#define BATTERY_MISSING_INT_RT_STS_BIT BIT(0)
#define QG_INT_LATCHED_STS_REG 0x18
#define FIFO_UPDATE_DONE_INT_LAT_STS_BIT BIT(3)
@@ -60,11 +66,25 @@
#define QG_S3_ENTRY_IBAT_THRESHOLD_REG 0x5E
#define QG_S3_EXIT_IBAT_THRESHOLD_REG 0x5F
+#define QG_S5_OCV_VALIDATE_MEAS_CTL1_REG 0x60
+#define ALLOW_S5_BIT BIT(7)
+
+#define QG_S7_PON_OCV_MEAS_CTL1_REG 0x64
+#define ADC_CONV_DLY_MASK GENMASK(3, 0)
+
+#define QG_ESR_MEAS_TRIG_REG 0x68
+#define HW_ESR_MEAS_START_BIT BIT(0)
+
#define QG_S7_PON_OCV_V_DATA0_REG 0x70
#define QG_S7_PON_OCV_I_DATA0_REG 0x72
#define QG_S3_GOOD_OCV_V_DATA0_REG 0x74
#define QG_S3_GOOD_OCV_I_DATA0_REG 0x76
+#define QG_PRE_ESR_V_DATA0_REG 0x78
+#define QG_PRE_ESR_I_DATA0_REG 0x7A
+#define QG_POST_ESR_V_DATA0_REG 0x7C
+#define QG_POST_ESR_I_DATA0_REG 0x7E
+
#define QG_V_ACCUM_DATA0_RT_REG 0x88
#define QG_I_ACCUM_DATA0_RT_REG 0x8B
#define QG_ACCUM_CNT_RT_REG 0x8E
@@ -80,13 +100,22 @@
#define QG_LAST_S3_SLEEP_V_DATA0_REG 0xCC
/* SDAM offsets */
-#define QG_SDAM_VALID_OFFSET 0x46
-#define QG_SDAM_SOC_OFFSET 0x47
-#define QG_SDAM_TEMP_OFFSET 0x48
-#define QG_SDAM_RBAT_OFFSET 0x4A
-#define QG_SDAM_OCV_OFFSET 0x4C
-#define QG_SDAM_IBAT_OFFSET 0x50
-#define QG_SDAM_TIME_OFFSET 0x54
-#define QG_SDAM_PON_OCV_OFFSET 0x7C
+#define QG_SDAM_VALID_OFFSET 0x46 /* 1-byte 0x46 */
+#define QG_SDAM_SOC_OFFSET 0x47 /* 1-byte 0x47 */
+#define QG_SDAM_TEMP_OFFSET 0x48 /* 2-byte 0x48-0x49 */
+#define QG_SDAM_RBAT_OFFSET 0x4A /* 2-byte 0x4A-0x4B */
+#define QG_SDAM_OCV_OFFSET 0x4C /* 4-byte 0x4C-0x4F */
+#define QG_SDAM_IBAT_OFFSET 0x50 /* 4-byte 0x50-0x53 */
+#define QG_SDAM_TIME_OFFSET 0x54 /* 4-byte 0x54-0x57 */
+#define QG_SDAM_CYCLE_COUNT_OFFSET 0x58 /* 16-byte 0x58-0x67 */
+#define QG_SDAM_LEARNED_CAPACITY_OFFSET 0x68 /* 2-byte 0x68-0x69 */
+#define QG_SDAM_ESR_CHARGE_DELTA_OFFSET 0x6A /* 4-byte 0x6A-0x6D */
+#define QG_SDAM_ESR_DISCHARGE_DELTA_OFFSET 0x6E /* 4-byte 0x6E-0x71 */
+#define QG_SDAM_ESR_CHARGE_SF_OFFSET 0x72 /* 2-byte 0x72-0x73 */
+#define QG_SDAM_ESR_DISCHARGE_SF_OFFSET 0x74 /* 2-byte 0x74-0x75 */
+#define QG_SDAM_MAX_OFFSET 0xA4
+
+/* Below offset is used by PBS */
+#define QG_SDAM_PON_OCV_OFFSET 0xBC /* 2-byte 0xBC-0xBD */
#endif
diff --git a/drivers/power/supply/qcom/qg-sdam.c b/drivers/power/supply/qcom/qg-sdam.c
index 54eed16..a7cb97e 100644
--- a/drivers/power/supply/qcom/qg-sdam.c
+++ b/drivers/power/supply/qcom/qg-sdam.c
@@ -68,6 +68,26 @@
.offset = QG_SDAM_PON_OCV_OFFSET,
.length = 2,
},
+ [SDAM_ESR_CHARGE_DELTA] = {
+ .name = "SDAM_ESR_CHARGE_DELTA",
+ .offset = QG_SDAM_ESR_CHARGE_DELTA_OFFSET,
+ .length = 4,
+ },
+ [SDAM_ESR_DISCHARGE_DELTA] = {
+ .name = "SDAM_ESR_DISCHARGE_DELTA",
+ .offset = QG_SDAM_ESR_DISCHARGE_DELTA_OFFSET,
+ .length = 4,
+ },
+ [SDAM_ESR_CHARGE_SF] = {
+ .name = "SDAM_ESR_CHARGE_SF_OFFSET",
+ .offset = QG_SDAM_ESR_CHARGE_SF_OFFSET,
+ .length = 2,
+ },
+ [SDAM_ESR_DISCHARGE_SF] = {
+ .name = "SDAM_ESR_DISCHARGE_SF_OFFSET",
+ .offset = QG_SDAM_ESR_DISCHARGE_SF_OFFSET,
+ .length = 2,
+ },
};
int qg_sdam_write(u8 param, u32 data)
@@ -91,7 +111,7 @@
length = sdam_info[param].length;
rc = regmap_bulk_write(chip->regmap, offset, (u8 *)&data, length);
if (rc < 0)
- pr_err("Failed to write offset=%0x4x param=%d value=%d\n",
+ pr_err("Failed to write offset=%0x4 param=%d value=%d\n",
offset, param, data);
else
pr_debug("QG SDAM write param=%s value=%d\n",
@@ -117,11 +137,12 @@
return -EINVAL;
}
+ *data = 0;
offset = chip->sdam_base + sdam_info[param].offset;
length = sdam_info[param].length;
rc = regmap_raw_read(chip->regmap, offset, (u8 *)data, length);
if (rc < 0)
- pr_err("Failed to read offset=%0x4x param=%d\n",
+ pr_err("Failed to read offset=%0x4 param=%d\n",
offset, param);
else
pr_debug("QG SDAM read param=%s value=%d\n",
@@ -130,6 +151,53 @@
return rc;
}
+int qg_sdam_multibyte_write(u32 offset, u8 *data, u32 length)
+{
+ int rc, i;
+ struct qg_sdam *chip = the_chip;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ offset = chip->sdam_base + offset;
+ rc = regmap_bulk_write(chip->regmap, offset, data, (size_t)length);
+ if (rc < 0) {
+ pr_err("Failed to write offset=%0x4 value=%d\n",
+ offset, *data);
+ } else {
+ for (i = 0; i < length; i++)
+ pr_debug("QG SDAM write offset=%0x4 value=%d\n",
+ offset++, data[i]);
+ }
+
+ return rc;
+}
+
+int qg_sdam_multibyte_read(u32 offset, u8 *data, u32 length)
+{
+ int rc, i;
+ struct qg_sdam *chip = the_chip;
+
+ if (!chip) {
+ pr_err("Invalid sdam-chip pointer\n");
+ return -EINVAL;
+ }
+
+ offset = chip->sdam_base + offset;
+ rc = regmap_raw_read(chip->regmap, offset, (u8 *)data, (size_t)length);
+ if (rc < 0) {
+ pr_err("Failed to read offset=%0x4\n", offset);
+ } else {
+ for (i = 0; i < length; i++)
+ pr_debug("QG SDAM read offset=%0x4 value=%d\n",
+ offset++, data[i]);
+ }
+
+ return rc;
+}
+
int qg_sdam_read_all(u32 *sdam_data)
{
int i, rc;
diff --git a/drivers/power/supply/qcom/qg-sdam.h b/drivers/power/supply/qcom/qg-sdam.h
index 51af04c..45218a8 100644
--- a/drivers/power/supply/qcom/qg-sdam.h
+++ b/drivers/power/supply/qcom/qg-sdam.h
@@ -24,6 +24,10 @@
SDAM_IBAT_UA,
SDAM_TIME_SEC,
SDAM_PON_OCV_UV,
+ SDAM_ESR_CHARGE_DELTA,
+ SDAM_ESR_DISCHARGE_DELTA,
+ SDAM_ESR_CHARGE_SF,
+ SDAM_ESR_DISCHARGE_SF,
SDAM_MAX,
};
@@ -37,5 +41,7 @@
int qg_sdam_read(u8 param, u32 *data);
int qg_sdam_write_all(u32 *sdam_data);
int qg_sdam_read_all(u32 *sdam_data);
+int qg_sdam_multibyte_write(u32 offset, u8 *sdam_data, u32 length);
+int qg_sdam_multibyte_read(u32 offset, u8 *sdam_data, u32 length);
#endif
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index c4d5043..af8b158 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -13,9 +13,11 @@
#define pr_fmt(fmt) "QG-K: %s: " fmt, __func__
#include <linux/alarmtimer.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <uapi/linux/qg.h>
+#include "fg-alg.h"
#include "qg-sdam.h"
#include "qg-core.h"
#include "qg-reg.h"
@@ -98,11 +100,12 @@
static void update_msoc(struct qpnp_qg *chip)
{
- int rc;
+ int rc = 0, batt_temp = 0, batt_soc_32bit = 0;
+ bool usb_present = is_usb_present(chip);
if (chip->catch_up_soc > chip->msoc) {
/* SOC increased */
- if (is_usb_present(chip)) /* Increment if USB is present */
+ if (usb_present) /* Increment if USB is present */
chip->msoc += chip->dt.delta_soc;
} else if (chip->catch_up_soc < chip->msoc) {
/* SOC dropped */
@@ -130,6 +133,25 @@
if (rc < 0)
pr_err("Failed to update SDAM with MSOC rc=%d\n", rc);
+ if (!chip->dt.cl_disable && chip->cl->active) {
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Failed to read BATT_TEMP rc=%d\n", rc);
+ } else {
+ batt_soc_32bit = div64_u64(
+ chip->batt_soc * BATT_SOC_32BIT,
+ QG_SOC_FULL);
+ cap_learning_update(chip->cl, batt_temp, batt_soc_32bit,
+ chip->charge_status, chip->charge_done,
+ usb_present, false);
+ }
+ }
+
+ cycle_count_update(chip->counter,
+ DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+ chip->charge_status, chip->charge_done,
+ usb_present);
+
qg_dbg(chip, QG_DEBUG_SOC,
"SOC scale: Update maint_soc=%d msoc=%d catch_up_soc=%d delta_soc=%d\n",
chip->maint_soc, chip->msoc,
diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c
index d354799..824d914 100644
--- a/drivers/power/supply/qcom/qg-util.c
+++ b/drivers/power/supply/qcom/qg-util.c
@@ -111,6 +111,22 @@
return rc;
}
+int qg_read_raw_data(struct qpnp_qg *chip, int addr, u32 *data)
+{
+ int rc;
+ u8 reg[2] = {0};
+
+ rc = qg_read(chip, chip->qg_base + addr, ®[0], 2);
+ if (rc < 0) {
+ pr_err("Failed to read QG addr %d rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ *data = reg[0] | (reg[1] << 8);
+
+ return rc;
+}
+
int get_fifo_length(struct qpnp_qg *chip, u32 *fifo_length, bool rt)
{
int rc;
diff --git a/drivers/power/supply/qcom/qg-util.h b/drivers/power/supply/qcom/qg-util.h
index 385c9e0..bb17afb 100644
--- a/drivers/power/supply/qcom/qg-util.h
+++ b/drivers/power/supply/qcom/qg-util.h
@@ -15,6 +15,7 @@
int qg_read(struct qpnp_qg *chip, u32 addr, u8 *val, int len);
int qg_write(struct qpnp_qg *chip, u32 addr, u8 *val, int len);
int qg_masked_write(struct qpnp_qg *chip, int addr, u32 mask, u32 val);
+int qg_read_raw_data(struct qpnp_qg *chip, int addr, u32 *data);
int get_fifo_length(struct qpnp_qg *chip, u32 *fifo_length, bool rt);
int get_sample_count(struct qpnp_qg *chip, u32 *sample_count);
int get_sample_interval(struct qpnp_qg *chip, u32 *sample_interval);
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 440bf74..eaf138c 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -1417,16 +1417,16 @@
QNOVO_CL_SKEW_DECIPCT, chip->cl.final_cc_uah);
chip->cl.final_cc_uah = chip->cl.final_cc_uah *
(1000 + QNOVO_CL_SKEW_DECIPCT);
- div64_s64(chip->cl.final_cc_uah, 1000);
+ chip->cl.final_cc_uah = div64_u64(chip->cl.final_cc_uah, 1000);
}
max_inc_val = chip->cl.learned_cc_uah
* (1000 + chip->dt.cl_max_cap_inc);
- div64_s64(max_inc_val, 1000);
+ max_inc_val = div64_u64(max_inc_val, 1000);
min_dec_val = chip->cl.learned_cc_uah
* (1000 - chip->dt.cl_max_cap_dec);
- div64_s64(min_dec_val, 1000);
+ min_dec_val = div64_u64(min_dec_val, 1000);
old_cap = chip->cl.learned_cc_uah;
if (chip->cl.final_cc_uah > max_inc_val)
@@ -1440,7 +1440,7 @@
if (chip->dt.cl_max_cap_limit) {
max_inc_val = (int64_t)chip->cl.nom_cap_uah * (1000 +
chip->dt.cl_max_cap_limit);
- div64_s64(max_inc_val, 1000);
+ max_inc_val = div64_u64(max_inc_val, 1000);
if (chip->cl.final_cc_uah > max_inc_val) {
fg_dbg(chip, FG_CAP_LEARN, "learning capacity %lld goes above max limit %lld\n",
chip->cl.final_cc_uah, max_inc_val);
@@ -1451,7 +1451,7 @@
if (chip->dt.cl_min_cap_limit) {
min_dec_val = (int64_t)chip->cl.nom_cap_uah * (1000 -
chip->dt.cl_min_cap_limit);
- div64_s64(min_dec_val, 1000);
+ min_dec_val = div64_u64(min_dec_val, 1000);
if (chip->cl.final_cc_uah < min_dec_val) {
fg_dbg(chip, FG_CAP_LEARN, "learning capacity %lld goes below min limit %lld\n",
chip->cl.final_cc_uah, min_dec_val);
@@ -1955,7 +1955,7 @@
}
val *= scaling_factor;
- div64_s64(val, 1000);
+ val = div64_u64(val, 1000);
rc = fg_sram_write(chip, ESR_RSLOW_CHG_WORD,
ESR_RSLOW_CHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -1972,7 +1972,7 @@
}
val *= scaling_factor;
- div64_s64(val, 1000);
+ val = div64_u64(val, 1000);
rc = fg_sram_write(chip, ESR_RSLOW_DISCHG_WORD,
ESR_RSLOW_DISCHG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -2080,12 +2080,21 @@
static int fg_adjust_recharge_soc(struct fg_chip *chip)
{
+ union power_supply_propval prop = {0, };
int rc, msoc, recharge_soc, new_recharge_soc = 0;
bool recharge_soc_status;
if (!chip->dt.auto_recharge_soc)
return 0;
+ rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH,
+ &prop);
+ if (rc < 0) {
+ pr_err("Error in getting battery health, rc=%d\n", rc);
+ return rc;
+ }
+ chip->health = prop.intval;
+
recharge_soc = chip->dt.recharge_soc_thr;
recharge_soc_status = chip->recharge_soc_adjusted;
/*
@@ -2116,6 +2125,9 @@
if (!chip->recharge_soc_adjusted)
return 0;
+ if (chip->health != POWER_SUPPLY_HEALTH_GOOD)
+ return 0;
+
/* Restore the default value */
new_recharge_soc = recharge_soc;
chip->recharge_soc_adjusted = false;
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
index 015da41..deccb20 100644
--- a/drivers/power/supply/qcom/qpnp-fg.c
+++ b/drivers/power/supply/qcom/qpnp-fg.c
@@ -33,6 +33,7 @@
#include <linux/ktime.h>
#include <linux/power_supply.h>
#include <linux/of_batterydata.h>
+#include <linux/spinlock.h>
#include <linux/string_helpers.h>
#include <linux/alarmtimer.h>
#include <linux/qpnp/qpnp-revid.h>
@@ -72,6 +73,7 @@
#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
#define MEM_IF_TIMEOUT_MS 5000
+#define FG_CYCLE_MS 1500
#define BUCKET_COUNT 8
#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
@@ -108,6 +110,7 @@
PMI8950 = 17,
PMI8996 = 19,
PMI8937 = 55,
+ PMI8940 = 64,
};
enum wa_flags {
@@ -150,6 +153,8 @@
int min_temp;
int max_temp;
int vbat_est_thr_uv;
+ int max_cap_limit;
+ int min_cap_limit;
};
struct fg_rslow_data {
@@ -275,11 +280,45 @@
DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL),
};
+enum fg_mem_backup_index {
+ FG_BACKUP_SOC = 0,
+ FG_BACKUP_CYCLE_COUNT,
+ FG_BACKUP_CC_SOC_COEFF,
+ FG_BACKUP_IGAIN,
+ FG_BACKUP_VCOR,
+ FG_BACKUP_TEMP_COUNTER,
+ FG_BACKUP_AGING_STORAGE,
+ FG_BACKUP_MAH_TO_SOC,
+ FG_BACKUP_MAX,
+};
+
+#define BACKUP(_idx, _address, _offset, _length, _value) \
+ [FG_BACKUP_##_idx] = { \
+ .address = _address, \
+ .offset = _offset, \
+ .len = _length, \
+ .value = _value, \
+ } \
+
+static struct fg_mem_data fg_backup_regs[FG_BACKUP_MAX] = {
+ /* ID Address, Offset, Length, Value*/
+ BACKUP(SOC, 0x564, 0, 24, -EINVAL),
+ BACKUP(CYCLE_COUNT, 0x5E8, 0, 16, -EINVAL),
+ BACKUP(CC_SOC_COEFF, 0x5BC, 0, 8, -EINVAL),
+ BACKUP(IGAIN, 0x424, 0, 4, -EINVAL),
+ BACKUP(VCOR, 0x484, 0, 4, -EINVAL),
+ BACKUP(TEMP_COUNTER, 0x580, 0, 4, -EINVAL),
+ BACKUP(AGING_STORAGE, 0x5E4, 0, 4, -EINVAL),
+ BACKUP(MAH_TO_SOC, 0x4A0, 0, 4, -EINVAL),
+};
+
static int fg_debug_mask;
module_param_named(
debug_mask, fg_debug_mask, int, 00600
);
+static int fg_reset_on_lockup;
+
static int fg_sense_type = -EINVAL;
static int fg_restart;
@@ -298,9 +337,18 @@
sram_update_period_ms, fg_sram_update_period_ms, int, 00600
);
+static bool fg_batt_valid_ocv;
+module_param_named(batt_valid_ocv, fg_batt_valid_ocv, bool, 0600
+);
+
+static int fg_batt_range_pct;
+module_param_named(batt_range_pct, fg_batt_range_pct, int, 0600
+);
+
struct fg_irq {
int irq;
- unsigned long disabled;
+ bool disabled;
+ bool wakeup;
};
enum fg_soc_irq {
@@ -348,6 +396,16 @@
MAX_ADDRESS,
};
+enum batt_info_params {
+ BATT_INFO_NOTIFY = 0,
+ BATT_INFO_SOC,
+ BATT_INFO_RES_ID,
+ BATT_INFO_VOLTAGE,
+ BATT_INFO_TEMP,
+ BATT_INFO_FCC,
+ BATT_INFO_MAX,
+};
+
struct register_offset {
u16 address[MAX_ADDRESS];
};
@@ -395,6 +453,22 @@
}
}
+enum slope_limit_status {
+ LOW_TEMP_CHARGE,
+ HIGH_TEMP_CHARGE,
+ LOW_TEMP_DISCHARGE,
+ HIGH_TEMP_DISCHARGE,
+ SLOPE_LIMIT_MAX,
+};
+
+#define VOLT_GAIN_MAX 3
+struct dischg_gain_soc {
+ bool enable;
+ u32 soc[VOLT_GAIN_MAX];
+ u32 medc_gain[VOLT_GAIN_MAX];
+ u32 highc_gain[VOLT_GAIN_MAX];
+};
+
#define THERMAL_COEFF_N_BYTES 6
struct fg_chip {
struct device *dev;
@@ -420,6 +494,7 @@
struct completion first_soc_done;
struct power_supply *bms_psy;
struct power_supply_desc bms_psy_d;
+ spinlock_t sec_access_lock;
struct mutex rw_lock;
struct mutex sysfs_restart_lock;
struct delayed_work batt_profile_init;
@@ -449,6 +524,7 @@
struct fg_wakeup_source update_sram_wakeup_source;
bool fg_restarting;
bool profile_loaded;
+ bool soc_reporting_ready;
bool use_otp_profile;
bool battery_missing;
bool power_supply_registered;
@@ -459,6 +535,7 @@
bool charge_done;
bool resume_soc_lowered;
bool vbat_low_irq_enabled;
+ bool full_soc_irq_enabled;
bool charge_full;
bool hold_soc_while_full;
bool input_present;
@@ -467,6 +544,10 @@
bool bad_batt_detection_en;
bool bcl_lpm_disabled;
bool charging_disabled;
+ bool use_vbat_low_empty_soc;
+ bool fg_shutdown;
+ bool use_soft_jeita_irq;
+ bool allow_false_negative_isense;
struct delayed_work update_jeita_setting;
struct delayed_work update_sram_data;
struct delayed_work update_temp_work;
@@ -491,6 +572,7 @@
int prev_status;
int health;
enum fg_batt_aging_mode batt_aging_mode;
+ struct alarm hard_jeita_alarm;
/* capacity learning */
struct fg_learning_data learning_data;
struct alarm fg_cap_learning_alarm;
@@ -498,6 +580,7 @@
struct fg_cc_soc_data sw_cc_soc_data;
/* rslow compensation */
struct fg_rslow_data rslow_comp;
+ int rconn_mohm;
/* cycle counter */
struct fg_cyc_ctr_data cyc_ctr;
/* iadc compensation */
@@ -510,6 +593,8 @@
bool jeita_hysteresis_support;
bool batt_hot;
bool batt_cold;
+ bool batt_warm;
+ bool batt_cool;
int cold_hysteresis;
int hot_hysteresis;
/* ESR pulse tuning */
@@ -518,6 +603,47 @@
bool esr_extract_disabled;
bool imptr_pulse_slow_en;
bool esr_pulse_tune_en;
+ /* Slope limiter */
+ struct work_struct slope_limiter_work;
+ struct fg_wakeup_source slope_limit_wakeup_source;
+ bool soc_slope_limiter_en;
+ enum slope_limit_status slope_limit_sts;
+ u32 slope_limit_temp;
+ u32 slope_limit_coeffs[SLOPE_LIMIT_MAX];
+ /* Discharge soc gain */
+ struct work_struct dischg_gain_work;
+ struct fg_wakeup_source dischg_gain_wakeup_source;
+ struct dischg_gain_soc dischg_gain;
+ /* IMA error recovery */
+ struct completion fg_reset_done;
+ struct work_struct ima_error_recovery_work;
+ struct fg_wakeup_source fg_reset_wakeup_source;
+ struct mutex ima_recovery_lock;
+ bool ima_error_handling;
+ bool block_sram_access;
+ bool irqs_enabled;
+ bool use_last_soc;
+ int last_soc;
+ /* Validating temperature */
+ int last_good_temp;
+ int batt_temp_low_limit;
+ int batt_temp_high_limit;
+ /* Validating CC_SOC */
+ struct work_struct cc_soc_store_work;
+ struct fg_wakeup_source cc_soc_wakeup_source;
+ int cc_soc_limit_pct;
+ bool use_last_cc_soc;
+ int64_t last_cc_soc;
+ /* Sanity check */
+ struct delayed_work check_sanity_work;
+ struct fg_wakeup_source sanity_wakeup_source;
+ u8 last_beat_count;
+ /* Batt_info restore */
+ int batt_info[BATT_INFO_MAX];
+ int batt_info_id;
+ bool batt_info_restore;
+ bool *batt_range_ocv;
+ int *batt_range_pct;
};
/* FG_MEMIF DEBUGFS structures */
@@ -661,17 +787,56 @@
return rc;
}
-static int fg_masked_write(struct fg_chip *chip, u16 addr,
+static int fg_masked_write_raw(struct fg_chip *chip, u16 addr,
u8 mask, u8 val, int len)
{
int rc;
rc = regmap_update_bits(chip->regmap, addr, mask, val);
- if (rc) {
+ if (rc)
pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
+
+ return rc;
+}
+
+static int fg_masked_write(struct fg_chip *chip, u16 addr,
+ u8 mask, u8 val, int len)
+{
+ int rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->sec_access_lock, flags);
+ rc = fg_masked_write_raw(chip, addr, mask, val, len);
+ spin_unlock_irqrestore(&chip->sec_access_lock, flags);
+
+ return rc;
+}
+
+#define SEC_ACCESS_OFFSET 0xD0
+#define SEC_ACCESS_VALUE 0xA5
+#define PERIPHERAL_MASK 0xFF
+static int fg_sec_masked_write(struct fg_chip *chip, u16 addr, u8 mask, u8 val,
+ int len)
+{
+ int rc;
+ unsigned long flags;
+ u8 temp;
+ u16 base = addr & (~PERIPHERAL_MASK);
+
+ spin_lock_irqsave(&chip->sec_access_lock, flags);
+ temp = SEC_ACCESS_VALUE;
+ rc = fg_write(chip, &temp, base + SEC_ACCESS_OFFSET, 1);
+ if (rc) {
+ pr_err("Unable to unlock sec_access: %d\n", rc);
+ goto out;
}
+ rc = fg_masked_write_raw(chip, addr, mask, val, len);
+ if (rc)
+ pr_err("Unable to write securely to address 0x%x: %d", addr,
+ rc);
+out:
+ spin_unlock_irqrestore(&chip->sec_access_lock, flags);
return rc;
}
@@ -952,6 +1117,7 @@
int rc = 0, user_cnt = 0, sublen;
bool access_configured = false;
u8 *wr_data = val, word[4];
+ u16 orig_address = address;
char str[DEBUG_PRINT_BUFFER_SIZE];
if (address < RAM_OFFSET)
@@ -960,8 +1126,8 @@
if (offset > 3)
return -EINVAL;
- address = ((address + offset) / 4) * 4;
- offset = (address + offset) % 4;
+ address = ((orig_address + offset) / 4) * 4;
+ offset = (orig_address + offset) % 4;
user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
@@ -1061,50 +1227,253 @@
#define MEM_INTF_IMA_EXP_STS 0x55
#define MEM_INTF_IMA_HW_STS 0x56
#define MEM_INTF_IMA_BYTE_EN 0x60
-#define IMA_ADDR_STBL_ERR BIT(7)
-#define IMA_WR_ACS_ERR BIT(6)
-#define IMA_RD_ACS_ERR BIT(5)
#define IMA_IACS_CLR BIT(2)
#define IMA_IACS_RDY BIT(1)
-static int fg_check_ima_exception(struct fg_chip *chip)
+static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
+{
+ int rc = 0;
+ u8 temp;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Running IACS clear sequence\n");
+
+ /* clear the error */
+ rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+ IMA_IACS_CLR, IMA_IACS_CLR, 1);
+ if (rc) {
+ pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = 0x4;
+ rc = fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
+ if (rc) {
+ pr_err("Error writing to MEM_INTF_ADDR_MSB, rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = 0x0;
+ rc = fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
+ if (rc) {
+ pr_err("Error writing to WR_DATA3, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
+ if (rc) {
+ pr_err("Error writing to RD_DATA3, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+ IMA_IACS_CLR, 0, 1);
+ if (rc) {
+ pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("IACS clear sequence complete!\n");
+ return rc;
+}
+
+#define IACS_ERR_BIT BIT(0)
+#define XCT_ERR_BIT BIT(1)
+#define DATA_RD_ERR_BIT BIT(3)
+#define DATA_WR_ERR_BIT BIT(4)
+#define ADDR_BURST_WRAP_BIT BIT(5)
+#define ADDR_RNG_ERR_BIT BIT(6)
+#define ADDR_SRC_ERR_BIT BIT(7)
+static int fg_check_ima_exception(struct fg_chip *chip, bool check_hw_sts)
{
int rc = 0, ret = 0;
- u8 err_sts, exp_sts = 0, hw_sts = 0;
+ u8 err_sts = 0, exp_sts = 0, hw_sts = 0;
+ bool run_err_clr_seq = false;
rc = fg_read(chip, &err_sts,
chip->mem_base + MEM_INTF_IMA_ERR_STS, 1);
if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
+ pr_err("failed to read IMA_ERR_STS, rc=%d\n", rc);
return rc;
}
- if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) {
- u8 temp;
-
- fg_read(chip, &exp_sts,
+ rc = fg_read(chip, &exp_sts,
chip->mem_base + MEM_INTF_IMA_EXP_STS, 1);
- fg_read(chip, &hw_sts,
- chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
- pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
- err_sts, exp_sts, hw_sts);
- rc = err_sts;
-
- /* clear the error */
- ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
- IMA_IACS_CLR, IMA_IACS_CLR, 1);
- temp = 0x4;
- ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
- temp = 0x0;
- ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
- ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
- ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
- IMA_IACS_CLR, 0, 1);
- if (!ret)
- return -EAGAIN;
-
- pr_err("Error clearing IMA exception ret=%d\n", ret);
+ if (rc) {
+ pr_err("Error in reading IMA_EXP_STS, rc=%d\n", rc);
+ return rc;
}
+ rc = fg_read(chip, &hw_sts,
+ chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
+ if (rc) {
+ pr_err("Error in reading IMA_HW_STS, rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_info_once("Initial ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+ err_sts, exp_sts, hw_sts);
+
+ if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+ pr_info("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+ err_sts, exp_sts, hw_sts);
+
+ if (check_hw_sts) {
+ /*
+ * Lower nibble should be equal to upper nibble before SRAM
+ * transactions begins from SW side. If they are unequal, then
+ * the error clear sequence should be run irrespective of IMA
+ * exception errors.
+ */
+ if ((hw_sts & 0x0F) != hw_sts >> 4) {
+ pr_err("IMA HW not in correct state, hw_sts=%x\n",
+ hw_sts);
+ run_err_clr_seq = true;
+ }
+ }
+
+ if (exp_sts & (IACS_ERR_BIT | XCT_ERR_BIT | DATA_RD_ERR_BIT |
+ DATA_WR_ERR_BIT | ADDR_BURST_WRAP_BIT | ADDR_RNG_ERR_BIT |
+ ADDR_SRC_ERR_BIT)) {
+ pr_err("IMA exception bit set, exp_sts=%x\n", exp_sts);
+ run_err_clr_seq = true;
+ }
+
+ if (run_err_clr_seq) {
+ ret = fg_run_iacs_clear_sequence(chip);
+ if (!ret)
+ return -EAGAIN;
+ else
+ pr_err("Error clearing IMA exception ret=%d\n", ret);
+ }
+
+ return rc;
+}
+
+static void fg_enable_irqs(struct fg_chip *chip, bool enable)
+{
+ if (!(enable ^ chip->irqs_enabled))
+ return;
+
+ if (enable) {
+ enable_irq(chip->soc_irq[DELTA_SOC].irq);
+ enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
+ if (!chip->full_soc_irq_enabled) {
+ enable_irq(chip->soc_irq[FULL_SOC].irq);
+ enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+ chip->full_soc_irq_enabled = true;
+ }
+ enable_irq(chip->batt_irq[BATT_MISSING].irq);
+ if (!chip->vbat_low_irq_enabled) {
+ enable_irq(chip->batt_irq[VBATT_LOW].irq);
+ enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = true;
+ }
+ if (!chip->use_vbat_low_empty_soc) {
+ enable_irq(chip->soc_irq[EMPTY_SOC].irq);
+ enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+ }
+ chip->irqs_enabled = true;
+ } else {
+ disable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
+ disable_irq_nosync(chip->soc_irq[DELTA_SOC].irq);
+ if (chip->full_soc_irq_enabled) {
+ disable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+ disable_irq_nosync(chip->soc_irq[FULL_SOC].irq);
+ chip->full_soc_irq_enabled = false;
+ }
+ disable_irq(chip->batt_irq[BATT_MISSING].irq);
+ if (chip->vbat_low_irq_enabled) {
+ disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = false;
+ }
+ if (!chip->use_vbat_low_empty_soc) {
+ disable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+ disable_irq_nosync(chip->soc_irq[EMPTY_SOC].irq);
+ }
+ chip->irqs_enabled = false;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("FG interrupts are %sabled\n", enable ? "en" : "dis");
+}
+
+static void fg_check_ima_error_handling(struct fg_chip *chip)
+{
+ if (chip->ima_error_handling) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("IMA error is handled already!\n");
+ return;
+ }
+ mutex_lock(&chip->ima_recovery_lock);
+ fg_enable_irqs(chip, false);
+ chip->use_last_cc_soc = true;
+ chip->ima_error_handling = true;
+ if (!work_pending(&chip->ima_error_recovery_work))
+ schedule_work(&chip->ima_error_recovery_work);
+ mutex_unlock(&chip->ima_recovery_lock);
+}
+
+#define SOC_ALG_ST 0xCF
+#define FGXCT_PRD BIT(7)
+#define ALG_ST_CHECK_COUNT 20
+static int fg_check_alg_status(struct fg_chip *chip)
+{
+ int rc = 0, timeout = ALG_ST_CHECK_COUNT, count = 0;
+ u8 ima_opr_sts, alg_sts = 0, temp = 0;
+
+ if (!fg_reset_on_lockup) {
+ pr_info("FG lockup detection cannot be run\n");
+ return 0;
+ }
+
+ rc = fg_read(chip, &alg_sts, chip->soc_base + SOC_ALG_ST, 1);
+ if (rc) {
+ pr_err("Error in reading SOC_ALG_ST, rc=%d\n", rc);
+ return rc;
+ }
+
+ while (1) {
+ rc = fg_read(chip, &ima_opr_sts,
+ chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
+ if (!rc && !(ima_opr_sts & FGXCT_PRD))
+ break;
+
+ if (rc) {
+ pr_err("Error in reading IMA_OPR_STS, rc=%d\n",
+ rc);
+ break;
+ }
+
+ rc = fg_read(chip, &temp, chip->soc_base + SOC_ALG_ST,
+ 1);
+ if (rc) {
+ pr_err("Error in reading SOC_ALG_ST, rc=%d\n",
+ rc);
+ break;
+ }
+
+ if ((ima_opr_sts & FGXCT_PRD) && (temp == alg_sts))
+ count++;
+
+ /* Wait for ~10ms while polling ALG_ST & IMA_OPR_STS */
+ usleep_range(9000, 11000);
+
+ if (!(--timeout))
+ break;
+ }
+
+ if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+ pr_info("ima_opr_sts: %x alg_sts: %x count=%d\n", ima_opr_sts,
+ alg_sts, count);
+
+ if (count == ALG_ST_CHECK_COUNT) {
+ /* If we are here, that means FG ALG is stuck */
+ pr_err("ALG is stuck\n");
+ fg_check_ima_error_handling(chip);
+ rc = -EBUSY;
+ }
return rc;
}
@@ -1122,19 +1491,25 @@
while (1) {
rc = fg_read(chip, &ima_opr_sts,
chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
- if (!rc && (ima_opr_sts & IMA_IACS_RDY))
+ if (!rc && (ima_opr_sts & IMA_IACS_RDY)) {
break;
+ } else {
+ if (!(--timeout) || rc)
+ break;
- if (!(--timeout) || rc)
- break;
- /* delay for iacs_ready to be asserted */
- usleep_range(5000, 7000);
+ /* delay for iacs_ready to be asserted */
+ usleep_range(5000, 7000);
+ }
}
if (!timeout || rc) {
- pr_err("IACS_RDY not set\n");
+ pr_err("IACS_RDY not set, ima_opr_sts: %x\n", ima_opr_sts);
+ rc = fg_check_alg_status(chip);
+ if (rc && rc != -EBUSY)
+ pr_err("Couldn't check FG ALG status, rc=%d\n",
+ rc);
/* perform IACS_CLR sequence */
- fg_check_ima_exception(chip);
+ fg_check_ima_exception(chip, false);
return -EBUSY;
}
@@ -1154,15 +1529,16 @@
while (len > 0) {
num_bytes = (offset + len) > BUF_LEN ?
- (BUF_LEN - offset) : len;
+ (BUF_LEN - offset) : len;
/* write to byte_enable */
for (i = offset; i < (offset + num_bytes); i++)
byte_enable |= BIT(i);
rc = fg_write(chip, &byte_enable,
- chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
+ chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
if (rc) {
- pr_err("Unable to write to byte_en_reg rc=%d\n", rc);
+ pr_err("Unable to write to byte_en_reg rc=%d\n",
+ rc);
return rc;
}
/* write data */
@@ -1193,12 +1569,13 @@
rc = fg_check_iacs_ready(chip);
if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
+ pr_err("IACS_RDY failed post write to address %x offset %d rc=%d\n",
+ address, offset, rc);
return rc;
}
/* check for error condition */
- rc = fg_check_ima_exception(chip);
+ rc = fg_check_ima_exception(chip, false);
if (rc) {
pr_err("IMA transaction failed rc=%d", rc);
return rc;
@@ -1239,12 +1616,13 @@
rc = fg_check_iacs_ready(chip);
if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
+ pr_err("IACS_RDY failed post read for address %x offset %d rc=%d\n",
+ address, offset, rc);
return rc;
}
/* check for error condition */
- rc = fg_check_ima_exception(chip);
+ rc = fg_check_ima_exception(chip, false);
if (rc) {
pr_err("IMA transaction failed rc=%d", rc);
return rc;
@@ -1296,7 +1674,7 @@
* clear, then return an error instead of waiting for it again.
*/
if (time_count > 4) {
- pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n");
+ pr_err("Waited for ~16ms polling RIF_MEM_ACCESS_REQ\n");
return -ETIMEDOUT;
}
@@ -1322,7 +1700,8 @@
rc = fg_check_iacs_ready(chip);
if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
+ pr_err("IACS_RDY failed before setting address: %x offset: %d rc=%d\n",
+ address, offset, rc);
return rc;
}
@@ -1335,7 +1714,8 @@
rc = fg_check_iacs_ready(chip);
if (rc)
- pr_debug("IACS_RDY failed rc=%d\n", rc);
+ pr_err("IACS_RDY failed after setting address: %x offset: %d rc=%d\n",
+ address, offset, rc);
return rc;
}
@@ -1346,10 +1726,13 @@
static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
int len, int offset)
{
- int rc = 0, orig_address = address;
+ int rc = 0, ret, orig_address = address;
u8 start_beat_count, end_beat_count, count = 0;
bool retry = false;
+ if (chip->fg_shutdown)
+ return -EINVAL;
+
if (offset > 3) {
pr_err("offset too large %d\n", offset);
return -EINVAL;
@@ -1372,11 +1755,22 @@
}
mutex_lock(&chip->rw_lock);
+ if (fg_debug_mask & FG_MEM_DEBUG_READS)
+ pr_info("Read for %d bytes is attempted @ 0x%x[%d]\n",
+ len, address, offset);
retry:
+ if (count >= RETRY_COUNT) {
+ pr_err("Retried reading 3 times\n");
+ retry = false;
+ goto out;
+ }
+
rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0);
if (rc) {
pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+ retry = true;
+ count++;
goto out;
}
@@ -1385,18 +1779,21 @@
chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
if (rc) {
pr_err("failed to read beat count rc=%d\n", rc);
+ retry = true;
+ count++;
goto out;
}
/* read data */
rc = __fg_interleaved_mem_read(chip, val, address, offset, len);
if (rc) {
+ count++;
if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- count++;
pr_err("IMA access failed retry_count = %d\n", count);
goto retry;
} else {
pr_err("failed to read SRAM address rc = %d\n", rc);
+ retry = true;
goto out;
}
}
@@ -1406,6 +1803,8 @@
chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
if (rc) {
pr_err("failed to read beat count rc=%d\n", rc);
+ retry = true;
+ count++;
goto out;
}
@@ -1418,12 +1817,13 @@
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("Beat count do not match - retry transaction\n");
retry = true;
+ count++;
}
out:
/* Release IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
- if (rc)
- pr_err("failed to reset IMA access bit rc = %d\n", rc);
+ ret = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
+ if (ret)
+ pr_err("failed to reset IMA access bit ret = %d\n", ret);
if (retry) {
retry = false;
@@ -1439,8 +1839,12 @@
static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address,
int len, int offset)
{
- int rc = 0, orig_address = address;
+ int rc = 0, ret, orig_address = address;
u8 count = 0;
+ bool retry = false;
+
+ if (chip->fg_shutdown)
+ return -EINVAL;
if (address < RAM_OFFSET)
return -EINVAL;
@@ -1455,32 +1859,49 @@
offset = (orig_address + offset) % 4;
mutex_lock(&chip->rw_lock);
+ if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+ pr_info("Write for %d bytes is attempted @ 0x%x[%d]\n",
+ len, address, offset);
retry:
+ if (count >= RETRY_COUNT) {
+ pr_err("Retried writing 3 times\n");
+ retry = false;
+ goto out;
+ }
+
rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1);
if (rc) {
- pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc);
+ pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+ retry = true;
+ count++;
goto out;
}
/* write data */
rc = __fg_interleaved_mem_write(chip, val, address, offset, len);
if (rc) {
+ count++;
if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- count++;
pr_err("IMA access failed retry_count = %d\n", count);
goto retry;
} else {
pr_err("failed to write SRAM address rc = %d\n", rc);
+ retry = true;
goto out;
}
}
out:
/* Release IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
- if (rc)
- pr_err("failed to reset IMA access bit rc = %d\n", rc);
+ ret = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
+ if (ret)
+ pr_err("failed to reset IMA access bit ret = %d\n", ret);
+
+ if (retry) {
+ retry = false;
+ goto retry;
+ }
mutex_unlock(&chip->rw_lock);
fg_relax(&chip->memif_wakeup_source);
@@ -1490,6 +1911,9 @@
static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
int len, int offset, bool keep_access)
{
+ if (chip->block_sram_access)
+ return -EBUSY;
+
if (chip->ima_supported)
return fg_interleaved_mem_read(chip, val, address,
len, offset);
@@ -1501,6 +1925,9 @@
static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
int len, int offset, bool keep_access)
{
+ if (chip->block_sram_access)
+ return -EBUSY;
+
if (chip->ima_supported)
return fg_interleaved_mem_write(chip, val, address,
len, offset);
@@ -1538,6 +1965,62 @@
return rc;
}
+static u8 sram_backup_buffer[100];
+static int fg_backup_sram_registers(struct fg_chip *chip, bool save)
+{
+ int rc, i, len, offset;
+ u16 address;
+ u8 *ptr;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("%sing SRAM registers\n", save ? "Back" : "Restor");
+
+ ptr = sram_backup_buffer;
+ for (i = 0; i < FG_BACKUP_MAX; i++) {
+ address = fg_backup_regs[i].address;
+ offset = fg_backup_regs[i].offset;
+ len = fg_backup_regs[i].len;
+ if (save)
+ rc = fg_interleaved_mem_read(chip, ptr, address,
+ len, offset);
+ else
+ rc = fg_interleaved_mem_write(chip, ptr, address,
+ len, offset);
+ if (rc) {
+ pr_err("Error in reading %d bytes from %x[%d], rc=%d\n",
+ len, address, offset, rc);
+ break;
+ }
+ ptr += len;
+ }
+
+ return rc;
+}
+
+#define SOC_FG_RESET 0xF3
+#define RESET_MASK (BIT(7) | BIT(5))
+static int fg_reset(struct fg_chip *chip, bool reset)
+{
+ int rc;
+
+ rc = fg_sec_masked_write(chip, chip->soc_base + SOC_FG_RESET,
+ 0xFF, reset ? RESET_MASK : 0, 1);
+ if (rc)
+ pr_err("Error in writing to 0x%x, rc=%d\n", SOC_FG_RESET, rc);
+
+ return rc;
+}
+
+static void fg_handle_battery_insertion(struct fg_chip *chip)
+{
+ reinit_completion(&chip->batt_id_avail);
+ reinit_completion(&chip->fg_reset_done);
+ schedule_delayed_work(&chip->batt_profile_init, 0);
+ cancel_delayed_work(&chip->update_sram_data);
+ schedule_delayed_work(&chip->update_sram_data, msecs_to_jiffies(0));
+}
+
+
static int soc_to_setpoint(int soc)
{
return DIV_ROUND_CLOSEST(soc * 255, 100);
@@ -1550,6 +2033,7 @@
val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
data[0] = val & 0xFF;
data[1] = val >> 8;
+ return;
}
static u8 batt_to_setpoint_8b(int vbatt_mv)
@@ -1678,14 +2162,37 @@
return rc;
}
+#define VBATT_LOW_STS_BIT BIT(2)
+static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
+{
+ int rc = 0;
+ u8 fg_batt_sts;
+
+ rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
+ if (rc)
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->batt_base), rc);
+ else
+ *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
+
+ return rc;
+}
+
#define SOC_EMPTY BIT(3)
static bool fg_is_batt_empty(struct fg_chip *chip)
{
u8 fg_soc_sts;
int rc;
+ bool vbatt_low_sts;
- rc = fg_read(chip, &fg_soc_sts,
- INT_RT_STS(chip->soc_base), 1);
+ if (chip->use_vbat_low_empty_soc) {
+ if (fg_get_vbatt_status(chip, &vbatt_low_sts))
+ return false;
+
+ return vbatt_low_sts;
+ }
+
+ rc = fg_read(chip, &fg_soc_sts, INT_RT_STS(chip->soc_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->soc_base), rc);
@@ -1732,7 +2239,16 @@
#define FULL_SOC_RAW 0xFF
static int get_prop_capacity(struct fg_chip *chip)
{
- int msoc;
+ int msoc, rc;
+ bool vbatt_low_sts;
+
+ if (chip->use_last_soc && chip->last_soc) {
+ if (chip->last_soc == FULL_SOC_RAW)
+ return FULL_CAPACITY;
+ return DIV_ROUND_CLOSEST((chip->last_soc - 1) *
+ (FULL_CAPACITY - 2),
+ FULL_SOC_RAW - 2) + 1;
+ }
if (chip->battery_missing)
return MISSING_CAPACITY;
@@ -1747,10 +2263,28 @@
return EMPTY_CAPACITY;
}
msoc = get_monotonic_soc_raw(chip);
- if (msoc == 0)
- return EMPTY_CAPACITY;
- else if (msoc == FULL_SOC_RAW)
+ if (msoc == 0) {
+ if (fg_reset_on_lockup && chip->use_vbat_low_empty_soc) {
+ rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
+ if (rc) {
+ pr_err("Error in reading vbatt_status, rc=%d\n",
+ rc);
+ return EMPTY_CAPACITY;
+ }
+
+ if (!vbatt_low_sts)
+ return DIV_ROUND_CLOSEST((chip->last_soc - 1) *
+ (FULL_CAPACITY - 2),
+ FULL_SOC_RAW - 2) + 1;
+ else
+ return EMPTY_CAPACITY;
+ } else {
+ return EMPTY_CAPACITY;
+ }
+ } else if (msoc == FULL_SOC_RAW) {
return FULL_CAPACITY;
+ }
+
return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2),
FULL_SOC_RAW - 2) + 1;
}
@@ -1843,6 +2377,25 @@
return 0;
}
+#define IGNORE_FALSE_NEGATIVE_ISENSE_BIT BIT(3)
+static int set_prop_ignore_false_negative_isense(struct fg_chip *chip,
+ bool ignore)
+{
+ int rc;
+
+ rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+ IGNORE_FALSE_NEGATIVE_ISENSE_BIT,
+ ignore ? IGNORE_FALSE_NEGATIVE_ISENSE_BIT : 0,
+ EXTERNAL_SENSE_OFFSET);
+ if (rc) {
+ pr_err("failed to %s isense false negative ignore rc=%d\n",
+ ignore ? "enable" : "disable", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
#define EXPONENT_MASK 0xF800
#define MANTISSA_MASK 0x3FF
#define SIGN BIT(10)
@@ -1953,8 +2506,7 @@
return rc;
}
- if (fg_debug_mask & FG_IRQS)
- pr_info("fg batt sts 0x%x\n", fg_batt_sts);
+ pr_debug("fg batt sts 0x%x\n", fg_batt_sts);
return (fg_batt_sts & BATT_IDED) ? 1 : 0;
}
@@ -1984,7 +2536,7 @@
#define DECIKELVIN 2730
#define SRAM_PERIOD_NO_ID_UPDATE_MS 100
#define FULL_PERCENT_28BIT 0xFFFFFFF
-static void update_sram_data(struct fg_chip *chip, int *resched_ms)
+static int update_sram_data(struct fg_chip *chip, int *resched_ms)
{
int i, j, rc = 0;
u8 reg[4];
@@ -2060,6 +2612,31 @@
}
fg_mem_release(chip);
+ /* Backup the registers whenever no error happens during update */
+ if (fg_reset_on_lockup && !chip->ima_error_handling) {
+ if (!rc) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("backing up SRAM registers\n");
+ rc = fg_backup_sram_registers(chip, true);
+ if (rc) {
+ pr_err("Couldn't save sram registers\n");
+ goto out;
+ }
+ if (!chip->use_last_soc) {
+ chip->last_soc = get_monotonic_soc_raw(chip);
+ chip->last_cc_soc = div64_s64(
+ (int64_t)chip->last_soc *
+ FULL_PERCENT_28BIT, FULL_SOC_RAW);
+ }
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("last_soc: %d last_cc_soc: %lld\n",
+ chip->last_soc, chip->last_cc_soc);
+ } else {
+ pr_err("update_sram failed\n");
+ goto out;
+ }
+ }
+
if (!rc)
get_current_time(&chip->last_sram_update_time);
@@ -2070,7 +2647,55 @@
} else {
*resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
}
+out:
fg_relax(&chip->update_sram_wakeup_source);
+ return rc;
+}
+
+#define SANITY_CHECK_PERIOD_MS 5000
+static void check_sanity_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ check_sanity_work.work);
+ int rc = 0;
+ u8 beat_count;
+ bool tried_once = false;
+
+ fg_stay_awake(&chip->sanity_wakeup_source);
+
+try_again:
+ rc = fg_read(chip, &beat_count,
+ chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
+ if (rc) {
+ pr_err("failed to read beat count rc=%d\n", rc);
+ goto resched;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("current: %d, prev: %d\n", beat_count,
+ chip->last_beat_count);
+
+ if (chip->last_beat_count == beat_count) {
+ if (!tried_once) {
+ /* Wait for 1 FG cycle and read it once again */
+ msleep(1500);
+ tried_once = true;
+ goto try_again;
+ } else {
+ pr_err("Beat count not updating\n");
+ fg_check_ima_error_handling(chip);
+ goto out;
+ }
+ } else {
+ chip->last_beat_count = beat_count;
+ }
+resched:
+ schedule_delayed_work(
+ &chip->check_sanity_work,
+ msecs_to_jiffies(SANITY_CHECK_PERIOD_MS));
+out:
+ fg_relax(&chip->sanity_wakeup_source);
}
#define SRAM_TIMEOUT_MS 3000
@@ -2079,8 +2704,9 @@
struct fg_chip *chip = container_of(work,
struct fg_chip,
update_sram_data.work);
- int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret;
+ int resched_ms, ret;
bool tried_again = false;
+ int rc = 0;
wait:
/* Wait for MEMIF access revoked */
@@ -2094,14 +2720,19 @@
goto wait;
} else if (ret <= 0) {
pr_err("transaction timed out ret=%d\n", ret);
+ if (fg_is_batt_id_valid(chip))
+ resched_ms = fg_sram_update_period_ms;
+ else
+ resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
goto out;
}
- update_sram_data(chip, &resched_ms);
+ rc = update_sram_data(chip, &resched_ms);
out:
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(resched_ms));
+ if (!rc)
+ schedule_delayed_work(
+ &chip->update_sram_data,
+ msecs_to_jiffies(resched_ms));
}
#define BATT_TEMP_OFFSET 3
@@ -2115,6 +2746,8 @@
TEMP_SENSE_CHARGE_BIT)
#define TEMP_PERIOD_UPDATE_MS 10000
#define TEMP_PERIOD_TIMEOUT_MS 3000
+#define BATT_TEMP_LOW_LIMIT -600
+#define BATT_TEMP_HIGH_LIMIT 1500
static void update_temp_data(struct work_struct *work)
{
s16 temp;
@@ -2166,14 +2799,44 @@
}
temp = reg[0] | (reg[1] << 8);
- fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
- - DECIKELVIN;
+ temp = (temp * TEMP_LSB_16B / 1000) - DECIKELVIN;
+
+ /*
+ * If temperature is within the specified range (e.g. -60C and 150C),
+ * update it to the userspace. Otherwise, use the last read good
+ * temperature.
+ */
+ if (temp > chip->batt_temp_low_limit &&
+ temp < chip->batt_temp_high_limit) {
+ chip->last_good_temp = temp;
+ fg_data[0].value = temp;
+ } else {
+ fg_data[0].value = chip->last_good_temp;
+
+ /*
+ * If the temperature is read before and seems to be in valid
+ * range, then a bad temperature reading could be because of
+ * FG lockup. Trigger the FG reset sequence in such cases.
+ */
+ if (chip->last_temp_update_time && fg_reset_on_lockup &&
+ (chip->last_good_temp > chip->batt_temp_low_limit &&
+ chip->last_good_temp < chip->batt_temp_high_limit)) {
+ pr_err("Batt_temp is %d !, triggering FG reset\n",
+ temp);
+ fg_check_ima_error_handling(chip);
+ }
+ }
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value);
get_current_time(&chip->last_temp_update_time);
+ if (chip->soc_slope_limiter_en) {
+ fg_stay_awake(&chip->slope_limit_wakeup_source);
+ schedule_work(&chip->slope_limiter_work);
+ }
+
out:
if (chip->sw_rbias_ctrl) {
rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
@@ -2226,18 +2889,6 @@
return rc;
}
-#define VBATT_LOW_STS_BIT BIT(2)
-static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
-{
- int rc = 0;
- u8 fg_batt_sts;
-
- rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
- if (!rc)
- *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
- return rc;
-}
-
#define BATT_CYCLE_NUMBER_REG 0x5E8
#define BATT_CYCLE_OFFSET 0
static void restore_cycle_counter(struct fg_chip *chip)
@@ -2301,6 +2952,9 @@
bucket, rc);
else
chip->cyc_ctr.count[bucket] = cyc_count;
+
+ if (fg_debug_mask & FG_POWER_SUPPLY)
+ pr_info("Stored bucket %d cyc_count: %d\n", bucket, cyc_count);
return rc;
}
@@ -2416,6 +3070,62 @@
return ((int)val) * 1000;
}
+#define SLOPE_LIMITER_COEFF_REG 0x430
+#define SLOPE_LIMITER_COEFF_OFFSET 3
+#define SLOPE_LIMIT_TEMP_THRESHOLD 100
+#define SLOPE_LIMIT_LOW_TEMP_CHG 45
+#define SLOPE_LIMIT_HIGH_TEMP_CHG 2
+#define SLOPE_LIMIT_LOW_TEMP_DISCHG 45
+#define SLOPE_LIMIT_HIGH_TEMP_DISCHG 2
+static void slope_limiter_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ slope_limiter_work);
+ enum slope_limit_status status;
+ int batt_temp, rc;
+ u8 buf[2];
+ int64_t val;
+
+ batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+
+ if (chip->status == POWER_SUPPLY_STATUS_CHARGING ||
+ chip->status == POWER_SUPPLY_STATUS_FULL) {
+ if (batt_temp < chip->slope_limit_temp)
+ status = LOW_TEMP_CHARGE;
+ else
+ status = HIGH_TEMP_CHARGE;
+ } else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (batt_temp < chip->slope_limit_temp)
+ status = LOW_TEMP_DISCHARGE;
+ else
+ status = HIGH_TEMP_DISCHARGE;
+ } else {
+ goto out;
+ }
+
+ if (status == chip->slope_limit_sts)
+ goto out;
+
+ val = chip->slope_limit_coeffs[status];
+ val *= MICRO_UNIT;
+ half_float_to_buffer(val, buf);
+ rc = fg_mem_write(chip, buf,
+ SLOPE_LIMITER_COEFF_REG, 2,
+ SLOPE_LIMITER_COEFF_OFFSET, 0);
+ if (rc) {
+ pr_err("Couldn't write to slope_limiter_coeff_reg, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ chip->slope_limit_sts = status;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Slope limit sts: %d val: %lld buf[%x %x] written\n",
+ status, val, buf[0], buf[1]);
+out:
+ fg_relax(&chip->slope_limit_wakeup_source);
+}
+
static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
{
int64_t *coeffs;
@@ -2481,6 +3191,7 @@
#define ESR_ACTUAL_REG 0x554
#define BATTERY_ESR_REG 0x4F4
#define TEMP_RS_TO_RSLOW_REG 0x514
+#define ESR_OFFSET 2
static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
{
int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
@@ -2519,7 +3230,7 @@
rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0);
esr_actual = half_float(buffer);
- rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0);
+ rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, ESR_OFFSET, 0);
battery_esr = half_float(buffer);
if (rc) {
@@ -2594,124 +3305,6 @@
estimate_battery_age(chip, &chip->actual_cap_uah);
}
-static enum power_supply_property fg_power_props[] = {
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_CAPACITY_RAW,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_OCV,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_CHARGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
- POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
- POWER_SUPPLY_PROP_CHARGE_FULL,
- POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_COOL_TEMP,
- POWER_SUPPLY_PROP_WARM_TEMP,
- POWER_SUPPLY_PROP_RESISTANCE,
- POWER_SUPPLY_PROP_RESISTANCE_ID,
- POWER_SUPPLY_PROP_BATTERY_TYPE,
- POWER_SUPPLY_PROP_UPDATE_NOW,
- POWER_SUPPLY_PROP_ESR_COUNT,
- POWER_SUPPLY_PROP_VOLTAGE_MIN,
- POWER_SUPPLY_PROP_CYCLE_COUNT,
- POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
- POWER_SUPPLY_PROP_HI_POWER,
-};
-
-static int fg_power_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
- bool vbatt_low_sts;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_BATTERY_TYPE:
- if (chip->battery_missing)
- val->strval = missing_batt_type;
- else if (chip->fg_restarting)
- val->strval = loading_batt_type;
- else
- val->strval = chip->batt_type;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = get_prop_capacity(chip);
- break;
- case POWER_SUPPLY_PROP_CAPACITY_RAW:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
- val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_OCV:
- val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = chip->batt_max_voltage_uv;
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
- break;
- case POWER_SUPPLY_PROP_COOL_TEMP:
- val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
- break;
- case POWER_SUPPLY_PROP_WARM_TEMP:
- val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
- break;
- case POWER_SUPPLY_PROP_RESISTANCE:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
- break;
- case POWER_SUPPLY_PROP_ESR_COUNT:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT:
- val->intval = fg_get_cycle_count(chip);
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- val->intval = chip->cyc_ctr.id;
- break;
- case POWER_SUPPLY_PROP_RESISTANCE_ID:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
- break;
- case POWER_SUPPLY_PROP_UPDATE_NOW:
- val->intval = 0;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN:
- if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
- val->intval = (int)vbatt_low_sts;
- else
- val->intval = 1;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- val->intval = chip->nom_cap_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL:
- val->intval = chip->learning_data.learned_cc_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW:
- val->intval = chip->learning_data.cc_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
- val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
- break;
- case POWER_SUPPLY_PROP_HI_POWER:
- val->intval = !!chip->bcl_lpm_disabled;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
static int correction_times[] = {
1470,
2940,
@@ -2853,11 +3446,8 @@
goto fail;
}
- if (chip->wa_flag & USE_CC_SOC_REG) {
- mutex_unlock(&chip->learning_data.learning_lock);
- fg_relax(&chip->capacity_learning_wakeup_source);
- return;
- }
+ if (chip->wa_flag & USE_CC_SOC_REG)
+ goto fail;
fg_mem_lock(chip);
@@ -2888,6 +3478,8 @@
pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
fail:
+ if (chip->wa_flag & USE_CC_SOC_REG)
+ fg_relax(&chip->capacity_learning_wakeup_source);
mutex_unlock(&chip->learning_data.learning_lock);
return;
@@ -2901,7 +3493,7 @@
{
int rc;
u8 reg[4];
- unsigned int temp, magnitude;
+ int temp;
rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
if (rc) {
@@ -2910,20 +3502,61 @@
}
temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0];
- magnitude = temp & CC_SOC_MAGNITUDE_MASK;
- if (temp & CC_SOC_NEGATIVE_BIT)
- *cc_soc = -1 * (~magnitude + 1);
- else
- *cc_soc = magnitude;
-
+ *cc_soc = sign_extend32(temp, 29);
return 0;
}
+static int fg_get_current_cc(struct fg_chip *chip)
+{
+ int cc_soc, rc;
+ int64_t current_capacity;
+
+ if (!(chip->wa_flag & USE_CC_SOC_REG))
+ return chip->learning_data.cc_uah;
+
+ if (!chip->learning_data.learned_cc_uah)
+ return -EINVAL;
+
+ rc = fg_get_cc_soc(chip, &cc_soc);
+ if (rc < 0) {
+ pr_err("Failed to get cc_soc, rc=%d\n", rc);
+ return rc;
+ }
+
+ current_capacity = cc_soc * chip->learning_data.learned_cc_uah;
+ current_capacity = div64_u64(current_capacity, FULL_PERCENT_28BIT);
+ return current_capacity;
+}
+
+#define BATT_MISSING_STS BIT(6)
+static bool is_battery_missing(struct fg_chip *chip)
+{
+ int rc;
+ u8 fg_batt_sts;
+
+ rc = fg_read(chip, &fg_batt_sts,
+ INT_RT_STS(chip->batt_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->batt_base), rc);
+ return false;
+ }
+
+ return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
+}
+
static int fg_cap_learning_process_full_data(struct fg_chip *chip)
{
int cc_pc_val, rc = -EINVAL;
unsigned int cc_soc_delta_pc;
int64_t delta_cc_uah;
+ uint64_t temp;
+ bool batt_missing = is_battery_missing(chip);
+
+ if (batt_missing) {
+ pr_err("Battery is missing!\n");
+ goto fail;
+ }
if (!chip->learning_data.active)
goto fail;
@@ -2940,9 +3573,8 @@
goto fail;
}
- cc_soc_delta_pc = DIV_ROUND_CLOSEST(
- abs(cc_pc_val - chip->learning_data.init_cc_pc_val)
- * 100, FULL_PERCENT_28BIT);
+ temp = abs(cc_pc_val - chip->learning_data.init_cc_pc_val);
+ cc_soc_delta_pc = DIV_ROUND_CLOSEST_ULL(temp * 100, FULL_PERCENT_28BIT);
delta_cc_uah = div64_s64(
chip->learning_data.learned_cc_uah * cc_soc_delta_pc,
@@ -2950,8 +3582,11 @@
chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah;
if (fg_debug_mask & FG_AGING)
- pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n",
+ pr_info("current cc_soc=%d cc_soc_pc=%d init_cc_pc_val=%d delta_cc_uah=%lld learned_cc_uah=%lld total_cc_uah = %lld\n",
cc_pc_val, cc_soc_delta_pc,
+ chip->learning_data.init_cc_pc_val,
+ delta_cc_uah,
+ chip->learning_data.learned_cc_uah,
chip->learning_data.cc_uah);
return 0;
@@ -3044,6 +3679,12 @@
{
int16_t cc_mah;
int rc;
+ bool batt_missing = is_battery_missing(chip);
+
+ if (batt_missing) {
+ pr_err("Battery is missing!\n");
+ return;
+ }
cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
@@ -3065,6 +3706,12 @@
static void fg_cap_learning_post_process(struct fg_chip *chip)
{
int64_t max_inc_val, min_dec_val, old_cap;
+ bool batt_missing = is_battery_missing(chip);
+
+ if (batt_missing) {
+ pr_err("Battery is missing!\n");
+ return;
+ }
max_inc_val = chip->learning_data.learned_cc_uah
* (1000 + chip->learning_data.max_increment);
@@ -3083,6 +3730,32 @@
chip->learning_data.learned_cc_uah =
chip->learning_data.cc_uah;
+ if (chip->learning_data.max_cap_limit) {
+ max_inc_val = (int64_t)chip->nom_cap_uah * (1000 +
+ chip->learning_data.max_cap_limit);
+ max_inc_val = div64_u64(max_inc_val, 1000);
+ if (chip->learning_data.cc_uah > max_inc_val) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("learning capacity %lld goes above max limit %lld\n",
+ chip->learning_data.cc_uah,
+ max_inc_val);
+ chip->learning_data.learned_cc_uah = max_inc_val;
+ }
+ }
+
+ if (chip->learning_data.min_cap_limit) {
+ min_dec_val = (int64_t)chip->nom_cap_uah * (1000 -
+ chip->learning_data.min_cap_limit);
+ min_dec_val = div64_u64(min_dec_val, 1000);
+ if (chip->learning_data.cc_uah < min_dec_val) {
+ if (fg_debug_mask & FG_AGING)
+ pr_info("learning capacity %lld goes below min limit %lld\n",
+ chip->learning_data.cc_uah,
+ min_dec_val);
+ chip->learning_data.learned_cc_uah = min_dec_val;
+ }
+ }
+
fg_cap_learning_save_data(chip);
if (fg_debug_mask & FG_AGING)
pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
@@ -3142,7 +3815,7 @@
if (battery_soc * 100 / FULL_PERCENT_3B
> chip->learning_data.max_start_soc) {
if (fg_debug_mask & FG_AGING)
- pr_info("battery soc too low (%d < %d), aborting\n",
+ pr_info("battery soc too high (%d > %d), aborting\n",
battery_soc * 100 / FULL_PERCENT_3B,
chip->learning_data.max_start_soc);
fg_mem_release(chip);
@@ -3226,6 +3899,17 @@
}
fg_cap_learning_stop(chip);
+ } else if (chip->status == POWER_SUPPLY_STATUS_FULL) {
+ if (chip->wa_flag & USE_CC_SOC_REG) {
+ /* reset SW_CC_SOC register to 100% upon charge_full */
+ rc = fg_mem_write(chip, (u8 *)&cc_pc_100,
+ CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+ if (rc)
+ pr_err("Failed to reset CC_SOC_REG rc=%d\n",
+ rc);
+ else if (fg_debug_mask & FG_STATUS)
+ pr_info("Reset SW_CC_SOC to full value\n");
+ }
}
fail:
@@ -3323,7 +4007,14 @@
struct fg_chip,
status_change_work);
unsigned long current_time = 0;
- int cc_soc, rc, capacity = get_prop_capacity(chip);
+ int cc_soc, batt_soc, rc, capacity = get_prop_capacity(chip);
+ bool batt_missing = is_battery_missing(chip);
+
+ if (batt_missing) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Battery is missing\n");
+ return;
+ }
if (chip->esr_pulse_tune_en) {
fg_stay_awake(&chip->esr_extract_wakeup_source);
@@ -3343,19 +4034,34 @@
}
if (chip->status == POWER_SUPPLY_STATUS_FULL ||
chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- if (!chip->vbat_low_irq_enabled) {
+ if (!chip->vbat_low_irq_enabled &&
+ !chip->use_vbat_low_empty_soc) {
enable_irq(chip->batt_irq[VBATT_LOW].irq);
enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
chip->vbat_low_irq_enabled = true;
}
+
+ if (!chip->full_soc_irq_enabled) {
+ enable_irq(chip->soc_irq[FULL_SOC].irq);
+ enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+ chip->full_soc_irq_enabled = true;
+ }
+
if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100)
fg_configure_soc(chip);
} else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
- if (chip->vbat_low_irq_enabled) {
+ if (chip->vbat_low_irq_enabled &&
+ !chip->use_vbat_low_empty_soc) {
disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
chip->vbat_low_irq_enabled = false;
}
+
+ if (chip->full_soc_irq_enabled) {
+ disable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+ disable_irq_nosync(chip->soc_irq[FULL_SOC].irq);
+ chip->full_soc_irq_enabled = false;
+ }
}
fg_cap_learning_check(chip);
schedule_work(&chip->update_esr_work);
@@ -3368,6 +4074,42 @@
}
if (chip->prev_status != chip->status && chip->last_sram_update_time) {
+ /*
+ * Reset SW_CC_SOC to a value based off battery SOC when
+ * the device is discharging.
+ */
+ if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ batt_soc = get_battery_soc_raw(chip);
+ if (!batt_soc)
+ return;
+
+ batt_soc = div64_s64((int64_t)batt_soc *
+ FULL_PERCENT_28BIT, FULL_PERCENT_3B);
+ rc = fg_mem_write(chip, (u8 *)&batt_soc,
+ CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+ if (rc)
+ pr_err("Failed to reset CC_SOC_REG rc=%d\n",
+ rc);
+ else if (fg_debug_mask & FG_STATUS)
+ pr_info("Reset SW_CC_SOC to %x\n", batt_soc);
+ }
+
+ /*
+ * Schedule the update_temp_work whenever there is a status
+ * change. This is essential for applying the slope limiter
+ * coefficients when that feature is enabled.
+ */
+ if (chip->last_temp_update_time && chip->soc_slope_limiter_en) {
+ cancel_delayed_work_sync(&chip->update_temp_work);
+ schedule_delayed_work(&chip->update_temp_work,
+ msecs_to_jiffies(0));
+ }
+
+ if (chip->dischg_gain.enable) {
+ fg_stay_awake(&chip->dischg_gain_wakeup_source);
+ schedule_work(&chip->dischg_gain_work);
+ }
+
get_current_time(¤t_time);
/*
* When charging status changes, update SRAM parameters if it
@@ -3393,10 +4135,10 @@
}
if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en
&& chip->safety_timer_expired) {
- chip->sw_cc_soc_data.delta_soc =
- DIV_ROUND_CLOSEST(abs(cc_soc -
- chip->sw_cc_soc_data.init_cc_soc)
- * 100, FULL_PERCENT_28BIT);
+ uint64_t delta_cc_soc = abs(cc_soc -
+ chip->sw_cc_soc_data.init_cc_soc);
+ chip->sw_cc_soc_data.delta_soc = DIV_ROUND_CLOSEST_ULL(
+ delta_cc_soc * 100, FULL_PERCENT_28BIT);
chip->sw_cc_soc_data.full_capacity =
chip->sw_cc_soc_data.delta_soc +
chip->sw_cc_soc_data.init_sys_soc;
@@ -3539,6 +4281,395 @@
return rc;
}
+static int fg_restore_cc_soc(struct fg_chip *chip)
+{
+ int rc;
+
+ if (!chip->use_last_cc_soc || !chip->last_cc_soc)
+ return 0;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Restoring cc_soc: %lld\n", chip->last_cc_soc);
+
+ rc = fg_mem_write(chip, (u8 *)&chip->last_cc_soc,
+ fg_data[FG_DATA_CC_CHARGE].address, 4,
+ fg_data[FG_DATA_CC_CHARGE].offset, 0);
+ if (rc)
+ pr_err("failed to update CC_SOC rc=%d\n", rc);
+ else
+ chip->use_last_cc_soc = false;
+
+ return rc;
+}
+
+#define SRAM_MONOTONIC_SOC_REG 0x574
+#define SRAM_MONOTONIC_SOC_OFFSET 2
+static int fg_restore_soc(struct fg_chip *chip)
+{
+ int rc;
+ u16 msoc;
+
+ if (chip->use_last_soc && chip->last_soc)
+ msoc = DIV_ROUND_CLOSEST(chip->last_soc * 0xFFFF,
+ FULL_SOC_RAW);
+ else
+ return 0;
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Restored soc: %d\n", msoc);
+
+ rc = fg_mem_write(chip, (u8 *)&msoc, SRAM_MONOTONIC_SOC_REG, 2,
+ SRAM_MONOTONIC_SOC_OFFSET, 0);
+ if (rc)
+ pr_err("failed to write M_SOC_REG rc=%d\n", rc);
+
+ return rc;
+}
+
+#define NOM_CAP_REG 0x4F4
+#define CAPACITY_DELTA_DECIPCT 500
+static int load_battery_aging_data(struct fg_chip *chip)
+{
+ int rc = 0;
+ u8 buffer[2];
+ int16_t cc_mah;
+ int64_t delta_cc_uah, pct_nom_cap_uah;
+
+ rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
+ if (rc) {
+ pr_err("Failed to read nominal capacitance: %d\n", rc);
+ goto out;
+ }
+
+ chip->nom_cap_uah = bcap_uah_2b(buffer);
+ chip->actual_cap_uah = chip->nom_cap_uah;
+
+ if (chip->learning_data.learned_cc_uah == 0) {
+ chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
+ fg_cap_learning_save_data(chip);
+ } else if (chip->learning_data.feedback_on) {
+ delta_cc_uah = abs(chip->learning_data.learned_cc_uah -
+ chip->nom_cap_uah);
+ pct_nom_cap_uah = div64_s64((int64_t)chip->nom_cap_uah *
+ CAPACITY_DELTA_DECIPCT, 1000);
+ /*
+ * If the learned capacity is out of range, say by 50%
+ * from the nominal capacity, then overwrite the learned
+ * capacity with the nominal capacity.
+ */
+ if (chip->nom_cap_uah && delta_cc_uah > pct_nom_cap_uah) {
+ if (fg_debug_mask & FG_AGING) {
+ pr_info("learned_cc_uah: %lld is higher than expected\n",
+ chip->learning_data.learned_cc_uah);
+ pr_info("Capping it to nominal:%d\n",
+ chip->nom_cap_uah);
+ }
+ chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
+ fg_cap_learning_save_data(chip);
+ } else {
+ cc_mah = div64_s64(chip->learning_data.learned_cc_uah,
+ 1000);
+ rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
+ if (rc)
+ pr_err("Error in restoring cc_soc_coeff, rc:%d\n",
+ rc);
+ }
+ }
+out:
+ return rc;
+}
+
+static void fg_restore_battery_info(struct fg_chip *chip)
+{
+ int rc;
+ char buf[4] = {0, 0, 0, 0};
+
+ chip->last_soc = DIV_ROUND_CLOSEST(chip->batt_info[BATT_INFO_SOC] *
+ FULL_SOC_RAW, FULL_CAPACITY);
+ chip->last_cc_soc = div64_s64((int64_t)chip->last_soc *
+ FULL_PERCENT_28BIT, FULL_SOC_RAW);
+ chip->use_last_soc = true;
+ chip->use_last_cc_soc = true;
+ rc = fg_restore_soc(chip);
+ if (rc) {
+ pr_err("Error in restoring soc, rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_restore_cc_soc(chip);
+ if (rc) {
+ pr_err("Error in restoring cc_soc, rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_mem_write(chip, buf,
+ fg_data[FG_DATA_VINT_ERR].address,
+ fg_data[FG_DATA_VINT_ERR].len,
+ fg_data[FG_DATA_VINT_ERR].offset, 0);
+ if (rc) {
+ pr_err("Failed to write to VINT_ERR, rc=%d\n", rc);
+ goto out;
+ }
+
+ chip->learning_data.learned_cc_uah = chip->batt_info[BATT_INFO_FCC];
+ rc = load_battery_aging_data(chip);
+ if (rc) {
+ pr_err("Failed to load battery aging data, rc:%d\n", rc);
+ goto out;
+ }
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Restored battery info!\n");
+
+out:
+ return;
+}
+
+#define DELTA_BATT_TEMP 30
+static bool fg_validate_battery_info(struct fg_chip *chip)
+{
+ int i, delta_pct, batt_id_kohm, batt_temp, batt_volt_mv, batt_soc;
+
+ for (i = 1; i < BATT_INFO_MAX; i++) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt_info[%d]: %d\n", i, chip->batt_info[i]);
+
+ if ((chip->batt_info[i] == 0 && i != BATT_INFO_TEMP) ||
+ chip->batt_info[i] == INT_MAX) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt_info[%d]:%d is invalid\n", i,
+ chip->batt_info[i]);
+ return false;
+ }
+ }
+
+ batt_id_kohm = get_sram_prop_now(chip, FG_DATA_BATT_ID) / 1000;
+ if (batt_id_kohm != chip->batt_info[BATT_INFO_RES_ID]) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt_id(%dK) does not match the stored batt_id(%dK)\n",
+ batt_id_kohm,
+ chip->batt_info[BATT_INFO_RES_ID]);
+ return false;
+ }
+
+ batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+ if (abs(chip->batt_info[BATT_INFO_TEMP] - batt_temp) >
+ DELTA_BATT_TEMP) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt_temp(%d) is higher/lower than stored batt_temp(%d)\n",
+ batt_temp, chip->batt_info[BATT_INFO_TEMP]);
+ return false;
+ }
+
+ if (chip->batt_info[BATT_INFO_FCC] < 0) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt_fcc cannot be %d\n",
+ chip->batt_info[BATT_INFO_FCC]);
+ return false;
+ }
+
+ batt_volt_mv = get_sram_prop_now(chip, FG_DATA_VOLTAGE) / 1000;
+ batt_soc = get_monotonic_soc_raw(chip);
+ if (batt_soc != 0 && batt_soc != FULL_SOC_RAW)
+ batt_soc = DIV_ROUND_CLOSEST((batt_soc - 1) *
+ (FULL_CAPACITY - 2), FULL_SOC_RAW - 2) + 1;
+
+ if (*chip->batt_range_ocv && chip->batt_max_voltage_uv > 1000)
+ delta_pct = DIV_ROUND_CLOSEST(abs(batt_volt_mv -
+ chip->batt_info[BATT_INFO_VOLTAGE]) * 100,
+ chip->batt_max_voltage_uv / 1000);
+ else
+ delta_pct = abs(batt_soc - chip->batt_info[BATT_INFO_SOC]);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Validating by %s batt_voltage:%d capacity:%d delta_pct:%d\n",
+ *chip->batt_range_ocv ? "OCV" : "SOC", batt_volt_mv,
+ batt_soc, delta_pct);
+
+ if (*chip->batt_range_pct && delta_pct > *chip->batt_range_pct) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("delta_pct(%d) is higher than batt_range_pct(%d)\n",
+ delta_pct, *chip->batt_range_pct);
+ return false;
+ }
+
+ return true;
+}
+
+static int fg_set_battery_info(struct fg_chip *chip, int val)
+{
+ if (chip->batt_info_id < 0 ||
+ chip->batt_info_id >= BATT_INFO_MAX) {
+ pr_err("Invalid batt_info_id %d\n", chip->batt_info_id);
+ chip->batt_info_id = 0;
+ return -EINVAL;
+ }
+
+ if (chip->batt_info_id == BATT_INFO_NOTIFY && val == INT_MAX - 1) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Notified from userspace\n");
+ if (chip->batt_info_restore && !chip->ima_error_handling) {
+ if (!fg_validate_battery_info(chip)) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Validating battery info failed\n");
+ } else {
+ fg_restore_battery_info(chip);
+ }
+ }
+ }
+
+ chip->batt_info[chip->batt_info_id] = val;
+ return 0;
+}
+
+static enum power_supply_property fg_power_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_RAW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
+ POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_COOL_TEMP,
+ POWER_SUPPLY_PROP_WARM_TEMP,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_RESISTANCE_ID,
+ POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_UPDATE_NOW,
+ POWER_SUPPLY_PROP_ESR_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
+ POWER_SUPPLY_PROP_HI_POWER,
+ POWER_SUPPLY_PROP_SOC_REPORTING_READY,
+ POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE,
+ POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION,
+ POWER_SUPPLY_PROP_BATTERY_INFO,
+ POWER_SUPPLY_PROP_BATTERY_INFO_ID,
+};
+
+static int fg_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct fg_chip *chip = power_supply_get_drvdata(psy);
+ bool vbatt_low_sts;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_BATTERY_TYPE:
+ if (chip->battery_missing)
+ val->strval = missing_batt_type;
+ else if (chip->fg_restarting)
+ val->strval = loading_batt_type;
+ else
+ val->strval = chip->batt_type;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_RAW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
+ val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = chip->batt_max_voltage_uv;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+ break;
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
+ break;
+ case POWER_SUPPLY_PROP_ESR_COUNT:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = fg_get_cycle_count(chip);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ val->intval = chip->cyc_ctr.id;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_ID:
+ val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
+ break;
+ case POWER_SUPPLY_PROP_UPDATE_NOW:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
+ val->intval = (int)vbatt_low_sts;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = chip->nom_cap_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = chip->learning_data.learned_cc_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = chip->learning_data.cc_uah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
+ val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = fg_get_current_cc(chip);
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ val->intval = !!chip->bcl_lpm_disabled;
+ break;
+ case POWER_SUPPLY_PROP_SOC_REPORTING_READY:
+ val->intval = !!chip->soc_reporting_ready;
+ break;
+ case POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE:
+ val->intval = !chip->allow_false_negative_isense;
+ break;
+ case POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION:
+ val->intval = chip->use_soft_jeita_irq;
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_INFO:
+ if (chip->batt_info_id < 0 ||
+ chip->batt_info_id >= BATT_INFO_MAX)
+ return -EINVAL;
+ val->intval = chip->batt_info[chip->batt_info_id];
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_INFO_ID:
+ val->intval = chip->batt_info_id;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int fg_power_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
@@ -3557,6 +4688,67 @@
if (val->intval)
update_sram_data(chip, &unused);
break;
+ case POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE:
+ rc = set_prop_ignore_false_negative_isense(chip, !!val->intval);
+ if (rc)
+ pr_err("set_prop_ignore_false_negative_isense failed, rc=%d\n",
+ rc);
+ else
+ chip->allow_false_negative_isense = !val->intval;
+ break;
+ case POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION:
+ if (chip->use_soft_jeita_irq == !!val->intval) {
+ pr_debug("JEITA irq %s, ignore!\n",
+ chip->use_soft_jeita_irq ?
+ "enabled" : "disabled");
+ break;
+ }
+ chip->use_soft_jeita_irq = !!val->intval;
+ if (chip->use_soft_jeita_irq) {
+ if (chip->batt_irq[JEITA_SOFT_COLD].disabled) {
+ enable_irq(
+ chip->batt_irq[JEITA_SOFT_COLD].irq);
+ chip->batt_irq[JEITA_SOFT_COLD].disabled =
+ false;
+ }
+ if (!chip->batt_irq[JEITA_SOFT_COLD].wakeup) {
+ enable_irq_wake(
+ chip->batt_irq[JEITA_SOFT_COLD].irq);
+ chip->batt_irq[JEITA_SOFT_COLD].wakeup = true;
+ }
+ if (chip->batt_irq[JEITA_SOFT_HOT].disabled) {
+ enable_irq(
+ chip->batt_irq[JEITA_SOFT_HOT].irq);
+ chip->batt_irq[JEITA_SOFT_HOT].disabled = false;
+ }
+ if (!chip->batt_irq[JEITA_SOFT_HOT].wakeup) {
+ enable_irq_wake(
+ chip->batt_irq[JEITA_SOFT_HOT].irq);
+ chip->batt_irq[JEITA_SOFT_HOT].wakeup = true;
+ }
+ } else {
+ if (chip->batt_irq[JEITA_SOFT_COLD].wakeup) {
+ disable_irq_wake(
+ chip->batt_irq[JEITA_SOFT_COLD].irq);
+ chip->batt_irq[JEITA_SOFT_COLD].wakeup = false;
+ }
+ if (!chip->batt_irq[JEITA_SOFT_COLD].disabled) {
+ disable_irq_nosync(
+ chip->batt_irq[JEITA_SOFT_COLD].irq);
+ chip->batt_irq[JEITA_SOFT_COLD].disabled = true;
+ }
+ if (chip->batt_irq[JEITA_SOFT_HOT].wakeup) {
+ disable_irq_wake(
+ chip->batt_irq[JEITA_SOFT_HOT].irq);
+ chip->batt_irq[JEITA_SOFT_HOT].wakeup = false;
+ }
+ if (!chip->batt_irq[JEITA_SOFT_HOT].disabled) {
+ disable_irq_nosync(
+ chip->batt_irq[JEITA_SOFT_HOT].irq);
+ chip->batt_irq[JEITA_SOFT_HOT].disabled = true;
+ }
+ }
+ break;
case POWER_SUPPLY_PROP_STATUS:
chip->prev_status = chip->status;
chip->status = val->intval;
@@ -3599,6 +4791,12 @@
schedule_work(&chip->bcl_hi_power_work);
}
break;
+ case POWER_SUPPLY_PROP_BATTERY_INFO:
+ rc = fg_set_battery_info(chip, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_INFO_ID:
+ chip->batt_info_id = val->intval;
+ break;
default:
return -EINVAL;
};
@@ -3613,6 +4811,8 @@
case POWER_SUPPLY_PROP_COOL_TEMP:
case POWER_SUPPLY_PROP_WARM_TEMP:
case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ case POWER_SUPPLY_PROP_BATTERY_INFO:
+ case POWER_SUPPLY_PROP_BATTERY_INFO_ID:
return 1;
default:
break;
@@ -3807,21 +5007,197 @@
fg_relax(&chip->gain_comp_wakeup_source);
}
-#define BATT_MISSING_STS BIT(6)
-static bool is_battery_missing(struct fg_chip *chip)
+static void cc_soc_store_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ cc_soc_store_work);
+ int cc_soc_pct;
+
+ if (!chip->nom_cap_uah) {
+ pr_err("nom_cap_uah zero!\n");
+ fg_relax(&chip->cc_soc_wakeup_source);
+ return;
+ }
+
+ cc_soc_pct = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
+ cc_soc_pct = div64_s64(cc_soc_pct * 100,
+ chip->nom_cap_uah);
+ chip->last_cc_soc = div64_s64((int64_t)chip->last_soc *
+ FULL_PERCENT_28BIT, FULL_SOC_RAW);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("cc_soc_pct: %d last_cc_soc: %lld\n", cc_soc_pct,
+ chip->last_cc_soc);
+
+ if (fg_reset_on_lockup && (chip->cc_soc_limit_pct > 0 &&
+ cc_soc_pct >= chip->cc_soc_limit_pct)) {
+ pr_err("CC_SOC out of range\n");
+ fg_check_ima_error_handling(chip);
+ }
+
+ fg_relax(&chip->cc_soc_wakeup_source);
+}
+
+#define HARD_JEITA_ALARM_CHECK_NS 10000000000
+static enum alarmtimer_restart fg_hard_jeita_alarm_cb(struct alarm *alarm,
+ ktime_t now)
+{
+ struct fg_chip *chip = container_of(alarm,
+ struct fg_chip, hard_jeita_alarm);
+ int rc, health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ u8 regval;
+ bool batt_hot, batt_cold;
+ union power_supply_propval val = {0, };
+
+ if (!is_usb_present(chip)) {
+ pr_debug("USB plugged out, stop the timer!\n");
+ return ALARMTIMER_NORESTART;
+ }
+
+ rc = fg_read(chip, ®val, BATT_INFO_STS(chip->batt_base), 1);
+ if (rc) {
+ pr_err("read batt_sts failed, rc=%d\n", rc);
+ goto recheck;
+ }
+
+ batt_hot = !!(regval & JEITA_HARD_HOT_RT_STS);
+ batt_cold = !!(regval & JEITA_HARD_COLD_RT_STS);
+ if (batt_hot && batt_cold) {
+ pr_debug("Hot && cold can't co-exist\n");
+ goto recheck;
+ }
+
+ if ((batt_hot == chip->batt_hot) && (batt_cold == chip->batt_cold)) {
+ pr_debug("battery JEITA state not changed, ignore\n");
+ goto recheck;
+ }
+
+ if (batt_cold != chip->batt_cold) {
+ /* cool --> cold */
+ if (chip->batt_cool) {
+ chip->batt_cool = false;
+ chip->batt_cold = true;
+ health = POWER_SUPPLY_HEALTH_COLD;
+ } else if (chip->batt_cold) { /* cold --> cool */
+ chip->batt_cool = true;
+ chip->batt_cold = false;
+ health = POWER_SUPPLY_HEALTH_COOL;
+ }
+ }
+
+ if (batt_hot != chip->batt_hot) {
+ /* warm --> hot */
+ if (chip->batt_warm) {
+ chip->batt_warm = false;
+ chip->batt_hot = true;
+ health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ } else if (chip->batt_hot) { /* hot --> warm */
+ chip->batt_hot = false;
+ chip->batt_warm = true;
+ health = POWER_SUPPLY_HEALTH_WARM;
+ }
+ }
+
+ if (health != POWER_SUPPLY_HEALTH_UNKNOWN) {
+ pr_debug("FG report battery health: %d\n", health);
+ val.intval = health;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_HEALTH, &val);
+ if (rc)
+ pr_err("Set batt_psy health: %d failed\n", health);
+ }
+
+recheck:
+ alarm_forward_now(alarm, ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
+ return ALARMTIMER_RESTART;
+}
+
+#define BATT_SOFT_COLD_STS BIT(0)
+#define BATT_SOFT_HOT_STS BIT(1)
+static irqreturn_t fg_jeita_soft_hot_irq_handler(int irq, void *_chip)
{
int rc;
- u8 fg_batt_sts;
+ struct fg_chip *chip = _chip;
+ u8 regval;
+ bool batt_warm;
+ union power_supply_propval val = {0, };
- rc = fg_read(chip, &fg_batt_sts,
- INT_RT_STS(chip->batt_base), 1);
+ if (!is_charger_available(chip))
+ return IRQ_HANDLED;
+
+ rc = fg_read(chip, ®val, INT_RT_STS(chip->batt_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
INT_RT_STS(chip->batt_base), rc);
- return false;
+ return IRQ_HANDLED;
}
- return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
+ batt_warm = !!(regval & BATT_SOFT_HOT_STS);
+ if (chip->batt_warm == batt_warm) {
+ pr_debug("warm state not change, ignore!\n");
+ return IRQ_HANDLED;
+ }
+
+ chip->batt_warm = batt_warm;
+ if (batt_warm) {
+ val.intval = POWER_SUPPLY_HEALTH_WARM;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_HEALTH, &val);
+ /* kick the alarm timer for hard hot polling */
+ alarm_start_relative(&chip->hard_jeita_alarm,
+ ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
+ } else {
+ val.intval = POWER_SUPPLY_HEALTH_GOOD;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_HEALTH, &val);
+ /* cancel the alarm timer */
+ alarm_try_to_cancel(&chip->hard_jeita_alarm);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_jeita_soft_cold_irq_handler(int irq, void *_chip)
+{
+ int rc;
+ struct fg_chip *chip = _chip;
+ u8 regval;
+ bool batt_cool;
+ union power_supply_propval val = {0, };
+
+ if (!is_charger_available(chip))
+ return IRQ_HANDLED;
+
+ rc = fg_read(chip, ®val, INT_RT_STS(chip->batt_base), 1);
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->batt_base), rc);
+ return IRQ_HANDLED;
+ }
+
+ batt_cool = !!(regval & BATT_SOFT_COLD_STS);
+ if (chip->batt_cool == batt_cool) {
+ pr_debug("cool state not change, ignore\n");
+ return IRQ_HANDLED;
+ }
+
+ chip->batt_cool = batt_cool;
+ if (batt_cool) {
+ val.intval = POWER_SUPPLY_HEALTH_COOL;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_HEALTH, &val);
+ /* kick the alarm timer for hard cold polling */
+ alarm_start_relative(&chip->hard_jeita_alarm,
+ ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
+ } else {
+ val.intval = POWER_SUPPLY_HEALTH_GOOD;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_HEALTH, &val);
+ /* cancel the alarm timer */
+ alarm_try_to_cancel(&chip->hard_jeita_alarm);
+ }
+
+ return IRQ_HANDLED;
}
#define SOC_FIRST_EST_DONE BIT(5)
@@ -3841,21 +5217,40 @@
return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false;
}
+#define FG_EMPTY_DEBOUNCE_MS 1500
static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
- int rc;
bool vbatt_low_sts;
if (fg_debug_mask & FG_IRQS)
pr_info("vbatt-low triggered\n");
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
- if (rc) {
- pr_err("error in reading vbatt_status, rc:%d\n", rc);
+ /* handle empty soc based on vbatt-low interrupt */
+ if (chip->use_vbat_low_empty_soc) {
+ if (fg_get_vbatt_status(chip, &vbatt_low_sts))
goto out;
+
+ if (vbatt_low_sts) {
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("Vbatt is low\n");
+ disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = false;
+ fg_stay_awake(&chip->empty_check_wakeup_source);
+ schedule_delayed_work(&chip->check_empty_work,
+ msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
+ } else {
+ if (fg_debug_mask & FG_IRQS)
+ pr_info("Vbatt is high\n");
+ chip->soc_empty = false;
}
+ goto out;
+ }
+
+ if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (fg_get_vbatt_status(chip, &vbatt_low_sts))
+ goto out;
if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
if (fg_debug_mask & FG_IRQS)
pr_info("disabling vbatt_low irq\n");
@@ -3876,8 +5271,10 @@
bool batt_missing = is_battery_missing(chip);
if (batt_missing) {
+ fg_cap_learning_stop(chip);
chip->battery_missing = true;
chip->profile_loaded = false;
+ chip->soc_reporting_ready = false;
chip->batt_type = default_batt_type;
mutex_lock(&chip->cyc_ctr.lock);
if (fg_debug_mask & FG_IRQS)
@@ -3885,17 +5282,10 @@
clear_cycle_counter(chip);
mutex_unlock(&chip->cyc_ctr.lock);
} else {
- if (!chip->use_otp_profile) {
- reinit_completion(&chip->batt_id_avail);
- reinit_completion(&chip->first_soc_done);
- schedule_delayed_work(&chip->batt_profile_init, 0);
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(0));
- } else {
+ if (!chip->use_otp_profile)
+ fg_handle_battery_insertion(chip);
+ else
chip->battery_missing = false;
- }
}
if (fg_debug_mask & FG_IRQS)
@@ -3943,7 +5333,7 @@
{
struct fg_chip *chip = _chip;
u8 soc_rt_sts;
- int rc;
+ int rc, msoc;
rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
if (rc) {
@@ -3954,6 +5344,37 @@
if (fg_debug_mask & FG_IRQS)
pr_info("triggered 0x%x\n", soc_rt_sts);
+ if (chip->dischg_gain.enable) {
+ fg_stay_awake(&chip->dischg_gain_wakeup_source);
+ schedule_work(&chip->dischg_gain_work);
+ }
+
+ if (chip->soc_slope_limiter_en) {
+ fg_stay_awake(&chip->slope_limit_wakeup_source);
+ schedule_work(&chip->slope_limiter_work);
+ }
+
+ /* Backup last soc every delta soc interrupt */
+ chip->use_last_soc = false;
+ if (fg_reset_on_lockup) {
+ if (!chip->ima_error_handling)
+ chip->last_soc = get_monotonic_soc_raw(chip);
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("last_soc: %d\n", chip->last_soc);
+
+ fg_stay_awake(&chip->cc_soc_wakeup_source);
+ schedule_work(&chip->cc_soc_store_work);
+ }
+
+ if (chip->use_vbat_low_empty_soc) {
+ msoc = get_monotonic_soc_raw(chip);
+ if (msoc == 0 || chip->soc_empty) {
+ fg_stay_awake(&chip->empty_check_wakeup_source);
+ schedule_delayed_work(&chip->check_empty_work,
+ msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
+ }
+ }
+
schedule_work(&chip->battery_age_work);
if (chip->power_supply_registered)
@@ -3988,7 +5409,6 @@
return IRQ_HANDLED;
}
-#define FG_EMPTY_DEBOUNCE_MS 1500
static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip)
{
struct fg_chip *chip = _chip;
@@ -4100,16 +5520,15 @@
fg_relax(&chip->resume_soc_wakeup_source);
}
-
#define OCV_COEFFS_START_REG 0x4C0
#define OCV_JUNCTION_REG 0x4D8
-#define NOM_CAP_REG 0x4F4
#define CUTOFF_VOLTAGE_REG 0x40C
#define RSLOW_CFG_REG 0x538
#define RSLOW_CFG_OFFSET 2
#define RSLOW_THRESH_REG 0x52C
#define RSLOW_THRESH_OFFSET 0
-#define TEMP_RS_TO_RSLOW_OFFSET 2
+#define RS_TO_RSLOW_CHG_OFFSET 2
+#define RS_TO_RSLOW_DISCHG_OFFSET 0
#define RSLOW_COMP_REG 0x528
#define RSLOW_COMP_C1_OFFSET 0
#define RSLOW_COMP_C2_OFFSET 2
@@ -4117,7 +5536,6 @@
{
u8 buffer[24];
int rc, i;
- int16_t cc_mah;
fg_mem_lock(chip);
rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0);
@@ -4138,30 +5556,21 @@
chip->ocv_coeffs[8], chip->ocv_coeffs[9],
chip->ocv_coeffs[10], chip->ocv_coeffs[11]);
}
- rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0);
- chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
- rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0);
- chip->ocv_junction_p2p3 = buffer[0] * 100 / 255;
+ rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 2, 0, 0);
if (rc) {
pr_err("Failed to read ocv junctions: %d\n", rc);
goto done;
}
- rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
+
+ chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
+ chip->ocv_junction_p2p3 = buffer[1] * 100 / 255;
+
+ rc = load_battery_aging_data(chip);
if (rc) {
- pr_err("Failed to read nominal capacitance: %d\n", rc);
+ pr_err("Failed to load battery aging data, rc:%d\n", rc);
goto done;
}
- chip->nom_cap_uah = bcap_uah_2b(buffer);
- chip->actual_cap_uah = chip->nom_cap_uah;
- if (chip->learning_data.learned_cc_uah == 0) {
- chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
- fg_cap_learning_save_data(chip);
- } else if (chip->learning_data.feedback_on) {
- cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
- rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
- if (rc)
- pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc);
- }
+
rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0);
if (rc) {
pr_err("Failed to read cutoff voltage: %d\n", rc);
@@ -4188,9 +5597,9 @@
}
chip->rslow_comp.rslow_thr = buffer[0];
rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
- RSLOW_THRESH_OFFSET, 0);
+ RS_TO_RSLOW_CHG_OFFSET, 0);
if (rc) {
- pr_err("unable to read rs to rslow: %d\n", rc);
+ pr_err("unable to read rs to rslow_chg: %d\n", rc);
goto done;
}
memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2);
@@ -4207,6 +5616,68 @@
return rc;
}
+static int fg_update_batt_rslow_settings(struct fg_chip *chip)
+{
+ int64_t rs_to_rslow_chg, rs_to_rslow_dischg, batt_esr, rconn_uohm;
+ u8 buffer[2];
+ int rc;
+
+ rc = fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, ESR_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read battery_esr: %d\n", rc);
+ goto done;
+ }
+ batt_esr = half_float(buffer);
+
+ rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+ RS_TO_RSLOW_DISCHG_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read rs to rslow dischg: %d\n", rc);
+ goto done;
+ }
+ rs_to_rslow_dischg = half_float(buffer);
+
+ rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+ RS_TO_RSLOW_CHG_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to read rs to rslow chg: %d\n", rc);
+ goto done;
+ }
+ rs_to_rslow_chg = half_float(buffer);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("rs_rslow_chg: %lld, rs_rslow_dischg: %lld, esr: %lld\n",
+ rs_to_rslow_chg, rs_to_rslow_dischg, batt_esr);
+
+ rconn_uohm = chip->rconn_mohm * 1000;
+ rs_to_rslow_dischg = div64_s64(rs_to_rslow_dischg * batt_esr,
+ batt_esr + rconn_uohm);
+ rs_to_rslow_chg = div64_s64(rs_to_rslow_chg * batt_esr,
+ batt_esr + rconn_uohm);
+
+ half_float_to_buffer(rs_to_rslow_chg, buffer);
+ rc = fg_mem_write(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+ RS_TO_RSLOW_CHG_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rs_to_rslow_chg: %d\n", rc);
+ goto done;
+ }
+
+ half_float_to_buffer(rs_to_rslow_dischg, buffer);
+ rc = fg_mem_write(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+ RS_TO_RSLOW_DISCHG_OFFSET, 0);
+ if (rc) {
+ pr_err("unable to write rs_to_rslow_dischg: %d\n", rc);
+ goto done;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Modified rs_rslow_chg: %lld, rs_rslow_dischg: %lld\n",
+ rs_to_rslow_chg, rs_to_rslow_dischg);
+done:
+ return rc;
+}
+
#define RSLOW_CFG_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5))
#define RSLOW_CFG_ON_VAL (BIT(2) | BIT(3))
#define RSLOW_THRESH_FULL_VAL 0xFF
@@ -4233,7 +5704,7 @@
half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer);
rc = fg_mem_write(chip, buffer,
- TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+ TEMP_RS_TO_RSLOW_REG, 2, RS_TO_RSLOW_CHG_OFFSET, 0);
if (rc) {
pr_err("unable to write rs to rslow: %d\n", rc);
goto done;
@@ -4286,7 +5757,7 @@
}
rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow,
- TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+ TEMP_RS_TO_RSLOW_REG, 2, RS_TO_RSLOW_CHG_OFFSET, 0);
if (rc) {
pr_err("unable to write rs to rslow: %d\n", rc);
goto done;
@@ -4510,6 +5981,58 @@
fg_relax(&chip->esr_extract_wakeup_source);
}
+#define KI_COEFF_MEDC_REG 0x400
+#define KI_COEFF_MEDC_OFFSET 0
+#define KI_COEFF_HIGHC_REG 0x404
+#define KI_COEFF_HIGHC_OFFSET 0
+#define DEFAULT_MEDC_VOLTAGE_GAIN 3
+#define DEFAULT_HIGHC_VOLTAGE_GAIN 2
+static void discharge_gain_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work, struct fg_chip,
+ dischg_gain_work);
+ u8 buf[2];
+ int capacity, rc, i;
+ int64_t medc_val = DEFAULT_MEDC_VOLTAGE_GAIN;
+ int64_t highc_val = DEFAULT_HIGHC_VOLTAGE_GAIN;
+
+ capacity = get_prop_capacity(chip);
+ if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ for (i = VOLT_GAIN_MAX - 1; i >= 0; i--) {
+ if (capacity <= chip->dischg_gain.soc[i]) {
+ medc_val = chip->dischg_gain.medc_gain[i];
+ highc_val = chip->dischg_gain.highc_gain[i];
+ }
+ }
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Capacity: %d, medc_gain: %lld highc_gain: %lld\n",
+ capacity, medc_val, highc_val);
+
+ medc_val *= MICRO_UNIT;
+ half_float_to_buffer(medc_val, buf);
+ rc = fg_mem_write(chip, buf, KI_COEFF_MEDC_REG, 2,
+ KI_COEFF_MEDC_OFFSET, 0);
+ if (rc)
+ pr_err("Couldn't write to ki_coeff_medc_reg, rc=%d\n", rc);
+ else if (fg_debug_mask & FG_STATUS)
+ pr_info("Value [%x %x] written to ki_coeff_medc\n", buf[0],
+ buf[1]);
+
+ highc_val *= MICRO_UNIT;
+ half_float_to_buffer(highc_val, buf);
+ rc = fg_mem_write(chip, buf, KI_COEFF_HIGHC_REG, 2,
+ KI_COEFF_HIGHC_OFFSET, 0);
+ if (rc)
+ pr_err("Couldn't write to ki_coeff_highc_reg, rc=%d\n", rc);
+ else if (fg_debug_mask & FG_STATUS)
+ pr_info("Value [%x %x] written to ki_coeff_highc\n", buf[0],
+ buf[1]);
+
+ fg_relax(&chip->dischg_gain_wakeup_source);
+}
+
#define LOW_LATENCY BIT(6)
#define BATT_PROFILE_OFFSET 0x4C0
#define PROFILE_INTEGRITY_REG 0x53C
@@ -4529,7 +6052,7 @@
pr_info("restarting fuel gauge...\n");
try_again:
- if (write_profile) {
+ if (write_profile && !chip->ima_error_handling) {
if (!chip->charging_disabled) {
pr_err("Charging not yet disabled!\n");
return -EINVAL;
@@ -4770,7 +6293,8 @@
#define BATTERY_PSY_WAIT_MS 2000
static int fg_batt_profile_init(struct fg_chip *chip)
{
- int rc = 0, ret, len, batt_id;
+ int rc = 0, ret;
+ int len, batt_id;
struct device_node *node = chip->pdev->dev.of_node;
struct device_node *batt_node, *profile_node;
const char *data, *batt_type_str;
@@ -4792,6 +6316,19 @@
goto no_profile;
}
+ /* Check whether the charger is ready */
+ if (!is_charger_available(chip))
+ goto reschedule;
+
+ /* Disable charging for a FG cycle before calculating vbat_in_range */
+ if (!chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, false);
+ if (rc)
+ pr_err("Failed to disable charging, rc=%d\n", rc);
+
+ goto update;
+ }
+
batt_node = of_find_node_by_name(node, "qcom,battery-data");
if (!batt_node) {
pr_warn("No available batterydata, using OTP defaults\n");
@@ -4808,8 +6345,12 @@
fg_batt_type);
if (IS_ERR_OR_NULL(profile_node)) {
rc = PTR_ERR(profile_node);
- pr_err("couldn't find profile handle %d\n", rc);
- goto no_profile;
+ if (rc == -EPROBE_DEFER) {
+ goto reschedule;
+ } else {
+ pr_err("couldn't find profile handle rc=%d\n", rc);
+ goto no_profile;
+ }
}
/* read rslow compensation values if they're available */
@@ -4903,18 +6444,6 @@
goto no_profile;
}
- /* Check whether the charger is ready */
- if (!is_charger_available(chip))
- goto reschedule;
-
- /* Disable charging for a FG cycle before calculating vbat_in_range */
- if (!chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, false);
- if (rc)
- pr_err("Failed to disable charging, rc=%d\n", rc);
-
- goto reschedule;
- }
vbat_in_range = get_vbat_est_diff(chip)
< settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
@@ -4956,11 +6485,7 @@
chip->batt_profile, len, false);
}
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
memcpy(chip->batt_profile, data, len);
-
chip->batt_profile_len = len;
if (fg_debug_mask & FG_STATUS)
@@ -4995,6 +6520,11 @@
}
}
+ if (chip->rconn_mohm > 0) {
+ rc = fg_update_batt_rslow_settings(chip);
+ if (rc)
+ pr_err("Error in updating ESR, rc=%d\n", rc);
+ }
done:
if (chip->charging_disabled) {
rc = set_prop_enable_charging(chip, true);
@@ -5008,8 +6538,22 @@
chip->batt_type = fg_batt_type;
else
chip->batt_type = batt_type_str;
+
+ if (chip->first_profile_loaded && fg_reset_on_lockup) {
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("restoring SRAM registers\n");
+ rc = fg_backup_sram_registers(chip, false);
+ if (rc)
+ pr_err("Couldn't restore sram registers\n");
+
+ /* Read the cycle counter back from FG SRAM */
+ if (chip->cyc_ctr.en)
+ restore_cycle_counter(chip);
+ }
+
chip->first_profile_loaded = true;
chip->profile_loaded = true;
+ chip->soc_reporting_ready = true;
chip->battery_missing = is_battery_missing(chip);
update_chg_iterm(chip);
update_cc_cv_setpoint(chip);
@@ -5025,8 +6569,10 @@
fg_relax(&chip->profile_wakeup_source);
pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip),
fg_data[FG_DATA_VOLTAGE].value);
+ complete_all(&chip->fg_reset_done);
return rc;
no_profile:
+ chip->soc_reporting_ready = true;
if (chip->charging_disabled) {
rc = set_prop_enable_charging(chip, true);
if (rc)
@@ -5039,14 +6585,15 @@
power_supply_changed(chip->bms_psy);
fg_relax(&chip->profile_wakeup_source);
return rc;
-reschedule:
- schedule_delayed_work(
- &chip->batt_profile_init,
- msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
+update:
cancel_delayed_work(&chip->update_sram_data);
schedule_delayed_work(
&chip->update_sram_data,
msecs_to_jiffies(0));
+reschedule:
+ schedule_delayed_work(
+ &chip->batt_profile_init,
+ msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
fg_relax(&chip->profile_wakeup_source);
return 0;
}
@@ -5056,14 +6603,41 @@
struct fg_chip *chip = container_of(work,
struct fg_chip,
check_empty_work.work);
+ bool vbatt_low_sts;
+ int msoc;
- if (fg_is_batt_empty(chip)) {
+ /* handle empty soc based on vbatt-low interrupt */
+ if (chip->use_vbat_low_empty_soc) {
+ if (fg_get_vbatt_status(chip, &vbatt_low_sts))
+ goto out;
+
+ msoc = get_monotonic_soc_raw(chip);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Vbatt_low: %d, msoc: %d\n", vbatt_low_sts,
+ msoc);
+ if (vbatt_low_sts || (msoc == 0))
+ chip->soc_empty = true;
+ else
+ chip->soc_empty = false;
+
+ if (chip->power_supply_registered)
+ power_supply_changed(chip->bms_psy);
+
+ if (!chip->vbat_low_irq_enabled) {
+ enable_irq(chip->batt_irq[VBATT_LOW].irq);
+ enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = true;
+ }
+ } else if (fg_is_batt_empty(chip)) {
if (fg_debug_mask & FG_STATUS)
pr_info("EMPTY SOC high\n");
chip->soc_empty = true;
if (chip->power_supply_registered)
power_supply_changed(chip->bms_psy);
}
+
+out:
fg_relax(&chip->empty_check_wakeup_source);
}
@@ -5103,7 +6677,7 @@
int rc;
u8 buffer[3];
int bsoc;
- int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value;
+ int resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
bool disable = false;
u8 reg;
@@ -5318,6 +6892,98 @@
} \
} while (0)
+static int fg_dischg_gain_dt_init(struct fg_chip *chip)
+{
+ struct device_node *node = chip->pdev->dev.of_node;
+ struct property *prop;
+ int i, rc = 0;
+ size_t size;
+
+ prop = of_find_property(node, "qcom,fg-dischg-voltage-gain-soc",
+ NULL);
+ if (!prop) {
+ pr_err("qcom-fg-dischg-voltage-gain-soc not specified\n");
+ goto out;
+ }
+
+ size = prop->length / sizeof(u32);
+ if (size != VOLT_GAIN_MAX) {
+ pr_err("Voltage gain SOC specified is of incorrect size\n");
+ goto out;
+ }
+
+ rc = of_property_read_u32_array(node,
+ "qcom,fg-dischg-voltage-gain-soc", chip->dischg_gain.soc, size);
+ if (rc < 0) {
+ pr_err("Reading qcom-fg-dischg-voltage-gain-soc failed, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ for (i = 0; i < VOLT_GAIN_MAX; i++) {
+ if (chip->dischg_gain.soc[i] > 100) {
+ pr_err("Incorrect dischg-voltage-gain-soc\n");
+ goto out;
+ }
+ }
+
+ prop = of_find_property(node, "qcom,fg-dischg-med-voltage-gain",
+ NULL);
+ if (!prop) {
+ pr_err("qcom-fg-dischg-med-voltage-gain not specified\n");
+ goto out;
+ }
+
+ size = prop->length / sizeof(u32);
+ if (size != VOLT_GAIN_MAX) {
+ pr_err("med-voltage-gain specified is of incorrect size\n");
+ goto out;
+ }
+
+ rc = of_property_read_u32_array(node,
+ "qcom,fg-dischg-med-voltage-gain", chip->dischg_gain.medc_gain,
+ size);
+ if (rc < 0) {
+ pr_err("Reading qcom-fg-dischg-med-voltage-gain failed, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ prop = of_find_property(node, "qcom,fg-dischg-high-voltage-gain",
+ NULL);
+ if (!prop) {
+ pr_err("qcom-fg-dischg-high-voltage-gain not specified\n");
+ goto out;
+ }
+
+ size = prop->length / sizeof(u32);
+ if (size != VOLT_GAIN_MAX) {
+ pr_err("high-voltage-gain specified is of incorrect size\n");
+ goto out;
+ }
+
+ rc = of_property_read_u32_array(node,
+ "qcom,fg-dischg-high-voltage-gain",
+ chip->dischg_gain.highc_gain, size);
+ if (rc < 0) {
+ pr_err("Reading qcom-fg-dischg-high-voltage-gain failed, rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ if (fg_debug_mask & FG_STATUS) {
+ for (i = 0; i < VOLT_GAIN_MAX; i++)
+ pr_info("SOC:%d MedC_Gain:%d HighC_Gain: %d\n",
+ chip->dischg_gain.soc[i],
+ chip->dischg_gain.medc_gain[i],
+ chip->dischg_gain.highc_gain[i]);
+ }
+ return 0;
+out:
+ chip->dischg_gain.enable = false;
+ return rc;
+}
+
#define DEFAULT_EVALUATION_CURRENT_MA 1000
static int fg_of_init(struct fg_chip *chip)
{
@@ -5395,6 +7061,10 @@
"cl-max-start-capacity", rc, 15);
OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv,
"cl-vbat-est-thr-uv", rc, 40000);
+ OF_READ_PROPERTY(chip->learning_data.max_cap_limit,
+ "cl-max-limit-deciperc", rc, 0);
+ OF_READ_PROPERTY(chip->learning_data.min_cap_limit,
+ "cl-min-limit-deciperc", rc, 0);
OF_READ_PROPERTY(chip->evaluation_current,
"aging-eval-current-ma", rc,
DEFAULT_EVALUATION_CURRENT_MA);
@@ -5455,6 +7125,77 @@
chip->esr_pulse_tune_en = of_property_read_bool(node,
"qcom,esr-pulse-tuning-en");
+ chip->soc_slope_limiter_en = of_property_read_bool(node,
+ "qcom,fg-control-slope-limiter");
+ if (chip->soc_slope_limiter_en) {
+ OF_READ_PROPERTY(chip->slope_limit_temp,
+ "fg-slope-limit-temp-threshold", rc,
+ SLOPE_LIMIT_TEMP_THRESHOLD);
+
+ OF_READ_PROPERTY(chip->slope_limit_coeffs[LOW_TEMP_CHARGE],
+ "fg-slope-limit-low-temp-chg", rc,
+ SLOPE_LIMIT_LOW_TEMP_CHG);
+
+ OF_READ_PROPERTY(chip->slope_limit_coeffs[HIGH_TEMP_CHARGE],
+ "fg-slope-limit-high-temp-chg", rc,
+ SLOPE_LIMIT_HIGH_TEMP_CHG);
+
+ OF_READ_PROPERTY(chip->slope_limit_coeffs[LOW_TEMP_DISCHARGE],
+ "fg-slope-limit-low-temp-dischg", rc,
+ SLOPE_LIMIT_LOW_TEMP_DISCHG);
+
+ OF_READ_PROPERTY(chip->slope_limit_coeffs[HIGH_TEMP_DISCHARGE],
+ "fg-slope-limit-high-temp-dischg", rc,
+ SLOPE_LIMIT_HIGH_TEMP_DISCHG);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("slope-limiter, temp: %d coeffs: [%d %d %d %d]\n",
+ chip->slope_limit_temp,
+ chip->slope_limit_coeffs[LOW_TEMP_CHARGE],
+ chip->slope_limit_coeffs[HIGH_TEMP_CHARGE],
+ chip->slope_limit_coeffs[LOW_TEMP_DISCHARGE],
+ chip->slope_limit_coeffs[HIGH_TEMP_DISCHARGE]);
+ }
+
+ OF_READ_PROPERTY(chip->rconn_mohm, "fg-rconn-mohm", rc, 0);
+
+ chip->dischg_gain.enable = of_property_read_bool(node,
+ "qcom,fg-dischg-voltage-gain-ctrl");
+ if (chip->dischg_gain.enable) {
+ rc = fg_dischg_gain_dt_init(chip);
+ if (rc) {
+ pr_err("Error in reading dischg_gain parameters, rc=%d\n",
+ rc);
+ rc = 0;
+ }
+ }
+
+ chip->use_vbat_low_empty_soc = of_property_read_bool(node,
+ "qcom,fg-use-vbat-low-empty-soc");
+
+ OF_READ_PROPERTY(chip->batt_temp_low_limit,
+ "fg-batt-temp-low-limit", rc, BATT_TEMP_LOW_LIMIT);
+
+ OF_READ_PROPERTY(chip->batt_temp_high_limit,
+ "fg-batt-temp-high-limit", rc, BATT_TEMP_HIGH_LIMIT);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("batt-temp-low_limit: %d batt-temp-high_limit: %d\n",
+ chip->batt_temp_low_limit, chip->batt_temp_high_limit);
+
+ OF_READ_PROPERTY(chip->cc_soc_limit_pct, "fg-cc-soc-limit-pct", rc, 0);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("cc-soc-limit-pct: %d\n", chip->cc_soc_limit_pct);
+
+ chip->batt_info_restore = of_property_read_bool(node,
+ "qcom,fg-restore-batt-info");
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("restore: %d validate_by_ocv: %d range_pct: %d\n",
+ chip->batt_info_restore, fg_batt_valid_ocv,
+ fg_batt_range_pct);
+
return rc;
}
@@ -5528,15 +7269,22 @@
chip->soc_irq[FULL_SOC].irq, rc);
return rc;
}
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[EMPTY_SOC].irq,
- fg_empty_soc_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "empty-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d empty-soc: %d\n",
- chip->soc_irq[EMPTY_SOC].irq, rc);
- return rc;
+ enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+ chip->full_soc_irq_enabled = true;
+
+ if (!chip->use_vbat_low_empty_soc) {
+ rc = devm_request_irq(chip->dev,
+ chip->soc_irq[EMPTY_SOC].irq,
+ fg_empty_soc_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "empty-soc", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d empty-soc: %d\n",
+ chip->soc_irq[EMPTY_SOC].irq,
+ rc);
+ return rc;
+ }
}
rc = devm_request_irq(chip->dev,
chip->soc_irq[DELTA_SOC].irq,
@@ -5558,8 +7306,8 @@
}
enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
- enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
- enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+ if (!chip->use_vbat_low_empty_soc)
+ enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
break;
case FG_MEMIF:
chip->mem_irq[FG_MEM_AVAIL].irq
@@ -5581,8 +7329,53 @@
}
break;
case FG_BATT:
- chip->batt_irq[BATT_MISSING].irq
- = of_irq_get_byname(child, "batt-missing");
+ chip->batt_irq[JEITA_SOFT_COLD].irq =
+ of_irq_get_byname(child, "soft-cold");
+ if (chip->batt_irq[JEITA_SOFT_COLD].irq < 0) {
+ pr_err("Unable to get soft-cold irq\n");
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = devm_request_threaded_irq(chip->dev,
+ chip->batt_irq[JEITA_SOFT_COLD].irq,
+ NULL,
+ fg_jeita_soft_cold_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "soft-cold", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d soft-cold: %d\n",
+ chip->batt_irq[JEITA_SOFT_COLD].irq,
+ rc);
+ return rc;
+ }
+ disable_irq(chip->batt_irq[JEITA_SOFT_COLD].irq);
+ chip->batt_irq[JEITA_SOFT_COLD].disabled = true;
+ chip->batt_irq[JEITA_SOFT_HOT].irq =
+ of_irq_get_byname(child, "soft-hot");
+ if (chip->batt_irq[JEITA_SOFT_HOT].irq < 0) {
+ pr_err("Unable to get soft-hot irq\n");
+ rc = -EINVAL;
+ return rc;
+ }
+ rc = devm_request_threaded_irq(chip->dev,
+ chip->batt_irq[JEITA_SOFT_HOT].irq,
+ NULL,
+ fg_jeita_soft_hot_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "soft-hot", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d soft-hot: %d\n",
+ chip->batt_irq[JEITA_SOFT_HOT].irq, rc);
+ return rc;
+ }
+ disable_irq(chip->batt_irq[JEITA_SOFT_HOT].irq);
+ chip->batt_irq[JEITA_SOFT_HOT].disabled = true;
+ chip->batt_irq[BATT_MISSING].irq =
+ of_irq_get_byname(child, "batt-missing");
if (chip->batt_irq[BATT_MISSING].irq < 0) {
pr_err("Unable to get batt-missing irq\n");
rc = -EINVAL;
@@ -5619,8 +7412,14 @@
chip->batt_irq[VBATT_LOW].irq, rc);
return rc;
}
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
+ if (chip->use_vbat_low_empty_soc) {
+ enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = true;
+ } else {
+ disable_irq_nosync(
+ chip->batt_irq[VBATT_LOW].irq);
+ chip->vbat_low_irq_enabled = false;
+ }
break;
case FG_ADC:
break;
@@ -5630,17 +7429,22 @@
}
}
+ chip->irqs_enabled = true;
return rc;
}
-static void fg_cleanup(struct fg_chip *chip)
+static void fg_cancel_all_works(struct fg_chip *chip)
{
+ cancel_delayed_work_sync(&chip->check_sanity_work);
cancel_delayed_work_sync(&chip->update_sram_data);
cancel_delayed_work_sync(&chip->update_temp_work);
cancel_delayed_work_sync(&chip->update_jeita_setting);
cancel_delayed_work_sync(&chip->check_empty_work);
cancel_delayed_work_sync(&chip->batt_profile_init);
alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
+ alarm_try_to_cancel(&chip->hard_jeita_alarm);
+ if (!chip->ima_error_handling)
+ cancel_work_sync(&chip->ima_error_recovery_work);
cancel_work_sync(&chip->rslow_comp_work);
cancel_work_sync(&chip->set_resume_soc_work);
cancel_work_sync(&chip->fg_cap_learning_work);
@@ -5652,12 +7456,23 @@
cancel_work_sync(&chip->gain_comp_work);
cancel_work_sync(&chip->init_work);
cancel_work_sync(&chip->charge_full_work);
+ cancel_work_sync(&chip->bcl_hi_power_work);
cancel_work_sync(&chip->esr_extract_config_work);
+ cancel_work_sync(&chip->slope_limiter_work);
+ cancel_work_sync(&chip->dischg_gain_work);
+ cancel_work_sync(&chip->cc_soc_store_work);
+}
+
+static void fg_cleanup(struct fg_chip *chip)
+{
+ fg_cancel_all_works(chip);
+ power_supply_unregister(chip->bms_psy);
mutex_destroy(&chip->rslow_comp.lock);
mutex_destroy(&chip->rw_lock);
mutex_destroy(&chip->cyc_ctr.lock);
mutex_destroy(&chip->learning_data.learning_lock);
mutex_destroy(&chip->sysfs_restart_lock);
+ mutex_destroy(&chip->ima_recovery_lock);
wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
wakeup_source_trash(&chip->empty_check_wakeup_source.source);
wakeup_source_trash(&chip->memif_wakeup_source.source);
@@ -5667,6 +7482,11 @@
wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
+ wakeup_source_trash(&chip->slope_limit_wakeup_source.source);
+ wakeup_source_trash(&chip->dischg_gain_wakeup_source.source);
+ wakeup_source_trash(&chip->fg_reset_wakeup_source.source);
+ wakeup_source_trash(&chip->cc_soc_wakeup_source.source);
+ wakeup_source_trash(&chip->sanity_wakeup_source.source);
}
static int fg_remove(struct platform_device *pdev)
@@ -6155,12 +7975,13 @@
return 0;
}
-#define FG_ALG_SYSCTL_1 0x4B0
-#define SOC_CNFG 0x450
-#define SOC_DELTA_OFFSET 3
-#define DELTA_SOC_PERCENT 1
-#define I_TERM_QUAL_BIT BIT(1)
-#define PATCH_NEG_CURRENT_BIT BIT(3)
+#define FG_ALG_SYSCTL_1 0x4B0
+#define SOC_CNFG 0x450
+#define SOC_DELTA_OFFSET 3
+#define DELTA_SOC_PERCENT 1
+#define ALERT_CFG_OFFSET 3
+#define I_TERM_QUAL_BIT BIT(1)
+#define PATCH_NEG_CURRENT_BIT BIT(3)
#define KI_COEFF_PRED_FULL_ADDR 0x408
#define KI_COEFF_PRED_FULL_4_0_MSB 0x88
#define KI_COEFF_PRED_FULL_4_0_LSB 0x00
@@ -6168,6 +7989,12 @@
#define FG_ADC_CONFIG_REG 0x4B8
#define FG_BCL_CONFIG_OFFSET 0x3
#define BCL_FORCED_HPM_IN_CHARGE BIT(2)
+#define IRQ_USE_VOLTAGE_HYST_BIT BIT(0)
+#define EMPTY_FROM_VOLTAGE_BIT BIT(1)
+#define EMPTY_FROM_SOC_BIT BIT(2)
+#define EMPTY_SOC_IRQ_MASK (IRQ_USE_VOLTAGE_HYST_BIT | \
+ EMPTY_FROM_SOC_BIT | \
+ EMPTY_FROM_VOLTAGE_BIT)
static int fg_common_hw_init(struct fg_chip *chip)
{
int rc;
@@ -6176,8 +8003,9 @@
update_iterm(chip);
update_cutoff_voltage(chip);
- update_irq_volt_empty(chip);
update_bcl_thresholds(chip);
+ if (!chip->use_vbat_low_empty_soc)
+ update_irq_volt_empty(chip);
resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
if (resume_soc_raw > 0) {
@@ -6207,6 +8035,11 @@
return rc;
}
+ /* Override the voltage threshold for vbatt_low with empty_volt */
+ if (chip->use_vbat_low_empty_soc)
+ settings[FG_MEM_BATT_LOW].value =
+ settings[FG_MEM_IRQ_VOLT_EMPTY].value;
+
rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF,
batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value),
settings[FG_MEM_BATT_LOW].offset);
@@ -6274,20 +8107,41 @@
if (fg_debug_mask & FG_STATUS)
pr_info("imptr_pulse_slow is %sabled\n",
chip->imptr_pulse_slow_en ? "en" : "dis");
+ }
- rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
- 0);
- if (rc) {
- pr_err("unable to read rslow cfg: %d\n", rc);
- return rc;
- }
+ rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
+ 0);
+ if (rc) {
+ pr_err("unable to read rslow cfg: %d\n", rc);
+ return rc;
+ }
- if (val & RSLOW_CFG_ON_VAL)
- chip->rslow_comp.active = true;
+ if (val & RSLOW_CFG_ON_VAL)
+ chip->rslow_comp.active = true;
- if (fg_debug_mask & FG_STATUS)
- pr_info("rslow_comp active is %sabled\n",
- chip->rslow_comp.active ? "en" : "dis");
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("rslow_comp active is %sabled\n",
+ chip->rslow_comp.active ? "en" : "dis");
+
+ /*
+ * Clear bits 0-2 in 0x4B3 and set them again to make empty_soc irq
+ * trigger again.
+ */
+ rc = fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, EMPTY_SOC_IRQ_MASK,
+ 0, ALERT_CFG_OFFSET);
+ if (rc) {
+ pr_err("failed to write to 0x4B3 rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Wait for a FG cycle before enabling empty soc irq configuration */
+ msleep(FG_CYCLE_MS);
+
+ rc = fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, EMPTY_SOC_IRQ_MASK,
+ EMPTY_SOC_IRQ_MASK, ALERT_CFG_OFFSET);
+ if (rc) {
+ pr_err("failed to write to 0x4B3 rc=%d\n", rc);
+ return rc;
}
return 0;
@@ -6414,12 +8268,13 @@
/* Setup workaround flag based on PMIC type */
if (fg_sense_type == INTERNAL_CURRENT_SENSE)
chip->wa_flag |= IADC_GAIN_COMP_WA;
- if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
+ if (chip->pmic_revision[REVID_DIG_MAJOR] >= 1)
chip->wa_flag |= USE_CC_SOC_REG;
break;
case PMI8950:
case PMI8937:
+ case PMI8940:
rc = fg_8950_hw_init(chip);
/* Setup workaround flag based on PMIC type */
chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA;
@@ -6438,12 +8293,223 @@
return rc;
}
+static int fg_init_iadc_config(struct fg_chip *chip)
+{
+ u8 reg[2];
+ int rc;
+
+ /* read default gain config */
+ rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read default gain rc=%d\n", rc);
+ return rc;
+ }
+
+ if (reg[1] || reg[0]) {
+ /*
+ * Default gain register has valid value:
+ * - write to gain register.
+ */
+ rc = fg_mem_write(chip, reg, GAIN_REG, 2,
+ GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to write gain rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ /*
+ * Default gain register is invalid:
+ * - read gain register for default gain value
+ * - write to default gain register.
+ */
+ rc = fg_mem_read(chip, reg, GAIN_REG, 2,
+ GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to read gain rc=%d\n", rc);
+ return rc;
+ }
+ rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
+ DEF_GAIN_OFFSET, 0);
+ if (rc) {
+ pr_err("Failed to write default gain rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
+ chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
+ chip->iadc_comp_data.dfl_gain = half_float(reg);
+
+ pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
+ reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
+ return 0;
+}
+
+#define EN_WR_FGXCT_PRD BIT(6)
+#define EN_RD_FGXCT_PRD BIT(5)
+#define FG_RESTART_TIMEOUT_MS 12000
+static void ima_error_recovery_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ ima_error_recovery_work);
+ bool tried_again = false;
+ int rc;
+ u8 buf[4] = {0, 0, 0, 0};
+
+ fg_stay_awake(&chip->fg_reset_wakeup_source);
+ mutex_lock(&chip->ima_recovery_lock);
+ if (!chip->ima_error_handling) {
+ pr_err("Scheduled by mistake?\n");
+ mutex_unlock(&chip->ima_recovery_lock);
+ fg_relax(&chip->fg_reset_wakeup_source);
+ return;
+ }
+
+ /*
+ * SOC should be read and used until the error recovery completes.
+ * Without this, there could be a fluctuation in SOC values notified
+ * to the userspace.
+ */
+ chip->use_last_soc = true;
+
+ /* Block SRAM access till FG reset is complete */
+ chip->block_sram_access = true;
+
+ /* Release the mutex to avoid deadlock while cancelling the works */
+ mutex_unlock(&chip->ima_recovery_lock);
+
+ /* Cancel all the works */
+ fg_cancel_all_works(chip);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("last_soc: %d\n", chip->last_soc);
+
+ mutex_lock(&chip->ima_recovery_lock);
+ /* Acquire IMA access forcibly from FG ALG */
+ rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+ EN_WR_FGXCT_PRD | EN_RD_FGXCT_PRD,
+ EN_WR_FGXCT_PRD | EN_RD_FGXCT_PRD, 1);
+ if (rc) {
+ pr_err("Error in writing to IMA_CFG, rc=%d\n", rc);
+ goto out;
+ }
+
+ /* Release the IMA access now so that FG reset can go through */
+ rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+ EN_WR_FGXCT_PRD | EN_RD_FGXCT_PRD, 0, 1);
+ if (rc) {
+ pr_err("Error in writing to IMA_CFG, rc=%d\n", rc);
+ goto out;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("resetting FG\n");
+
+ /* Assert FG reset */
+ rc = fg_reset(chip, true);
+ if (rc) {
+ pr_err("Couldn't reset FG\n");
+ goto out;
+ }
+
+ /* Wait for a small time before deasserting FG reset */
+ msleep(100);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("clearing FG from reset\n");
+
+ /* Deassert FG reset */
+ rc = fg_reset(chip, false);
+ if (rc) {
+ pr_err("Couldn't clear FG reset\n");
+ goto out;
+ }
+
+ /* Wait for at least a FG cycle before doing SRAM access */
+ msleep(2000);
+
+ chip->block_sram_access = false;
+
+ if (!chip->init_done) {
+ schedule_work(&chip->init_work);
+ goto wait;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("Calling hw_init\n");
+
+ /*
+ * Once FG is reset, everything in SRAM will be wiped out. Redo
+ * hw_init, update jeita settings etc., again to make sure all
+ * the settings got restored again.
+ */
+ rc = fg_hw_init(chip);
+ if (rc) {
+ pr_err("Error in hw_init, rc=%d\n", rc);
+ goto out;
+ }
+
+ update_jeita_setting(&chip->update_jeita_setting.work);
+
+ if (chip->wa_flag & IADC_GAIN_COMP_WA) {
+ rc = fg_init_iadc_config(chip);
+ if (rc)
+ goto out;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("loading battery profile\n");
+ if (!chip->use_otp_profile) {
+ chip->battery_missing = true;
+ chip->profile_loaded = false;
+ chip->soc_reporting_ready = false;
+ chip->batt_type = default_batt_type;
+ fg_handle_battery_insertion(chip);
+ }
+
+wait:
+ rc = wait_for_completion_interruptible_timeout(&chip->fg_reset_done,
+ msecs_to_jiffies(FG_RESTART_TIMEOUT_MS));
+
+ /* If we were interrupted wait again one more time. */
+ if (rc == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ pr_debug("interrupted, waiting again\n");
+ goto wait;
+ } else if (rc <= 0) {
+ pr_err("fg_restart taking long time rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_mem_write(chip, buf, fg_data[FG_DATA_VINT_ERR].address,
+ fg_data[FG_DATA_VINT_ERR].len,
+ fg_data[FG_DATA_VINT_ERR].offset, 0);
+ if (rc < 0)
+ pr_err("Error in clearing VACT_INT_ERR, rc=%d\n", rc);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("IMA error recovery done...\n");
+out:
+ fg_restore_soc(chip);
+ fg_restore_cc_soc(chip);
+ fg_enable_irqs(chip, true);
+ update_sram_data_work(&chip->update_sram_data.work);
+ update_temp_data(&chip->update_temp_work.work);
+ schedule_delayed_work(&chip->check_sanity_work,
+ msecs_to_jiffies(1000));
+ chip->ima_error_handling = false;
+ mutex_unlock(&chip->ima_recovery_lock);
+ fg_relax(&chip->fg_reset_wakeup_source);
+}
+
#define DIG_MINOR 0x0
#define DIG_MAJOR 0x1
#define ANA_MINOR 0x2
#define ANA_MAJOR 0x3
#define IACS_INTR_SRC_SLCT BIT(3)
-static int fg_setup_memif_offset(struct fg_chip *chip)
+static int fg_memif_init(struct fg_chip *chip)
{
int rc;
@@ -6464,7 +8530,7 @@
break;
default:
pr_err("Digital Major rev=%d not supported\n",
- chip->revision[DIG_MAJOR]);
+ chip->revision[DIG_MAJOR]);
return -EINVAL;
}
@@ -6481,6 +8547,13 @@
pr_err("failed to configure interrupt source %d\n", rc);
return rc;
}
+
+ /* check for error condition */
+ rc = fg_check_ima_exception(chip, true);
+ if (rc) {
+ pr_err("Error in clearing IMA exception rc=%d", rc);
+ return rc;
+ }
}
return 0;
@@ -6515,6 +8588,7 @@
case PMI8950:
case PMI8937:
case PMI8996:
+ case PMI8940:
chip->pmic_subtype = pmic_rev_id->pmic_subtype;
chip->pmic_revision[REVID_RESERVED] = pmic_rev_id->rev1;
chip->pmic_revision[REVID_VARIANT] = pmic_rev_id->rev2;
@@ -6531,10 +8605,8 @@
}
#define INIT_JEITA_DELAY_MS 1000
-
static void delayed_init_work(struct work_struct *work)
{
- u8 reg[2];
int rc;
struct fg_chip *chip = container_of(work,
struct fg_chip,
@@ -6546,6 +8618,14 @@
rc = fg_hw_init(chip);
if (rc) {
pr_err("failed to hw init rc = %d\n", rc);
+ if (!chip->init_done && chip->ima_supported) {
+ rc = fg_check_alg_status(chip);
+ if (rc && rc != -EBUSY)
+ pr_err("Couldn't check FG ALG status, rc=%d\n",
+ rc);
+ fg_mem_release(chip);
+ return;
+ }
fg_mem_release(chip);
fg_cleanup(chip);
return;
@@ -6566,57 +8646,19 @@
if (!chip->use_otp_profile)
schedule_delayed_work(&chip->batt_profile_init, 0);
+ if (chip->ima_supported && fg_reset_on_lockup)
+ schedule_delayed_work(&chip->check_sanity_work,
+ msecs_to_jiffies(1000));
+
if (chip->wa_flag & IADC_GAIN_COMP_WA) {
- /* read default gain config */
- rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read default gain rc=%d\n", rc);
+ rc = fg_init_iadc_config(chip);
+ if (rc)
goto done;
- }
-
- if (reg[1] || reg[0]) {
- /*
- * Default gain register has valid value:
- * - write to gain register.
- */
- rc = fg_mem_write(chip, reg, GAIN_REG, 2,
- GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write gain rc=%d\n", rc);
- goto done;
- }
- } else {
- /*
- * Default gain register is invalid:
- * - read gain register for default gain value
- * - write to default gain register.
- */
- rc = fg_mem_read(chip, reg, GAIN_REG, 2,
- GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read gain rc=%d\n", rc);
- goto done;
- }
- rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
- DEF_GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write default gain rc=%d\n",
- rc);
- goto done;
- }
- }
-
- chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
- chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
- chip->iadc_comp_data.dfl_gain = half_float(reg);
- chip->input_present = is_input_present(chip);
- chip->otg_present = is_otg_present(chip);
- chip->init_done = true;
-
- pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
- reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
}
+ chip->input_present = is_input_present(chip);
+ chip->otg_present = is_otg_present(chip);
+ chip->init_done = true;
pr_debug("FG: HW_init success\n");
return;
@@ -6675,16 +8717,30 @@
"qpnp_fg_cap_learning");
wakeup_source_init(&chip->esr_extract_wakeup_source.source,
"qpnp_fg_esr_extract");
+ wakeup_source_init(&chip->slope_limit_wakeup_source.source,
+ "qpnp_fg_slope_limit");
+ wakeup_source_init(&chip->dischg_gain_wakeup_source.source,
+ "qpnp_fg_dischg_gain");
+ wakeup_source_init(&chip->fg_reset_wakeup_source.source,
+ "qpnp_fg_reset");
+ wakeup_source_init(&chip->cc_soc_wakeup_source.source,
+ "qpnp_fg_cc_soc");
+ wakeup_source_init(&chip->sanity_wakeup_source.source,
+ "qpnp_fg_sanity_check");
+ spin_lock_init(&chip->sec_access_lock);
mutex_init(&chip->rw_lock);
mutex_init(&chip->cyc_ctr.lock);
mutex_init(&chip->learning_data.learning_lock);
mutex_init(&chip->rslow_comp.lock);
mutex_init(&chip->sysfs_restart_lock);
+ mutex_init(&chip->ima_recovery_lock);
INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting);
INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work);
INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data);
INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work);
INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init);
+ INIT_DELAYED_WORK(&chip->check_sanity_work, check_sanity_work);
+ INIT_WORK(&chip->ima_error_recovery_work, ima_error_recovery_work);
INIT_WORK(&chip->rslow_comp_work, rslow_comp_work);
INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work);
INIT_WORK(&chip->dump_sram, dump_sram);
@@ -6699,13 +8755,19 @@
INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work);
INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work);
INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work);
+ INIT_WORK(&chip->slope_limiter_work, slope_limiter_work);
+ INIT_WORK(&chip->dischg_gain_work, discharge_gain_work);
+ INIT_WORK(&chip->cc_soc_store_work, cc_soc_store_work);
alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
fg_cap_learning_alarm_cb);
+ alarm_init(&chip->hard_jeita_alarm, ALARM_BOOTTIME,
+ fg_hard_jeita_alarm_cb);
init_completion(&chip->sram_access_granted);
init_completion(&chip->sram_access_revoked);
complete_all(&chip->sram_access_revoked);
init_completion(&chip->batt_id_avail);
init_completion(&chip->first_soc_done);
+ init_completion(&chip->fg_reset_done);
dev_set_drvdata(&pdev->dev, chip);
if (of_get_available_child_count(pdev->dev.of_node) == 0) {
@@ -6763,7 +8825,7 @@
return rc;
}
- rc = fg_setup_memif_offset(chip);
+ rc = fg_memif_init(chip);
if (rc) {
pr_err("Unable to setup mem_if offsets rc=%d\n", rc);
goto of_init_fail;
@@ -6834,10 +8896,18 @@
rc = fg_dfs_create(chip);
if (rc < 0) {
pr_err("failed to create debugfs rc = %d\n", rc);
- goto cancel_work;
+ goto power_supply_unregister;
}
}
+ /* Fake temperature till the actual temperature is read */
+ chip->last_good_temp = 250;
+
+ /* Initialize batt_info variables */
+ chip->batt_range_ocv = &fg_batt_valid_ocv;
+ chip->batt_range_pct = &fg_batt_range_pct;
+ memset(chip->batt_info, INT_MAX, sizeof(chip->batt_info));
+
schedule_work(&chip->init_work);
pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n",
@@ -6847,32 +8917,17 @@
return rc;
+power_supply_unregister:
+ power_supply_unregister(chip->bms_psy);
cancel_work:
- cancel_delayed_work_sync(&chip->update_jeita_setting);
- cancel_delayed_work_sync(&chip->update_sram_data);
- cancel_delayed_work_sync(&chip->update_temp_work);
- cancel_delayed_work_sync(&chip->check_empty_work);
- cancel_delayed_work_sync(&chip->batt_profile_init);
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
- cancel_work_sync(&chip->set_resume_soc_work);
- cancel_work_sync(&chip->fg_cap_learning_work);
- cancel_work_sync(&chip->dump_sram);
- cancel_work_sync(&chip->status_change_work);
- cancel_work_sync(&chip->cycle_count_work);
- cancel_work_sync(&chip->update_esr_work);
- cancel_work_sync(&chip->rslow_comp_work);
- cancel_work_sync(&chip->sysfs_restart_work);
- cancel_work_sync(&chip->gain_comp_work);
- cancel_work_sync(&chip->init_work);
- cancel_work_sync(&chip->charge_full_work);
- cancel_work_sync(&chip->bcl_hi_power_work);
- cancel_work_sync(&chip->esr_extract_config_work);
+ fg_cancel_all_works(chip);
of_init_fail:
mutex_destroy(&chip->rslow_comp.lock);
mutex_destroy(&chip->rw_lock);
mutex_destroy(&chip->cyc_ctr.lock);
mutex_destroy(&chip->learning_data.learning_lock);
mutex_destroy(&chip->sysfs_restart_lock);
+ mutex_destroy(&chip->ima_recovery_lock);
wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
wakeup_source_trash(&chip->empty_check_wakeup_source.source);
wakeup_source_trash(&chip->memif_wakeup_source.source);
@@ -6882,6 +8937,11 @@
wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
+ wakeup_source_trash(&chip->slope_limit_wakeup_source.source);
+ wakeup_source_trash(&chip->dischg_gain_wakeup_source.source);
+ wakeup_source_trash(&chip->fg_reset_wakeup_source.source);
+ wakeup_source_trash(&chip->cc_soc_wakeup_source.source);
+ wakeup_source_trash(&chip->sanity_wakeup_source.source);
return rc;
}
@@ -6938,11 +8998,103 @@
return 0;
}
+static void fg_check_ima_idle(struct fg_chip *chip)
+{
+ bool rif_mem_sts = true;
+ int rc, time_count = 0;
+
+ mutex_lock(&chip->rw_lock);
+ /* Make sure IMA is idle */
+ while (1) {
+ rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
+ if (rc)
+ break;
+
+ if (!rif_mem_sts)
+ break;
+
+ if (time_count > 4) {
+ pr_err("Waited for ~16ms polling RIF_MEM_ACCESS_REQ\n");
+ fg_run_iacs_clear_sequence(chip);
+ break;
+ }
+
+ /* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */
+ usleep_range(4000, 4100);
+ time_count++;
+ }
+ mutex_unlock(&chip->rw_lock);
+}
+
+static void fg_shutdown(struct platform_device *pdev)
+{
+ struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_emerg("FG shutdown started\n");
+ fg_cancel_all_works(chip);
+ fg_check_ima_idle(chip);
+ chip->fg_shutdown = true;
+ if (fg_debug_mask & FG_STATUS)
+ pr_emerg("FG shutdown complete\n");
+}
+
static const struct dev_pm_ops qpnp_fg_pm_ops = {
.suspend = fg_suspend,
.resume = fg_resume,
};
+static int fg_reset_lockup_set(const char *val, const struct kernel_param *kp)
+{
+ int rc;
+ struct power_supply *bms_psy;
+ struct fg_chip *chip;
+ int old_val = fg_reset_on_lockup;
+
+ rc = param_set_int(val, kp);
+ if (rc) {
+ pr_err("Unable to set fg_reset_on_lockup: %d\n", rc);
+ return rc;
+ }
+
+ if (fg_reset_on_lockup != 0 && fg_reset_on_lockup != 1) {
+ pr_err("Bad value %d\n", fg_reset_on_lockup);
+ fg_reset_on_lockup = old_val;
+ return -EINVAL;
+ }
+
+ bms_psy = power_supply_get_by_name("bms");
+ if (!bms_psy) {
+ pr_err("bms psy not found\n");
+ return 0;
+ }
+
+ chip = power_supply_get_drvdata(bms_psy);
+ if (!chip->ima_supported) {
+ pr_err("Cannot set this for non-IMA supported FG\n");
+ fg_reset_on_lockup = old_val;
+ return -EINVAL;
+ }
+
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("fg_reset_on_lockup set to %d\n", fg_reset_on_lockup);
+
+ if (fg_reset_on_lockup)
+ schedule_delayed_work(&chip->check_sanity_work,
+ msecs_to_jiffies(1000));
+ else
+ cancel_delayed_work_sync(&chip->check_sanity_work);
+
+ return rc;
+}
+
+static struct kernel_param_ops fg_reset_ops = {
+ .set = fg_reset_lockup_set,
+ .get = param_get_int,
+};
+
+module_param_cb(reset_on_lockup, &fg_reset_ops, &fg_reset_on_lockup, 0644);
+
static int fg_sense_type_set(const char *val, const struct kernel_param *kp)
{
int rc;
@@ -7025,6 +9177,7 @@
},
.probe = fg_probe,
.remove = fg_remove,
+ .shutdown = fg_shutdown,
};
static int __init fg_init(void)
diff --git a/drivers/power/supply/qcom/qpnp-linear-charger.c b/drivers/power/supply/qcom/qpnp-linear-charger.c
new file mode 100644
index 0000000..aedc77a
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-linear-charger.c
@@ -0,0 +1,3544 @@
+/* Copyright (c) 2013-2015, 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt) "CHG: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/alarmtimer.h>
+#include <linux/bitops.h>
+#include <linux/leds.h>
+#include <linux/debugfs.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+
+#define CREATE_MASK(NUM_BITS, POS) \
+ ((unsigned char) (((1 << (NUM_BITS)) - 1) << (POS)))
+#define LBC_MASK(MSB_BIT, LSB_BIT) \
+ CREATE_MASK(MSB_BIT - LSB_BIT + 1, LSB_BIT)
+
+/* Interrupt offsets */
+#define INT_RT_STS_REG 0x10
+#define FAST_CHG_ON_IRQ BIT(5)
+#define OVERTEMP_ON_IRQ BIT(4)
+#define BAT_TEMP_OK_IRQ BIT(1)
+#define BATT_PRES_IRQ BIT(0)
+
+/* USB CHARGER PATH peripheral register offsets */
+#define USB_IN_VALID_MASK BIT(1)
+#define CHG_GONE_BIT BIT(2)
+#define USB_SUSP_REG 0x47
+#define USB_SUSPEND_BIT BIT(0)
+#define USB_COMP_OVR1_REG 0xEA
+#define USBIN_LLIMIT_OK_MASK LBC_MASK(1, 0)
+#define USBIN_LLIMIT_OK_NO_OVERRIDE 0x00
+#define USBIN_LLIMIT_OK_OVERRIDE_1 0x03
+#define USB_OVP_TST5_REG 0xE7
+#define CHG_GONE_OK_EN_BIT BIT(2)
+
+/* CHARGER peripheral register offset */
+#define CHG_OPTION_REG 0x08
+#define CHG_OPTION_MASK BIT(7)
+#define CHG_STATUS_REG 0x09
+#define CHG_ON_BIT BIT(0)
+#define CHG_VDD_LOOP_BIT BIT(1)
+#define VINMIN_LOOP_BIT BIT(3)
+#define CHG_VDD_MAX_REG 0x40
+#define CHG_VDD_SAFE_REG 0x41
+#define CHG_IBAT_MAX_REG 0x44
+#define CHG_IBAT_SAFE_REG 0x45
+#define CHG_VIN_MIN_REG 0x47
+#define CHG_CTRL_REG 0x49
+#define CHG_ENABLE BIT(7)
+#define CHG_FORCE_BATT_ON BIT(0)
+#define CHG_EN_MASK (BIT(7) | BIT(0))
+#define CHG_FAILED_REG 0x4A
+#define CHG_FAILED_BIT BIT(7)
+#define CHG_VBAT_WEAK_REG 0x52
+#define CHG_IBATTERM_EN_REG 0x5B
+#define CHG_USB_ENUM_T_STOP_REG 0x4E
+#define CHG_TCHG_MAX_EN_REG 0x60
+#define CHG_TCHG_MAX_EN_BIT BIT(7)
+#define CHG_TCHG_MAX_MASK LBC_MASK(6, 0)
+#define CHG_TCHG_MAX_REG 0x61
+#define CHG_WDOG_EN_REG 0x65
+#define CHG_PERPH_RESET_CTRL3_REG 0xDA
+#define CHG_COMP_OVR1 0xEE
+#define CHG_VBAT_DET_OVR_MASK LBC_MASK(1, 0)
+#define CHG_TEST_LOOP_REG 0xE5
+#define VIN_MIN_LOOP_DISABLE_BIT BIT(0)
+#define OVERRIDE_0 0x2
+#define OVERRIDE_NONE 0x0
+
+/* BATTIF peripheral register offset */
+#define BAT_IF_PRES_STATUS_REG 0x08
+#define BATT_PRES_MASK BIT(7)
+#define BAT_IF_TEMP_STATUS_REG 0x09
+#define BATT_TEMP_HOT_MASK BIT(6)
+#define BATT_TEMP_COLD_MASK LBC_MASK(7, 6)
+#define BATT_TEMP_OK_MASK BIT(7)
+#define BAT_IF_VREF_BAT_THM_CTRL_REG 0x4A
+#define VREF_BATT_THERM_FORCE_ON LBC_MASK(7, 6)
+#define VREF_BAT_THM_ENABLED_FSM BIT(7)
+#define BAT_IF_BPD_CTRL_REG 0x48
+#define BATT_BPD_CTRL_SEL_MASK LBC_MASK(1, 0)
+#define BATT_BPD_OFFMODE_EN BIT(3)
+#define BATT_THM_EN BIT(1)
+#define BATT_ID_EN BIT(0)
+#define BAT_IF_BTC_CTRL 0x49
+#define BTC_COMP_EN_MASK BIT(7)
+#define BTC_COLD_MASK BIT(1)
+#define BTC_HOT_MASK BIT(0)
+#define BTC_COMP_OVERRIDE_REG 0xE5
+
+/* MISC peripheral register offset */
+#define MISC_REV2_REG 0x01
+#define MISC_BOOT_DONE_REG 0x42
+#define MISC_BOOT_DONE BIT(7)
+#define MISC_TRIM3_REG 0xF3
+#define MISC_TRIM3_VDD_MASK LBC_MASK(5, 4)
+#define MISC_TRIM4_REG 0xF4
+#define MISC_TRIM4_VDD_MASK BIT(4)
+
+#define PERP_SUBTYPE_REG 0x05
+#define SEC_ACCESS 0xD0
+
+/* Linear peripheral subtype values */
+#define LBC_CHGR_SUBTYPE 0x15
+#define LBC_BAT_IF_SUBTYPE 0x16
+#define LBC_USB_PTH_SUBTYPE 0x17
+#define LBC_MISC_SUBTYPE 0x18
+
+#define QPNP_CHG_I_MAX_MIN_90 90
+
+/* Feature flags */
+#define VDD_TRIM_SUPPORTED BIT(0)
+
+#define QPNP_CHARGER_DEV_NAME "qcom,qpnp-linear-charger"
+
+/* usb_interrupts */
+
+struct qpnp_lbc_irq {
+ int irq;
+ unsigned long disabled;
+ bool is_wake;
+};
+
+enum {
+ USBIN_VALID = 0,
+ USB_OVER_TEMP,
+ USB_CHG_GONE,
+ BATT_PRES,
+ BATT_TEMPOK,
+ CHG_DONE,
+ CHG_FAILED,
+ CHG_FAST_CHG,
+ CHG_VBAT_DET_LO,
+ MAX_IRQS,
+};
+
+enum {
+ USER = BIT(0),
+ THERMAL = BIT(1),
+ CURRENT = BIT(2),
+ SOC = BIT(3),
+ PARALLEL = BIT(4),
+ COLLAPSE = BIT(5),
+};
+
+enum bpd_type {
+ BPD_TYPE_BAT_ID,
+ BPD_TYPE_BAT_THM,
+ BPD_TYPE_BAT_THM_BAT_ID,
+};
+
+static const char * const bpd_label[] = {
+ [BPD_TYPE_BAT_ID] = "bpd_id",
+ [BPD_TYPE_BAT_THM] = "bpd_thm",
+ [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
+};
+
+enum btc_type {
+ HOT_THD_25_PCT = 25,
+ HOT_THD_35_PCT = 35,
+ COLD_THD_70_PCT = 70,
+ COLD_THD_80_PCT = 80,
+};
+
+static u8 btc_value[] = {
+ [HOT_THD_25_PCT] = 0x0,
+ [HOT_THD_35_PCT] = BIT(0),
+ [COLD_THD_70_PCT] = 0x0,
+ [COLD_THD_80_PCT] = BIT(1),
+};
+
+static inline int get_bpd(const char *name)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
+ if (strcmp(bpd_label[i], name) == 0)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static enum power_supply_property msm_batt_power_props[] = {
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_COOL_TEMP,
+ POWER_SUPPLY_PROP_WARM_TEMP,
+ POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+};
+
+static char *pm_batt_supplied_to[] = {
+ "bms",
+};
+
+struct vddtrim_map {
+ int trim_uv;
+ int trim_val;
+};
+
+/*
+ * VDDTRIM is a 3 bit value which is split across two
+ * register TRIM3(bit 5:4) -> VDDTRIM bit(2:1)
+ * register TRIM4(bit 4) -> VDDTRIM bit(0)
+ */
+#define TRIM_CENTER 4
+#define MAX_VDD_EA_TRIM_CFG 8
+#define VDD_TRIM3_MASK LBC_MASK(2, 1)
+#define VDD_TRIM3_SHIFT 3
+#define VDD_TRIM4_MASK BIT(0)
+#define VDD_TRIM4_SHIFT 4
+#define AVG(VAL1, VAL2) ((VAL1 + VAL2) / 2)
+
+/*
+ * VDDTRIM table containing map of trim voltage and
+ * corresponding trim value.
+ */
+static struct vddtrim_map vddtrim_map[] = {
+ {36700, 0x00},
+ {28000, 0x01},
+ {19800, 0x02},
+ {10760, 0x03},
+ {0, 0x04},
+ {-8500, 0x05},
+ {-16800, 0x06},
+ {-25440, 0x07},
+};
+
+static const unsigned int qpnp_lbc_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+/*
+ * struct qpnp_lbc_chip - device information
+ * @dev: device pointer to access the parent
+ * @pdev: pdev pointer to access platform information
+ * @chgr_base: charger peripheral base address
+ * @bat_if_base: battery interface peripheral base address
+ * @usb_chgpth_base: USB charge path peripheral base address
+ * @misc_base: misc peripheral base address
+ * @bat_is_cool: indicates that battery is cool
+ * @bat_is_warm: indicates that battery is warm
+ * @chg_done: indicates that charging is completed
+ * @usb_present: present status of USB
+ * @batt_present: present status of battery
+ * @cfg_charging_disabled: disable drawing current from USB.
+ * @cfg_use_fake_battery: flag to report default battery properties
+ * @fastchg_on: indicate charger in fast charge mode
+ * @cfg_btc_disabled: flag to disable btc (disables hot and cold
+ * irqs)
+ * @cfg_max_voltage_mv: the max volts the batt should be charged up to
+ * @cfg_min_voltage_mv: VIN_MIN configuration
+ * @cfg_batt_weak_voltage_uv: weak battery voltage threshold
+ * @cfg_warm_bat_chg_ma: warm battery maximum charge current in mA
+ * @cfg_cool_bat_chg_ma: cool battery maximum charge current in mA
+ * @cfg_safe_voltage_mv: safe voltage to which battery can charge
+ * @cfg_warm_bat_mv: warm temperature battery target voltage
+ * @cfg_warm_bat_mv: warm temperature battery target voltage
+ * @cfg_cool_bat_mv: cool temperature battery target voltage
+ * @cfg_soc_resume_limit: SOC at which battery resumes charging
+ * @cfg_float_charge: enable float charging
+ * @charger_disabled: maintain USB path state.
+ * @cfg_charger_detect_eoc: charger can detect end of charging
+ * @cfg_disable_vbatdet_based_recharge: keep VBATDET comparator overridden to
+ * low and VBATDET irq disabled.
+ * @cfg_collapsible_chgr_support: support collapsible charger
+ * @cfg_chgr_led_support: support charger led work.
+ * @cfg_safe_current: battery safety current setting
+ * @cfg_hot_batt_p: hot battery threshold setting
+ * @cfg_cold_batt_p: eold battery threshold setting
+ * @cfg_warm_bat_decidegc: warm battery temperature in degree Celsius
+ * @cfg_cool_bat_decidegc: cool battery temperature in degree Celsius
+ * @fake_battery_soc: SOC value to be reported to userspace
+ * @cfg_tchg_mins: maximum allowed software initiated charge time
+ * @chg_failed_count: counter to maintained number of times charging
+ * failed
+ * @cfg_bpd_detection: battery present detection mechanism selection
+ * @cfg_thermal_levels: amount of thermal mitigation levels
+ * @cfg_thermal_mitigation: thermal mitigation level values
+ * @therm_lvl_sel: thermal mitigation level selection
+ * @jeita_configure_lock: lock to serialize jeita configuration request
+ * @hw_access_lock: lock to serialize access to charger registers
+ * @ibat_change_lock: lock to serialize ibat change requests from
+ * USB and thermal.
+ * @irq_lock lock to serialize enabling/disabling of irq
+ * @supported_feature_flag bitmask for all supported features
+ * @vddtrim_alarm alarm to schedule trim work at regular
+ * interval
+ * @vddtrim_work work to perform actual vddmax trimming
+ * @init_trim_uv initial trim voltage at bootup
+ * @delta_vddmax_uv current vddmax trim voltage
+ * @chg_enable_lock: lock to serialize charging enable/disable for
+ * SOC based resume charging
+ * @usb_psy: power supply to export information to
+ * userspace
+ * @bms_psy: power supply to export information to
+ * userspace
+ * @batt_psy: power supply to export information to
+ * userspace
+ */
+struct qpnp_lbc_chip {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ u16 chgr_base;
+ u16 bat_if_base;
+ u16 usb_chgpth_base;
+ u16 misc_base;
+ bool bat_is_cool;
+ bool bat_is_warm;
+ bool chg_done;
+ bool usb_present;
+ bool batt_present;
+ bool cfg_charging_disabled;
+ bool cfg_btc_disabled;
+ bool cfg_use_fake_battery;
+ bool fastchg_on;
+ bool cfg_use_external_charger;
+ bool cfg_chgr_led_support;
+ bool non_collapsible_chgr_detected;
+ unsigned int cfg_warm_bat_chg_ma;
+ unsigned int cfg_cool_bat_chg_ma;
+ unsigned int cfg_safe_voltage_mv;
+ unsigned int cfg_max_voltage_mv;
+ unsigned int cfg_min_voltage_mv;
+ unsigned int cfg_charger_detect_eoc;
+ unsigned int cfg_disable_vbatdet_based_recharge;
+ unsigned int cfg_batt_weak_voltage_uv;
+ unsigned int cfg_collapsible_chgr_support;
+ unsigned int cfg_warm_bat_mv;
+ unsigned int cfg_cool_bat_mv;
+ unsigned int cfg_hot_batt_p;
+ unsigned int cfg_cold_batt_p;
+ unsigned int cfg_thermal_levels;
+ unsigned int therm_lvl_sel;
+ unsigned int *thermal_mitigation;
+ unsigned int cfg_safe_current;
+ unsigned int cfg_tchg_mins;
+ unsigned int chg_failed_count;
+ unsigned int supported_feature_flag;
+ int usb_online;
+ int cfg_bpd_detection;
+ int cfg_warm_bat_decidegc;
+ int cfg_cool_bat_decidegc;
+ int fake_battery_soc;
+ int cfg_soc_resume_limit;
+ int cfg_float_charge;
+ int charger_disabled;
+ int prev_max_ma;
+ int usb_psy_ma;
+ int delta_vddmax_uv;
+ int init_trim_uv;
+ enum power_supply_type usb_supply_type;
+ struct delayed_work collapsible_detection_work;
+
+ /* parallel-chg params */
+ int parallel_charging_enabled;
+ int lbc_max_chg_current;
+ int ichg_now;
+
+ struct alarm vddtrim_alarm;
+ struct work_struct vddtrim_work;
+ struct qpnp_lbc_irq irqs[MAX_IRQS];
+ struct mutex jeita_configure_lock;
+ struct mutex chg_enable_lock;
+ spinlock_t ibat_change_lock;
+ spinlock_t hw_access_lock;
+ spinlock_t irq_lock;
+ struct power_supply *usb_psy;
+ struct power_supply_desc usb_psy_d;
+ struct power_supply *bms_psy;
+ struct power_supply *batt_psy;
+ struct power_supply_desc batt_psy_d;
+ struct qpnp_adc_tm_btm_param adc_param;
+ struct qpnp_vadc_chip *vadc_dev;
+ struct qpnp_adc_tm_chip *adc_tm_dev;
+ struct led_classdev led_cdev;
+ struct dentry *debug_root;
+
+ /* parallel-chg params */
+ struct power_supply *parallel_psy;
+ struct power_supply_desc parallel_psy_d;
+ struct delayed_work parallel_work;
+ struct extcon_dev *extcon;
+};
+
+static void qpnp_lbc_enable_irq(struct qpnp_lbc_chip *chip,
+ struct qpnp_lbc_irq *irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ pr_debug("number = %d\n", irq->irq);
+ enable_irq(irq->irq);
+ if (irq->is_wake)
+ enable_irq_wake(irq->irq);
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+
+static void qpnp_lbc_disable_irq(struct qpnp_lbc_chip *chip,
+ struct qpnp_lbc_irq *irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ pr_debug("number = %d\n", irq->irq);
+ disable_irq_nosync(irq->irq);
+ if (irq->is_wake)
+ disable_irq_wake(irq->irq);
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+
+static int __qpnp_lbc_read(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc = 0;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_read(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("SPMI read failed base=0x%02x rc=%d\n", base, rc);
+
+ return rc;
+}
+
+static int __qpnp_lbc_write(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ rc = regmap_bulk_write(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("SPMI write failed base=0x%02x rc=%d\n", base, rc);
+
+ return rc;
+}
+
+static int __qpnp_lbc_secure_write(struct qpnp_lbc_chip *chip, u16 base,
+ u16 offset, u8 *val, int count)
+{
+ int rc;
+ u8 reg_val;
+
+ reg_val = 0xA5;
+ rc = __qpnp_lbc_write(chip, base + SEC_ACCESS, ®_val, 1);
+ if (rc)
+ return rc;
+
+ return __qpnp_lbc_write(chip, base + offset, val, 1);
+}
+
+static int qpnp_lbc_read(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ rc = __qpnp_lbc_read(chip, base, val, count);
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+
+ return rc;
+}
+
+static int qpnp_lbc_write(struct qpnp_lbc_chip *chip, u16 base,
+ u8 *val, int count)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (base == 0) {
+ pr_err("base addr cannot be zero\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ rc = __qpnp_lbc_write(chip, base, val, count);
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+
+ return rc;
+}
+
+static int qpnp_lbc_masked_write(struct qpnp_lbc_chip *chip, u16 base,
+ u8 mask, u8 val)
+{
+ int rc;
+ u8 reg_val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ rc = __qpnp_lbc_read(chip, base, ®_val, 1);
+ if (rc)
+ goto out;
+
+ pr_debug("addr = 0x%x read 0x%x\n", base, reg_val);
+
+ reg_val &= ~mask;
+ reg_val |= val & mask;
+
+ pr_debug("writing to base=%x val=%x\n", base, reg_val);
+
+ rc = __qpnp_lbc_write(chip, base, ®_val, 1);
+
+out:
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+ return rc;
+}
+
+static int __qpnp_lbc_secure_masked_write(struct qpnp_lbc_chip *chip, u16 base,
+ u16 offset, u8 mask, u8 val)
+{
+ int rc;
+ u8 reg_val, reg_val1;
+
+ rc = __qpnp_lbc_read(chip, base + offset, ®_val, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("addr = 0x%x read 0x%x\n", base, reg_val);
+
+ reg_val &= ~mask;
+ reg_val |= val & mask;
+ pr_debug("writing to base=%x val=%x\n", base, reg_val);
+
+ reg_val1 = 0xA5;
+ rc = __qpnp_lbc_write(chip, base + SEC_ACCESS, ®_val1, 1);
+ if (rc)
+ return rc;
+
+ rc = __qpnp_lbc_write(chip, base + offset, ®_val, 1);
+
+ return rc;
+}
+
+static int qpnp_lbc_get_trim_voltage(u8 trim_reg)
+{
+ int i;
+
+ for (i = 0; i < MAX_VDD_EA_TRIM_CFG; i++)
+ if (trim_reg == vddtrim_map[i].trim_val)
+ return vddtrim_map[i].trim_uv;
+
+ pr_err("Invalid trim reg reg_val=%x\n", trim_reg);
+ return -EINVAL;
+}
+
+static u8 qpnp_lbc_get_trim_val(struct qpnp_lbc_chip *chip)
+{
+ int i, sign;
+ int delta_uv;
+
+ sign = (chip->delta_vddmax_uv >= 0) ? -1 : 1;
+
+ switch (sign) {
+ case -1:
+ for (i = TRIM_CENTER; i >= 0; i--) {
+ if (vddtrim_map[i].trim_uv > chip->delta_vddmax_uv) {
+ delta_uv = AVG(vddtrim_map[i].trim_uv,
+ vddtrim_map[i + 1].trim_uv);
+ if (chip->delta_vddmax_uv >= delta_uv)
+ return vddtrim_map[i].trim_val;
+ else
+ return vddtrim_map[i + 1].trim_val;
+ }
+ }
+ i = 0;
+ break;
+ case 1:
+ for (i = TRIM_CENTER; i < ARRAY_SIZE(vddtrim_map); i++) {
+ if (vddtrim_map[i].trim_uv < chip->delta_vddmax_uv) {
+ delta_uv = AVG(vddtrim_map[i].trim_uv,
+ vddtrim_map[i - 1].trim_uv);
+ if (chip->delta_vddmax_uv >= delta_uv)
+ return vddtrim_map[i - 1].trim_val;
+ else
+ return vddtrim_map[i].trim_val;
+ }
+ }
+ i = ARRAY_SIZE(vddtrim_map) - 1;
+ break;
+ }
+
+ return vddtrim_map[i].trim_val;
+}
+
+static int qpnp_lbc_is_usb_chg_plugged_in(struct qpnp_lbc_chip *chip)
+{
+ u8 usbin_valid_rt_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG,
+ &usbin_valid_rt_sts, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("rt_sts 0x%x\n", usbin_valid_rt_sts);
+
+ return (usbin_valid_rt_sts & USB_IN_VALID_MASK) ? 1 : 0;
+}
+
+static int qpnp_lbc_is_chg_gone(struct qpnp_lbc_chip *chip)
+{
+ u8 rt_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG,
+ &rt_sts, 1);
+ if (rc)
+ return rc;
+
+ pr_debug("rt_sts 0x%x\n", rt_sts);
+
+ return (rt_sts & CHG_GONE_BIT) ? 1 : 0;
+}
+
+static int qpnp_lbc_charger_enable(struct qpnp_lbc_chip *chip, int reason,
+ int enable)
+{
+ int disabled = chip->charger_disabled;
+ u8 reg_val;
+ int rc = 0;
+
+ pr_debug("reason=%d requested_enable=%d disabled_status=%d\n",
+ reason, enable, disabled);
+ if (enable)
+ disabled &= ~reason;
+ else
+ disabled |= reason;
+
+ if (!!chip->charger_disabled == !!disabled)
+ goto skip;
+
+ reg_val = !!disabled ? CHG_FORCE_BATT_ON : CHG_ENABLE;
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_CTRL_REG,
+ CHG_EN_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to %s charger\n",
+ reg_val ? "enable" : "disable");
+ return rc;
+ }
+skip:
+ chip->charger_disabled = disabled;
+ return rc;
+}
+
+static int qpnp_lbc_is_batt_present(struct qpnp_lbc_chip *chip)
+{
+ u8 batt_pres_rt_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + INT_RT_STS_REG,
+ &batt_pres_rt_sts, 1);
+ if (rc)
+ return rc;
+
+ return (batt_pres_rt_sts & BATT_PRES_IRQ) ? 1 : 0;
+}
+
+static int qpnp_lbc_bat_if_configure_btc(struct qpnp_lbc_chip *chip)
+{
+ u8 btc_cfg = 0, mask = 0, rc;
+
+ /* Do nothing if battery peripheral not present */
+ if (!chip->bat_if_base)
+ return 0;
+
+ if ((chip->cfg_hot_batt_p == HOT_THD_25_PCT)
+ || (chip->cfg_hot_batt_p == HOT_THD_35_PCT)) {
+ btc_cfg |= btc_value[chip->cfg_hot_batt_p];
+ mask |= BTC_HOT_MASK;
+ }
+
+ if ((chip->cfg_cold_batt_p == COLD_THD_70_PCT) ||
+ (chip->cfg_cold_batt_p == COLD_THD_80_PCT)) {
+ btc_cfg |= btc_value[chip->cfg_cold_batt_p];
+ mask |= BTC_COLD_MASK;
+ }
+
+ mask |= BTC_COMP_EN_MASK;
+ if (!chip->cfg_btc_disabled)
+ btc_cfg |= BTC_COMP_EN_MASK;
+
+ pr_debug("BTC configuration mask=%x\n", btc_cfg);
+
+ rc = qpnp_lbc_masked_write(chip,
+ chip->bat_if_base + BAT_IF_BTC_CTRL,
+ mask, btc_cfg);
+ if (rc)
+ pr_err("Failed to configure BTC\n");
+
+ return rc;
+}
+
+static int qpnp_chg_collapsible_chgr_config(struct qpnp_lbc_chip *chip,
+ bool enable)
+{
+ u8 reg_val;
+ int rc;
+
+ pr_debug("Configure for %scollapsible charger\n",
+ enable ? "" : "non-");
+ /*
+ * The flow to enable/disable the collapsible charger configuration:
+ * Enable: Override USBIN_LLIMIT_OK -->
+ * Disable VIN_MIN comparator -->
+ * Enable CHG_GONE comparator
+ * Disable: Enable VIN_MIN comparator -->
+ * Enable USBIN_LLIMIT_OK -->
+ * Disable CHG_GONE comparator
+ */
+ if (enable) {
+ /* Override USBIN_LLIMIT_OK */
+ reg_val = USBIN_LLIMIT_OK_OVERRIDE_1;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->usb_chgpth_base,
+ USB_COMP_OVR1_REG,
+ USBIN_LLIMIT_OK_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to override USB_LLIMIT_OK\n");
+ return rc;
+ }
+ }
+
+ /* Configure VIN_MIN comparator */
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->chgr_base, CHG_TEST_LOOP_REG,
+ VIN_MIN_LOOP_DISABLE_BIT,
+ enable ? VIN_MIN_LOOP_DISABLE_BIT : 0);
+ if (rc) {
+ pr_err("Failed to %s VIN_MIN comparator\n",
+ enable ? "disable" : "enable");
+ return rc;
+ }
+
+ if (!enable) {
+ /* Enable USBIN_LLIMIT_OK */
+ reg_val = USBIN_LLIMIT_OK_NO_OVERRIDE;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->usb_chgpth_base,
+ USB_COMP_OVR1_REG,
+ USBIN_LLIMIT_OK_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to override USB_LLIMIT_OK\n");
+ return rc;
+ }
+ }
+
+ /* Configure CHG_GONE comparator */
+ reg_val = enable ? CHG_GONE_OK_EN_BIT : 0;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->usb_chgpth_base, USB_OVP_TST5_REG,
+ CHG_GONE_OK_EN_BIT, reg_val);
+ if (rc) {
+ pr_err("Failed to write CHG_GONE comparator\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+#define QPNP_LBC_VBATWEAK_MIN_UV 3000000
+#define QPNP_LBC_VBATWEAK_MAX_UV 3581250
+#define QPNP_LBC_VBATWEAK_STEP_UV 18750
+static int qpnp_lbc_vbatweak_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc;
+
+ if (voltage < QPNP_LBC_VBATWEAK_MIN_UV ||
+ voltage > QPNP_LBC_VBATWEAK_MAX_UV) {
+ rc = -EINVAL;
+ } else {
+ reg_val = (voltage - QPNP_LBC_VBATWEAK_MIN_UV) /
+ QPNP_LBC_VBATWEAK_STEP_UV;
+ pr_debug("VBAT_WEAK=%d setting %02x\n",
+ chip->cfg_batt_weak_voltage_uv, reg_val);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_VBAT_WEAK_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set VBAT_WEAK\n");
+ }
+
+ return rc;
+}
+
+#define QPNP_LBC_VBAT_MIN_MV 4000
+#define QPNP_LBC_VBAT_MAX_MV 4775
+#define QPNP_LBC_VBAT_STEP_MV 25
+static int qpnp_lbc_vddsafe_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc;
+
+ if (voltage < QPNP_LBC_VBAT_MIN_MV
+ || voltage > QPNP_LBC_VBAT_MAX_MV) {
+ pr_err("Invalid vddsafe voltage mV=%d min=%d max=%d\n",
+ voltage, QPNP_LBC_VBAT_MIN_MV,
+ QPNP_LBC_VBAT_MAX_MV);
+ return -EINVAL;
+ }
+ reg_val = (voltage - QPNP_LBC_VBAT_MIN_MV) / QPNP_LBC_VBAT_STEP_MV;
+ pr_debug("voltage=%d setting %02x\n", voltage, reg_val);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_VDD_SAFE_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set VDD_SAFE\n");
+
+ return rc;
+}
+
+static int qpnp_lbc_vddmax_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc, trim_val;
+ unsigned long flags;
+
+ if (voltage < QPNP_LBC_VBAT_MIN_MV
+ || voltage > QPNP_LBC_VBAT_MAX_MV) {
+ pr_err("Invalid vddmax voltage mV=%d min=%d max=%d\n",
+ voltage, QPNP_LBC_VBAT_MIN_MV,
+ QPNP_LBC_VBAT_MAX_MV);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+ reg_val = (voltage - QPNP_LBC_VBAT_MIN_MV) / QPNP_LBC_VBAT_STEP_MV;
+ pr_debug("voltage=%d setting %02x\n", voltage, reg_val);
+ rc = __qpnp_lbc_write(chip, chip->chgr_base + CHG_VDD_MAX_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to set VDD_MAX\n");
+ goto out;
+ }
+
+ /* Update trim value */
+ if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED) {
+ trim_val = qpnp_lbc_get_trim_val(chip);
+ reg_val = (trim_val & VDD_TRIM3_MASK) << VDD_TRIM3_SHIFT;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->misc_base, MISC_TRIM3_REG,
+ MISC_TRIM3_VDD_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to set MISC_TRIM3_REG\n");
+ goto out;
+ }
+
+ reg_val = (trim_val & VDD_TRIM4_MASK) << VDD_TRIM4_SHIFT;
+ rc = __qpnp_lbc_secure_masked_write(chip,
+ chip->misc_base, MISC_TRIM4_REG,
+ MISC_TRIM4_VDD_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to set MISC_TRIM4_REG\n");
+ goto out;
+ }
+
+ chip->delta_vddmax_uv = qpnp_lbc_get_trim_voltage(trim_val);
+ if (chip->delta_vddmax_uv == -EINVAL) {
+ pr_err("Invalid trim voltage=%d\n",
+ chip->delta_vddmax_uv);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("VDD_MAX delta=%d trim value=%x\n",
+ chip->delta_vddmax_uv, trim_val);
+ }
+
+out:
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+ return rc;
+}
+
+static int qpnp_lbc_set_appropriate_vddmax(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+
+ if (chip->bat_is_cool)
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_cool_bat_mv);
+ else if (chip->bat_is_warm)
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_warm_bat_mv);
+ else
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_max_voltage_mv);
+ if (rc)
+ pr_err("Failed to set appropriate vddmax\n");
+
+ return rc;
+}
+
+#define QPNP_LBC_MIN_DELTA_UV 13000
+static void qpnp_lbc_adjust_vddmax(struct qpnp_lbc_chip *chip, int vbat_uv)
+{
+ int delta_uv, prev_delta_uv, rc;
+
+ prev_delta_uv = chip->delta_vddmax_uv;
+ delta_uv = (int)(chip->cfg_max_voltage_mv * 1000) - vbat_uv;
+
+ /*
+ * If delta_uv is positive, apply trim if delta_uv > 13mv
+ * If delta_uv is negative always apply trim.
+ */
+ if (delta_uv > 0 && delta_uv < QPNP_LBC_MIN_DELTA_UV) {
+ pr_debug("vbat is not low enough to increase vdd\n");
+ return;
+ }
+
+ pr_debug("vbat=%d current delta_uv=%d prev delta_vddmax_uv=%d\n",
+ vbat_uv, delta_uv, chip->delta_vddmax_uv);
+ chip->delta_vddmax_uv = delta_uv + chip->delta_vddmax_uv;
+ pr_debug("new delta_vddmax_uv %d\n", chip->delta_vddmax_uv);
+ rc = qpnp_lbc_set_appropriate_vddmax(chip);
+ if (rc)
+ chip->delta_vddmax_uv = prev_delta_uv;
+}
+
+#define QPNP_LBC_VINMIN_MIN_MV 4200
+#define QPNP_LBC_VINMIN_MAX_MV 5037
+#define QPNP_LBC_VINMIN_STEP_MV 27
+static int qpnp_lbc_vinmin_set(struct qpnp_lbc_chip *chip, int voltage)
+{
+ u8 reg_val;
+ int rc;
+
+ if ((voltage < QPNP_LBC_VINMIN_MIN_MV)
+ || (voltage > QPNP_LBC_VINMIN_MAX_MV)) {
+ pr_err("Invalid vinmin voltage mV=%d min=%d max=%d\n",
+ voltage, QPNP_LBC_VINMIN_MIN_MV,
+ QPNP_LBC_VINMIN_MAX_MV);
+ return -EINVAL;
+ }
+
+ reg_val = (voltage - QPNP_LBC_VINMIN_MIN_MV) / QPNP_LBC_VINMIN_STEP_MV;
+ pr_debug("VIN_MIN=%d setting %02x\n", voltage, reg_val);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_VIN_MIN_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set VIN_MIN\n");
+
+ return rc;
+}
+
+#define QPNP_LBC_IBATSAFE_MIN_MA 90
+#define QPNP_LBC_IBATSAFE_MAX_MA 1440
+#define QPNP_LBC_I_STEP_MA 90
+static int qpnp_lbc_ibatsafe_set(struct qpnp_lbc_chip *chip, int safe_current)
+{
+ u8 reg_val;
+ int rc;
+
+ if (safe_current < QPNP_LBC_IBATSAFE_MIN_MA
+ || safe_current > QPNP_LBC_IBATSAFE_MAX_MA) {
+ pr_err("Invalid safecurrent mA=%d min=%d max=%d\n",
+ safe_current, QPNP_LBC_IBATSAFE_MIN_MA,
+ QPNP_LBC_IBATSAFE_MAX_MA);
+ return -EINVAL;
+ }
+
+ reg_val = (safe_current - QPNP_LBC_IBATSAFE_MIN_MA)
+ / QPNP_LBC_I_STEP_MA;
+ pr_debug("Ibate_safe=%d setting %02x\n", safe_current, reg_val);
+
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_IBAT_SAFE_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set IBAT_SAFE\n");
+
+ return rc;
+}
+
+#define QPNP_LBC_IBATMAX_MIN 90
+#define QPNP_LBC_IBATMAX_MAX 1440
+/*
+ * Set maximum current limit from charger
+ * ibat = System current + charging current
+ */
+static int qpnp_lbc_ibatmax_set(struct qpnp_lbc_chip *chip, int chg_current)
+{
+ u8 reg_val;
+ int rc;
+
+ if (chg_current > QPNP_LBC_IBATMAX_MAX)
+ pr_debug("Invalid max charge current mA=%d max=%d\n",
+ chg_current,
+ QPNP_LBC_IBATMAX_MAX);
+
+ chg_current = clamp(chg_current, QPNP_LBC_IBATMAX_MIN,
+ QPNP_LBC_IBATMAX_MAX);
+ reg_val = (chg_current - QPNP_LBC_IBATMAX_MIN) / QPNP_LBC_I_STEP_MA;
+
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_IBAT_MAX_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set IBAT_MAX\n");
+ else
+ chip->prev_max_ma = chg_current;
+
+ return rc;
+}
+
+#define QPNP_LBC_TCHG_MIN 4
+#define QPNP_LBC_TCHG_MAX 512
+#define QPNP_LBC_TCHG_STEP 4
+static int qpnp_lbc_tchg_max_set(struct qpnp_lbc_chip *chip, int minutes)
+{
+ u8 reg_val = 0;
+ int rc;
+
+ /* Disable timer */
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_TCHG_MAX_EN_REG,
+ CHG_TCHG_MAX_EN_BIT, 0);
+ if (rc) {
+ pr_err("Failed to write tchg_max_en\n");
+ return rc;
+ }
+
+ /* If minutes is 0, just disable timer */
+ if (!minutes) {
+ pr_debug("Charger safety timer disabled\n");
+ return rc;
+ }
+
+ minutes = clamp(minutes, QPNP_LBC_TCHG_MIN, QPNP_LBC_TCHG_MAX);
+
+ reg_val = (minutes / QPNP_LBC_TCHG_STEP) - 1;
+
+ pr_debug("TCHG_MAX=%d mins setting %x\n", minutes, reg_val);
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_TCHG_MAX_REG,
+ CHG_TCHG_MAX_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to write tchg_max_reg\n");
+ return rc;
+ }
+
+ /* Enable timer */
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_TCHG_MAX_EN_REG,
+ CHG_TCHG_MAX_EN_BIT, CHG_TCHG_MAX_EN_BIT);
+ if (rc)
+ pr_err("Failed to write tchg_max_en\n");
+
+ return rc;
+}
+
+#define LBC_CHGR_LED 0x4D
+#define CHGR_LED_ON BIT(0)
+#define CHGR_LED_OFF 0x0
+#define CHGR_LED_STAT_MASK LBC_MASK(1, 0)
+static void qpnp_lbc_chgr_led_brightness_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct qpnp_lbc_chip *chip = container_of(cdev, struct qpnp_lbc_chip,
+ led_cdev);
+ u8 reg;
+ int rc;
+
+ if (value > LED_FULL)
+ value = LED_FULL;
+
+ pr_debug("set the charger led brightness to value=%d\n", value);
+ reg = (value > LED_OFF) ? CHGR_LED_ON : CHGR_LED_OFF;
+
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + LBC_CHGR_LED,
+ CHGR_LED_STAT_MASK, reg);
+ if (rc)
+ pr_err("Failed to write charger led\n");
+}
+
+static enum
+led_brightness qpnp_lbc_chgr_led_brightness_get(struct led_classdev *cdev)
+{
+
+ struct qpnp_lbc_chip *chip = container_of(cdev, struct qpnp_lbc_chip,
+ led_cdev);
+ u8 reg_val, chgr_led_sts;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + LBC_CHGR_LED,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read charger led\n");
+ return rc;
+ }
+
+ chgr_led_sts = reg_val & CHGR_LED_STAT_MASK;
+ pr_debug("charger led brightness chgr_led_sts=%d\n", chgr_led_sts);
+
+ return (chgr_led_sts == CHGR_LED_ON) ? LED_FULL : LED_OFF;
+}
+
+static int qpnp_lbc_register_chgr_led(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+
+ chip->led_cdev.name = "red";
+ chip->led_cdev.brightness_set = qpnp_lbc_chgr_led_brightness_set;
+ chip->led_cdev.brightness_get = qpnp_lbc_chgr_led_brightness_get;
+
+ rc = led_classdev_register(chip->dev, &chip->led_cdev);
+ if (rc)
+ pr_err("unable to register charger led, rc=%d\n", rc);
+
+ return rc;
+};
+
+static int is_vinmin_set(struct qpnp_lbc_chip *chip)
+{
+ u8 reg;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + CHG_STATUS_REG, ®, 1);
+ if (rc) {
+ pr_err("Unable to read charger status\n");
+ return false;
+ }
+ pr_debug("chg_status=0x%x\n", reg);
+
+ return !!(reg & VINMIN_LOOP_BIT);
+
+}
+
+static int is_battery_charging(struct qpnp_lbc_chip *chip)
+{
+ u8 reg;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + CHG_STATUS_REG, ®, 1);
+ if (rc) {
+ pr_err("Unable to read charger status\n");
+ return false;
+ }
+ pr_debug("chg_status=0x%x\n", reg);
+
+ return !!(reg & CHG_ON_BIT);
+}
+
+static int qpnp_lbc_vbatdet_override(struct qpnp_lbc_chip *chip, int ovr_val)
+{
+ int rc;
+ u8 reg_val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->hw_access_lock, flags);
+
+ rc = __qpnp_lbc_read(chip, chip->chgr_base + CHG_COMP_OVR1,
+ ®_val, 1);
+ if (rc)
+ goto out;
+
+ pr_debug("addr = 0x%x read 0x%x\n", chip->chgr_base, reg_val);
+
+ reg_val &= ~CHG_VBAT_DET_OVR_MASK;
+ reg_val |= ovr_val & CHG_VBAT_DET_OVR_MASK;
+
+ pr_debug("writing to base=%x val=%x\n", chip->chgr_base, reg_val);
+
+ rc = __qpnp_lbc_secure_write(chip, chip->chgr_base, CHG_COMP_OVR1,
+ ®_val, 1);
+
+out:
+ spin_unlock_irqrestore(&chip->hw_access_lock, flags);
+ return rc;
+}
+
+static int get_prop_battery_voltage_now(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+ struct qpnp_vadc_result results;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &results);
+ if (rc) {
+ pr_err("Unable to read vbat rc=%d\n", rc);
+ return 0;
+ }
+
+ return results.physical;
+}
+
+static int get_prop_batt_present(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + BAT_IF_PRES_STATUS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read battery status read failed\n");
+ return 0;
+ }
+
+ return (reg_val & BATT_PRES_MASK) ? 1 : 0;
+}
+
+static int get_prop_batt_health(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + BAT_IF_TEMP_STATUS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read battery health\n");
+ return POWER_SUPPLY_HEALTH_UNKNOWN;
+ }
+
+ if (BATT_TEMP_HOT_MASK & reg_val)
+ return POWER_SUPPLY_HEALTH_OVERHEAT;
+ if (!(BATT_TEMP_COLD_MASK & reg_val))
+ return POWER_SUPPLY_HEALTH_COLD;
+ if (chip->bat_is_cool)
+ return POWER_SUPPLY_HEALTH_COOL;
+ if (chip->bat_is_warm)
+ return POWER_SUPPLY_HEALTH_WARM;
+
+ return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int get_prop_charge_type(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ if (!get_prop_batt_present(chip))
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt sts\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (reg_val & FAST_CHG_ON_IRQ)
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int get_prop_batt_status(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ if (qpnp_lbc_is_usb_chg_plugged_in(chip) && chip->chg_done)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt sts\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (reg_val & FAST_CHG_ON_IRQ)
+ return POWER_SUPPLY_STATUS_CHARGING;
+
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static int get_prop_current_now(struct qpnp_lbc_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CURRENT_NOW, &ret);
+ return ret.intval;
+ }
+
+ pr_debug("No BMS supply registered return 0\n");
+ return 0;
+}
+
+#define DEFAULT_CAPACITY 50
+static int get_prop_capacity(struct qpnp_lbc_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+ int soc;
+
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (chip->fake_battery_soc >= 0)
+ return chip->fake_battery_soc;
+
+ if (chip->cfg_use_fake_battery || !get_prop_batt_present(chip))
+ return DEFAULT_CAPACITY;
+
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CAPACITY, &ret);
+ soc = ret.intval;
+ if (soc == 0) {
+ if (!qpnp_lbc_is_usb_chg_plugged_in(chip))
+ pr_warn_ratelimited("Batt 0, CHG absent\n");
+ }
+ return soc;
+ }
+ pr_debug("No BMS supply registered return %d\n", DEFAULT_CAPACITY);
+
+ /*
+ * Return default capacity to avoid userspace
+ * from shutting down unecessarily
+ */
+ return DEFAULT_CAPACITY;
+}
+
+static int get_prop_charge_count(struct qpnp_lbc_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (chip->bms_psy) {
+ power_supply_get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER, &ret);
+ } else {
+ pr_debug("No BMS supply registered return 0\n");
+ }
+
+ return ret.intval;
+}
+
+#define DEFAULT_TEMP 250
+static int get_prop_batt_temp(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+ struct qpnp_vadc_result results;
+
+ if (chip->cfg_use_fake_battery || !get_prop_batt_present(chip))
+ return DEFAULT_TEMP;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &results);
+ if (rc) {
+ pr_debug("Unable to read batt temperature rc=%d\n", rc);
+ return DEFAULT_TEMP;
+ }
+ pr_debug("get_bat_temp %d, %lld\n", results.adc_code,
+ results.physical);
+
+ return (int)results.physical;
+}
+
+static void qpnp_lbc_set_appropriate_current(struct qpnp_lbc_chip *chip)
+{
+ unsigned int chg_current = chip->usb_psy_ma;
+
+ if (chip->bat_is_cool && chip->cfg_cool_bat_chg_ma)
+ chg_current = min(chg_current, chip->cfg_cool_bat_chg_ma);
+ if (chip->bat_is_warm && chip->cfg_warm_bat_chg_ma)
+ chg_current = min(chg_current, chip->cfg_warm_bat_chg_ma);
+ if (chip->therm_lvl_sel != 0 && chip->thermal_mitigation)
+ chg_current = min(chg_current,
+ chip->thermal_mitigation[chip->therm_lvl_sel]);
+
+ pr_debug("setting charger current %d mA\n", chg_current);
+ qpnp_lbc_ibatmax_set(chip, chg_current);
+}
+
+static int qpnp_lbc_system_temp_level_set(struct qpnp_lbc_chip *chip,
+ int lvl_sel)
+{
+ int rc = 0;
+ int prev_therm_lvl;
+ unsigned long flags;
+
+ if (!chip->thermal_mitigation) {
+ pr_err("Thermal mitigation not supported\n");
+ return -EINVAL;
+ }
+
+ if (lvl_sel < 0) {
+ pr_err("Unsupported level selected %d\n", lvl_sel);
+ return -EINVAL;
+ }
+
+ if (lvl_sel >= chip->cfg_thermal_levels) {
+ pr_err("Unsupported level selected %d forcing %d\n", lvl_sel,
+ chip->cfg_thermal_levels - 1);
+ lvl_sel = chip->cfg_thermal_levels - 1;
+ }
+
+ if (lvl_sel == chip->therm_lvl_sel)
+ return 0;
+
+ spin_lock_irqsave(&chip->ibat_change_lock, flags);
+ prev_therm_lvl = chip->therm_lvl_sel;
+ chip->therm_lvl_sel = lvl_sel;
+ if (chip->therm_lvl_sel == (chip->cfg_thermal_levels - 1)) {
+ /* Disable charging if highest value selected by */
+ rc = qpnp_lbc_charger_enable(chip, THERMAL, 0);
+ if (rc < 0)
+ pr_err("Failed to set disable charging\n");
+ goto out;
+ }
+
+ qpnp_lbc_set_appropriate_current(chip);
+
+ if (prev_therm_lvl == chip->cfg_thermal_levels - 1) {
+ /*
+ * If previously highest value was selected charging must have
+ * been disabed. Enable charging.
+ */
+ rc = qpnp_lbc_charger_enable(chip, THERMAL, 1);
+ if (rc < 0)
+ pr_err("Failed to enable charging\n");
+ }
+out:
+ spin_unlock_irqrestore(&chip->ibat_change_lock, flags);
+ return rc;
+}
+
+#define MIN_COOL_TEMP -300
+#define MAX_WARM_TEMP 1000
+#define HYSTERISIS_DECIDEGC 20
+
+static int qpnp_lbc_configure_jeita(struct qpnp_lbc_chip *chip,
+ enum power_supply_property psp, int temp_degc)
+{
+ int rc = 0;
+
+ if ((temp_degc < MIN_COOL_TEMP) || (temp_degc > MAX_WARM_TEMP)) {
+ pr_err("Invalid temp range=%d min=%d max=%d\n",
+ temp_degc, MIN_COOL_TEMP, MAX_WARM_TEMP);
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->jeita_configure_lock);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ if (temp_degc >=
+ (chip->cfg_warm_bat_decidegc - HYSTERISIS_DECIDEGC)) {
+ pr_err("Can't set cool %d higher than warm %d - hysterisis %d\n",
+ temp_degc,
+ chip->cfg_warm_bat_decidegc,
+ HYSTERISIS_DECIDEGC);
+ rc = -EINVAL;
+ goto mutex_unlock;
+ }
+ if (chip->bat_is_cool)
+ chip->adc_param.high_temp =
+ temp_degc + HYSTERISIS_DECIDEGC;
+ else if (!chip->bat_is_warm)
+ chip->adc_param.low_temp = temp_degc;
+
+ chip->cfg_cool_bat_decidegc = temp_degc;
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ if (temp_degc <=
+ (chip->cfg_cool_bat_decidegc + HYSTERISIS_DECIDEGC)) {
+ pr_err("Can't set warm %d higher than cool %d + hysterisis %d\n",
+ temp_degc,
+ chip->cfg_warm_bat_decidegc,
+ HYSTERISIS_DECIDEGC);
+ rc = -EINVAL;
+ goto mutex_unlock;
+ }
+ if (chip->bat_is_warm)
+ chip->adc_param.low_temp =
+ temp_degc - HYSTERISIS_DECIDEGC;
+ else if (!chip->bat_is_cool)
+ chip->adc_param.high_temp = temp_degc;
+
+ chip->cfg_warm_bat_decidegc = temp_degc;
+ break;
+ default:
+ rc = -EINVAL;
+ goto mutex_unlock;
+ }
+
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param))
+ pr_err("request ADC error\n");
+
+mutex_unlock:
+ mutex_unlock(&chip->jeita_configure_lock);
+ return rc;
+}
+
+static int qpnp_batt_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * End of charge happens only when BMS reports the battery status as full. For
+ * charging to end the s/w must put the usb path in suspend. Note that there
+ * is no battery fet and usb path suspend is the only control to prevent any
+ * current going in to the battery (and the system)
+ * Charging can begin only when VBATDET comparator outputs 0. This indicates
+ * that the battery is a at a lower voltage than 4% of the vddmax value.
+ * S/W can override this comparator to output a favourable value - this is
+ * used while resuming charging when the battery hasnt fallen below 4% but
+ * the SOC has fallen below the resume threshold.
+ *
+ * In short, when SOC resume happens:
+ * a. overide the comparator to output 0
+ * b. enable charging
+ *
+ * When vbatdet based resume happens:
+ * a. enable charging
+ *
+ * When end of charge happens:
+ * a. disable the overrides in the comparator
+ * (may be from a previous soc resume)
+ * b. disable charging
+ */
+static int qpnp_batt_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ mutex_lock(&chip->chg_enable_lock);
+ switch (val->intval) {
+ case POWER_SUPPLY_STATUS_FULL:
+ if (chip->cfg_float_charge)
+ break;
+ /* Disable charging */
+ rc = qpnp_lbc_charger_enable(chip, SOC, 0);
+ if (!rc)
+ chip->chg_done = true;
+
+ /*
+ * Enable VBAT_DET based charging:
+ * To enable charging when VBAT falls below VBAT_DET
+ * and device stays suspended after EOC.
+ */
+ if (!chip->cfg_disable_vbatdet_based_recharge) {
+ /* No override for VBAT_DET_LO comp */
+ rc = qpnp_lbc_vbatdet_override(chip,
+ OVERRIDE_NONE);
+ if (rc)
+ pr_err("Failed to override VBAT_DET rc=%d\n",
+ rc);
+ else
+ qpnp_lbc_enable_irq(chip,
+ &chip->irqs[CHG_VBAT_DET_LO]);
+ }
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ chip->chg_done = false;
+ pr_debug("resuming charging by bms\n");
+ if (!chip->cfg_disable_vbatdet_based_recharge)
+ qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+
+ qpnp_lbc_charger_enable(chip, SOC, 1);
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ chip->chg_done = false;
+ pr_debug("status = DISCHARGING chg_done = %d\n",
+ chip->chg_done);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&chip->chg_enable_lock);
+ break;
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ rc = qpnp_lbc_configure_jeita(chip, psp, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ rc = qpnp_lbc_configure_jeita(chip, psp, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ chip->fake_battery_soc = val->intval;
+ pr_debug("power supply changed batt_psy\n");
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ chip->cfg_charging_disabled = !(val->intval);
+ rc = qpnp_lbc_charger_enable(chip, USER,
+ !chip->cfg_charging_disabled);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ qpnp_lbc_vinmin_set(chip, val->intval / 1000);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ qpnp_lbc_system_temp_level_set(chip, val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(chip->batt_psy);
+ return rc;
+}
+
+static int qpnp_batt_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = get_prop_batt_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = get_prop_batt_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = chip->cfg_max_voltage_mv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = chip->cfg_min_voltage_mv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_prop_battery_voltage_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_prop_batt_temp(chip);
+ break;
+ case POWER_SUPPLY_PROP_COOL_TEMP:
+ val->intval = chip->cfg_cool_bat_decidegc;
+ break;
+ case POWER_SUPPLY_PROP_WARM_TEMP:
+ val->intval = chip->cfg_warm_bat_decidegc;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_prop_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = get_prop_charge_count(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !(chip->cfg_charging_disabled);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ val->intval = chip->therm_lvl_sel;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define VINMIN_DELAY msecs_to_jiffies(500)
+static void qpnp_lbc_parallel_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_lbc_chip *chip = container_of(dwork,
+ struct qpnp_lbc_chip, parallel_work);
+
+ if (is_vinmin_set(chip)) {
+ /* vinmin-loop triggered - stop ibat increase */
+ pr_debug("vinmin_loop triggered ichg_now=%d\n", chip->ichg_now);
+ goto exit_work;
+ } else {
+ int temp = chip->ichg_now + QPNP_LBC_I_STEP_MA;
+
+ if (temp > chip->lbc_max_chg_current) {
+ pr_debug("ichg_now=%d beyond max_chg_limit=%d - stopping\n",
+ temp, chip->lbc_max_chg_current);
+ goto exit_work;
+ }
+ chip->ichg_now = temp;
+ qpnp_lbc_ibatmax_set(chip, chip->ichg_now);
+ pr_debug("ichg_now increased to %d\n", chip->ichg_now);
+ }
+
+ schedule_delayed_work(&chip->parallel_work, VINMIN_DELAY);
+
+ return;
+
+exit_work:
+ pm_relax(chip->dev);
+}
+
+static int qpnp_lbc_parallel_charging_config(struct qpnp_lbc_chip *chip,
+ int enable)
+{
+ chip->parallel_charging_enabled = !!enable;
+
+ if (enable) {
+ /* Prevent sleep until charger is configured */
+ chip->ichg_now = QPNP_LBC_IBATMAX_MIN;
+ qpnp_lbc_ibatmax_set(chip, chip->ichg_now);
+ qpnp_lbc_charger_enable(chip, PARALLEL, 1);
+ pm_stay_awake(chip->dev);
+ schedule_delayed_work(&chip->parallel_work, VINMIN_DELAY);
+ } else {
+ cancel_delayed_work_sync(&chip->parallel_work);
+ pm_relax(chip->dev);
+ /* set minimum charging current and disable charging */
+ chip->ichg_now = 0;
+ chip->lbc_max_chg_current = 0;
+ qpnp_lbc_ibatmax_set(chip, 0);
+ qpnp_lbc_charger_enable(chip, PARALLEL, 0);
+ }
+
+ pr_debug("charging=%d ichg_now=%d max_chg_current=%d\n",
+ enable, chip->ichg_now, chip->lbc_max_chg_current);
+
+ return 0;
+}
+
+static void qpnp_lbc_set_current(struct qpnp_lbc_chip *chip, int current_ma)
+{
+ pr_debug("USB present=%d current_ma=%dmA\n", chip->usb_present,
+ current_ma);
+
+ if (current_ma <= 2 && get_prop_batt_present(chip)) {
+ qpnp_lbc_charger_enable(chip, CURRENT, 0);
+ chip->usb_psy_ma = QPNP_CHG_I_MAX_MIN_90;
+ qpnp_lbc_set_appropriate_current(chip);
+ } else {
+ chip->usb_psy_ma = current_ma;
+ qpnp_lbc_set_appropriate_current(chip);
+ qpnp_lbc_charger_enable(chip, CURRENT, 1);
+ }
+}
+
+static enum power_supply_property qpnp_lbc_usb_properties[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_REAL_TYPE,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+};
+
+static int qpnp_lbc_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->usb_psy_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = chip->usb_present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (is_battery_charging(chip))
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_UNKNOWN;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_lbc_usb_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ qpnp_lbc_set_current(chip, (val->intval / 1000));
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ chip->usb_supply_type = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(psy);
+ return 0;
+}
+static int qpnp_lbc_usb_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static enum power_supply_property qpnp_lbc_parallel_properties[] = {
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION,
+};
+
+static int qpnp_lbc_parallel_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ qpnp_lbc_parallel_charging_config(chip, !!val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ chip->lbc_max_chg_current = val->intval / 1000;
+ pr_debug("lbc_max_current=%d\n", chip->lbc_max_chg_current);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int qpnp_lbc_parallel_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ int rc;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static int qpnp_lbc_parallel_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct qpnp_lbc_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = chip->parallel_charging_enabled;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = chip->lbc_max_chg_current * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = chip->ichg_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION:
+ val->intval = is_vinmin_set(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static void qpnp_lbc_jeita_adc_notification(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_lbc_chip *chip = ctx;
+ bool bat_warm = 0, bat_cool = 0;
+ int temp;
+ unsigned long flags;
+
+ if (state >= ADC_TM_STATE_NUM) {
+ pr_err("invalid notification %d\n", state);
+ return;
+ }
+
+ temp = get_prop_batt_temp(chip);
+
+ pr_debug("temp = %d state = %s\n", temp,
+ state == ADC_TM_WARM_STATE ? "warm" : "cool");
+
+ if (state == ADC_TM_WARM_STATE) {
+ if (temp >= chip->cfg_warm_bat_decidegc) {
+ /* Normal to warm */
+ bat_warm = true;
+ bat_cool = false;
+ chip->adc_param.low_temp =
+ chip->cfg_warm_bat_decidegc
+ - HYSTERISIS_DECIDEGC;
+ chip->adc_param.state_request =
+ ADC_TM_COOL_THR_ENABLE;
+ } else if (temp >=
+ chip->cfg_cool_bat_decidegc + HYSTERISIS_DECIDEGC) {
+ /* Cool to normal */
+ bat_warm = false;
+ bat_cool = false;
+
+ chip->adc_param.low_temp =
+ chip->cfg_cool_bat_decidegc;
+ chip->adc_param.high_temp =
+ chip->cfg_warm_bat_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ }
+ } else {
+ if (temp <= chip->cfg_cool_bat_decidegc) {
+ /* Normal to cool */
+ bat_warm = false;
+ bat_cool = true;
+ chip->adc_param.high_temp =
+ chip->cfg_cool_bat_decidegc
+ + HYSTERISIS_DECIDEGC;
+ chip->adc_param.state_request =
+ ADC_TM_WARM_THR_ENABLE;
+ } else if (temp <= (chip->cfg_warm_bat_decidegc -
+ HYSTERISIS_DECIDEGC)){
+ /* Warm to normal */
+ bat_warm = false;
+ bat_cool = false;
+
+ chip->adc_param.low_temp =
+ chip->cfg_cool_bat_decidegc;
+ chip->adc_param.high_temp =
+ chip->cfg_warm_bat_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ }
+ }
+
+ if (chip->bat_is_cool ^ bat_cool || chip->bat_is_warm ^ bat_warm) {
+ spin_lock_irqsave(&chip->ibat_change_lock, flags);
+ chip->bat_is_cool = bat_cool;
+ chip->bat_is_warm = bat_warm;
+ qpnp_lbc_set_appropriate_vddmax(chip);
+ qpnp_lbc_set_appropriate_current(chip);
+ spin_unlock_irqrestore(&chip->ibat_change_lock, flags);
+ }
+
+ pr_debug("warm %d, cool %d, low = %d deciDegC, high = %d deciDegC\n",
+ chip->bat_is_warm, chip->bat_is_cool,
+ chip->adc_param.low_temp, chip->adc_param.high_temp);
+
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param))
+ pr_err("request ADC error\n");
+}
+
+#define IBAT_TERM_EN_MASK BIT(3)
+static int qpnp_lbc_chg_init(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ qpnp_lbc_vbatweak_set(chip, chip->cfg_batt_weak_voltage_uv);
+ rc = qpnp_lbc_vinmin_set(chip, chip->cfg_min_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vin_min rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddsafe_set(chip, chip->cfg_safe_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_safe rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_max_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_safe rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_ibatsafe_set(chip, chip->cfg_safe_current);
+ if (rc) {
+ pr_err("Failed to set ibat_safe rc=%d\n", rc);
+ return rc;
+ }
+
+ if (of_find_property(chip->dev->of_node, "qcom,tchg-mins", NULL)) {
+ rc = qpnp_lbc_tchg_max_set(chip, chip->cfg_tchg_mins);
+ if (rc) {
+ pr_err("Failed to set tchg_mins rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /*
+ * Override VBAT_DET comparator to enable charging
+ * irrespective of VBAT above VBAT_DET.
+ */
+ rc = qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+ if (rc) {
+ pr_err("Failed to override comp rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * Disable iterm comparator of linear charger to disable charger
+ * detecting end of charge condition based on DT configuration
+ * and float charge configuration.
+ */
+ if (!chip->cfg_charger_detect_eoc || chip->cfg_float_charge) {
+ rc = qpnp_lbc_masked_write(chip,
+ chip->chgr_base + CHG_IBATTERM_EN_REG,
+ IBAT_TERM_EN_MASK, 0);
+ if (rc) {
+ pr_err("Failed to disable EOC comp rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* Disable charger watchdog */
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_WDOG_EN_REG,
+ ®_val, 1);
+
+ return rc;
+}
+
+static int qpnp_lbc_bat_if_init(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ /* Select battery presence detection */
+ switch (chip->cfg_bpd_detection) {
+ case BPD_TYPE_BAT_THM:
+ reg_val = BATT_THM_EN;
+ break;
+ case BPD_TYPE_BAT_ID:
+ reg_val = BATT_ID_EN;
+ break;
+ case BPD_TYPE_BAT_THM_BAT_ID:
+ reg_val = BATT_THM_EN | BATT_ID_EN;
+ break;
+ default:
+ reg_val = BATT_THM_EN;
+ break;
+ }
+
+ rc = qpnp_lbc_masked_write(chip,
+ chip->bat_if_base + BAT_IF_BPD_CTRL_REG,
+ BATT_BPD_CTRL_SEL_MASK, reg_val);
+ if (rc) {
+ pr_err("Failed to choose BPD rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Force on VREF_BAT_THM */
+ reg_val = VREF_BATT_THERM_FORCE_ON;
+ rc = qpnp_lbc_write(chip,
+ chip->bat_if_base + BAT_IF_VREF_BAT_THM_CTRL_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to force on VREF_BAT_THM rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qpnp_lbc_usb_path_init(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val;
+
+ if (qpnp_lbc_is_usb_chg_plugged_in(chip)) {
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip,
+ chip->usb_chgpth_base + CHG_USB_ENUM_T_STOP_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to write enum stop rc=%d\n", rc);
+ return -ENXIO;
+ }
+ }
+
+ if (chip->cfg_charging_disabled) {
+ rc = qpnp_lbc_charger_enable(chip, USER, 0);
+ if (rc)
+ pr_err("Failed to disable charging rc=%d\n", rc);
+
+ /*
+ * Disable follow-on-reset if charging is explicitly disabled,
+ * this forces the charging to be disabled across reset.
+ * Note: Explicitly disabling charging is only a debug/test
+ * configuration
+ */
+ reg_val = 0x0;
+ rc = __qpnp_lbc_secure_write(chip, chip->chgr_base,
+ CHG_PERPH_RESET_CTRL3_REG, ®_val, 1);
+ if (rc)
+ pr_err("Failed to configure PERPH_CTRL3 rc=%d\n", rc);
+ else
+ pr_debug("Charger is not following PMIC reset\n");
+ } else {
+ /*
+ * Enable charging explicitly,
+ * because not sure the default behavior.
+ */
+ reg_val = CHG_ENABLE;
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_CTRL_REG,
+ CHG_EN_MASK, reg_val);
+ if (rc)
+ pr_err("Failed to enable charger rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+#define LBC_MISC_DIG_VERSION_1 0x01
+static int qpnp_lbc_misc_init(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg_val, reg_val1, trim_center;
+
+ /* Check if this LBC MISC version supports VDD trimming */
+ rc = qpnp_lbc_read(chip, chip->misc_base + MISC_REV2_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read VDD_EA TRIM3 reg rc=%d\n", rc);
+ return rc;
+ }
+
+ if (reg_val >= LBC_MISC_DIG_VERSION_1) {
+ chip->supported_feature_flag |= VDD_TRIM_SUPPORTED;
+ /* Read initial VDD trim value */
+ rc = qpnp_lbc_read(chip, chip->misc_base + MISC_TRIM3_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read VDD_EA TRIM3 reg rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_lbc_read(chip, chip->misc_base + MISC_TRIM4_REG,
+ ®_val1, 1);
+ if (rc) {
+ pr_err("Failed to read VDD_EA TRIM3 reg rc=%d\n", rc);
+ return rc;
+ }
+
+ trim_center = ((reg_val & MISC_TRIM3_VDD_MASK)
+ >> VDD_TRIM3_SHIFT)
+ | ((reg_val1 & MISC_TRIM4_VDD_MASK)
+ >> VDD_TRIM4_SHIFT);
+ chip->init_trim_uv = qpnp_lbc_get_trim_voltage(trim_center);
+ chip->delta_vddmax_uv = chip->init_trim_uv;
+ pr_debug("Initial trim center %x trim_uv %d\n",
+ trim_center, chip->init_trim_uv);
+ }
+
+ pr_debug("Setting BOOT_DONE\n");
+ reg_val = MISC_BOOT_DONE;
+ rc = qpnp_lbc_write(chip, chip->misc_base + MISC_BOOT_DONE_REG,
+ ®_val, 1);
+
+ return rc;
+}
+
+static int show_lbc_config(struct seq_file *m, void *data)
+{
+ struct qpnp_lbc_chip *chip = m->private;
+
+ seq_printf(m, "cfg_charging_disabled\t=\t%d\n"
+ "cfg_btc_disabled\t=\t%d\n"
+ "cfg_use_fake_battery\t=\t%d\n"
+ "cfg_use_external_charger\t=\t%d\n"
+ "cfg_chgr_led_support\t=\t%d\n"
+ "cfg_warm_bat_chg_ma\t=\t%d\n"
+ "cfg_cool_bat_chg_ma\t=\t%d\n"
+ "cfg_safe_voltage_mv\t=\t%d\n"
+ "cfg_max_voltage_mv\t=\t%d\n"
+ "cfg_min_voltage_mv\t=\t%d\n"
+ "cfg_charger_detect_eoc\t=\t%d\n"
+ "cfg_disable_vbatdet_based_recharge\t=\t%d\n"
+ "cfg_collapsible_chgr_support\t=\t%d\n"
+ "cfg_batt_weak_voltage_uv\t=\t%d\n"
+ "cfg_warm_bat_mv\t=\t%d\n"
+ "cfg_cool_bat_mv\t=\t%d\n"
+ "cfg_hot_batt_p\t=\t%d\n"
+ "cfg_cold_batt_p\t=\t%d\n"
+ "cfg_thermal_levels\t=\t%d\n"
+ "cfg_safe_current\t=\t%d\n"
+ "cfg_tchg_mins\t=\t%d\n"
+ "cfg_bpd_detection\t=\t%d\n"
+ "cfg_warm_bat_decidegc\t=\t%d\n"
+ "cfg_cool_bat_decidegc\t=\t%d\n"
+ "cfg_soc_resume_limit\t=\t%d\n"
+ "cfg_float_charge\t=\t%d\n",
+ chip->cfg_charging_disabled,
+ chip->cfg_btc_disabled,
+ chip->cfg_use_fake_battery,
+ chip->cfg_use_external_charger,
+ chip->cfg_chgr_led_support,
+ chip->cfg_warm_bat_chg_ma,
+ chip->cfg_cool_bat_chg_ma,
+ chip->cfg_safe_voltage_mv,
+ chip->cfg_max_voltage_mv,
+ chip->cfg_min_voltage_mv,
+ chip->cfg_charger_detect_eoc,
+ chip->cfg_disable_vbatdet_based_recharge,
+ chip->cfg_collapsible_chgr_support,
+ chip->cfg_batt_weak_voltage_uv,
+ chip->cfg_warm_bat_mv,
+ chip->cfg_cool_bat_mv,
+ chip->cfg_hot_batt_p,
+ chip->cfg_cold_batt_p,
+ chip->cfg_thermal_levels,
+ chip->cfg_safe_current,
+ chip->cfg_tchg_mins,
+ chip->cfg_bpd_detection,
+ chip->cfg_warm_bat_decidegc,
+ chip->cfg_cool_bat_decidegc,
+ chip->cfg_soc_resume_limit,
+ chip->cfg_float_charge);
+
+ return 0;
+}
+
+static int qpnp_lbc_config_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_lbc_chip *chip = inode->i_private;
+
+ return single_open(file, show_lbc_config, chip);
+}
+
+static const struct file_operations qpnp_lbc_config_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = qpnp_lbc_config_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define OF_PROP_READ(chip, prop, qpnp_dt_property, retval, optional) \
+do { \
+ if (retval) \
+ break; \
+ \
+ retval = of_property_read_u32(chip->dev->of_node, \
+ "qcom," qpnp_dt_property, \
+ &chip->prop); \
+ \
+ if ((retval == -EINVAL) && optional) \
+ retval = 0; \
+ else if (retval) \
+ pr_err("Error reading " #qpnp_dt_property \
+ " property rc = %d\n", rc); \
+} while (0)
+
+static int qpnp_charger_read_dt_props(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+ const char *bpd;
+
+ OF_PROP_READ(chip, cfg_max_voltage_mv, "vddmax-mv", rc, 0);
+ OF_PROP_READ(chip, cfg_safe_voltage_mv, "vddsafe-mv", rc, 0);
+ OF_PROP_READ(chip, cfg_min_voltage_mv, "vinmin-mv", rc, 0);
+ OF_PROP_READ(chip, cfg_safe_current, "ibatsafe-ma", rc, 0);
+ if (rc)
+ pr_err("Error reading required property rc=%d\n", rc);
+
+ OF_PROP_READ(chip, cfg_tchg_mins, "tchg-mins", rc, 1);
+ OF_PROP_READ(chip, cfg_warm_bat_decidegc, "warm-bat-decidegc", rc, 1);
+ OF_PROP_READ(chip, cfg_cool_bat_decidegc, "cool-bat-decidegc", rc, 1);
+ OF_PROP_READ(chip, cfg_hot_batt_p, "batt-hot-percentage", rc, 1);
+ OF_PROP_READ(chip, cfg_cold_batt_p, "batt-cold-percentage", rc, 1);
+ OF_PROP_READ(chip, cfg_batt_weak_voltage_uv, "vbatweak-uv", rc, 1);
+ OF_PROP_READ(chip, cfg_soc_resume_limit, "resume-soc", rc, 1);
+ if (rc) {
+ pr_err("Error reading optional property rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_string(chip->dev->of_node,
+ "qcom,bpd-detection", &bpd);
+ if (rc) {
+
+ chip->cfg_bpd_detection = BPD_TYPE_BAT_THM;
+ rc = 0;
+ } else {
+ chip->cfg_bpd_detection = get_bpd(bpd);
+ if (chip->cfg_bpd_detection < 0) {
+ pr_err("Failed to determine bpd schema rc=%d\n", rc);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Look up JEITA compliance parameters if cool and warm temp
+ * provided
+ */
+ if (chip->cfg_cool_bat_decidegc || chip->cfg_warm_bat_decidegc) {
+ chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "chg");
+ if (IS_ERR(chip->adc_tm_dev)) {
+ rc = PTR_ERR(chip->adc_tm_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("Failed to get adc-tm rc=%d\n", rc);
+ return rc;
+ }
+
+ OF_PROP_READ(chip, cfg_warm_bat_chg_ma, "ibatmax-warm-ma",
+ rc, 1);
+ OF_PROP_READ(chip, cfg_cool_bat_chg_ma, "ibatmax-cool-ma",
+ rc, 1);
+ OF_PROP_READ(chip, cfg_warm_bat_mv, "warm-bat-mv", rc, 1);
+ OF_PROP_READ(chip, cfg_cool_bat_mv, "cool-bat-mv", rc, 1);
+ if (rc) {
+ pr_err("Error reading battery temp prop rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* Get the btc-disabled property */
+ chip->cfg_btc_disabled = of_property_read_bool(
+ chip->dev->of_node, "qcom,btc-disabled");
+
+ /* Get the charging-disabled property */
+ chip->cfg_charging_disabled =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,charging-disabled");
+
+ /* Get the fake-batt-values property */
+ chip->cfg_use_fake_battery =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,use-default-batt-values");
+
+ /* Get the float charging property */
+ chip->cfg_float_charge =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,float-charge");
+
+ /* Get the charger EOC detect property */
+ chip->cfg_charger_detect_eoc =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,charger-detect-eoc");
+
+ /* Get the vbatdet disable property */
+ chip->cfg_disable_vbatdet_based_recharge =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,disable-vbatdet-based-recharge");
+
+ /* Get the charger led support property */
+ chip->cfg_chgr_led_support =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,chgr-led-support");
+
+ /* Get the collapsible charger support property */
+ chip->cfg_collapsible_chgr_support =
+ of_property_read_bool(chip->dev->of_node,
+ "qcom,collapsible-chgr-support");
+
+ /* Disable charging when faking battery values */
+ if (chip->cfg_use_fake_battery)
+ chip->cfg_charging_disabled = true;
+
+ chip->cfg_use_external_charger = of_property_read_bool(
+ chip->dev->of_node, "qcom,use-external-charger");
+
+ if (of_find_property(chip->dev->of_node,
+ "qcom,thermal-mitigation",
+ &chip->cfg_thermal_levels)) {
+ chip->thermal_mitigation = devm_kzalloc(chip->dev,
+ chip->cfg_thermal_levels,
+ GFP_KERNEL);
+
+ if (chip->thermal_mitigation == NULL) {
+ pr_err("thermal mitigation kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ chip->cfg_thermal_levels /= sizeof(int);
+ rc = of_property_read_u32_array(chip->dev->of_node,
+ "qcom,thermal-mitigation",
+ chip->thermal_mitigation,
+ chip->cfg_thermal_levels);
+ if (rc) {
+ pr_err("Failed to read threm limits rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ pr_debug("vddmax-mv=%d, vddsafe-mv=%d, vinmin-mv=%d, ibatsafe-ma=$=%d\n",
+ chip->cfg_max_voltage_mv,
+ chip->cfg_safe_voltage_mv,
+ chip->cfg_min_voltage_mv,
+ chip->cfg_safe_current);
+ pr_debug("warm-bat-decidegc=%d, cool-bat-decidegc=%d, batt-hot-percentage=%d, batt-cold-percentage=%d\n",
+ chip->cfg_warm_bat_decidegc,
+ chip->cfg_cool_bat_decidegc,
+ chip->cfg_hot_batt_p,
+ chip->cfg_cold_batt_p);
+ pr_debug("tchg-mins=%d, vbatweak-uv=%d, resume-soc=%d\n",
+ chip->cfg_tchg_mins,
+ chip->cfg_batt_weak_voltage_uv,
+ chip->cfg_soc_resume_limit);
+ pr_debug("bpd-detection=%d, ibatmax-warm-ma=%d, ibatmax-cool-ma=%d, warm-bat-mv=%d, cool-bat-mv=%d\n",
+ chip->cfg_bpd_detection,
+ chip->cfg_warm_bat_chg_ma,
+ chip->cfg_cool_bat_chg_ma,
+ chip->cfg_warm_bat_mv,
+ chip->cfg_cool_bat_mv);
+ pr_debug("btc-disabled=%d, charging-disabled=%d, use-default-batt-values=%d, float-charge=%d\n",
+ chip->cfg_btc_disabled,
+ chip->cfg_charging_disabled,
+ chip->cfg_use_fake_battery,
+ chip->cfg_float_charge);
+ pr_debug("charger-detect-eoc=%d, disable-vbatdet-based-recharge=%d, chgr-led-support=%d\n",
+ chip->cfg_charger_detect_eoc,
+ chip->cfg_disable_vbatdet_based_recharge,
+ chip->cfg_chgr_led_support);
+ pr_debug("collapsible-chg-support=%d, use-external-charger=%d, thermal_levels=%d\n",
+ chip->cfg_collapsible_chgr_support,
+ chip->cfg_use_external_charger,
+ chip->cfg_thermal_levels);
+ return rc;
+}
+
+#define CHG_REMOVAL_DETECT_DLY_MS 300
+static irqreturn_t qpnp_lbc_chg_gone_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int chg_gone;
+
+ if (chip->cfg_collapsible_chgr_support) {
+ chg_gone = qpnp_lbc_is_chg_gone(chip);
+ pr_debug("chg-gone triggered, rt_sts: %d\n", chg_gone);
+ if (chg_gone) {
+ /*
+ * Disable charger to prevent fastchg irq storming
+ * if a non-collapsible charger is being used.
+ */
+ pr_debug("disable charging for non-collapsbile charger\n");
+ qpnp_lbc_charger_enable(chip, COLLAPSE, 0);
+ qpnp_lbc_disable_irq(chip, &chip->irqs[USBIN_VALID]);
+ qpnp_lbc_disable_irq(chip, &chip->irqs[USB_CHG_GONE]);
+ qpnp_chg_collapsible_chgr_config(chip, 0);
+ /*
+ * Check after a delay if the charger is still
+ * inserted. It decides if a non-collapsible
+ * charger is being used, or charger has been
+ * removed.
+ */
+ schedule_delayed_work(&chip->collapsible_detection_work,
+ msecs_to_jiffies(CHG_REMOVAL_DETECT_DLY_MS));
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_usbin_valid_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int usb_present;
+ unsigned long flags;
+
+ usb_present = qpnp_lbc_is_usb_chg_plugged_in(chip);
+ pr_debug("usbin-valid triggered: %d\n", usb_present);
+
+ if (chip->usb_present ^ usb_present) {
+ chip->usb_present = usb_present;
+ if (!usb_present) {
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ qpnp_lbc_charger_enable(chip, CURRENT, 0);
+ spin_lock_irqsave(&chip->ibat_change_lock, flags);
+ chip->usb_psy_ma = QPNP_CHG_I_MAX_MIN_90;
+ qpnp_lbc_set_appropriate_current(chip);
+ spin_unlock_irqrestore(&chip->ibat_change_lock,
+ flags);
+ if (chip->cfg_collapsible_chgr_support)
+ chip->non_collapsible_chgr_detected = false;
+
+ if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED)
+ alarm_try_to_cancel(&chip->vddtrim_alarm);
+ } else {
+ /*
+ * Override VBAT_DET comparator to start charging
+ * even if VBAT > VBAT_DET.
+ */
+ if (!chip->cfg_disable_vbatdet_based_recharge)
+ qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+
+ /*
+ * If collapsible charger supported, enable chgr_gone
+ * irq, and configure for collapsible charger.
+ */
+ if (chip->cfg_collapsible_chgr_support &&
+ !chip->non_collapsible_chgr_detected) {
+ qpnp_lbc_enable_irq(chip,
+ &chip->irqs[USB_CHG_GONE]);
+ qpnp_chg_collapsible_chgr_config(chip, 1);
+ }
+ /*
+ * Enable SOC based charging to make sure
+ * charging gets enabled on USB insertion
+ * irrespective of battery SOC above resume_soc.
+ */
+ qpnp_lbc_charger_enable(chip, SOC, 1);
+ }
+
+ pr_debug("Updating usb_psy PRESENT property\n");
+ if (chip->usb_present)
+ extcon_set_cable_state_(chip->extcon,
+ EXTCON_USB, true);
+ else
+ extcon_set_cable_state_(chip->extcon,
+ EXTCON_USB, false);
+ power_supply_changed(chip->usb_psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_lbc_is_batt_temp_ok(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->bat_if_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("reg read failed: addr=%03X, rc=%d\n",
+ chip->bat_if_base + INT_RT_STS_REG, rc);
+ return rc;
+ }
+
+ return (reg_val & BAT_TEMP_OK_IRQ) ? 1 : 0;
+}
+
+static irqreturn_t qpnp_lbc_batt_temp_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int batt_temp_good;
+
+ batt_temp_good = qpnp_lbc_is_batt_temp_ok(chip);
+ pr_debug("batt-temp triggered: %d\n", batt_temp_good);
+
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_batt_pres_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int batt_present;
+
+ batt_present = qpnp_lbc_is_batt_present(chip);
+ pr_debug("batt-pres triggered: %d\n", batt_present);
+
+ if (chip->batt_present ^ batt_present) {
+ chip->batt_present = batt_present;
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+
+ if ((chip->cfg_cool_bat_decidegc
+ || chip->cfg_warm_bat_decidegc)
+ && batt_present) {
+ pr_debug("enabling vadc notifications\n");
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->adc_param))
+ pr_err("request ADC error\n");
+ } else if ((chip->cfg_cool_bat_decidegc
+ || chip->cfg_warm_bat_decidegc)
+ && !batt_present) {
+ qpnp_adc_tm_disable_chan_meas(chip->adc_tm_dev,
+ &chip->adc_param);
+ pr_debug("disabling vadc notifications\n");
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_chg_failed_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int rc;
+ u8 reg_val = CHG_FAILED_BIT;
+
+ pr_debug("chg_failed triggered count=%u\n", ++chip->chg_failed_count);
+ rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_FAILED_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to write chg_fail clear bit rc=%d\n", rc);
+
+ if (chip->bat_if_base) {
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_lbc_is_fastchg_on(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->chgr_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt status rc=%d\n", rc);
+ return rc;
+ }
+ pr_debug("charger status %x\n", reg_val);
+ return (reg_val & FAST_CHG_ON_IRQ) ? 1 : 0;
+}
+
+#define TRIM_PERIOD_NS (50LL * NSEC_PER_SEC)
+static irqreturn_t qpnp_lbc_fastchg_irq_handler(int irq, void *_chip)
+{
+ ktime_t kt;
+ struct qpnp_lbc_chip *chip = _chip;
+ bool fastchg_on = false;
+
+ fastchg_on = qpnp_lbc_is_fastchg_on(chip);
+
+ pr_debug("FAST_CHG IRQ triggered, fastchg_on: %d\n", fastchg_on);
+
+ if (chip->fastchg_on ^ fastchg_on) {
+ chip->fastchg_on = fastchg_on;
+ if (fastchg_on) {
+ mutex_lock(&chip->chg_enable_lock);
+ chip->chg_done = false;
+ mutex_unlock(&chip->chg_enable_lock);
+ /*
+ * Start alarm timer to periodically calculate
+ * and update VDD_MAX trim value.
+ */
+ if (chip->supported_feature_flag &
+ VDD_TRIM_SUPPORTED) {
+ kt = ns_to_ktime(TRIM_PERIOD_NS);
+ alarm_start_relative(&chip->vddtrim_alarm,
+ kt);
+ }
+ }
+
+ if (chip->bat_if_base) {
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_chg_done_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+
+ pr_debug("charging done triggered\n");
+
+ chip->chg_done = true;
+ pr_debug("power supply changed batt_psy\n");
+ power_supply_changed(chip->batt_psy);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_lbc_vbatdet_lo_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int rc;
+
+ pr_debug("vbatdet-lo triggered\n");
+
+ /*
+ * Disable vbatdet irq to prevent interrupt storm when VBAT is
+ * close to VBAT_DET.
+ */
+ qpnp_lbc_disable_irq(chip, &chip->irqs[CHG_VBAT_DET_LO]);
+
+ /*
+ * Override VBAT_DET comparator to 0 to fix comparator toggling
+ * near VBAT_DET threshold.
+ */
+ qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+
+ /*
+ * Battery has fallen below the vbatdet threshold and it is
+ * time to resume charging.
+ */
+ rc = qpnp_lbc_charger_enable(chip, SOC, 1);
+ if (rc)
+ pr_err("Failed to enable charging\n");
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_lbc_is_overtemp(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_read(chip, chip->usb_chgpth_base + INT_RT_STS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read interrupt status rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("OVERTEMP rt status %x\n", reg_val);
+ return (reg_val & OVERTEMP_ON_IRQ) ? 1 : 0;
+}
+
+static irqreturn_t qpnp_lbc_usb_overtemp_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_lbc_chip *chip = _chip;
+ int overtemp = qpnp_lbc_is_overtemp(chip);
+
+ pr_warn_ratelimited("charger %s temperature limit\n",
+ overtemp ? "exceeds" : "within");
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_disable_lbc_charger(struct qpnp_lbc_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ reg = CHG_FORCE_BATT_ON;
+ rc = qpnp_lbc_masked_write(chip, chip->chgr_base + CHG_CTRL_REG,
+ CHG_EN_MASK, reg);
+ /* disable BTC */
+ rc |= qpnp_lbc_masked_write(chip, chip->bat_if_base + BAT_IF_BTC_CTRL,
+ BTC_COMP_EN_MASK, 0);
+ /* Enable BID and disable THM based BPD */
+ reg = BATT_ID_EN | BATT_BPD_OFFMODE_EN;
+ rc |= qpnp_lbc_write(chip, chip->bat_if_base + BAT_IF_BPD_CTRL_REG,
+ ®, 1);
+ return rc;
+}
+
+#define REQUEST_IRQ(chip, idx, rc, irq_name, threaded, flags, wake)\
+do { \
+ if (rc) \
+ break; \
+ if (chip->irqs[idx].irq) { \
+ if (threaded) \
+ rc = devm_request_threaded_irq(chip->dev, \
+ chip->irqs[idx].irq, NULL, \
+ qpnp_lbc_##irq_name##_irq_handler, \
+ flags, #irq_name, chip); \
+ else \
+ rc = devm_request_irq(chip->dev, \
+ chip->irqs[idx].irq, \
+ qpnp_lbc_##irq_name##_irq_handler, \
+ flags, #irq_name, chip); \
+ if (rc < 0) { \
+ pr_err("Unable to request " #irq_name " %d\n", \
+ rc); \
+ } else { \
+ rc = 0; \
+ if (wake) { \
+ enable_irq_wake(chip->irqs[idx].irq); \
+ chip->irqs[idx].is_wake = true; \
+ } \
+ } \
+ } \
+} while (0)
+
+static inline void get_irq_resource(struct qpnp_lbc_chip *chip, int idx,
+ const char *name, struct device_node *child)
+{
+ int rc = 0;
+
+ rc = of_irq_get_byname(child, name);
+ if (rc < 0)
+ pr_err("Unable to get irq resource for %s - %d\n", name, rc);
+ else
+ chip->irqs[idx].irq = rc;
+}
+
+static int qpnp_lbc_request_irqs(struct qpnp_lbc_chip *chip)
+{
+ int rc = 0;
+
+ REQUEST_IRQ(chip, CHG_FAILED, rc, chg_failed, 0,
+ IRQF_TRIGGER_RISING, 1);
+
+ REQUEST_IRQ(chip, CHG_FAST_CHG, rc, fastchg, 1,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, 1);
+
+ REQUEST_IRQ(chip, CHG_DONE, rc, chg_done, 0,
+ IRQF_TRIGGER_RISING, 0);
+
+ REQUEST_IRQ(chip, CHG_VBAT_DET_LO, rc, vbatdet_lo, 0,
+ IRQF_TRIGGER_FALLING, 1);
+
+ REQUEST_IRQ(chip, BATT_PRES, rc, batt_pres, 1,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, 1);
+
+ REQUEST_IRQ(chip, BATT_TEMPOK, rc, batt_temp, 0,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1);
+
+ REQUEST_IRQ(chip, USBIN_VALID, rc, usbin_valid, 1,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 1);
+
+ REQUEST_IRQ(chip, USB_CHG_GONE, rc, chg_gone, 0,
+ IRQF_TRIGGER_RISING, 1);
+
+ REQUEST_IRQ(chip, USB_OVER_TEMP, rc, usb_overtemp, 0,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 0);
+
+ return 0;
+}
+
+static int qpnp_lbc_get_irqs(struct qpnp_lbc_chip *chip, u8 subtype,
+ struct device_node *child)
+{
+ switch (subtype) {
+ case LBC_CHGR_SUBTYPE:
+ get_irq_resource(chip, CHG_FAST_CHG, "fast-chg-on", child);
+ get_irq_resource(chip, CHG_FAILED, "chg-failed", child);
+
+ if (!chip->cfg_disable_vbatdet_based_recharge)
+ get_irq_resource(chip, CHG_VBAT_DET_LO,
+ "vbat-det-lo", child);
+ if (chip->cfg_charger_detect_eoc)
+ get_irq_resource(chip, CHG_DONE, "chg-done", child);
+ break;
+
+ case LBC_BAT_IF_SUBTYPE:
+ get_irq_resource(chip, BATT_PRES, "batt-pres", child);
+ get_irq_resource(chip, BATT_TEMPOK, "bat-temp-ok", child);
+ break;
+
+ case LBC_USB_PTH_SUBTYPE:
+ get_irq_resource(chip, USBIN_VALID, "usbin-valid", child);
+ get_irq_resource(chip, USB_OVER_TEMP, "usb-over-temp", child);
+ get_irq_resource(chip, USB_CHG_GONE, "chg-gone", child);
+ break;
+ };
+ return 0;
+}
+
+/* Get/Set initial state of charger */
+static void determine_initial_status(struct qpnp_lbc_chip *chip)
+{
+ chip->usb_present = qpnp_lbc_is_usb_chg_plugged_in(chip);
+ power_supply_changed(chip->usb_psy);
+ /*
+ * Set USB psy online to avoid userspace from shutting down if battery
+ * capacity is at zero and no chargers online.
+ */
+ if (chip->usb_present) {
+ if (chip->cfg_collapsible_chgr_support &&
+ !chip->non_collapsible_chgr_detected) {
+ qpnp_lbc_enable_irq(chip,
+ &chip->irqs[USB_CHG_GONE]);
+ qpnp_chg_collapsible_chgr_config(chip, 1);
+ }
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+ } else {
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+ }
+ power_supply_changed(chip->usb_psy);
+}
+
+static void qpnp_lbc_collapsible_detection_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_lbc_chip *chip = container_of(dwork,
+ struct qpnp_lbc_chip,
+ collapsible_detection_work);
+
+ if (qpnp_lbc_is_usb_chg_plugged_in(chip)) {
+ chip->non_collapsible_chgr_detected = true;
+ pr_debug("Non-collapsible charger detected\n");
+ } else {
+ chip->non_collapsible_chgr_detected = false;
+ pr_debug("Charger removal detected\n");
+ }
+ qpnp_lbc_charger_enable(chip, COLLAPSE, 1);
+ qpnp_lbc_enable_irq(chip, &chip->irqs[USBIN_VALID]);
+}
+
+#define IBAT_TRIM -300
+static void qpnp_lbc_vddtrim_work_fn(struct work_struct *work)
+{
+ int rc, vbat_now_uv, ibat_now;
+ u8 reg_val;
+ ktime_t kt;
+ struct qpnp_lbc_chip *chip = container_of(work, struct qpnp_lbc_chip,
+ vddtrim_work);
+
+ vbat_now_uv = get_prop_battery_voltage_now(chip);
+ ibat_now = get_prop_current_now(chip) / 1000;
+ pr_debug("vbat %d ibat %d capacity %d\n",
+ vbat_now_uv, ibat_now, get_prop_capacity(chip));
+
+ /*
+ * Stop trimming under following condition:
+ * USB removed
+ * Charging Stopped
+ */
+ if (!qpnp_lbc_is_fastchg_on(chip) ||
+ !qpnp_lbc_is_usb_chg_plugged_in(chip)) {
+ pr_debug("stop trim charging stopped\n");
+ goto exit;
+ } else {
+ rc = qpnp_lbc_read(chip, chip->chgr_base + CHG_STATUS_REG,
+ ®_val, 1);
+ if (rc) {
+ pr_err("Failed to read chg status rc=%d\n", rc);
+ goto out;
+ }
+
+ /*
+ * Update VDD trim voltage only if following conditions are
+ * met:
+ * If charger is in VDD loop AND
+ * If ibat is between 0 ma and -300 ma
+ */
+ if ((reg_val & CHG_VDD_LOOP_BIT) &&
+ ((ibat_now < 0) && (ibat_now > IBAT_TRIM)))
+ qpnp_lbc_adjust_vddmax(chip, vbat_now_uv);
+ }
+
+out:
+ kt = ns_to_ktime(TRIM_PERIOD_NS);
+ alarm_start_relative(&chip->vddtrim_alarm, kt);
+exit:
+ pm_relax(chip->dev);
+}
+
+static enum alarmtimer_restart vddtrim_callback(struct alarm *alarm,
+ ktime_t now)
+{
+ struct qpnp_lbc_chip *chip = container_of(alarm, struct qpnp_lbc_chip,
+ vddtrim_alarm);
+
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->vddtrim_work);
+
+ return ALARMTIMER_NORESTART;
+}
+
+static int qpnp_lbc_parallel_charger_init(struct qpnp_lbc_chip *chip)
+{
+ u8 reg_val;
+ int rc;
+
+ rc = qpnp_lbc_vinmin_set(chip, chip->cfg_min_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vin_min rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddsafe_set(chip, chip->cfg_max_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_safe rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_vddmax_set(chip, chip->cfg_max_voltage_mv);
+ if (rc) {
+ pr_err("Failed to set vdd_max rc=%d\n", rc);
+ return rc;
+ }
+
+ /* set the minimum charging current */
+ rc = qpnp_lbc_ibatmax_set(chip, 0);
+ if (rc) {
+ pr_err("Failed to set IBAT_MAX to 0 rc=%d\n", rc);
+ return rc;
+ }
+
+ /* disable charging */
+ rc = qpnp_lbc_charger_enable(chip, PARALLEL, 0);
+ if (rc) {
+ pr_err("Unable to disable charging rc=%d\n", rc);
+ return 0;
+ }
+
+ /* Enable BID and disable THM based BPD */
+ reg_val = BATT_ID_EN | BATT_BPD_OFFMODE_EN;
+ rc = qpnp_lbc_write(chip, chip->bat_if_base + BAT_IF_BPD_CTRL_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to override BPD configuration rc=%d\n", rc);
+
+ /* Disable and override BTC */
+ reg_val = 0x2A;
+ rc = __qpnp_lbc_secure_write(chip, chip->bat_if_base,
+ BTC_COMP_OVERRIDE_REG, ®_val, 1);
+ if (rc)
+ pr_err("Failed to disable BTC override rc=%d\n", rc);
+
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip,
+ chip->bat_if_base + BAT_IF_BTC_CTRL, ®_val, 1);
+ if (rc)
+ pr_err("Failed to disable BTC rc=%d\n", rc);
+
+ /* override VBAT_DET */
+ rc = qpnp_lbc_vbatdet_override(chip, OVERRIDE_0);
+ if (rc)
+ pr_err("Failed to override VBAT_DET rc=%d\n", rc);
+
+ /* Set BOOT_DONE and ENUM complete */
+ reg_val = 0;
+ rc = qpnp_lbc_write(chip,
+ chip->usb_chgpth_base + CHG_USB_ENUM_T_STOP_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to stop enum-timer rc=%d\n", rc);
+
+ reg_val = MISC_BOOT_DONE;
+ rc = qpnp_lbc_write(chip, chip->misc_base + MISC_BOOT_DONE_REG,
+ ®_val, 1);
+ if (rc)
+ pr_err("Failed to set boot-done rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_lbc_parse_resources(struct qpnp_lbc_chip *chip)
+{
+ u8 subtype;
+ int rc = 0;
+ struct platform_device *pdev = chip->pdev;
+ struct device_node *child;
+ unsigned int base;
+
+ if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+ pr_err("no child nodes\n");
+ goto fail_charger_enable;
+ }
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ pr_debug("register address = %#X rc = %d\n", base, rc);
+ if (rc < 0) {
+ pr_err("Couldn`t find reg in node = %s rc = %d\n",
+ child->full_name, rc);
+ goto fail_charger_enable;
+ }
+
+ rc = qpnp_lbc_read(chip, base + PERP_SUBTYPE_REG, &subtype, 1);
+ if (rc) {
+ pr_err("Peripheral subtype read failed rc=%d\n", rc);
+ return rc;
+ }
+
+ switch (subtype) {
+ case LBC_CHGR_SUBTYPE:
+ chip->chgr_base = base;
+ rc = qpnp_lbc_get_irqs(chip, subtype, child);
+ if (rc) {
+ pr_err("Failed to get CHGR irqs rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case LBC_USB_PTH_SUBTYPE:
+ chip->usb_chgpth_base = base;
+ rc = qpnp_lbc_get_irqs(chip, subtype, child);
+ if (rc) {
+ pr_err("Failed to get USB_PTH irqs rc=%d\n",
+ rc);
+ return rc;
+ }
+ break;
+ case LBC_BAT_IF_SUBTYPE:
+ chip->bat_if_base = base;
+ rc = qpnp_lbc_get_irqs(chip, subtype, child);
+ if (rc) {
+ pr_err("Failed to get BAT_IF irqs rc=%d\n", rc);
+ return rc;
+ }
+ break;
+ case LBC_MISC_SUBTYPE:
+ chip->misc_base = base;
+ break;
+ default:
+ pr_err("Invalid peripheral subtype=0x%x\n", subtype);
+ rc = -EINVAL;
+ }
+ }
+
+ pr_debug("chgr_base=%x usb_chgpth_base=%x bat_if_base=%x misc_base=%x\n",
+ chip->chgr_base, chip->usb_chgpth_base,
+ chip->bat_if_base, chip->misc_base);
+
+ return rc;
+
+fail_charger_enable:
+ dev_set_drvdata(&pdev->dev, NULL);
+ return -ENXIO;
+}
+
+static int qpnp_lbc_parallel_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct qpnp_lbc_chip *chip;
+ struct power_supply_config parallel_psy_cfg = {};
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_lbc_chip),
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ pr_err("Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ chip->dev = &pdev->dev;
+ chip->pdev = pdev;
+ dev_set_drvdata(&pdev->dev, chip);
+ device_init_wakeup(&pdev->dev, 1);
+ spin_lock_init(&chip->hw_access_lock);
+ spin_lock_init(&chip->ibat_change_lock);
+ INIT_DELAYED_WORK(&chip->parallel_work, qpnp_lbc_parallel_work);
+
+ OF_PROP_READ(chip, cfg_max_voltage_mv, "vddmax-mv", rc, 0);
+ if (rc)
+ return rc;
+ OF_PROP_READ(chip, cfg_min_voltage_mv, "vinmin-mv", rc, 0);
+ if (rc)
+ return rc;
+
+ rc = qpnp_lbc_parse_resources(chip);
+ if (rc) {
+ pr_err("Unable to parse LBC(parallel) resources rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_lbc_parallel_charger_init(chip);
+ if (rc) {
+ pr_err("Unable to initialize LBC(parallel) rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->parallel_psy_d.name = "parallel";
+ chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_PARALLEL;
+ chip->parallel_psy_d.get_property = qpnp_lbc_parallel_get_property;
+ chip->parallel_psy_d.set_property = qpnp_lbc_parallel_set_property;
+ chip->parallel_psy_d.properties = qpnp_lbc_parallel_properties;
+ chip->parallel_psy_d.property_is_writeable =
+ qpnp_lbc_parallel_is_writeable;
+ chip->parallel_psy_d.num_properties =
+ ARRAY_SIZE(qpnp_lbc_parallel_properties);
+
+ parallel_psy_cfg.drv_data = chip;
+ parallel_psy_cfg.num_supplicants = 0;
+
+ chip->parallel_psy = devm_power_supply_register(chip->dev,
+ &chip->parallel_psy_d,
+ ¶llel_psy_cfg);
+ if (IS_ERR(chip->parallel_psy)) {
+ pr_err("Unable to register LBC parallel_psy rc = %ld\n",
+ PTR_ERR(chip->parallel_psy));
+ return PTR_ERR(chip->parallel_psy);
+ }
+
+ pr_debug("LBC (parallel) registered successfully!\n");
+
+ return 0;
+}
+
+static int qpnp_lbc_main_probe(struct platform_device *pdev)
+{
+ ktime_t kt;
+ struct qpnp_lbc_chip *chip;
+ struct power_supply_config batt_psy_cfg = {};
+ struct power_supply_config usb_psy_cfg = {};
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_lbc_chip),
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap)
+ return -EINVAL;
+
+ chip->dev = &pdev->dev;
+ chip->pdev = pdev;
+ dev_set_drvdata(&pdev->dev, chip);
+ device_init_wakeup(&pdev->dev, 1);
+ chip->fake_battery_soc = -EINVAL;
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ chip->extcon = devm_extcon_dev_allocate(chip->dev,
+ qpnp_lbc_extcon_cable);
+ if (IS_ERR(chip->extcon)) {
+ pr_err("failed to allocate extcon device\n");
+ rc = PTR_ERR(chip->extcon);
+ return rc;
+ }
+
+ rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+ if (rc) {
+ pr_err("failed to register extcon device\n");
+ return rc;
+ }
+
+ mutex_init(&chip->jeita_configure_lock);
+ mutex_init(&chip->chg_enable_lock);
+ spin_lock_init(&chip->hw_access_lock);
+ spin_lock_init(&chip->ibat_change_lock);
+ spin_lock_init(&chip->irq_lock);
+ INIT_WORK(&chip->vddtrim_work, qpnp_lbc_vddtrim_work_fn);
+ alarm_init(&chip->vddtrim_alarm, ALARM_REALTIME, vddtrim_callback);
+ INIT_DELAYED_WORK(&chip->collapsible_detection_work,
+ qpnp_lbc_collapsible_detection_work);
+
+ /* Get all device-tree properties */
+ rc = qpnp_charger_read_dt_props(chip);
+ if (rc) {
+ pr_err("Failed to read DT properties rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_lbc_parse_resources(chip);
+ if (rc) {
+ pr_err("Unable to parse LBC resources rc=%d\n", rc);
+ goto fail_chg_enable;
+ }
+
+ if (chip->cfg_use_external_charger) {
+ pr_warn("Disabling Linear Charger (e-external-charger = 1)\n");
+ rc = qpnp_disable_lbc_charger(chip);
+ if (rc)
+ pr_err("Unable to disable charger rc=%d\n", rc);
+ return -ENODEV;
+ }
+
+ chip->usb_psy_d.name = "usb";
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+ chip->usb_psy_d.properties = qpnp_lbc_usb_properties;
+ chip->usb_psy_d.num_properties = ARRAY_SIZE(qpnp_lbc_usb_properties);
+ chip->usb_psy_d.get_property = qpnp_lbc_usb_get_property;
+ chip->usb_psy_d.set_property = qpnp_lbc_usb_set_property;
+ chip->usb_psy_d.property_is_writeable = qpnp_lbc_usb_is_writeable;
+
+ usb_psy_cfg.drv_data = chip;
+ usb_psy_cfg.num_supplicants = 0;
+
+ chip->usb_psy = devm_power_supply_register(chip->dev,
+ &chip->usb_psy_d, &usb_psy_cfg);
+ if (IS_ERR(chip->usb_psy)) {
+ pr_err("Unable to register usb_psy rc = %ld\n",
+ PTR_ERR(chip->usb_psy));
+ rc = PTR_ERR(chip->usb_psy);
+ goto fail_chg_enable;
+ }
+
+ chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("vadc prop missing rc=%d\n",
+ rc);
+ goto fail_chg_enable;
+ }
+
+ /* Initialize h/w */
+ rc = qpnp_lbc_misc_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC MISC rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_chg_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC charger rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_bat_if_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC BAT_IF rc=%d\n", rc);
+ return rc;
+ }
+ rc = qpnp_lbc_usb_path_init(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC USB path rc=%d\n", rc);
+ return rc;
+ }
+
+ if (chip->cfg_chgr_led_support) {
+ rc = qpnp_lbc_register_chgr_led(chip);
+ if (rc) {
+ pr_err("unable to register charger led rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->bat_if_base) {
+ chip->batt_present = qpnp_lbc_is_batt_present(chip);
+ chip->batt_psy_d.name = "battery";
+ chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy_d.properties = msm_batt_power_props;
+ chip->batt_psy_d.num_properties =
+ ARRAY_SIZE(msm_batt_power_props);
+ chip->batt_psy_d.get_property = qpnp_batt_power_get_property;
+ chip->batt_psy_d.set_property = qpnp_batt_power_set_property;
+ chip->batt_psy_d.property_is_writeable =
+ qpnp_batt_property_is_writeable;
+
+ batt_psy_cfg.drv_data = chip;
+ batt_psy_cfg.supplied_to = pm_batt_supplied_to;
+ batt_psy_cfg.num_supplicants =
+ ARRAY_SIZE(pm_batt_supplied_to);
+
+ chip->batt_psy = devm_power_supply_register(chip->dev,
+ &chip->batt_psy_d,
+ &batt_psy_cfg);
+ if (IS_ERR(chip->batt_psy)) {
+ pr_err("Unable to register LBC batt_psy rc = %ld\n",
+ PTR_ERR(chip->batt_psy));
+ goto fail_chg_enable;
+ }
+ }
+
+ if ((chip->cfg_cool_bat_decidegc || chip->cfg_warm_bat_decidegc)
+ && chip->bat_if_base) {
+ chip->adc_param.low_temp = chip->cfg_cool_bat_decidegc;
+ chip->adc_param.high_temp = chip->cfg_warm_bat_decidegc;
+ chip->adc_param.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->adc_param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->adc_param.btm_ctx = chip;
+ chip->adc_param.threshold_notification =
+ qpnp_lbc_jeita_adc_notification;
+ chip->adc_param.channel = LR_MUX1_BATT_THERM;
+
+ if (get_prop_batt_present(chip)) {
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->adc_param);
+ if (rc) {
+ pr_err("request ADC error rc=%d\n", rc);
+ goto unregister_batt;
+ }
+ }
+ }
+
+ rc = qpnp_lbc_bat_if_configure_btc(chip);
+ if (rc) {
+ pr_err("Failed to configure btc rc=%d\n", rc);
+ goto unregister_batt;
+ }
+
+ /* Get/Set charger's initial status */
+ determine_initial_status(chip);
+
+ rc = qpnp_lbc_request_irqs(chip);
+ if (rc) {
+ pr_err("unable to initialize LBC MISC rc=%d\n", rc);
+ goto unregister_batt;
+ }
+
+ if (chip->cfg_charging_disabled && !get_prop_batt_present(chip))
+ pr_info("Battery absent and charging disabled\n");
+
+ /* Configure initial alarm for VDD trim */
+ if ((chip->supported_feature_flag & VDD_TRIM_SUPPORTED) &&
+ qpnp_lbc_is_fastchg_on(chip)) {
+ kt = ns_to_ktime(TRIM_PERIOD_NS);
+ alarm_start_relative(&chip->vddtrim_alarm, kt);
+ }
+
+ chip->debug_root = debugfs_create_dir("qpnp_lbc", NULL);
+ if (!chip->debug_root)
+ pr_err("Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("lbc_config", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &qpnp_lbc_config_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create lbc_config debug file\n");
+ }
+
+ pr_debug("Probe chg_dis=%d bpd=%d usb=%d batt_pres=%d batt_volt=%d soc=%d\n",
+ chip->cfg_charging_disabled,
+ chip->cfg_bpd_detection,
+ qpnp_lbc_is_usb_chg_plugged_in(chip),
+ get_prop_batt_present(chip),
+ get_prop_battery_voltage_now(chip),
+ get_prop_capacity(chip));
+
+ return 0;
+
+unregister_batt:
+ if (chip->bat_if_base)
+ power_supply_unregister(chip->batt_psy);
+fail_chg_enable:
+ power_supply_unregister(chip->usb_psy);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return rc;
+}
+
+static int is_parallel_charger(struct platform_device *pdev)
+{
+ return of_property_read_bool(pdev->dev.of_node,
+ "qcom,parallel-charger");
+}
+
+static int qpnp_lbc_probe(struct platform_device *pdev)
+{
+ if (is_parallel_charger(pdev))
+ return qpnp_lbc_parallel_probe(pdev);
+ else
+ return qpnp_lbc_main_probe(pdev);
+}
+
+
+static int qpnp_lbc_remove(struct platform_device *pdev)
+{
+ struct qpnp_lbc_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ if (chip->supported_feature_flag & VDD_TRIM_SUPPORTED) {
+ alarm_cancel(&chip->vddtrim_alarm);
+ cancel_work_sync(&chip->vddtrim_work);
+ }
+ cancel_delayed_work_sync(&chip->collapsible_detection_work);
+ debugfs_remove_recursive(chip->debug_root);
+ if (chip->bat_if_base)
+ power_supply_unregister(chip->batt_psy);
+ power_supply_unregister(chip->usb_psy);
+ mutex_destroy(&chip->jeita_configure_lock);
+ mutex_destroy(&chip->chg_enable_lock);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return 0;
+}
+
+static const struct of_device_id qpnp_lbc_match_table[] = {
+ { .compatible = QPNP_CHARGER_DEV_NAME, },
+ {}
+};
+
+static struct platform_driver qpnp_lbc_driver = {
+ .probe = qpnp_lbc_probe,
+ .remove = qpnp_lbc_remove,
+ .driver = {
+ .name = QPNP_CHARGER_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_lbc_match_table,
+ },
+};
+
+/*
+ * qpnp_lbc_init() - register platform driver for qpnp-chg
+ */
+static int __init qpnp_lbc_init(void)
+{
+ return platform_driver_register(&qpnp_lbc_driver);
+}
+module_init(qpnp_lbc_init);
+
+static void __exit qpnp_lbc_exit(void)
+{
+ platform_driver_unregister(&qpnp_lbc_driver);
+}
+module_exit(qpnp_lbc_exit);
+
+MODULE_DESCRIPTION("QPNP Linear charger driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_CHARGER_DEV_NAME);
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index f7a6b7a..e86d2af 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -16,6 +16,7 @@
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
+#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -24,10 +25,13 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/pmic-voter.h>
#include <linux/qpnp/qpnp-adc.h>
#include <uapi/linux/qg.h>
+#include <uapi/linux/qg-profile.h>
+#include "fg-alg.h"
#include "qg-sdam.h"
#include "qg-core.h"
#include "qg-reg.h"
@@ -41,6 +45,16 @@
debug_mask, qg_debug_mask, int, 0600
);
+static int qg_esr_mod_count = 10;
+module_param_named(
+ esr_mod_count, qg_esr_mod_count, int, 0600
+);
+
+static int qg_esr_count = 5;
+module_param_named(
+ esr_count, qg_esr_count, int, 0600
+);
+
static bool is_battery_present(struct qpnp_qg *chip)
{
u8 reg = 0;
@@ -209,6 +223,14 @@
}
pr_debug("Notified charger on float voltage and FCC\n");
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, &prop);
+ if (rc < 0) {
+ pr_err("Failed to get charge term current, rc=%d\n", rc);
+ return;
+ }
+ chip->chg_iterm_ma = prop.intval;
}
static bool is_batt_available(struct qpnp_qg *chip)
@@ -226,7 +248,7 @@
return true;
}
-static int qg_update_sdam_params(struct qpnp_qg *chip)
+static int qg_store_soc_params(struct qpnp_qg *chip)
{
int rc, batt_temp = 0, i;
unsigned long rtc_sec = 0;
@@ -243,13 +265,11 @@
else
chip->sdam_data[SDAM_TEMP] = (u32)batt_temp;
- rc = qg_sdam_write_all(chip->sdam_data);
- if (rc < 0)
- pr_err("Failed to write to SDAM rc=%d\n", rc);
-
- for (i = 0; i < SDAM_MAX; i++)
+ for (i = 0; i <= SDAM_TIME_SEC; i++) {
+ rc |= qg_sdam_write(i, chip->sdam_data[i]);
qg_dbg(chip, QG_DEBUG_STATUS, "SDAM write param %d value=%d\n",
i, chip->sdam_data[i]);
+ }
return rc;
}
@@ -259,8 +279,13 @@
int rc = 0, i, j = 0, temp;
u8 v_fifo[MAX_FIFO_LENGTH * 2], i_fifo[MAX_FIFO_LENGTH * 2];
u32 sample_interval = 0, sample_count = 0, fifo_v = 0, fifo_i = 0;
+ unsigned long rtc_sec = 0;
- chip->kdata.fifo_time = (u32)ktime_get_seconds();
+ rc = get_rtc_time(&rtc_sec);
+ if (rc < 0)
+ pr_err("Failed to get RTC time, rc=%d\n", rc);
+
+ chip->kdata.fifo_time = (u32)rtc_sec;
if (!fifo_length) {
pr_debug("No FIFO data\n");
@@ -426,6 +451,90 @@
return rc;
}
+#define MIN_FIFO_FULL_TIME_MS 12000
+static int process_rt_fifo_data(struct qpnp_qg *chip,
+ bool update_vbat_low, bool update_smb)
+{
+ int rc = 0;
+ ktime_t now = ktime_get();
+ s64 time_delta;
+ u8 fifo_length;
+
+ /*
+ * Reject the FIFO read event if there are back-to-back requests
+ * This is done to gaurantee that there is always a minimum FIFO
+ * data to be processed, ignore this if vbat_low is set.
+ */
+ time_delta = ktime_ms_delta(now, chip->last_user_update_time);
+
+ qg_dbg(chip, QG_DEBUG_FIFO, "time_delta=%lld ms update_vbat_low=%d update_smb=%d\n",
+ time_delta, update_vbat_low, update_smb);
+
+ if (time_delta > MIN_FIFO_FULL_TIME_MS || update_vbat_low
+ || update_smb) {
+ rc = qg_master_hold(chip, true);
+ if (rc < 0) {
+ pr_err("Failed to hold master, rc=%d\n", rc);
+ goto done;
+ }
+
+ rc = qg_process_rt_fifo(chip);
+ if (rc < 0) {
+ pr_err("Failed to process FIFO real-time, rc=%d\n", rc);
+ goto done;
+ }
+
+ if (update_vbat_low) {
+ /* change FIFO length */
+ fifo_length = chip->vbat_low ?
+ chip->dt.s2_vbat_low_fifo_length :
+ chip->dt.s2_fifo_length;
+ rc = qg_update_fifo_length(chip, fifo_length);
+ if (rc < 0)
+ goto done;
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "FIFO length updated to %d vbat_low=%d\n",
+ fifo_length, chip->vbat_low);
+ }
+
+ if (update_smb) {
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_MODE_CTL1_REG, PARALLEL_IBAT_SENSE_EN_BIT,
+ chip->parallel_enabled ?
+ PARALLEL_IBAT_SENSE_EN_BIT : 0);
+ if (rc < 0) {
+ pr_err("Failed to update SMB_EN, rc=%d\n", rc);
+ goto done;
+ }
+ qg_dbg(chip, QG_DEBUG_STATUS, "Parallel SENSE %d\n",
+ chip->parallel_enabled);
+ }
+
+ rc = qg_master_hold(chip, false);
+ if (rc < 0) {
+ pr_err("Failed to release master, rc=%d\n", rc);
+ goto done;
+ }
+ /* FIFOs restarted */
+ chip->last_fifo_update_time = ktime_get();
+
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+ chip->last_user_update_time = now;
+
+ /* vote to stay awake until userspace reads data */
+ vote(chip->awake_votable, FIFO_RT_DONE_VOTER, true, 0);
+ } else {
+ qg_dbg(chip, QG_DEBUG_FIFO, "FIFO processing too early time_delta=%lld\n",
+ time_delta);
+ }
+done:
+ qg_master_hold(chip, false);
+ return rc;
+}
+
#define VBAT_LOW_HYST_UV 50000 /* 50mV */
static int qg_vbat_low_wa(struct qpnp_qg *chip)
{
@@ -554,82 +663,356 @@
return rc;
}
-#define MIN_FIFO_FULL_TIME_MS 12000
-static int process_rt_fifo_data(struct qpnp_qg *chip,
- bool vbat_low, bool update_smb)
+static void qg_retrieve_esr_params(struct qpnp_qg *chip)
{
- int rc = 0;
- ktime_t now = ktime_get();
- s64 time_delta;
+ u32 data = 0;
+ int rc;
+
+ rc = qg_sdam_read(SDAM_ESR_CHARGE_DELTA, &data);
+ if (!rc && data) {
+ chip->kdata.param[QG_ESR_CHARGE_DELTA].data = data;
+ chip->kdata.param[QG_ESR_CHARGE_DELTA].valid = true;
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR_CHARGE_DELTA SDAM=%d\n", data);
+ } else if (rc < 0) {
+ pr_err("Failed to read ESR_CHARGE_DELTA rc=%d\n", rc);
+ }
+
+ rc = qg_sdam_read(SDAM_ESR_DISCHARGE_DELTA, &data);
+ if (!rc && data) {
+ chip->kdata.param[QG_ESR_DISCHARGE_DELTA].data = data;
+ chip->kdata.param[QG_ESR_DISCHARGE_DELTA].valid = true;
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR_DISCHARGE_DELTA SDAM=%d\n", data);
+ } else if (rc < 0) {
+ pr_err("Failed to read ESR_DISCHARGE_DELTA rc=%d\n", rc);
+ }
+
+ rc = qg_sdam_read(SDAM_ESR_CHARGE_SF, &data);
+ if (!rc && data) {
+ data = CAP(QG_ESR_SF_MIN, QG_ESR_SF_MAX, data);
+ chip->kdata.param[QG_ESR_CHARGE_SF].data = data;
+ chip->kdata.param[QG_ESR_CHARGE_SF].valid = true;
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR_CHARGE_SF SDAM=%d\n", data);
+ } else if (rc < 0) {
+ pr_err("Failed to read ESR_CHARGE_SF rc=%d\n", rc);
+ }
+
+ rc = qg_sdam_read(SDAM_ESR_DISCHARGE_SF, &data);
+ if (!rc && data) {
+ data = CAP(QG_ESR_SF_MIN, QG_ESR_SF_MAX, data);
+ chip->kdata.param[QG_ESR_DISCHARGE_SF].data = data;
+ chip->kdata.param[QG_ESR_DISCHARGE_SF].valid = true;
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR_DISCHARGE_SF SDAM=%d\n", data);
+ } else if (rc < 0) {
+ pr_err("Failed to read ESR_DISCHARGE_SF rc=%d\n", rc);
+ }
+}
+
+static void qg_store_esr_params(struct qpnp_qg *chip)
+{
+ unsigned int esr;
+
+ if (chip->udata.param[QG_ESR_CHARGE_DELTA].valid) {
+ esr = chip->udata.param[QG_ESR_CHARGE_DELTA].data;
+ qg_sdam_write(SDAM_ESR_CHARGE_DELTA, esr);
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "SDAM store ESR_CHARGE_DELTA=%d\n", esr);
+ }
+
+ if (chip->udata.param[QG_ESR_DISCHARGE_DELTA].valid) {
+ esr = chip->udata.param[QG_ESR_DISCHARGE_DELTA].data;
+ qg_sdam_write(SDAM_ESR_DISCHARGE_DELTA, esr);
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "SDAM store ESR_DISCHARGE_DELTA=%d\n", esr);
+ }
+
+ if (chip->udata.param[QG_ESR_CHARGE_SF].valid) {
+ esr = chip->udata.param[QG_ESR_CHARGE_SF].data;
+ qg_sdam_write(SDAM_ESR_CHARGE_SF, esr);
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "SDAM store ESR_CHARGE_SF=%d\n", esr);
+ }
+
+ if (chip->udata.param[QG_ESR_DISCHARGE_SF].valid) {
+ esr = chip->udata.param[QG_ESR_DISCHARGE_SF].data;
+ qg_sdam_write(SDAM_ESR_DISCHARGE_SF, esr);
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "SDAM store ESR_DISCHARGE_SF=%d\n", esr);
+ }
+}
+
+#define MAX_ESR_RETRY_COUNT 10
+#define ESR_SD_PERCENT 10
+static int qg_process_esr_data(struct qpnp_qg *chip)
+{
+ int i;
+ int pre_i, post_i, pre_v, post_v, first_pre_i = 0;
+ int diff_v, diff_i, esr_avg = 0, count = 0;
+
+ for (i = 0; i < qg_esr_count; i++) {
+ if (!chip->esr_data[i].valid)
+ continue;
+
+ pre_i = chip->esr_data[i].pre_esr_i;
+ pre_v = chip->esr_data[i].pre_esr_v;
+ post_i = chip->esr_data[i].post_esr_i;
+ post_v = chip->esr_data[i].post_esr_v;
+
+ /*
+ * Check if any of the pre/post readings have changed
+ * signs by comparing it with the first valid
+ * pre_i value.
+ */
+ if (!first_pre_i)
+ first_pre_i = pre_i;
+
+ if ((first_pre_i < 0 && pre_i > 0) ||
+ (first_pre_i > 0 && post_i < 0) ||
+ (first_pre_i < 0 && post_i > 0)) {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR-sign mismatch %d reject all data\n", i);
+ esr_avg = count = 0;
+ break;
+ }
+
+ /* calculate ESR */
+ diff_v = abs(post_v - pre_v);
+ diff_i = abs(post_i - pre_i);
+
+ if (!diff_v || !diff_i ||
+ (diff_i < chip->dt.esr_qual_i_ua) ||
+ (diff_v < chip->dt.esr_qual_v_uv)) {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR (%d) V/I %duA %duV fails qualification\n",
+ i, diff_i, diff_v);
+ chip->esr_data[i].valid = false;
+ continue;
+ }
+
+ chip->esr_data[i].esr =
+ DIV_ROUND_CLOSEST(diff_v * 1000, diff_i);
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR qualified: i=%d pre_i=%d pre_v=%d post_i=%d post_v=%d esr_diff_v=%d esr_diff_i=%d esr=%d\n",
+ i, pre_i, pre_v, post_i, post_v,
+ diff_v, diff_i, chip->esr_data[i].esr);
+
+ esr_avg += chip->esr_data[i].esr;
+ count++;
+ }
+
+ if (!count) {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "No ESR samples qualified, ESR not found\n");
+ chip->esr_avg = 0;
+ return 0;
+ }
+
+ esr_avg /= count;
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR all sample average=%d count=%d apply_SD=%d\n",
+ esr_avg, count, (esr_avg * ESR_SD_PERCENT) / 100);
/*
- * Reject the FIFO read event if there are back-to-back requests
- * This is done to gaurantee that there is always a minimum FIFO
- * data to be processed, ignore this if vbat_low is set.
+ * Reject ESR samples which do not fall in
+ * 10% the standard-deviation
*/
- time_delta = ktime_ms_delta(now, chip->last_user_update_time);
+ count = 0;
+ for (i = 0; i < qg_esr_count; i++) {
+ if (!chip->esr_data[i].valid)
+ continue;
- qg_dbg(chip, QG_DEBUG_FIFO, "time_delta=%lld ms vbat_low=%d\n",
- time_delta, vbat_low);
+ if ((abs(chip->esr_data[i].esr - esr_avg) <=
+ (esr_avg * ESR_SD_PERCENT) / 100)) {
+ /* valid ESR */
+ chip->esr_avg += chip->esr_data[i].esr;
+ count++;
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "Valid ESR after SD (%d) %d mOhm\n",
+ i, chip->esr_data[i].esr);
+ } else {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR (%d) %d falls-out of SD(%d)\n",
+ i, chip->esr_data[i].esr, ESR_SD_PERCENT);
+ }
+ }
- if (time_delta > MIN_FIFO_FULL_TIME_MS || vbat_low || update_smb) {
- rc = qg_master_hold(chip, true);
+ if (count >= QG_MIN_ESR_COUNT) {
+ chip->esr_avg /= count;
+ qg_dbg(chip, QG_DEBUG_ESR, "Average estimated ESR %d mOhm\n",
+ chip->esr_avg);
+ } else {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "Not enough ESR samples, ESR not found\n");
+ chip->esr_avg = 0;
+ }
+
+ return 0;
+}
+
+static int qg_esr_estimate(struct qpnp_qg *chip)
+{
+ int rc, i, ibat;
+ u8 esr_done_count, reg0 = 0, reg1 = 0;
+ bool is_charging = false;
+
+ if (chip->dt.esr_disable)
+ return 0;
+
+ /*
+ * Charge - enable ESR estimation only during fast-charging.
+ * Discharge - enable ESR estimation only if enabled via DT.
+ */
+ if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
+ chip->charge_type != POWER_SUPPLY_CHARGE_TYPE_FAST) {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "Skip ESR, Not in fast-charge (CC)\n");
+ return 0;
+ }
+
+ if (chip->charge_status != POWER_SUPPLY_STATUS_CHARGING &&
+ !chip->dt.esr_discharge_enable)
+ return 0;
+
+ if (chip->batt_soc != INT_MIN && (chip->batt_soc <
+ chip->dt.esr_disable_soc)) {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "Skip ESR, batt-soc below %d\n",
+ chip->dt.esr_disable_soc);
+ return 0;
+ }
+
+ qg_dbg(chip, QG_DEBUG_ESR, "FIFO done count=%d ESR mod count=%d\n",
+ chip->fifo_done_count, qg_esr_mod_count);
+
+ if ((chip->fifo_done_count % qg_esr_mod_count) != 0)
+ return 0;
+
+ if (qg_esr_count > QG_MAX_ESR_COUNT)
+ qg_esr_count = QG_MAX_ESR_COUNT;
+
+ if (qg_esr_count < QG_MIN_ESR_COUNT)
+ qg_esr_count = QG_MIN_ESR_COUNT;
+
+ /* clear all data */
+ chip->esr_avg = 0;
+ memset(&chip->esr_data, 0, sizeof(chip->esr_data));
+
+ rc = qg_master_hold(chip, true);
+ if (rc < 0) {
+ pr_err("Failed to hold master, rc=%d\n", rc);
+ goto done;
+ }
+
+ for (i = 0; i < qg_esr_count; i++) {
+ /* Fire ESR measurement */
+ rc = qg_masked_write(chip,
+ chip->qg_base + QG_ESR_MEAS_TRIG_REG,
+ HW_ESR_MEAS_START_BIT, HW_ESR_MEAS_START_BIT);
if (rc < 0) {
- pr_err("Failed to hold master, rc=%d\n", rc);
- goto done;
+ pr_err("Failed to start ESR rc=%d\n", rc);
+ continue;
}
- rc = qg_process_rt_fifo(chip);
- if (rc < 0) {
- pr_err("Failed to process FIFO real-time, rc=%d\n", rc);
- goto done;
- }
+ esr_done_count = reg0 = reg1 = 0;
+ do {
+ /* delay for ESR processing to complete */
+ msleep(50);
- if (vbat_low) {
- /* change FIFO length */
- rc = qg_update_fifo_length(chip,
- chip->dt.s2_vbat_low_fifo_length);
+ esr_done_count++;
+
+ rc = qg_read(chip,
+ chip->qg_base + QG_STATUS1_REG, ®0, 1);
+ if (rc < 0)
+ continue;
+
+ rc = qg_read(chip,
+ chip->qg_base + QG_STATUS4_REG, ®1, 1);
+ if (rc < 0)
+ continue;
+
+ /* check ESR-done status */
+ if (!(reg1 & ESR_MEAS_IN_PROGRESS_BIT) &&
+ (reg0 & ESR_MEAS_DONE_BIT)) {
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR measurement done %d count %d\n",
+ i, esr_done_count);
+ break;
+ }
+ } while (esr_done_count < MAX_ESR_RETRY_COUNT);
+
+ if (esr_done_count == MAX_ESR_RETRY_COUNT) {
+ pr_err("Failed to get ESR done for %d iteration\n", i);
+ continue;
+ } else {
+ /* found a valid ESR, read pre-post data */
+ rc = qg_read_raw_data(chip, QG_PRE_ESR_V_DATA0_REG,
+ &chip->esr_data[i].pre_esr_v);
if (rc < 0)
goto done;
- qg_dbg(chip, QG_DEBUG_STATUS,
- "FIFO length updated to %d vbat_low=%d\n",
- chip->dt.s2_vbat_low_fifo_length,
- vbat_low);
- }
-
- if (update_smb) {
- rc = qg_masked_write(chip, chip->qg_base +
- QG_MODE_CTL1_REG, PARALLEL_IBAT_SENSE_EN_BIT,
- chip->parallel_enabled ?
- PARALLEL_IBAT_SENSE_EN_BIT : 0);
- if (rc < 0) {
- pr_err("Failed to update SMB_EN, rc=%d\n", rc);
+ rc = qg_read_raw_data(chip, QG_PRE_ESR_I_DATA0_REG,
+ &chip->esr_data[i].pre_esr_i);
+ if (rc < 0)
goto done;
- }
- qg_dbg(chip, QG_DEBUG_STATUS, "Parallel SENSE %d\n",
- chip->parallel_enabled);
+
+ rc = qg_read_raw_data(chip, QG_POST_ESR_V_DATA0_REG,
+ &chip->esr_data[i].post_esr_v);
+ if (rc < 0)
+ goto done;
+
+ rc = qg_read_raw_data(chip, QG_POST_ESR_I_DATA0_REG,
+ &chip->esr_data[i].post_esr_i);
+ if (rc < 0)
+ goto done;
+
+ chip->esr_data[i].pre_esr_v =
+ V_RAW_TO_UV(chip->esr_data[i].pre_esr_v);
+ ibat = sign_extend32(chip->esr_data[i].pre_esr_i, 15);
+ chip->esr_data[i].pre_esr_i = I_RAW_TO_UA(ibat);
+ chip->esr_data[i].post_esr_v =
+ V_RAW_TO_UV(chip->esr_data[i].post_esr_v);
+ ibat = sign_extend32(chip->esr_data[i].post_esr_i, 15);
+ chip->esr_data[i].post_esr_i = I_RAW_TO_UA(ibat);
+
+ chip->esr_data[i].valid = true;
+
+ if ((int)chip->esr_data[i].pre_esr_i < 0)
+ is_charging = true;
+
+ qg_dbg(chip, QG_DEBUG_ESR,
+ "ESR values for %d iteration pre_v=%d pre_i=%d post_v=%d post_i=%d\n",
+ i, chip->esr_data[i].pre_esr_v,
+ (int)chip->esr_data[i].pre_esr_i,
+ chip->esr_data[i].post_esr_v,
+ (int)chip->esr_data[i].post_esr_i);
}
-
- rc = qg_master_hold(chip, false);
- if (rc < 0) {
- pr_err("Failed to release master, rc=%d\n", rc);
- goto done;
- }
- /* FIFOs restarted */
- chip->last_fifo_update_time = ktime_get();
-
- /* signal the read thread */
- chip->data_ready = true;
- wake_up_interruptible(&chip->qg_wait_q);
- chip->last_user_update_time = now;
-
- /* vote to stay awake until userspace reads data */
- vote(chip->awake_votable, FIFO_RT_DONE_VOTER, true, 0);
- } else {
- qg_dbg(chip, QG_DEBUG_FIFO, "FIFO processing too early time_delta=%lld\n",
- time_delta);
+ /* delay before the next ESR measurement */
+ msleep(200);
}
+
+ rc = qg_process_esr_data(chip);
+ if (rc < 0)
+ pr_err("Failed to process ESR data rc=%d\n", rc);
+
+ rc = qg_master_hold(chip, false);
+ if (rc < 0) {
+ pr_err("Failed to release master, rc=%d\n", rc);
+ goto done;
+ }
+
+ if (chip->esr_avg) {
+ chip->kdata.param[QG_ESR].data = chip->esr_avg;
+ chip->kdata.param[QG_ESR].valid = true;
+ qg_dbg(chip, QG_DEBUG_ESR, "ESR_SW=%d during %s\n",
+ chip->esr_avg, is_charging ? "CHARGE" : "DISCHARGE");
+ qg_retrieve_esr_params(chip);
+ chip->esr_actual = chip->esr_avg;
+ }
+
+ return 0;
done:
qg_master_hold(chip, false);
return rc;
@@ -641,6 +1024,15 @@
struct qpnp_qg, udata_work);
int rc;
+ if (chip->udata.param[QG_CC_SOC].valid)
+ chip->cc_soc = chip->udata.param[QG_CC_SOC].data;
+
+ if (chip->udata.param[QG_BATT_SOC].valid)
+ chip->batt_soc = chip->udata.param[QG_BATT_SOC].data;
+
+ if (chip->udata.param[QG_FULL_SOC].valid)
+ chip->full_soc = chip->udata.param[QG_FULL_SOC].data;
+
if (chip->udata.param[QG_SOC].valid) {
qg_dbg(chip, QG_DEBUG_SOC, "udata SOC=%d last SOC=%d\n",
chip->udata.param[QG_SOC].data, chip->catch_up_soc);
@@ -656,7 +1048,7 @@
chip->udata.param[QG_RBAT_MOHM].data;
chip->sdam_data[SDAM_VALID] = 1;
- rc = qg_update_sdam_params(chip);
+ rc = qg_store_soc_params(chip);
if (rc < 0)
pr_err("Failed to update SDAM params, rc=%d\n", rc);
}
@@ -665,18 +1057,25 @@
chip->charge_counter_uah =
chip->udata.param[QG_CHARGE_COUNTER].data;
+ if (chip->udata.param[QG_ESR].valid)
+ chip->esr_last = chip->udata.param[QG_ESR].data;
+
+ if (chip->esr_actual != -EINVAL && chip->udata.param[QG_ESR].valid) {
+ chip->esr_nominal = chip->udata.param[QG_ESR].data;
+ if (chip->qg_psy)
+ power_supply_changed(chip->qg_psy);
+ }
+
+ if (!chip->dt.esr_disable)
+ qg_store_esr_params(chip);
+
+ qg_dbg(chip, QG_DEBUG_STATUS, "udata update: batt_soc=%d cc_soc=%d full_soc=%d qg_esr=%d\n",
+ (chip->batt_soc != INT_MIN) ? chip->batt_soc : -EINVAL,
+ (chip->cc_soc != INT_MIN) ? chip->cc_soc : -EINVAL,
+ chip->full_soc, chip->esr_last);
vote(chip->awake_votable, UDATA_READY_VOTER, false, 0);
}
-static irqreturn_t qg_default_irq_handler(int irq, void *data)
-{
- struct qpnp_qg *chip = data;
-
- qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
-
- return IRQ_HANDLED;
-}
-
#define MAX_FIFO_DELTA_PERCENT 10
static irqreturn_t qg_fifo_update_done_handler(int irq, void *data)
{
@@ -704,6 +1103,9 @@
goto done;
}
+ if (++chip->fifo_done_count == U32_MAX)
+ chip->fifo_done_count = 0;
+
rc = qg_vbat_thresholds_config(chip);
if (rc < 0)
pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc);
@@ -714,6 +1116,12 @@
goto done;
}
+ rc = qg_esr_estimate(chip);
+ if (rc < 0) {
+ pr_err("Failed to estimate ESR, rc=%d\n", rc);
+ goto done;
+ }
+
rc = get_fifo_done_time(chip, false, &hw_delta_ms);
if (rc < 0)
hw_delta_ms = 0;
@@ -753,9 +1161,14 @@
pr_err("Failed to read RT status, rc=%d\n", rc);
goto done;
}
+ /* ignore VBAT low if battery is missing */
+ if ((status & BATTERY_MISSING_INT_RT_STS_BIT) ||
+ chip->battery_missing)
+ goto done;
+
chip->vbat_low = !!(status & VBAT_LOW_INT_RT_STS_BIT);
- rc = process_rt_fifo_data(chip, chip->vbat_low, false);
+ rc = process_rt_fifo_data(chip, true, false);
if (rc < 0)
pr_err("Failed to process RT FIFO data, rc=%d\n", rc);
@@ -769,8 +1182,20 @@
{
struct qpnp_qg *chip = data;
u32 ocv_uv = 0;
+ int rc;
+ u8 status = 0;
qg_dbg(chip, QG_DEBUG_IRQ, "IRQ triggered\n");
+
+ rc = qg_read(chip, chip->qg_base + QG_INT_RT_STS_REG, &status, 1);
+ if (rc < 0)
+ pr_err("Failed to read RT status rc=%d\n", rc);
+
+ /* ignore VBAT empty if battery is missing */
+ if ((status & BATTERY_MISSING_INT_RT_STS_BIT) ||
+ chip->battery_missing)
+ return IRQ_HANDLED;
+
pr_warn("VBATT EMPTY SOC = 0\n");
chip->catch_up_soc = 0;
@@ -781,7 +1206,7 @@
chip->sdam_data[SDAM_OCV_UV] = ocv_uv;
chip->sdam_data[SDAM_VALID] = 1;
- qg_update_sdam_params(chip);
+ qg_store_soc_params(chip);
if (chip->qg_psy)
power_supply_changed(chip->qg_psy);
@@ -831,7 +1256,6 @@
static struct qg_irq_info qg_irqs[] = {
[QG_BATT_MISSING_IRQ] = {
.name = "qg-batt-missing",
- .handler = qg_default_irq_handler,
},
[QG_VBATT_LOW_IRQ] = {
.name = "qg-vbat-low",
@@ -855,11 +1279,9 @@
},
[QG_FSM_STAT_CHG_IRQ] = {
.name = "qg-fsm-state-chg",
- .handler = qg_default_irq_handler,
},
[QG_EVENT_IRQ] = {
.name = "qg-event",
- .handler = qg_default_irq_handler,
},
};
@@ -946,6 +1368,127 @@
return 0;
}
+/* ALG callback functions below */
+
+static int qg_get_learned_capacity(void *data, int64_t *learned_cap_uah)
+{
+ struct qpnp_qg *chip = data;
+ int16_t cc_mah;
+ int rc;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -ENODEV;
+
+ rc = qg_sdam_multibyte_read(QG_SDAM_LEARNED_CAPACITY_OFFSET,
+ (u8 *)&cc_mah, 2);
+ if (rc < 0) {
+ pr_err("Error in reading learned_capacity, rc=%d\n", rc);
+ return rc;
+ }
+ *learned_cap_uah = cc_mah * 1000;
+
+ qg_dbg(chip, QG_DEBUG_ALG_CL, "Retrieved learned capacity %llduah\n",
+ *learned_cap_uah);
+ return 0;
+}
+
+static int qg_store_learned_capacity(void *data, int64_t learned_cap_uah)
+{
+ struct qpnp_qg *chip = data;
+ int16_t cc_mah;
+ int rc;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !learned_cap_uah)
+ return -ENODEV;
+
+ cc_mah = div64_s64(learned_cap_uah, 1000);
+ rc = qg_sdam_multibyte_write(QG_SDAM_LEARNED_CAPACITY_OFFSET,
+ (u8 *)&cc_mah, 2);
+ if (rc < 0) {
+ pr_err("Error in writing learned_capacity, rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_ALG_CL, "Stored learned capacity %llduah\n",
+ learned_cap_uah);
+ return 0;
+}
+
+static int qg_get_cc_soc(void *data, int *cc_soc)
+{
+ struct qpnp_qg *chip = data;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->cc_soc == INT_MIN)
+ return -EINVAL;
+
+ *cc_soc = chip->cc_soc;
+
+ return 0;
+}
+
+static int qg_restore_cycle_count(void *data, u16 *buf, int length)
+{
+ struct qpnp_qg *chip = data;
+ int id, rc = 0;
+ u8 tmp[2];
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -ENODEV;
+
+ if (!buf || length > BUCKET_COUNT)
+ return -EINVAL;
+
+ for (id = 0; id < length; id++) {
+ rc = qg_sdam_multibyte_read(
+ QG_SDAM_CYCLE_COUNT_OFFSET + (id * 2),
+ (u8 *)tmp, 2);
+ if (rc < 0) {
+ pr_err("failed to read bucket %d rc=%d\n", id, rc);
+ return rc;
+ }
+ *buf++ = tmp[0] | tmp[1] << 8;
+ }
+
+ return rc;
+}
+
+static int qg_store_cycle_count(void *data, u16 *buf, int id, int length)
+{
+ struct qpnp_qg *chip = data;
+ int rc = 0;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -ENODEV;
+
+ if (!buf || length > BUCKET_COUNT * 2 || id < 0 ||
+ id > BUCKET_COUNT - 1 ||
+ (((id * 2) + length) > BUCKET_COUNT * 2))
+ return -EINVAL;
+
+ rc = qg_sdam_multibyte_write(
+ QG_SDAM_CYCLE_COUNT_OFFSET + (id * 2),
+ (u8 *)buf, length);
+ if (rc < 0)
+ pr_err("failed to write bucket %d rc=%d\n", id, rc);
+
+ return rc;
+}
+
#define DEFAULT_BATT_TYPE "Unknown Battery"
#define MISSING_BATT_TYPE "Missing Battery"
#define DEBUG_BATT_TYPE "Debug Board"
@@ -968,17 +1511,36 @@
static int qg_get_battery_current(struct qpnp_qg *chip, int *ibat_ua)
{
int rc = 0, last_ibat = 0;
+ u32 fifo_length = 0;
if (chip->battery_missing) {
*ibat_ua = 0;
return 0;
}
- rc = qg_read(chip, chip->qg_base + QG_LAST_ADC_I_DATA0_REG,
- (u8 *)&last_ibat, 2);
- if (rc < 0) {
- pr_err("Failed to read LAST_ADV_I reg, rc=%d\n", rc);
- return rc;
+ if (chip->parallel_enabled) {
+ /* read the last real-time FIFO */
+ rc = get_fifo_length(chip, &fifo_length, true);
+ if (rc < 0) {
+ pr_err("Failed to read RT FIFO length, rc=%d\n", rc);
+ return rc;
+ }
+ fifo_length = (fifo_length == 0) ? 0 : fifo_length - 1;
+ fifo_length *= 2;
+ rc = qg_read(chip, chip->qg_base + QG_I_FIFO0_DATA0_REG +
+ fifo_length, (u8 *)&last_ibat, 2);
+ if (rc < 0) {
+ pr_err("Failed to read FIFO_I_%d reg, rc=%d\n",
+ fifo_length / 2, rc);
+ return rc;
+ }
+ } else {
+ rc = qg_read(chip, chip->qg_base + QG_LAST_ADC_I_DATA0_REG,
+ (u8 *)&last_ibat, 2);
+ if (rc < 0) {
+ pr_err("Failed to read LAST_ADV_I reg, rc=%d\n", rc);
+ return rc;
+ }
}
last_ibat = sign_extend32(last_ibat, 15);
@@ -1042,10 +1604,128 @@
return 0;
}
+static int qg_get_ttf_param(void *data, enum ttf_param param, int *val)
+{
+ union power_supply_propval prop = {0, };
+ struct qpnp_qg *chip = data;
+ int rc = 0;
+ int64_t temp = 0;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -ENODEV;
+
+ switch (param) {
+ case TTF_MSOC:
+ rc = qg_get_battery_capacity(chip, val);
+ break;
+ case TTF_VBAT:
+ rc = qg_get_battery_voltage(chip, val);
+ break;
+ case TTF_IBAT:
+ rc = qg_get_battery_current(chip, val);
+ break;
+ case TTF_FCC:
+ if (chip->qg_psy) {
+ rc = power_supply_get_property(chip->qg_psy,
+ POWER_SUPPLY_PROP_CHARGE_FULL, &prop);
+ if (rc >= 0) {
+ temp = div64_u64(prop.intval, 1000);
+ *val = div64_u64(chip->full_soc * temp,
+ QG_SOC_FULL);
+ }
+ }
+ break;
+ case TTF_MODE:
+ *val = TTF_MODE_NORMAL;
+ break;
+ case TTF_ITERM:
+ if (chip->chg_iterm_ma == INT_MIN)
+ *val = 0;
+ else
+ *val = chip->chg_iterm_ma;
+ break;
+ case TTF_RBATT:
+ rc = qg_sdam_read(SDAM_RBAT_MOHM, val);
+ if (!rc)
+ *val *= 1000;
+ break;
+ case TTF_VFLOAT:
+ *val = chip->bp.float_volt_uv;
+ break;
+ case TTF_CHG_TYPE:
+ *val = chip->charge_type;
+ break;
+ case TTF_CHG_STATUS:
+ *val = chip->charge_status;
+ break;
+ default:
+ pr_err("Unsupported property %d\n", param);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int qg_ttf_awake_voter(void *data, bool val)
+{
+ struct qpnp_qg *chip = data;
+
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->battery_missing || !chip->profile_loaded)
+ return -ENODEV;
+
+ vote(chip->awake_votable, TTF_AWAKE_VOTER, val, 0);
+
+ return 0;
+}
+
static int qg_psy_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *pval)
{
+ struct qpnp_qg *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ if (chip->dt.cl_disable) {
+ pr_warn("Capacity learning disabled!\n");
+ return 0;
+ }
+ if (chip->cl->active) {
+ pr_warn("Capacity learning active!\n");
+ return 0;
+ }
+ if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) {
+ pr_err("charge_full is out of bounds\n");
+ return -EINVAL;
+ }
+ mutex_lock(&chip->cl->lock);
+ rc = qg_store_learned_capacity(chip, pval->intval);
+ if (!rc)
+ chip->cl->learned_cap_uah = pval->intval;
+ mutex_unlock(&chip->cl->lock);
+ break;
+ case POWER_SUPPLY_PROP_SOH:
+ chip->soh = pval->intval;
+ qg_dbg(chip, QG_DEBUG_STATUS, "SOH update: SOH=%d esr_actual=%d esr_nominal=%d\n",
+ chip->soh, chip->esr_actual, chip->esr_nominal);
+ break;
+ case POWER_SUPPLY_PROP_ESR_ACTUAL:
+ chip->esr_actual = pval->intval;
+ break;
+ case POWER_SUPPLY_PROP_ESR_NOMINAL:
+ chip->esr_nominal = pval->intval;
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -1055,6 +1735,7 @@
{
struct qpnp_qg *chip = power_supply_get_drvdata(psy);
int rc = 0;
+ int64_t temp = 0;
pval->intval = 0;
@@ -1085,6 +1766,12 @@
if (!rc)
pval->intval *= 1000;
break;
+ case POWER_SUPPLY_PROP_RESISTANCE_NOW:
+ pval->intval = chip->esr_last;
+ break;
+ case POWER_SUPPLY_PROP_SOC_REPORTING_READY:
+ pval->intval = chip->soc_reporting_ready;
+ break;
case POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE:
pval->intval = chip->dt.rbat_conn_mohm;
break;
@@ -1106,6 +1793,44 @@
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
pval->intval = chip->charge_counter_uah;
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ if (!chip->dt.cl_disable && chip->dt.cl_feedback_on)
+ rc = qg_get_learned_capacity(chip, &temp);
+ else
+ rc = qg_get_nominal_capacity((int *)&temp, 250, true);
+ if (!rc)
+ pval->intval = (int)temp;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ rc = qg_get_nominal_capacity((int *)&temp, 250, true);
+ if (!rc)
+ pval->intval = (int)temp;
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNTS:
+ rc = get_cycle_counts(chip->counter, &pval->strval);
+ if (rc < 0)
+ pval->strval = NULL;
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ rc = get_cycle_count(chip->counter, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+ rc = ttf_get_time_to_full(chip->ttf, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+ rc = ttf_get_time_to_empty(chip->ttf, &pval->intval);
+ break;
+ case POWER_SUPPLY_PROP_ESR_ACTUAL:
+ pval->intval = (chip->esr_actual == -EINVAL) ? -EINVAL :
+ (chip->esr_actual * 1000);
+ break;
+ case POWER_SUPPLY_PROP_ESR_NOMINAL:
+ pval->intval = (chip->esr_nominal == -EINVAL) ? -EINVAL :
+ (chip->esr_nominal * 1000);
+ break;
+ case POWER_SUPPLY_PROP_SOH:
+ pval->intval = chip->soh;
+ break;
default:
pr_debug("Unsupported property %d\n", psp);
break;
@@ -1117,6 +1842,15 @@
static int qg_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_ESR_ACTUAL:
+ case POWER_SUPPLY_PROP_ESR_NOMINAL:
+ case POWER_SUPPLY_PROP_SOH:
+ return 1;
+ default:
+ break;
+ }
return 0;
}
@@ -1129,6 +1863,8 @@
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_RESISTANCE,
POWER_SUPPLY_PROP_RESISTANCE_ID,
+ POWER_SUPPLY_PROP_RESISTANCE_NOW,
+ POWER_SUPPLY_PROP_SOC_REPORTING_READY,
POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE,
POWER_SUPPLY_PROP_DEBUG_BATTERY,
POWER_SUPPLY_PROP_BATTERY_TYPE,
@@ -1136,6 +1872,15 @@
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_BATT_FULL_CURRENT,
POWER_SUPPLY_PROP_BATT_PROFILE_VERSION,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_CYCLE_COUNTS,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_ESR_ACTUAL,
+ POWER_SUPPLY_PROP_ESR_NOMINAL,
+ POWER_SUPPLY_PROP_SOH,
};
static const struct power_supply_desc qg_psy_desc = {
@@ -1222,6 +1967,7 @@
{
int rc;
bool parallel_enabled = is_parallel_enabled(chip);
+ bool update_smb = false;
if (parallel_enabled == chip->parallel_enabled)
return 0;
@@ -1232,7 +1978,14 @@
mutex_lock(&chip->data_lock);
- rc = process_rt_fifo_data(chip, false, true);
+ /*
+ * Parallel charger uses the same external sense, hence do not
+ * enable SMB sensing if PMI632 is configured for external sense.
+ */
+ if (!chip->dt.qg_ext_sense)
+ update_smb = true;
+
+ rc = process_rt_fifo_data(chip, false, update_smb);
if (rc < 0)
pr_err("Failed to process RT FIFO data, rc=%d\n", rc);
@@ -1257,18 +2010,133 @@
return 0;
}
+static int qg_handle_battery_removal(struct qpnp_qg *chip)
+{
+ int rc, length = QG_SDAM_MAX_OFFSET - QG_SDAM_VALID_OFFSET;
+ u8 *data;
+
+ /* clear SDAM */
+ data = kcalloc(length, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ rc = qg_sdam_multibyte_write(QG_SDAM_VALID_OFFSET, data, length);
+ if (rc < 0)
+ pr_err("Failed to clear SDAM rc=%d\n", rc);
+
+ return rc;
+}
+
+#define MAX_QG_OK_RETRIES 20
+static int qg_handle_battery_insertion(struct qpnp_qg *chip)
+{
+ int rc, count = 0;
+ u32 ocv_uv = 0, ocv_raw = 0;
+ u8 reg = 0;
+
+ do {
+ rc = qg_read(chip, chip->qg_base + QG_STATUS1_REG, ®, 1);
+ if (rc < 0) {
+ pr_err("Failed to read STATUS1_REG rc=%d\n", rc);
+ return rc;
+ }
+
+ if (reg & QG_OK_BIT)
+ break;
+
+ msleep(200);
+ count++;
+ } while (count < MAX_QG_OK_RETRIES);
+
+ if (count == MAX_QG_OK_RETRIES) {
+ qg_dbg(chip, QG_DEBUG_STATUS, "QG_OK not set!\n");
+ return 0;
+ }
+
+ /* read S7 PON OCV */
+ rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw, S7_PON_OCV);
+ if (rc < 0) {
+ pr_err("Failed to read PON OCV rc=%d\n", rc);
+ return rc;
+ }
+
+ qg_dbg(chip, QG_DEBUG_STATUS,
+ "S7_OCV on battery insertion = %duV\n", ocv_uv);
+
+ chip->kdata.param[QG_GOOD_OCV_UV].data = ocv_uv;
+ chip->kdata.param[QG_GOOD_OCV_UV].valid = true;
+ /* clear all the userspace data */
+ chip->kdata.param[QG_CLEAR_LEARNT_DATA].data = 1;
+ chip->kdata.param[QG_CLEAR_LEARNT_DATA].valid = true;
+
+ vote(chip->awake_votable, GOOD_OCV_VOTER, true, 0);
+ /* signal the read thread */
+ chip->data_ready = true;
+ wake_up_interruptible(&chip->qg_wait_q);
+
+ return 0;
+}
+
+static int qg_battery_status_update(struct qpnp_qg *chip)
+{
+ int rc;
+ union power_supply_propval prop = {0, };
+
+ if (!is_batt_available(chip))
+ return 0;
+
+ mutex_lock(&chip->data_lock);
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_PRESENT, &prop);
+ if (rc < 0) {
+ pr_err("Failed to get battery-present, rc=%d\n", rc);
+ goto done;
+ }
+
+ if (chip->battery_missing && prop.intval) {
+ pr_warn("Battery inserted!\n");
+ rc = qg_handle_battery_insertion(chip);
+ if (rc < 0)
+ pr_err("Failed in battery-insertion rc=%d\n", rc);
+ } else if (!chip->battery_missing && !prop.intval) {
+ pr_warn("Battery removed!\n");
+ rc = qg_handle_battery_removal(chip);
+ if (rc < 0)
+ pr_err("Failed in battery-removal rc=%d\n", rc);
+ }
+
+ chip->battery_missing = !prop.intval;
+
+done:
+ mutex_unlock(&chip->data_lock);
+ return rc;
+}
+
+
static void qg_status_change_work(struct work_struct *work)
{
struct qpnp_qg *chip = container_of(work,
struct qpnp_qg, qg_status_change_work);
union power_supply_propval prop = {0, };
- int rc = 0;
+ int rc = 0, batt_temp = 0, batt_soc_32b = 0;
if (!is_batt_available(chip)) {
pr_debug("batt-psy not available\n");
goto out;
}
+ rc = qg_battery_status_update(chip);
+ if (rc < 0)
+ pr_err("Failed to process battery status update rc=%d\n", rc);
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
+ if (rc < 0)
+ pr_err("Failed to get charge-type, rc=%d\n", rc);
+ else
+ chip->charge_type = prop.intval;
+
rc = power_supply_get_property(chip->batt_psy,
POWER_SUPPLY_PROP_STATUS, &prop);
if (rc < 0)
@@ -1294,9 +2162,29 @@
if (rc < 0)
pr_err("Failed to update usb status, rc=%d\n", rc);
+ cycle_count_update(chip->counter,
+ DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+ chip->charge_status, chip->charge_done,
+ chip->usb_present);
+
+ if (!chip->dt.cl_disable) {
+ rc = qg_get_battery_temp(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Failed to read BATT_TEMP at PON rc=%d\n", rc);
+ } else {
+ batt_soc_32b = div64_u64(
+ chip->batt_soc * BATT_SOC_32BIT,
+ QG_SOC_FULL);
+ cap_learning_update(chip->cl, batt_temp, batt_soc_32b,
+ chip->charge_status, chip->charge_done,
+ chip->usb_present, false);
+ }
+ }
rc = qg_charge_full_update(chip);
if (rc < 0)
pr_err("Failed in charge_full_update, rc=%d\n", rc);
+
+ ttf_update(chip->ttf, chip->usb_present);
out:
pm_relax(chip->dev);
}
@@ -1661,12 +2549,21 @@
return 0;
}
+
+static struct ocv_all ocv[] = {
+ [S7_PON_OCV] = { 0, 0, "S7_PON_OCV"},
+ [S3_GOOD_OCV] = { 0, 0, "S3_GOOD_OCV"},
+ [S3_LAST_OCV] = { 0, 0, "S3_LAST_OCV"},
+ [SDAM_PON_OCV] = { 0, 0, "SDAM_PON_OCV"},
+};
+
+#define S7_ERROR_MARGIN_UV 20000
static int qg_determine_pon_soc(struct qpnp_qg *chip)
{
- int rc = 0, batt_temp = 0;
+ int rc = 0, batt_temp = 0, i;
bool use_pon_ocv = true;
unsigned long rtc_sec = 0;
- u32 ocv_uv = 0, ocv_raw = 0, soc = 0, shutdown[SDAM_MAX] = {0};
+ u32 ocv_uv = 0, soc = 0, shutdown[SDAM_MAX] = {0};
char ocv_type[20] = "NONE";
if (!chip->profile_loaded) {
@@ -1715,33 +2612,47 @@
goto done;
}
- /*
- * Read S3_LAST_OCV, if S3_LAST_OCV is invalid,
- * read the SDAM_PON_OCV
- * if SDAM is not-set, use S7_PON_OCV.
- */
- strlcpy(ocv_type, "S3_LAST_SOC", 20);
- rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw, S3_LAST_OCV);
- if (rc < 0)
- goto done;
-
- if (ocv_raw == FIFO_V_RESET_VAL) {
- /* S3_LAST_OCV is invalid */
- strlcpy(ocv_type, "SDAM_PON_SOC", 20);
- rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw, SDAM_PON_OCV);
+ /* read all OCVs */
+ for (i = S7_PON_OCV; i < PON_OCV_MAX; i++) {
+ rc = qg_read_ocv(chip, &ocv[i].ocv_uv,
+ &ocv[i].ocv_raw, i);
if (rc < 0)
- goto done;
+ pr_err("Failed to read %s OCV rc=%d\n",
+ ocv[i].ocv_type, rc);
+ else
+ qg_dbg(chip, QG_DEBUG_PON, "%s OCV=%d\n",
+ ocv[i].ocv_type, ocv[i].ocv_uv);
+ }
- if (!ocv_uv) {
- /* SDAM_PON_OCV is not set */
+ if (ocv[S3_LAST_OCV].ocv_raw == FIFO_V_RESET_VAL) {
+ if (!ocv[SDAM_PON_OCV].ocv_uv) {
strlcpy(ocv_type, "S7_PON_SOC", 20);
- rc = qg_read_ocv(chip, &ocv_uv, &ocv_raw,
- S7_PON_OCV);
- if (rc < 0)
- goto done;
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
+ } else if (ocv[SDAM_PON_OCV].ocv_uv <=
+ ocv[S7_PON_OCV].ocv_uv) {
+ strlcpy(ocv_type, "S7_PON_SOC", 20);
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
+ } else if (!shutdown[SDAM_VALID] &&
+ ((ocv[SDAM_PON_OCV].ocv_uv -
+ ocv[S7_PON_OCV].ocv_uv) >
+ S7_ERROR_MARGIN_UV)) {
+ strlcpy(ocv_type, "S7_PON_SOC", 20);
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
+ } else {
+ strlcpy(ocv_type, "SDAM_PON_SOC", 20);
+ ocv_uv = ocv[SDAM_PON_OCV].ocv_uv;
+ }
+ } else {
+ if (ocv[S3_LAST_OCV].ocv_uv >= ocv[S7_PON_OCV].ocv_uv) {
+ strlcpy(ocv_type, "S3_LAST_SOC", 20);
+ ocv_uv = ocv[S3_LAST_OCV].ocv_uv;
+ } else {
+ strlcpy(ocv_type, "S7_PON_SOC", 20);
+ ocv_uv = ocv[S7_PON_OCV].ocv_uv;
}
}
+ ocv_uv = CAP(QG_MIN_OCV_UV, QG_MAX_OCV_UV, ocv_uv);
rc = lookup_soc_ocv(&soc, ocv_uv, batt_temp, false);
if (rc < 0) {
pr_err("Failed to lookup SOC@PON rc=%d\n", rc);
@@ -1767,13 +2678,16 @@
if (rc < 0)
pr_err("Failed to update MSOC register rc=%d\n", rc);
- rc = qg_update_sdam_params(chip);
+ rc = qg_store_soc_params(chip);
if (rc < 0)
pr_err("Failed to update sdam params rc=%d\n", rc);
pr_info("using %s @ PON ocv_uv=%duV soc=%d\n",
ocv_type, ocv_uv, chip->msoc);
+ /* SOC reporting is now ready */
+ chip->soc_reporting_ready = 1;
+
return 0;
}
@@ -1796,6 +2710,7 @@
return 0;
}
+#define ADC_CONV_DLY_512MS 0xA
static int qg_hw_init(struct qpnp_qg *chip)
{
int rc, temp;
@@ -1976,6 +2891,22 @@
return rc;
}
+ /* disable S5 */
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_S5_OCV_VALIDATE_MEAS_CTL1_REG,
+ ALLOW_S5_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to disable S5 rc=%d\n", rc);
+
+ /* change PON OCV time to 512ms */
+ rc = qg_masked_write(chip, chip->qg_base +
+ QG_S7_PON_OCV_MEAS_CTL1_REG,
+ ADC_CONV_DLY_MASK,
+ ADC_CONV_DLY_512MS);
+ if (rc < 0)
+ pr_err("Failed to reconfigure S7-delay rc=%d\n", rc);
+
+
return 0;
}
@@ -1995,6 +2926,10 @@
QG_INIT_STATE_IRQ_DISABLE, true, 0);
}
+ /* restore ESR data */
+ if (!chip->dt.esr_disable)
+ qg_retrieve_esr_params(chip);
+
return 0;
}
@@ -2069,6 +3004,89 @@
return 0;
}
+#define QG_TTF_ITERM_DELTA_MA 1
+static int qg_alg_init(struct qpnp_qg *chip)
+{
+ struct cycle_counter *counter;
+ struct cap_learning *cl;
+ struct ttf *ttf;
+ struct device_node *node = chip->dev->of_node;
+ int rc;
+
+ counter = devm_kzalloc(chip->dev, sizeof(*counter), GFP_KERNEL);
+ if (!counter)
+ return -ENOMEM;
+
+ counter->restore_count = qg_restore_cycle_count;
+ counter->store_count = qg_store_cycle_count;
+ counter->data = chip;
+
+ rc = cycle_count_init(counter);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing cycle counter, rc:%d\n",
+ rc);
+ counter->data = NULL;
+ devm_kfree(chip->dev, counter);
+ return rc;
+ }
+
+ chip->counter = counter;
+
+ ttf = devm_kzalloc(chip->dev, sizeof(*ttf), GFP_KERNEL);
+ if (!ttf)
+ return -ENOMEM;
+
+ ttf->get_ttf_param = qg_get_ttf_param;
+ ttf->awake_voter = qg_ttf_awake_voter;
+ ttf->iterm_delta = QG_TTF_ITERM_DELTA_MA;
+ ttf->data = chip;
+
+ rc = ttf_tte_init(ttf);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing ttf, rc:%d\n",
+ rc);
+ ttf->data = NULL;
+ counter->data = NULL;
+ devm_kfree(chip->dev, ttf);
+ devm_kfree(chip->dev, counter);
+ return rc;
+ }
+
+ chip->ttf = ttf;
+
+ chip->dt.cl_disable = of_property_read_bool(node,
+ "qcom,cl-disable");
+
+ /*Return if capacity learning is disabled*/
+ if (chip->dt.cl_disable)
+ return 0;
+
+ cl = devm_kzalloc(chip->dev, sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return -ENOMEM;
+
+ cl->cc_soc_max = QG_SOC_FULL;
+ cl->get_cc_soc = qg_get_cc_soc;
+ cl->get_learned_capacity = qg_get_learned_capacity;
+ cl->store_learned_capacity = qg_store_learned_capacity;
+ cl->data = chip;
+
+ rc = cap_learning_init(cl);
+ if (rc < 0) {
+ dev_err(chip->dev, "Error in initializing capacity learning, rc:%d\n",
+ rc);
+ counter->data = NULL;
+ cl->data = NULL;
+ devm_kfree(chip->dev, counter);
+ devm_kfree(chip->dev, ttf);
+ devm_kfree(chip->dev, cl);
+ return rc;
+ }
+
+ chip->cl = cl;
+ return 0;
+}
+
#define DEFAULT_VBATT_EMPTY_MV 3200
#define DEFAULT_VBATT_EMPTY_COLD_MV 3000
#define DEFAULT_VBATT_CUTOFF_MV 3400
@@ -2082,6 +3100,17 @@
#define DEFAULT_DELTA_SOC 1
#define DEFAULT_SHUTDOWN_SOC_SECS 360
#define DEFAULT_COLD_TEMP_THRESHOLD 0
+#define DEFAULT_CL_MIN_START_SOC 10
+#define DEFAULT_CL_MAX_START_SOC 15
+#define DEFAULT_CL_MIN_TEMP_DECIDEGC 150
+#define DEFAULT_CL_MAX_TEMP_DECIDEGC 500
+#define DEFAULT_CL_MAX_INC_DECIPERC 10
+#define DEFAULT_CL_MAX_DEC_DECIPERC 20
+#define DEFAULT_CL_MIN_LIM_DECIPERC 500
+#define DEFAULT_CL_MAX_LIM_DECIPERC 100
+#define DEFAULT_ESR_QUAL_CURRENT_UA 130000
+#define DEFAULT_ESR_QUAL_VBAT_UV 7000
+#define DEFAULT_ESR_DISABLE_SOC 1000
static int qg_parse_dt(struct qpnp_qg *chip)
{
int rc = 0;
@@ -2275,9 +3304,95 @@
else
chip->dt.rbat_conn_mohm = temp;
- qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d\n",
+ /* esr */
+ chip->dt.esr_disable = of_property_read_bool(node,
+ "qcom,esr-disable");
+
+ chip->dt.esr_discharge_enable = of_property_read_bool(node,
+ "qcom,esr-discharge-enable");
+
+ rc = of_property_read_u32(node, "qcom,esr-qual-current-ua", &temp);
+ if (rc < 0)
+ chip->dt.esr_qual_i_ua = DEFAULT_ESR_QUAL_CURRENT_UA;
+ else
+ chip->dt.esr_qual_i_ua = temp;
+
+ rc = of_property_read_u32(node, "qcom,esr-qual-vbatt-uv", &temp);
+ if (rc < 0)
+ chip->dt.esr_qual_v_uv = DEFAULT_ESR_QUAL_VBAT_UV;
+ else
+ chip->dt.esr_qual_v_uv = temp;
+
+ rc = of_property_read_u32(node, "qcom,esr-disable-soc", &temp);
+ if (rc < 0)
+ chip->dt.esr_disable_soc = DEFAULT_ESR_DISABLE_SOC;
+ else
+ chip->dt.esr_disable_soc = temp * 100;
+
+ chip->dt.qg_ext_sense = of_property_read_bool(node, "qcom,qg-ext-sns");
+
+ /* Capacity learning params*/
+ if (!chip->dt.cl_disable) {
+ chip->dt.cl_feedback_on = of_property_read_bool(node,
+ "qcom,cl-feedback-on");
+
+ rc = of_property_read_u32(node, "qcom,cl-min-start-soc", &temp);
+ if (rc < 0)
+ chip->cl->dt.min_start_soc = DEFAULT_CL_MIN_START_SOC;
+ else
+ chip->cl->dt.min_start_soc = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-start-soc", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_start_soc = DEFAULT_CL_MAX_START_SOC;
+ else
+ chip->cl->dt.max_start_soc = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp);
+ if (rc < 0)
+ chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC;
+ else
+ chip->cl->dt.min_temp = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC;
+ else
+ chip->cl->dt.max_temp = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC;
+ else
+ chip->cl->dt.max_cap_inc = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC;
+ else
+ chip->cl->dt.max_cap_dec = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp);
+ if (rc < 0)
+ chip->cl->dt.min_cap_limit =
+ DEFAULT_CL_MIN_LIM_DECIPERC;
+ else
+ chip->cl->dt.min_cap_limit = temp;
+
+ rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp);
+ if (rc < 0)
+ chip->cl->dt.max_cap_limit =
+ DEFAULT_CL_MAX_LIM_DECIPERC;
+ else
+ chip->cl->dt.max_cap_limit = temp;
+
+ qg_dbg(chip, QG_DEBUG_PON, "DT: cl_min_start_soc=%d cl_max_start_soc=%d cl_min_temp=%d cl_max_temp=%d\n",
+ chip->cl->dt.min_start_soc, chip->cl->dt.max_start_soc,
+ chip->cl->dt.min_temp, chip->cl->dt.max_temp);
+ }
+ qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d ext-sns=%d\n",
chip->dt.vbatt_empty_mv, chip->dt.vbatt_low_mv,
- chip->dt.delta_soc);
+ chip->dt.delta_soc, chip->dt.qg_ext_sense);
return 0;
}
@@ -2292,6 +3407,7 @@
if (!chip->profile_loaded)
return 0;
+ cancel_delayed_work_sync(&chip->ttf->ttf_work);
/* disable GOOD_OCV IRQ in sleep */
vote(chip->good_ocv_irq_disable_votable,
QG_INIT_STATE_IRQ_DISABLE, true, 0);
@@ -2392,7 +3508,9 @@
chip->kdata.param[QG_GOOD_OCV_UV].data = ocv_uv;
chip->kdata.param[QG_GOOD_OCV_UV].valid = true;
/* Clear suspend data as there has been a GOOD OCV */
+ memset(&chip->kdata, 0, sizeof(chip->kdata));
chip->suspend_data = false;
+
qg_dbg(chip, QG_DEBUG_PM, "GOOD OCV @ resume good_ocv=%d uV\n",
ocv_uv);
}
@@ -2422,6 +3540,8 @@
chip->suspend_data = false;
}
+ schedule_delayed_work(&chip->ttf->ttf_work, 0);
+
return rc;
}
@@ -2464,7 +3584,7 @@
static int qpnp_qg_probe(struct platform_device *pdev)
{
- int rc = 0, soc = 0;
+ int rc = 0, soc = 0, nom_cap_uah;
struct qpnp_qg *chip;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
@@ -2497,6 +3617,19 @@
mutex_init(&chip->data_lock);
init_waitqueue_head(&chip->qg_wait_q);
chip->maint_soc = -EINVAL;
+ chip->batt_soc = INT_MIN;
+ chip->cc_soc = INT_MIN;
+ chip->full_soc = QG_SOC_FULL;
+ chip->chg_iterm_ma = INT_MIN;
+ chip->soh = -EINVAL;
+ chip->esr_actual = -EINVAL;
+ chip->esr_nominal = -EINVAL;
+
+ rc = qg_alg_init(chip);
+ if (rc < 0) {
+ pr_err("Error in alg_init, rc:%d\n", rc);
+ return rc;
+ }
rc = qg_parse_dt(chip);
if (rc < 0) {
@@ -2534,6 +3667,31 @@
return rc;
}
+ if (chip->profile_loaded) {
+ if (!chip->dt.cl_disable) {
+ /*
+ * Use FCC @ 25 C and charge-profile for
+ * Nominal Capacity
+ */
+ rc = qg_get_nominal_capacity(&nom_cap_uah, 250, true);
+ if (!rc) {
+ rc = cap_learning_post_profile_init(chip->cl,
+ nom_cap_uah);
+ if (rc < 0) {
+ pr_err("Error in cap_learning_post_profile_init rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+ rc = restore_cycle_count(chip->counter);
+ if (rc < 0) {
+ pr_err("Error in restoring cycle_count, rc=%d\n", rc);
+ return rc;
+ }
+ schedule_delayed_work(&chip->ttf->ttf_work, 10000);
+ }
+
rc = qg_determine_pon_soc(chip);
if (rc < 0) {
pr_err("Failed to determine initial state, rc=%d\n", rc);
@@ -2627,6 +3785,22 @@
return 0;
}
+static void qpnp_qg_shutdown(struct platform_device *pdev)
+{
+ struct qpnp_qg *chip = platform_get_drvdata(pdev);
+
+ if (!is_usb_present(chip) || !chip->profile_loaded)
+ return;
+ /*
+ * Charging status doesn't matter when the device shuts down and we
+ * have to treat this as charge done. Hence pass charge_done as true.
+ */
+ cycle_count_update(chip->counter,
+ DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+ POWER_SUPPLY_STATUS_NOT_CHARGING,
+ true, chip->usb_present);
+}
+
static const struct of_device_id match_table[] = {
{ .compatible = "qcom,qpnp-qg", },
{ },
@@ -2641,6 +3815,7 @@
},
.probe = qpnp_qg_probe,
.remove = qpnp_qg_remove,
+ .shutdown = qpnp_qg_shutdown,
};
module_platform_driver(qpnp_qg_driver);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 5df241f..56a09c6 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -331,6 +331,9 @@
if (rc < 0)
chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS;
+ chg->disable_stat_sw_override = of_property_read_bool(node,
+ "qcom,disable-stat-sw-override");
+
return 0;
}
@@ -342,6 +345,7 @@
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_PD_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_MAX,
@@ -398,6 +402,9 @@
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_prop_usb_voltage_max(chg, val);
break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ rc = smblib_get_prop_usb_voltage_max_design(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
rc = smblib_get_prop_usb_voltage_now(chg, val);
break;
@@ -991,6 +998,8 @@
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -1048,9 +1057,6 @@
case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
val->intval = chg->sw_jeita_enabled;
break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- rc = smblib_get_prop_batt_voltage_now(chg, val);
- break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = get_client_vote(chg->fv_votable,
BATT_PROFILE_VOTER);
@@ -1062,9 +1068,6 @@
val->intval = get_client_vote_locked(chg->fv_votable,
QNOVO_VOTER);
break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- rc = smblib_get_prop_batt_current_now(chg, val);
- break;
case POWER_SUPPLY_PROP_CURRENT_QNOVO:
val->intval = get_client_vote_locked(chg->fcc_votable,
QNOVO_VOTER);
@@ -1073,9 +1076,6 @@
val->intval = get_client_vote(chg->fcc_votable,
BATT_PROFILE_VOTER);
break;
- case POWER_SUPPLY_PROP_TEMP:
- rc = smblib_get_prop_batt_temp(chg, val);
- break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
@@ -1103,7 +1103,12 @@
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
- rc = smblib_get_prop_batt_charge_counter(chg, val);
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_TEMP:
+ rc = smblib_get_prop_from_bms(chg, psp, val);
break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
@@ -1837,6 +1842,16 @@
}
}
+ if (chg->disable_stat_sw_override) {
+ rc = smblib_masked_write(chg, STAT_CFG_REG,
+ STAT_SW_OVERRIDE_CFG_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't disable STAT SW override rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
return rc;
}
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 7a3a4dc..e095eb0 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -25,6 +25,7 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/pmic-voter.h>
+#include <linux/qpnp/qpnp-adc.h>
#include "smb5-reg.h"
#include "smb5-lib.h"
#include "schgm-flash.h"
@@ -51,6 +52,13 @@
.max_u = 3000000,
.step_u = 50000,
},
+ .icl_max_stat = {
+ .name = "dcdc icl max status",
+ .reg = ICL_MAX_STATUS_REG,
+ .min_u = 0,
+ .max_u = 3000000,
+ .step_u = 50000,
+ },
.icl_stat = {
.name = "input current limit status",
.reg = AICL_ICL_STATUS_REG,
@@ -89,7 +97,7 @@
},
};
-static struct smb_params smb5_pmi855_params = {
+static struct smb_params smb5_pm855b_params = {
.fcc = {
.name = "fast charge current",
.reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG,
@@ -111,8 +119,15 @@
.max_u = 5000000,
.step_u = 50000,
},
+ .icl_max_stat = {
+ .name = "dcdc icl max status",
+ .reg = ICL_MAX_STATUS_REG,
+ .min_u = 0,
+ .max_u = 5000000,
+ .step_u = 50000,
+ },
.icl_stat = {
- .name = "input current limit status",
+ .name = "aicl icl status",
.reg = AICL_ICL_STATUS_REG,
.min_u = 0,
.max_u = 5000000,
@@ -176,11 +191,21 @@
debug_mask, __debug_mask, int, 0600
);
+static int __pd_disabled;
+module_param_named(
+ pd_disabled, __pd_disabled, int, 0600
+);
+
static int __weak_chg_icl_ua = 500000;
module_param_named(
weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600
);
+enum {
+ USBIN_CURRENT,
+ USBIN_VOLTAGE,
+};
+
#define PMI632_MAX_ICL_UA 3000000
static int smb5_chg_config_init(struct smb5 *chip)
{
@@ -210,7 +235,7 @@
switch (pmic_rev_id->pmic_subtype) {
case PM855B_SUBTYPE:
chip->chg.smb_version = PM855B_SUBTYPE;
- chg->param = smb5_pmi855_params;
+ chg->param = smb5_pm855b_params;
chg->name = "pm855b_charger";
break;
case PMI632_SUBTYPE:
@@ -218,6 +243,8 @@
chg->param = smb5_pmi632_params;
chg->use_extcon = true;
chg->name = "pmi632_charger";
+ /* PMI632 does not support PD */
+ chg->pd_not_supported = true;
chg->hw_max_icl_ua =
(chip->dt.usb_icl_ua > 0) ? chip->dt.usb_icl_ua
: PMI632_MAX_ICL_UA;
@@ -240,6 +267,7 @@
}
#define MICRO_1P5A 1500000
+#define MICRO_1PA 1000000
#define MICRO_P1A 100000
#define OTG_DEFAULT_DEGLITCH_TIME_MS 50
#define MIN_WD_BARK_TIME 16
@@ -290,7 +318,8 @@
rc = of_property_read_u32(node,
"qcom,otg-cl-ua", &chg->otg_cl_ua);
if (rc < 0)
- chg->otg_cl_ua = MICRO_1P5A;
+ chg->otg_cl_ua = (chip->chg.smb_version == PMI632_SUBTYPE) ?
+ MICRO_1PA : MICRO_1P5A;
if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) {
chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len,
@@ -361,6 +390,61 @@
return 0;
}
+static int smb5_get_adc_data(struct smb_charger *chg, int channel,
+ union power_supply_propval *val)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ if (!chg->vadc_dev) {
+ if (of_find_property(chg->dev->of_node, "qcom,chg-vadc",
+ NULL)) {
+ chg->vadc_dev = qpnp_get_vadc(chg->dev, "chg");
+ if (IS_ERR(chg->vadc_dev)) {
+ rc = PTR_ERR(chg->vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_debug("Failed to find VADC node, rc=%d\n",
+ rc);
+ else
+ chg->vadc_dev = NULL;
+
+ return rc;
+ }
+ } else {
+ return -ENODATA;
+ }
+ }
+
+ if (IS_ERR(chg->vadc_dev))
+ return PTR_ERR(chg->vadc_dev);
+
+ switch (channel) {
+ case USBIN_VOLTAGE:
+ rc = qpnp_vadc_read(chg->vadc_dev, VADC_USB_IN_V_DIV_16_PM5,
+ &result);
+ if (rc < 0) {
+ pr_err("Failed to read USBIN_V over vadc, rc=%d\n", rc);
+ return rc;
+ }
+ val->intval = result.physical;
+ break;
+ case USBIN_CURRENT:
+ rc = qpnp_vadc_read(chg->vadc_dev, VADC_USB_IN_I_PM5, &result);
+ if (rc < 0) {
+ pr_err("Failed to read USBIN_I over vadc, rc=%d\n", rc);
+ return rc;
+ }
+ val->intval = result.physical;
+ break;
+ default:
+ pr_debug("Invalid channel\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
/************************
* USB PSY REGISTRATION *
************************/
@@ -373,7 +457,6 @@
POWER_SUPPLY_PROP_TYPEC_MODE,
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION,
- POWER_SUPPLY_PROP_PD_ALLOWED,
POWER_SUPPLY_PROP_PD_ACTIVE,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
@@ -389,6 +472,8 @@
POWER_SUPPLY_PROP_CONNECTOR_TYPE,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED,
};
static int smb5_usb_get_prop(struct power_supply *psy,
@@ -452,9 +537,6 @@
else
rc = smblib_get_prop_typec_cc_orientation(chg, val);
break;
- case POWER_SUPPLY_PROP_PD_ALLOWED:
- rc = smblib_get_prop_pd_allowed(chg, val);
- break;
case POWER_SUPPLY_PROP_PD_ACTIVE:
val->intval = chg->pd_active;
break;
@@ -504,6 +586,22 @@
: chg->otg_present ? POWER_SUPPLY_SCOPE_SYSTEM
: POWER_SUPPLY_SCOPE_UNKNOWN;
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0 || !pval.intval) {
+ val->intval = 0;
+ return rc;
+ }
+ if (chg->smb_version == PMI632_SUBTYPE)
+ rc = smb5_get_adc_data(chg, USBIN_CURRENT, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (chg->smb_version == PMI632_SUBTYPE)
+ rc = smb5_get_adc_data(chg, USBIN_VOLTAGE, val);
+ break;
+ case POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED:
+ val->intval = !chg->flash_active;
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
@@ -526,12 +624,6 @@
struct smb_charger *chg = &chip->chg;
int rc = 0;
- mutex_lock(&chg->lock);
- if (!chg->typec_present) {
- rc = -EINVAL;
- goto unlock;
- }
-
switch (psp) {
case POWER_SUPPLY_PROP_PD_CURRENT_MAX:
rc = smblib_set_prop_pd_current_max(chg, val);
@@ -573,8 +665,6 @@
break;
}
-unlock:
- mutex_unlock(&chg->lock);
return rc;
}
@@ -789,6 +879,7 @@
{
struct smb5 *chip = power_supply_get_drvdata(psy);
struct smb_charger *chg = &chip->chg;
+ union power_supply_propval pval = {0, };
int rc = 0;
switch (psp) {
@@ -802,7 +893,31 @@
rc = smblib_set_icl_current(chg, val->intval);
break;
case POWER_SUPPLY_PROP_FLASH_ACTIVE:
- chg->flash_active = val->intval;
+ if ((chg->smb_version == PMI632_SUBTYPE)
+ && (chg->flash_active != val->intval)) {
+ chg->flash_active = val->intval;
+
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0)
+ pr_err("Failed to get USB preset status rc=%d\n",
+ rc);
+ if (pval.intval) {
+ rc = smblib_force_vbus_voltage(chg,
+ chg->flash_active ? FORCE_5V_BIT
+ : IDLE_BIT);
+ if (rc < 0)
+ pr_err("Failed to force 5V\n");
+ else
+ chg->pulse_cnt = 0;
+ }
+
+ pr_debug("flash active VBUS 5V restriction %s\n",
+ chg->flash_active ? "applied" : "removed");
+
+ /* Update userspace */
+ if (chg->batt_psy)
+ power_supply_changed(chg->batt_psy);
+ }
break;
default:
pr_err("set prop %d is not supported\n", psp);
@@ -973,6 +1088,7 @@
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_RECHARGE_SOC,
};
@@ -1067,6 +1183,9 @@
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
rc = smblib_get_prop_batt_charge_counter(chg, val);
break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ rc = smblib_get_prop_batt_cycle_count(chg, val);
+ break;
case POWER_SUPPLY_PROP_RECHARGE_SOC:
val->intval = chg->auto_recharge_soc;
break;
@@ -1146,7 +1265,8 @@
rc = smblib_rerun_aicl(chg);
break;
case POWER_SUPPLY_PROP_DP_DM:
- rc = smblib_dp_dm(chg, val->intval);
+ if (!chg->flash_active)
+ rc = smblib_dp_dm(chg, val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
rc = smblib_set_prop_input_current_limited(chg, val);
@@ -1319,6 +1439,42 @@
static int smb5_configure_typec(struct smb_charger *chg)
{
int rc;
+ u8 val = 0;
+
+ rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &val);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read Legacy status rc=%d\n", rc);
+ return rc;
+ }
+ /*
+ * If Legacy cable is detected re-trigger Legacy detection
+ * by disabling/enabling typeC mode.
+ */
+ if (val & TYPEC_LEGACY_CABLE_STATUS_BIT) {
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't disable TYPEC rc=%d\n", rc);
+ return rc;
+ }
+
+ /* delay before enabling typeC */
+ msleep(500);
+
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable TYPEC rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* disable apsd */
+ rc = smblib_configure_hvdcp_apsd(chg, false);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't disable APSD rc=%d\n", rc);
+ return rc;
+ }
rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_1_REG,
TYPEC_CCOUT_DETACH_INT_EN_BIT |
@@ -1337,14 +1493,18 @@
return rc;
}
- /* configure VCONN for software control */
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
+ /* Keep VCONN in h/w controlled mode for PMI632 */
+ if (chg->smb_version != PMI632_SUBTYPE) {
+ /* configure VCONN for software control */
+ rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
VCONN_EN_SRC_BIT | VCONN_EN_VALUE_BIT,
VCONN_EN_SRC_BIT);
- if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure VCONN for SW control rc=%d\n", rc);
- return rc;
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure VCONN for SW control rc=%d\n",
+ rc);
+ return rc;
+ }
}
return rc;
@@ -1354,14 +1514,6 @@
{
int rc;
- /* configure micro USB mode */
- rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
- EN_MICRO_USB_MODE_BIT, EN_MICRO_USB_MODE_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
- return rc;
- }
-
rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
MICRO_USB_STATE_CHANGE_INT_EN_BIT,
MICRO_USB_STATE_CHANGE_INT_EN_BIT);
@@ -1395,7 +1547,6 @@
&chg->default_icl_ua);
/* Use SW based VBUS control, disable HW autonomous mode */
- /* TODO: auth can be enabled through vote based on APSD flow */
rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT,
HVDCP_AUTH_ALG_EN_CFG_BIT);
@@ -1432,18 +1583,54 @@
type = !!(val & EN_MICRO_USB_MODE_BIT);
}
- chg->connector_type = type ? POWER_SUPPLY_CONNECTOR_MICRO_USB
- : POWER_SUPPLY_CONNECTOR_TYPEC;
pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC");
+ if (type) {
+ chg->connector_type = POWER_SUPPLY_CONNECTOR_MICRO_USB;
+ rc = smb5_configure_micro_usb(chg);
+ } else {
+ chg->connector_type = POWER_SUPPLY_CONNECTOR_TYPEC;
+ rc = smb5_configure_typec(chg);
+ }
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
+ return rc;
+ }
+
/*
* PMI632 based hw init:
+ * - Enable STAT pin function on SMB_EN
+ * - Rerun APSD to ensure proper charger detection if device
+ * boots with charger connected.
* - Initialize flash module for PMI632
*/
- if (chg->smb_version == PMI632_SUBTYPE)
- schgm_flash_init(chg);
+ if (chg->smb_version == PMI632_SUBTYPE) {
+ rc = smblib_masked_write(chg, MISC_SMB_EN_CMD_REG,
+ EN_STAT_CMD_BIT, EN_STAT_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't configure SMB_EN rc=%d\n",
+ rc);
+ return rc;
+ }
- smblib_rerun_apsd_if_required(chg);
+ schgm_flash_init(chg);
+ smblib_rerun_apsd_if_required(chg);
+ }
+
+ /* clear the ICL override if it is set */
+ rc = smblib_icl_override(chg, false);
+ if (rc < 0) {
+ pr_err("Couldn't disable ICL override rc=%d\n", rc);
+ return rc;
+ }
+
+ /* set OTG current limit */
+ rc = smblib_set_charge_param(chg, &chg->param.otg_cl, chg->otg_cl_ua);
+ if (rc < 0) {
+ pr_err("Couldn't set otg current limit rc=%d\n", rc);
+ return rc;
+ }
/* vote 0mA on usb_icl for non battery platforms */
vote(chg->usb_icl_votable,
@@ -1460,12 +1647,6 @@
vote(chg->fv_votable,
BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0,
chg->batt_profile_fv_uv);
- vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
- true, 0);
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- true, 0);
- vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER,
- chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB, 0);
/* Some h/w limit maximum supported ICL */
vote(chg->usb_icl_votable, HW_LIMIT_VOTER,
@@ -1473,13 +1654,15 @@
/*
* AICL configuration:
- * start from min and AICL ADC disable
+ * AICL ADC disable
*/
- rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG,
+ if (chg->smb_version != PMI632_SUBTYPE) {
+ rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG,
USBIN_AICL_ADC_EN_BIT, 0);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't configure AICL rc=%d\n", rc);
- return rc;
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't config AICL rc=%d\n", rc);
+ return rc;
+ }
}
/* enable the charging path */
@@ -1489,16 +1672,6 @@
return rc;
}
- if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
- rc = smb5_configure_micro_usb(chg);
- else
- rc = smb5_configure_typec(chg);
- if (rc < 0) {
- dev_err(chg->dev,
- "Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
- return rc;
- }
-
/* configure VBUS for software control */
rc = smblib_masked_write(chg, DCDC_OTG_CFG_REG, OTG_EN_SRC_CFG_BIT, 0);
if (rc < 0) {
@@ -1707,11 +1880,21 @@
{
struct smb_irq_data irq_data = {chip, "determine-initial-status"};
struct smb_charger *chg = &chip->chg;
+ union power_supply_propval val;
+ int rc;
+
+ rc = smblib_get_prop_usb_present(chg, &val);
+ if (rc < 0) {
+ pr_err("Couldn't get usb present rc=%d\n", rc);
+ return rc;
+ }
+ chg->early_usb_attach = val.intval;
if (chg->bms_psy)
smblib_suspend_on_debug_battery(chg);
usb_plugin_irq_handler(0, &irq_data);
+ typec_attach_detach_irq_handler(0, &irq_data);
typec_state_change_irq_handler(0, &irq_data);
usb_source_change_irq_handler(0, &irq_data);
chg_state_change_irq_handler(0, &irq_data);
@@ -1903,6 +2086,7 @@
},
[TYPEC_ATTACH_DETACH_IRQ] = {
.name = "typec-attach-detach",
+ .handler = typec_attach_detach_irq_handler,
},
[TYPEC_LEGACY_CABLE_DETECT_IRQ] = {
.name = "typec-legacy-cable-detect",
@@ -2198,6 +2382,7 @@
chg = &chip->chg;
chg->dev = &pdev->dev;
chg->debug_mask = &__debug_mask;
+ chg->pd_disabled = &__pd_disabled;
chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
chg->mode = PARALLEL_MASTER;
chg->irq_info = smb5_irqs;
@@ -2210,12 +2395,6 @@
return -EINVAL;
}
- rc = smb5_parse_dt(chip);
- if (rc < 0) {
- pr_err("Couldn't parse device tree rc=%d\n", rc);
- return rc;
- }
-
rc = smb5_chg_config_init(chip);
if (rc < 0) {
if (rc != -EPROBE_DEFER)
@@ -2223,6 +2402,12 @@
return rc;
}
+ rc = smb5_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse device tree rc=%d\n", rc);
+ return rc;
+ }
+
rc = smblib_init(chg);
if (rc < 0) {
pr_err("Smblib_init failed rc=%d\n", rc);
@@ -2347,6 +2532,10 @@
struct smb5 *chip = platform_get_drvdata(pdev);
struct smb_charger *chg = &chip->chg;
+ /* force enable APSD */
+ smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+ BC1P2_SRC_DETECT_BIT, BC1P2_SRC_DETECT_BIT);
+
smb5_free_interrupts(chg);
smblib_deinit(chg);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
index c4d10b0..3bb4810 100644
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -288,7 +288,7 @@
struct votable *hw_aicl_rerun_disable_votable;
struct votable *hw_aicl_rerun_enable_indirect_votable;
struct votable *aicl_deglitch_short_votable;
-
+ struct votable *hvdcp_enable_votable;
/* extcon for VBUS / ID notification to USB */
struct extcon_dev *extcon;
};
@@ -414,6 +414,10 @@
"VARB_WRKARND_SHORT_DEGLITCH_VOTER"
/* QC 2.0 */
#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER"
+/* Hvdcp enable voters*/
+#define HVDCP_PMIC_VOTER "HVDCP_PMIC_VOTER"
+#define HVDCP_OTG_VOTER "HVDCP_OTG_VOTER"
+#define HVDCP_PULSING_VOTER "HVDCP_PULSING_VOTER"
static const unsigned int smbchg_extcon_cable[] = {
EXTCON_USB,
@@ -1880,6 +1884,22 @@
&& (reg & USBIN_ACTIVE_PWR_SRC_BIT);
}
+static void smbchg_detect_parallel_charger(struct smbchg_chip *chip)
+{
+ int rc;
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+
+ if (parallel_psy) {
+ pval.intval = true;
+ rc = power_supply_set_property(parallel_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ chip->parallel_charger_detected = rc ? false : true;
+ if (rc)
+ pr_debug("parallel-charger absent rc=%d\n", rc);
+ }
+}
+
static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en)
{
struct power_supply *parallel_psy = get_parallel_psy(chip);
@@ -2017,7 +2037,8 @@
int parallel_fcc_ma, tries = 0;
u8 reg = 0;
- if (!parallel_psy || !chip->parallel_charger_detected)
+ smbchg_detect_parallel_charger(chip);
+ if (!chip->parallel_charger_detected)
return;
smbchg_stay_awake(chip, PM_PARALLEL_TAPER);
@@ -2436,6 +2457,27 @@
return rc;
}
+#define HVDCP_EN_BIT BIT(3)
+static int smbchg_hvdcp_enable_cb(struct votable *votable,
+ void *data,
+ int enable,
+ const char *client)
+{
+ int rc = 0;
+ struct smbchg_chip *chip = data;
+
+ pr_err("smbchg_hvdcp_enable_cb HVDCP %s\n",
+ enable ? "enabled" : "disabled");
+ rc = smbchg_sec_masked_write(chip,
+ chip->usb_chgpth_base + CHGPTH_CFG,
+ HVDCP_EN_BIT, enable ? HVDCP_EN_BIT : 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't %s HVDCP rc=%d\n",
+ enable ? "enable" : "disable", rc);
+
+ return rc;
+}
+
static int set_fastchg_current_vote_cb(struct votable *votable,
void *data,
int fcc_ma,
@@ -3796,7 +3838,6 @@
#define USBIN_ADAPTER_9V 0x3
#define USBIN_ADAPTER_5V_9V_CONT 0x2
#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5
-#define HVDCP_EN_BIT BIT(3)
static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
{
int rc = 0;
@@ -3820,9 +3861,7 @@
* allowance to 9V, so that the audio boost operating in reverse never
* gets detected as a valid input
*/
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_OTG_VOTER, true, 0);
if (rc < 0) {
dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
return rc;
@@ -3856,9 +3895,7 @@
* value in order to allow normal USBs to be recognized as a valid
* input.
*/
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_OTG_VOTER, false, 1);
if (rc < 0) {
dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
return rc;
@@ -4436,10 +4473,13 @@
goto out;
}
- /* otherwise if it is unknown, set type after the vote */
- if (type == POWER_SUPPLY_TYPE_UNKNOWN)
+ /* otherwise if it is unknown, set type after removing the vote */
+ if (type == POWER_SUPPLY_TYPE_UNKNOWN) {
+ rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, 0);
+ if (rc < 0)
+ pr_err("Couldn't vote for new USB ICL rc=%d\n", rc);
chip->usb_supply_type = type;
-
+ }
/*
* Update TYPE property to DCP for HVDCP/HVDCP3 charger types
* so that they can be recongized as AC chargers by healthd.
@@ -4566,11 +4606,6 @@
{
int rc;
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
/* switch to 9V HVDCP */
rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
@@ -4578,9 +4613,7 @@
pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
/* enable HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PULSING_VOTER, false, 1);
if (rc < 0)
pr_err("Couldn't enable HVDCP rc=%d\n", rc);
@@ -4605,6 +4638,19 @@
chip->hvdcp_3_det_ignore_uv = false;
chip->pulse_cnt = 0;
+
+ if ((chip->schg_version == QPNP_SCHG_LITE)
+ && is_hvdcp_present(chip)) {
+ pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n");
+ rc = force_9v_hvdcp(chip);
+ if (rc)
+ pr_err("Failed to force 9V HVDCP=%d\n", rc);
+ }
+
+ pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+ if (rc < 0)
+ pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
}
#define RESTRICTED_CHG_FCC_PERCENT 50
@@ -4648,6 +4694,7 @@
chip->typec_current_ma = 0;
/* cancel/wait for hvdcp pending work if any */
cancel_delayed_work_sync(&chip->hvdcp_det_work);
+ smbchg_relax(chip, PM_DETECT_HVDCP);
smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN);
extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present);
if (chip->dpdm_reg)
@@ -4700,8 +4747,6 @@
#define HVDCP_NOTIFY_MS 2500
static void handle_usb_insertion(struct smbchg_chip *chip)
{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
enum power_supply_type usb_supply_type;
int rc;
char *usb_type_name = "null";
@@ -4748,14 +4793,7 @@
msecs_to_jiffies(HVDCP_NOTIFY_MS));
}
- if (parallel_psy) {
- pval.intval = true;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- chip->parallel_charger_detected = rc ? false : true;
- if (rc)
- pr_debug("parallel-charger absent rc=%d\n", rc);
- }
+ smbchg_detect_parallel_charger(chip);
if (chip->parallel.avail && chip->aicl_done_irq
&& !chip->enable_aicl_wake) {
@@ -5119,8 +5157,7 @@
/* disable HVDCP */
pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PULSING_VOTER, true, 0);
if (rc < 0) {
pr_err("Couldn't disable HVDCP rc=%d\n", rc);
goto out;
@@ -5229,9 +5266,7 @@
/* enable HVDCP */
pr_smb(PR_MISC, "Enable HVDCP\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PULSING_VOTER, false, 1);
if (rc < 0) {
pr_err("Couldn't enable HVDCP rc=%d\n", rc);
return rc;
@@ -5418,6 +5453,12 @@
{
int rc = 0;
+ pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
+ rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
+ if (rc < 0) {
+ pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
+ return rc;
+ }
/* check if HVDCP is already in 5V continuous mode */
if (is_hvdcp_5v_cont_mode(chip)) {
pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n");
@@ -5444,13 +5485,6 @@
goto out;
}
- pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
- if (rc < 0) {
- pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
- goto out;
- }
-
pr_smb(PR_MISC, "Disable AICL\n");
smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
AICL_EN_BIT, 0);
@@ -6255,7 +6289,10 @@
struct smbchg_chip *chip = _chip;
pr_smb(PR_INTERRUPT, "p2f triggered\n");
- smbchg_parallel_usb_check_ok(chip);
+ if (is_usb_present(chip) || is_dc_present(chip)) {
+ smbchg_detect_parallel_charger(chip);
+ smbchg_parallel_usb_check_ok(chip);
+ }
if (chip->batt_psy)
power_supply_changed(chip->batt_psy);
smbchg_charging_status_change(chip);
@@ -6868,7 +6905,22 @@
chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
/* Setup 9V HVDCP */
- if (!chip->hvdcp_not_supported) {
+ if (chip->hvdcp_not_supported) {
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PMIC_VOTER,
+ true, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PMIC_VOTER,
+ true, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n",
+ rc);
+ return rc;
+ }
rc = smbchg_sec_masked_write(chip,
chip->usb_chgpth_base + CHGPTH_CFG,
HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
@@ -7996,6 +8048,7 @@
chip->schg_version = QPNP_SCHG;
break;
case PMI8950:
+ chip->wa_flags |= SMBCHG_RESTART_WA;
case PMI8937:
chip->wa_flags |= SMBCHG_BATT_OV_WA;
if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ {
@@ -8088,7 +8141,7 @@
int rc;
struct smbchg_chip *chip;
struct power_supply *typec_psy = NULL;
- struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev;
+ struct qpnp_vadc_chip *vadc_dev = NULL, *vchg_vadc_dev = NULL;
const char *typec_psy_name;
struct power_supply_config usb_psy_cfg = {};
struct power_supply_config batt_psy_cfg = {};
@@ -8222,6 +8275,15 @@
goto votables_cleanup;
}
+ chip->hvdcp_enable_votable = create_votable(
+ "HVDCP_ENABLE",
+ VOTE_MIN,
+ smbchg_hvdcp_enable_cb, chip);
+ if (IS_ERR(chip->hvdcp_enable_votable)) {
+ rc = PTR_ERR(chip->hvdcp_enable_votable);
+ goto votables_cleanup;
+ }
+
INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);
INIT_DELAYED_WORK(&chip->parallel_en_work,
smbchg_parallel_usb_en_work);
@@ -8408,6 +8470,7 @@
rerun_hvdcp_det_if_necessary(chip);
+ update_usb_status(chip, is_usb_present(chip), false);
dump_regs(chip);
create_debugfs_entries(chip);
dev_info(chip->dev,
@@ -8425,6 +8488,8 @@
out:
handle_usb_removal(chip);
votables_cleanup:
+ if (chip->hvdcp_enable_votable)
+ destroy_votable(chip->hvdcp_enable_votable);
if (chip->aicl_deglitch_short_votable)
destroy_votable(chip->aicl_deglitch_short_votable);
if (chip->hw_aicl_rerun_enable_indirect_votable)
@@ -8504,11 +8569,9 @@
disable_irq(chip->otg_oc_irq);
disable_irq(chip->power_ok_irq);
disable_irq(chip->recharge_irq);
- disable_irq(chip->src_detect_irq);
disable_irq(chip->taper_irq);
disable_irq(chip->usbid_change_irq);
disable_irq(chip->usbin_ov_irq);
- disable_irq(chip->usbin_uv_irq);
disable_irq(chip->vbat_low_irq);
disable_irq(chip->wdog_timeout_irq);
@@ -8551,8 +8614,7 @@
/* disable HVDCP */
pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
+ rc = vote(chip->hvdcp_enable_votable, HVDCP_PMIC_VOTER, true, 0);
if (rc < 0)
pr_err("Couldn't disable HVDCP rc=%d\n", rc);
@@ -8569,6 +8631,9 @@
if (rc < 0)
pr_err("Couldn't fake insertion rc=%d\n", rc);
+ disable_irq(chip->src_detect_irq);
+ disable_irq(chip->usbin_uv_irq);
+
pr_smb(PR_MISC, "Wait 1S to settle\n");
msleep(1000);
chip->hvdcp_3_det_ignore_uv = false;
diff --git a/drivers/power/supply/qcom/qpnp-vm-bms.c b/drivers/power/supply/qcom/qpnp-vm-bms.c
new file mode 100644
index 0000000..042ce99
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-vm-bms.c
@@ -0,0 +1,4255 @@
+/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "VBMS: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/power_supply.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/input/qpnp-power-on.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/of_batterydata.h>
+#include <linux/batterydata-interface.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <uapi/linux/vm_bms.h>
+
+#define _BMS_MASK(BITS, POS) \
+ ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
+#define BMS_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
+ _BMS_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
+ (RIGHT_BIT_POS))
+
+/* Config / Data registers */
+#define REVISION1_REG 0x0
+#define STATUS1_REG 0x8
+#define FSM_STATE_MASK BMS_MASK(5, 3)
+#define FSM_STATE_SHIFT 3
+
+#define STATUS2_REG 0x9
+#define FIFO_CNT_SD_MASK BMS_MASK(7, 4)
+#define FIFO_CNT_SD_SHIFT 4
+
+#define MODE_CTL_REG 0x40
+#define FORCE_S3_MODE BIT(0)
+#define ENABLE_S3_MODE BIT(1)
+#define FORCE_S2_MODE BIT(2)
+#define ENABLE_S2_MODE BIT(3)
+#define S2_MODE_MASK BMS_MASK(3, 2)
+#define S3_MODE_MASK BMS_MASK(1, 0)
+
+#define DATA_CTL1_REG 0x42
+#define MASTER_HOLD_BIT BIT(0)
+
+#define DATA_CTL2_REG 0x43
+#define FIFO_CNT_SD_CLR_BIT BIT(2)
+#define ACC_DATA_SD_CLR_BIT BIT(1)
+#define ACC_CNT_SD_CLR_BIT BIT(0)
+
+#define S3_OCV_TOL_CTL_REG 0x44
+
+#define EN_CTL_REG 0x46
+#define BMS_EN_BIT BIT(7)
+
+#define FIFO_LENGTH_REG 0x47
+#define S1_FIFO_LENGTH_MASK BMS_MASK(3, 0)
+#define S2_FIFO_LENGTH_MASK BMS_MASK(7, 4)
+#define S2_FIFO_LENGTH_SHIFT 4
+
+#define S1_SAMPLE_INTVL_REG 0x55
+#define S2_SAMPLE_INTVL_REG 0x56
+#define S3_SAMPLE_INTVL_REG 0x57
+
+#define S1_ACC_CNT_REG 0x5E
+#define S2_ACC_CNT_REG 0x5F
+#define ACC_CNT_MASK BMS_MASK(2, 0)
+
+#define ACC_DATA0_SD_REG 0x63
+#define ACC_CNT_SD_REG 0x67
+#define OCV_DATA0_REG 0x6A
+#define FIFO_0_LSB_REG 0xC0
+
+#define BMS_SOC_REG 0xB0
+#define BMS_OCV_REG 0xB1 /* B1 & B2 */
+#define SOC_STORAGE_MASK 0xFE
+
+#define CHARGE_INCREASE_STORAGE 0xB3
+#define CHARGE_CYCLE_STORAGE_LSB 0xB4 /* B4 & B5 */
+
+#define SEC_ACCESS 0xD0
+
+#define QPNP_CHARGER_PRESENT BIT(7)
+
+/* Constants */
+#define OCV_TOL_LSB_UV 300
+#define MAX_OCV_TOL_THRESHOLD (OCV_TOL_LSB_UV * 0xFF)
+#define MAX_SAMPLE_COUNT 256
+#define MAX_SAMPLE_INTERVAL 2550
+#define BMS_READ_TIMEOUT 500
+#define BMS_DEFAULT_TEMP 250
+#define OCV_INVALID 0xFFFF
+#define SOC_INVALID 0xFF
+#define OCV_UNINITIALIZED 0xFFFF
+#define VBATT_ERROR_MARGIN 20000
+#define CV_DROP_MARGIN 10000
+#define MIN_OCV_UV 2000000
+#define TIME_PER_PERCENT_UUC 60
+#define IAVG_SAMPLES 16
+#define MIN_SOC_UUC 3
+
+#define QPNP_VM_BMS_DEV_NAME "qcom,qpnp-vm-bms"
+
+/* indicates the state of BMS */
+enum {
+ IDLE_STATE,
+ S1_STATE,
+ S2_STATE,
+ S3_STATE,
+ S7_STATE,
+};
+
+enum {
+ WRKARND_PON_OCV_COMP = BIT(0),
+};
+
+struct bms_irq {
+ int irq;
+ unsigned long disabled;
+};
+
+struct bms_wakeup_source {
+ struct wakeup_source source;
+ unsigned long disabled;
+};
+
+struct temp_curr_comp_map {
+ int temp_decideg;
+ int current_ma;
+};
+
+struct bms_dt_cfg {
+ bool cfg_report_charger_eoc;
+ bool cfg_force_bms_active_on_charger;
+ bool cfg_force_s3_on_suspend;
+ bool cfg_ignore_shutdown_soc;
+ bool cfg_use_voltage_soc;
+ int cfg_v_cutoff_uv;
+ int cfg_max_voltage_uv;
+ int cfg_r_conn_mohm;
+ int cfg_shutdown_soc_valid_limit;
+ int cfg_low_soc_calc_threshold;
+ int cfg_low_soc_calculate_soc_ms;
+ int cfg_low_voltage_threshold;
+ int cfg_low_voltage_calculate_soc_ms;
+ int cfg_low_soc_fifo_length;
+ int cfg_calculate_soc_ms;
+ int cfg_voltage_soc_timeout_ms;
+ int cfg_s1_sample_interval_ms;
+ int cfg_s2_sample_interval_ms;
+ int cfg_s1_sample_count;
+ int cfg_s2_sample_count;
+ int cfg_s1_fifo_length;
+ int cfg_s2_fifo_length;
+ int cfg_disable_bms;
+ int cfg_s3_ocv_tol_uv;
+ int cfg_soc_resume_limit;
+ int cfg_low_temp_threshold;
+ int cfg_ibat_avg_samples;
+ int cfg_battery_aging_comp;
+ bool cfg_use_reported_soc;
+};
+
+struct qpnp_bms_chip {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ dev_t dev_no;
+ u16 base;
+ u8 revision[2];
+ u32 batt_pres_addr;
+ u32 chg_pres_addr;
+
+ /* status variables */
+ u8 current_fsm_state;
+ bool last_soc_invalid;
+ bool warm_reset;
+ bool bms_psy_registered;
+ bool battery_full;
+ bool bms_dev_open;
+ bool data_ready;
+ bool apply_suspend_config;
+ bool in_cv_state;
+ bool low_soc_fifo_set;
+ int battery_status;
+ int calculated_soc;
+ int current_now;
+ int prev_current_now;
+ int prev_voltage_based_soc;
+ int calculate_soc_ms;
+ int voltage_soc_uv;
+ int battery_present;
+ int last_soc;
+ int last_soc_unbound;
+ int last_soc_change_sec;
+ int charge_start_tm_sec;
+ int catch_up_time_sec;
+ int delta_time_s;
+ int uuc_delta_time_s;
+ int ocv_at_100;
+ int last_ocv_uv;
+ int s2_fifo_length;
+ int last_acc;
+ int hi_power_state;
+ unsigned int vadc_v0625;
+ unsigned int vadc_v1250;
+ unsigned long tm_sec;
+ unsigned long workaround_flag;
+ unsigned long uuc_tm_sec;
+ u32 seq_num;
+ u8 shutdown_soc;
+ bool shutdown_soc_invalid;
+ u16 last_ocv_raw;
+ u32 shutdown_ocv;
+ bool suspend_data_valid;
+ int iavg_num_samples;
+ unsigned int iavg_index;
+ int iavg_samples_ma[IAVG_SAMPLES];
+ int iavg_ma;
+ int prev_soc_uuc;
+ int eoc_reported;
+ u8 charge_increase;
+ u16 charge_cycles;
+ unsigned int start_soc;
+ unsigned int end_soc;
+ unsigned int chg_start_soc;
+
+ struct bms_battery_data *batt_data;
+ struct bms_dt_cfg dt;
+
+ struct dentry *debug_root;
+ struct bms_wakeup_source vbms_lv_wake_source;
+ struct bms_wakeup_source vbms_cv_wake_source;
+ struct bms_wakeup_source vbms_soc_wake_source;
+ wait_queue_head_t bms_wait_q;
+ struct delayed_work monitor_soc_work;
+ struct delayed_work voltage_soc_timeout_work;
+ struct mutex bms_data_mutex;
+ struct mutex bms_device_mutex;
+ struct mutex last_soc_mutex;
+ struct mutex state_change_mutex;
+ struct class *bms_class;
+ struct device *bms_device;
+ struct cdev bms_cdev;
+ struct qpnp_vm_bms_data bms_data;
+ struct qpnp_vadc_chip *vadc_dev;
+ struct qpnp_adc_tm_chip *adc_tm_dev;
+ struct pmic_revid_data *revid_data;
+ struct qpnp_adc_tm_btm_param vbat_monitor_params;
+ struct bms_irq fifo_update_done_irq;
+ struct bms_irq fsm_state_change_irq;
+ struct power_supply_desc bms_psy_d;
+ struct power_supply *bms_psy;
+ struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ bool reported_soc_in_use;
+ bool charger_removed_since_full;
+ bool charger_reinserted;
+ bool reported_soc_high_current;
+ int reported_soc;
+ int reported_soc_change_sec;
+ int reported_soc_delta;
+};
+
+static struct qpnp_bms_chip *the_chip;
+
+static struct temp_curr_comp_map temp_curr_comp_lut[] = {
+ {-300, 15},
+ {250, 17},
+ {850, 28},
+};
+
+static void disable_bms_irq(struct bms_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ disable_irq(irq->irq);
+ pr_debug("disabled irq %d\n", irq->irq);
+ }
+}
+
+static void enable_bms_irq(struct bms_irq *irq)
+{
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ enable_irq(irq->irq);
+ pr_debug("enable irq %d\n", irq->irq);
+ }
+}
+
+static void bms_stay_awake(struct bms_wakeup_source *source)
+{
+ if (__test_and_clear_bit(0, &source->disabled)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s\n", source->source.name);
+ }
+}
+
+static void bms_relax(struct bms_wakeup_source *source)
+{
+ if (!__test_and_set_bit(0, &source->disabled)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+}
+
+static bool bms_wake_active(struct bms_wakeup_source *source)
+{
+ return !source->disabled;
+}
+
+static int bound_soc(int soc)
+{
+ soc = max(0, soc);
+ soc = min(100, soc);
+
+ return soc;
+}
+
+static char *qpnp_vm_bms_supplicants[] = {
+ "battery",
+};
+
+static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base, int count)
+{
+ int rc;
+
+ rc = regmap_bulk_read(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("Regmap read failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_write_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base, int count)
+{
+ int rc;
+
+ rc = regmap_bulk_write(chip->regmap, base, val, count);
+ if (rc)
+ pr_err("Regmap write failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_masked_write_base(struct qpnp_bms_chip *chip, u16 addr,
+ u8 mask, u8 val)
+{
+ int rc;
+ u8 reg;
+
+ rc = qpnp_read_wrapper(chip, ®, addr, 1);
+ if (rc) {
+ pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
+ return rc;
+ }
+ reg &= ~mask;
+ reg |= val & mask;
+ rc = qpnp_write_wrapper(chip, ®, addr, 1);
+ if (rc)
+ pr_err("write failed addr = %03X, val = %02x, mask = %02x, reg = %02x, rc = %d\n",
+ addr, val, mask, reg, rc);
+
+ return rc;
+}
+
+static int qpnp_secure_write_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base)
+{
+ int rc;
+ u8 reg;
+
+ reg = 0xA5;
+ rc = qpnp_write_wrapper(chip, ®, chip->base + SEC_ACCESS, 1);
+ if (rc) {
+ pr_err("Error %d writing 0xA5 to 0x%x reg\n",
+ rc, SEC_ACCESS);
+ return rc;
+ }
+ rc = qpnp_write_wrapper(chip, val, base, 1);
+ if (rc)
+ pr_err("Error %d writing %d to 0x%x reg\n", rc, *val, base);
+
+ return rc;
+}
+
+static int backup_ocv_soc(struct qpnp_bms_chip *chip, int ocv_uv, int soc)
+{
+ int rc;
+ u16 ocv_mv = ocv_uv / 1000;
+
+ rc = qpnp_write_wrapper(chip, (u8 *)&ocv_mv,
+ chip->base + BMS_OCV_REG, 2);
+ if (rc)
+ pr_err("Unable to backup OCV rc=%d\n", rc);
+
+ rc = qpnp_masked_write_base(chip, chip->base + BMS_SOC_REG,
+ SOC_STORAGE_MASK, (soc + 1) << 1);
+ if (rc)
+ pr_err("Unable to backup SOC rc=%d\n", rc);
+
+ pr_debug("ocv_mv=%d soc=%d\n", ocv_mv, soc);
+
+ return rc;
+}
+
+static int get_current_time(unsigned long *now_tm_sec)
+{
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ int rc;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ return -EINVAL;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+ rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+ rtc_class_close(rtc);
+ return rc;
+}
+
+static int calculate_delta_time(unsigned long *time_stamp, int *delta_time_s)
+{
+ unsigned long now_tm_sec = 0;
+
+ /* default to delta time = 0 if anything fails */
+ *delta_time_s = 0;
+
+ if (get_current_time(&now_tm_sec)) {
+ pr_err("RTC read failed\n");
+ return 0;
+ }
+
+ *delta_time_s = (now_tm_sec - *time_stamp);
+
+ /* remember this time */
+ *time_stamp = now_tm_sec;
+ return 0;
+}
+
+static bool is_charger_present(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->usb_psy == NULL)
+ chip->usb_psy = power_supply_get_by_name("usb");
+ if (chip->usb_psy) {
+ power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &ret);
+ return ret.intval;
+ }
+
+ return false;
+}
+
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the type property */
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &ret);
+ return ret.intval != POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return false;
+}
+
+#define BAT_PRES_BIT BIT(7)
+static bool is_battery_present(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+ int rc;
+ u8 batt_pres;
+
+ /* first try to use the batt_pres register if given */
+ if (chip->batt_pres_addr) {
+ rc = qpnp_read_wrapper(chip, &batt_pres,
+ chip->batt_pres_addr, 1);
+ if (!rc && (batt_pres & BAT_PRES_BIT))
+ return true;
+ else
+ return false;
+ }
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the present property */
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_PRESENT, &ret);
+ return ret.intval;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return false;
+}
+
+#define BAT_REMOVED_OFFMODE_BIT BIT(6)
+static bool is_battery_replaced_in_offmode(struct qpnp_bms_chip *chip)
+{
+ u8 batt_pres;
+ int rc;
+
+ if (chip->batt_pres_addr) {
+ rc = qpnp_read_wrapper(chip, &batt_pres,
+ chip->batt_pres_addr, 1);
+ pr_debug("offmode removed: %02x\n", batt_pres);
+ if (!rc && (batt_pres & BAT_REMOVED_OFFMODE_BIT))
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_battery_taper_charging(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (chip->batt_psy) {
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &ret);
+ return ret.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER;
+ }
+
+ return false;
+}
+
+static int master_hold_control(struct qpnp_bms_chip *chip, bool enable)
+{
+ u8 reg = 0;
+ int rc;
+
+ reg = enable ? MASTER_HOLD_BIT : 0;
+
+ rc = qpnp_secure_write_wrapper(chip, ®,
+ chip->base + DATA_CTL1_REG);
+ if (rc)
+ pr_err("Unable to write reg=%x rc=%d\n", DATA_CTL1_REG, rc);
+
+ return rc;
+}
+
+static int force_fsm_state(struct qpnp_bms_chip *chip, u8 state)
+{
+ int rc;
+ u8 mode_ctl = 0;
+
+ switch (state) {
+ case S2_STATE:
+ mode_ctl = (FORCE_S2_MODE | ENABLE_S2_MODE);
+ break;
+ case S3_STATE:
+ mode_ctl = (FORCE_S3_MODE | ENABLE_S3_MODE);
+ break;
+ default:
+ pr_debug("Invalid state %d\n", state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_secure_write_wrapper(chip, &mode_ctl,
+ chip->base + MODE_CTL_REG);
+ if (rc) {
+ pr_err("Unable to write reg=%x rc=%d\n", MODE_CTL_REG, rc);
+ return rc;
+ }
+ /* delay for the FSM state to take affect in hardware */
+ usleep_range(500, 600);
+
+ pr_debug("force_mode=%d mode_cntl_reg=%x\n", state, mode_ctl);
+
+ return 0;
+}
+
+static int get_sample_interval(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 *interval)
+{
+ int rc;
+ u8 val = 0, reg;
+
+ *interval = 0;
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = S1_SAMPLE_INTVL_REG;
+ break;
+ case S2_STATE:
+ reg = S2_SAMPLE_INTVL_REG;
+ break;
+ case S3_STATE:
+ reg = S3_SAMPLE_INTVL_REG;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_read_wrapper(chip, &val, chip->base + reg, 1);
+ if (rc) {
+ pr_err("Failed to get state(%d) sample_interval, rc=%d\n",
+ fsm_state, rc);
+ return rc;
+ }
+
+ *interval = val * 10;
+
+ return 0;
+}
+
+static int get_sample_count(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 *count)
+{
+ int rc;
+ u8 val = 0, reg;
+
+ *count = 0;
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = S1_ACC_CNT_REG;
+ break;
+ case S2_STATE:
+ reg = S2_ACC_CNT_REG;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_read_wrapper(chip, &val, chip->base + reg, 1);
+ if (rc) {
+ pr_err("Failed to get state(%d) sample_count, rc=%d\n",
+ fsm_state, rc);
+ return rc;
+ }
+ val &= ACC_CNT_MASK;
+
+ *count = val ? (1 << (val + 1)) : 1;
+
+ return 0;
+}
+
+static int get_fifo_length(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 *fifo_length)
+{
+ int rc;
+ u8 val = 0, reg, mask = 0, shift = 0;
+
+ *fifo_length = 0;
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S1_FIFO_LENGTH_MASK;
+ shift = 0;
+ break;
+ case S2_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S2_FIFO_LENGTH_MASK;
+ shift = S2_FIFO_LENGTH_SHIFT;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = qpnp_read_wrapper(chip, &val, chip->base + reg, 1);
+ if (rc) {
+ pr_err("Failed to get state(%d) fifo_length, rc=%d\n",
+ fsm_state, rc);
+ return rc;
+ }
+
+ val &= mask;
+ val >>= shift;
+
+ *fifo_length = val;
+
+ return 0;
+}
+
+static int set_fifo_length(struct qpnp_bms_chip *chip,
+ u8 fsm_state, u32 fifo_length)
+{
+ int rc;
+ u8 reg, mask = 0, shift = 0;
+
+ /* fifo_length of 1 is not supported due to a hardware issue */
+ if ((fifo_length <= 1) || (fifo_length > MAX_FIFO_REGS)) {
+ pr_err("Invalid FIFO length = %d\n", fifo_length);
+ return -EINVAL;
+ }
+
+ switch (fsm_state) {
+ case S1_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S1_FIFO_LENGTH_MASK;
+ shift = 0;
+ break;
+ case S2_STATE:
+ reg = FIFO_LENGTH_REG;
+ mask = S2_FIFO_LENGTH_MASK;
+ shift = S2_FIFO_LENGTH_SHIFT;
+ break;
+ default:
+ pr_err("Invalid state %d\n", fsm_state);
+ return -EINVAL;
+ }
+
+ rc = master_hold_control(chip, true);
+ if (rc)
+ pr_err("Unable to apply master_hold rc=%d\n", rc);
+
+ rc = qpnp_masked_write_base(chip, chip->base + reg, mask,
+ fifo_length << shift);
+ if (rc)
+ pr_err("Unable to set fifo length rc=%d\n", rc);
+
+ rc = master_hold_control(chip, false);
+ if (rc)
+ pr_err("Unable to apply master_hold rc=%d\n", rc);
+
+ return rc;
+}
+
+static int get_fsm_state(struct qpnp_bms_chip *chip, u8 *state)
+{
+ int rc;
+
+ /*
+ * To read the STATUS1 register, write a value(any) to this register,
+ * wait for 10ms and then read the register.
+ */
+ *state = 0;
+ rc = qpnp_write_wrapper(chip, state, chip->base + STATUS1_REG, 1);
+ if (rc) {
+ pr_err("Unable to write STATUS1_REG rc=%d\n", rc);
+ return rc;
+ }
+ usleep_range(10000, 11000);
+
+ /* read the current FSM state */
+ rc = qpnp_read_wrapper(chip, state, chip->base + STATUS1_REG, 1);
+ if (rc) {
+ pr_err("Unable to read STATUS1_REG rc=%d\n", rc);
+ return rc;
+ }
+ *state = (*state & FSM_STATE_MASK) >> FSM_STATE_SHIFT;
+
+ return rc;
+}
+
+static int update_fsm_state(struct qpnp_bms_chip *chip)
+{
+ u8 state = 0;
+ int rc;
+
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc) {
+ pr_err("Unable to get fsm_state rc=%d\n", rc);
+ goto fail_fsm;
+ }
+
+ chip->current_fsm_state = state;
+
+fail_fsm:
+ mutex_unlock(&chip->state_change_mutex);
+ return rc;
+}
+
+static int backup_charge_cycle(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->charge_increase >= 0) {
+ rc = qpnp_write_wrapper(chip, &chip->charge_increase,
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
+ if (rc)
+ pr_err("Unable to backup charge_increase rc=%d\n", rc);
+ }
+
+ if (chip->charge_cycles >= 0) {
+ rc = qpnp_write_wrapper(chip, (u8 *)&chip->charge_cycles,
+ chip->base + CHARGE_CYCLE_STORAGE_LSB, 2);
+ if (rc)
+ pr_err("Unable to backup charge_cycles rc=%d\n", rc);
+ }
+
+ pr_debug("%s storing charge_increase=%u charge_cycle=%u\n",
+ rc ? "Unable to" : "Successfully",
+ chip->charge_increase, chip->charge_cycles);
+
+ return rc;
+}
+
+static int read_chgcycle_data_from_backup(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ uint16_t temp_u16 = 0;
+ u8 temp_u8 = 0;
+
+ rc = qpnp_read_wrapper(chip, &temp_u8,
+ chip->base + CHARGE_INCREASE_STORAGE, 1);
+ if (rc) {
+ pr_err("Unable to read charge_increase rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&temp_u16,
+ chip->base + CHARGE_CYCLE_STORAGE_LSB, 2);
+ if (rc) {
+ pr_err("Unable to read charge_cycle rc=%d\n", rc);
+ return rc;
+ }
+
+ if ((temp_u8 == 0xFF) || (temp_u16 == 0xFFFF)) {
+ chip->charge_cycles = 0;
+ chip->charge_increase = 0;
+ pr_info("rejecting aging data charge_increase=%u charge_cycle=%u\n",
+ temp_u8, temp_u16);
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to reset charge cycles rc=%d\n", rc);
+ } else {
+ chip->charge_increase = temp_u8;
+ chip->charge_cycles = temp_u16;
+ }
+
+ pr_debug("charge_increase=%u charge_cycle=%u\n",
+ chip->charge_increase, chip->charge_cycles);
+ return rc;
+}
+
+static int calculate_uuc_iavg(struct qpnp_bms_chip *chip)
+{
+ int i;
+ int iavg_ma = chip->current_now / 1000;
+
+ /* only continue if ibat has changed */
+ if (chip->current_now == chip->prev_current_now)
+ goto ibat_unchanged;
+ else
+ chip->prev_current_now = chip->current_now;
+
+ chip->iavg_samples_ma[chip->iavg_index] = iavg_ma;
+ chip->iavg_index = (chip->iavg_index + 1) %
+ chip->dt.cfg_ibat_avg_samples;
+ chip->iavg_num_samples++;
+ if (chip->iavg_num_samples >= chip->dt.cfg_ibat_avg_samples)
+ chip->iavg_num_samples = chip->dt.cfg_ibat_avg_samples;
+
+ if (chip->iavg_num_samples) {
+ iavg_ma = 0;
+ /* maintain a 16 sample average of ibat */
+ for (i = 0; i < chip->iavg_num_samples; i++) {
+ pr_debug("iavg_samples_ma[%d] = %d\n", i,
+ chip->iavg_samples_ma[i]);
+ iavg_ma += chip->iavg_samples_ma[i];
+ }
+
+ chip->iavg_ma = DIV_ROUND_CLOSEST(iavg_ma,
+ chip->iavg_num_samples);
+ }
+
+ibat_unchanged:
+ pr_debug("current_now_ma=%d averaged_iavg_ma=%d\n",
+ chip->current_now / 1000, chip->iavg_ma);
+
+ return chip->iavg_ma;
+}
+
+static int adjust_uuc(struct qpnp_bms_chip *chip, int soc_uuc)
+{
+ int max_percent_change;
+
+ calculate_delta_time(&chip->uuc_tm_sec, &chip->uuc_delta_time_s);
+
+ /* make sure that the UUC changes 1% at a time */
+ max_percent_change = max(chip->uuc_delta_time_s
+ / TIME_PER_PERCENT_UUC, 1);
+
+ if (chip->prev_soc_uuc == -EINVAL) {
+ /* start with a minimum UUC if the initial UUC is high */
+ if (soc_uuc > MIN_SOC_UUC)
+ chip->prev_soc_uuc = MIN_SOC_UUC;
+ else
+ chip->prev_soc_uuc = soc_uuc;
+ } else {
+ if (abs(chip->prev_soc_uuc - soc_uuc) <= max_percent_change)
+ chip->prev_soc_uuc = soc_uuc;
+ else if (soc_uuc > chip->prev_soc_uuc)
+ chip->prev_soc_uuc += max_percent_change;
+ else
+ chip->prev_soc_uuc -= max_percent_change;
+ }
+
+ pr_debug("soc_uuc=%d new_soc_uuc=%d\n", soc_uuc, chip->prev_soc_uuc);
+
+ return chip->prev_soc_uuc;
+}
+
+static int lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp)
+{
+ int soc_ocv = 0, soc_cutoff = 0, soc_final = 0;
+ int fcc, acc, soc_uuc = 0, soc_acc = 0, iavg_ma = 0;
+
+ soc_ocv = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
+ batt_temp, ocv_uv / 1000);
+ soc_cutoff = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
+ batt_temp, chip->dt.cfg_v_cutoff_uv / 1000);
+
+ soc_final = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_cutoff),
+ (100 - soc_cutoff));
+
+ if (chip->batt_data->ibat_acc_lut) {
+ /* Apply ACC logic only if we discharging */
+ if (chip->current_now > 0) {
+
+ /*
+ * IBAT averaging is disabled at low temp.
+ * allowing the SOC to catcup quickly.
+ */
+ if (batt_temp > chip->dt.cfg_low_temp_threshold)
+ iavg_ma = calculate_uuc_iavg(chip);
+ else
+ iavg_ma = chip->current_now / 1000;
+
+ fcc = interpolate_fcc(chip->batt_data->fcc_temp_lut,
+ batt_temp);
+ acc = interpolate_acc(chip->batt_data->ibat_acc_lut,
+ batt_temp, iavg_ma);
+ if (acc <= 0) {
+ if (chip->last_acc)
+ acc = chip->last_acc;
+ else
+ acc = fcc;
+ }
+ soc_uuc = ((fcc - acc) * 100) / fcc;
+
+ if (batt_temp > chip->dt.cfg_low_temp_threshold)
+ soc_uuc = adjust_uuc(chip, soc_uuc);
+
+ soc_acc = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_uuc),
+ (100 - soc_uuc));
+
+ pr_debug("fcc=%d acc=%d soc_final=%d soc_uuc=%d soc_acc=%d current_now=%d iavg_ma=%d\n",
+ fcc, acc, soc_final, soc_uuc,
+ soc_acc, chip->current_now / 1000, iavg_ma);
+
+ soc_final = soc_acc;
+ chip->last_acc = acc;
+ } else {
+ /* charging - reset all the counters */
+ chip->last_acc = 0;
+ chip->iavg_num_samples = 0;
+ chip->iavg_index = 0;
+ chip->iavg_ma = 0;
+ chip->prev_current_now = 0;
+ chip->prev_soc_uuc = -EINVAL;
+ }
+ }
+
+ soc_final = bound_soc(soc_final);
+
+ pr_debug("soc_final=%d soc_ocv=%d soc_cutoff=%d ocv_uv=%u batt_temp=%d\n",
+ soc_final, soc_ocv, soc_cutoff, ocv_uv, batt_temp);
+
+ return soc_final;
+}
+
+#define V_PER_BIT_MUL_FACTOR 97656
+#define V_PER_BIT_DIV_FACTOR 1000
+#define VADC_INTRINSIC_OFFSET 0x6000
+static int vadc_reading_to_uv(int reading, bool vadc_bms)
+{
+ int64_t value;
+
+ if (!vadc_bms) {
+ /*
+ * All the BMS H/W VADC values are pre-compensated
+ * for VADC_INTRINSIC_OFFSET, subtract this offset
+ * only if this reading is not obtained from BMS
+ */
+
+ if (reading <= VADC_INTRINSIC_OFFSET)
+ return 0;
+
+ reading -= VADC_INTRINSIC_OFFSET;
+ }
+
+ value = (reading * V_PER_BIT_MUL_FACTOR);
+
+ return div_u64(value, (u32)V_PER_BIT_DIV_FACTOR);
+}
+
+static int get_calculation_delay_ms(struct qpnp_bms_chip *chip)
+{
+ if (bms_wake_active(&chip->vbms_lv_wake_source))
+ return chip->dt.cfg_low_voltage_calculate_soc_ms;
+ if (chip->calculated_soc < chip->dt.cfg_low_soc_calc_threshold)
+ return chip->dt.cfg_low_soc_calculate_soc_ms;
+ else
+ return chip->dt.cfg_calculate_soc_ms;
+}
+
+#define VADC_CALIB_UV 625000
+#define VBATT_MUL_FACTOR 3
+static int adjust_vbatt_reading(struct qpnp_bms_chip *chip, int reading_uv)
+{
+ s64 numerator, denominator;
+
+ if (reading_uv == 0)
+ return 0;
+
+ /* don't adjust if not calibrated */
+ if (chip->vadc_v0625 == 0 || chip->vadc_v1250 == 0) {
+ pr_debug("No cal yet return %d\n",
+ VBATT_MUL_FACTOR * reading_uv);
+ return VBATT_MUL_FACTOR * reading_uv;
+ }
+
+ numerator = ((s64)reading_uv - chip->vadc_v0625) * VADC_CALIB_UV;
+ denominator = (s64)chip->vadc_v1250 - chip->vadc_v0625;
+
+ if (denominator == 0)
+ return reading_uv * VBATT_MUL_FACTOR;
+
+ return (VADC_CALIB_UV + div_s64(numerator, denominator))
+ * VBATT_MUL_FACTOR;
+}
+
+static int calib_vadc(struct qpnp_bms_chip *chip)
+{
+ int rc, raw_0625, raw_1250;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, REF_625MV, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ raw_0625 = result.adc_code;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, REF_125V, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ raw_1250 = result.adc_code;
+
+ chip->vadc_v0625 = vadc_reading_to_uv(raw_0625, false);
+ chip->vadc_v1250 = vadc_reading_to_uv(raw_1250, false);
+
+ pr_debug("vadc calib: 0625=%d raw (%d uv), 1250=%d raw (%d uv)\n",
+ raw_0625, chip->vadc_v0625, raw_1250, chip->vadc_v1250);
+
+ return 0;
+}
+
+static int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
+ u16 reading, bool is_pon_ocv)
+{
+ int64_t uv, vbatt;
+ int rc;
+
+ uv = vadc_reading_to_uv(reading, true);
+ pr_debug("%u raw converted into %lld uv\n", reading, uv);
+
+ uv = adjust_vbatt_reading(chip, uv);
+ pr_debug("adjusted into %lld uv\n", uv);
+
+ vbatt = uv;
+ rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv);
+ if (rc) {
+ pr_debug("Vbatt compensation failed rc = %d\n", rc);
+ uv = vbatt;
+ } else {
+ pr_debug("temp-compensated %lld into %lld uv\n", vbatt, uv);
+ }
+
+ return uv;
+}
+
+static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
+ int batt_temp, bool is_pon_ocv)
+{
+ int rc;
+
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
+
+ chip->last_ocv_uv = convert_vbatt_raw_to_uv(chip,
+ chip->last_ocv_raw, is_pon_ocv);
+
+ pr_debug("last_ocv_uv = %d\n", chip->last_ocv_uv);
+}
+
+static int read_and_update_ocv(struct qpnp_bms_chip *chip, int batt_temp,
+ bool is_pon_ocv)
+{
+ int rc, ocv_uv;
+ u16 ocv_data = 0;
+
+ /* read the BMS h/w OCV */
+ rc = qpnp_read_wrapper(chip, (u8 *)&ocv_data,
+ chip->base + OCV_DATA0_REG, 2);
+ if (rc) {
+ pr_err("Error reading ocv: rc = %d\n", rc);
+ return -ENXIO;
+ }
+
+ /* check if OCV is within limits */
+ ocv_uv = convert_vbatt_raw_to_uv(chip, ocv_data, is_pon_ocv);
+ if (ocv_uv < MIN_OCV_UV) {
+ pr_err("OCV too low or invalid (%d)- rejecting it\n", ocv_uv);
+ return 0;
+ }
+
+ if ((chip->last_ocv_raw == OCV_UNINITIALIZED) ||
+ (chip->last_ocv_raw != ocv_data)) {
+ pr_debug("new OCV!\n");
+ chip->last_ocv_raw = ocv_data;
+ convert_and_store_ocv(chip, batt_temp, is_pon_ocv);
+ }
+
+ pr_debug("ocv_raw=0x%x last_ocv_raw=0x%x last_ocv_uv=%d\n",
+ ocv_data, chip->last_ocv_raw, chip->last_ocv_uv);
+
+ return 0;
+}
+
+static int get_battery_voltage(struct qpnp_bms_chip *chip, int *result_uv)
+{
+ int rc;
+ struct qpnp_vadc_result adc_result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &adc_result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ VBAT_SNS, rc);
+ return rc;
+ }
+ pr_debug("mvolts phy=%lld meas=0x%llx\n", adc_result.physical,
+ adc_result.measurement);
+ *result_uv = (int)adc_result.physical;
+
+ return 0;
+}
+
+static int get_battery_status(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ return ret.intval;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static int get_batt_therm(struct qpnp_bms_chip *chip, int *batt_temp)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return rc;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n",
+ result.physical, result.measurement);
+
+ *batt_temp = (int)result.physical;
+
+ return 0;
+}
+
+static int get_prop_bms_rbatt(struct qpnp_bms_chip *chip)
+{
+ return chip->batt_data->default_rbatt_mohm;
+}
+
+static int get_rbatt(struct qpnp_bms_chip *chip, int soc, int batt_temp)
+{
+ int rbatt_mohm, scalefactor;
+
+ rbatt_mohm = chip->batt_data->default_rbatt_mohm;
+ if (chip->batt_data->rbatt_sf_lut == NULL) {
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+ }
+
+ scalefactor = interpolate_scalingfactor(chip->batt_data->rbatt_sf_lut,
+ batt_temp, soc);
+ rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
+
+ if (chip->dt.cfg_r_conn_mohm > 0)
+ rbatt_mohm += chip->dt.cfg_r_conn_mohm;
+
+ return rbatt_mohm;
+}
+
+static void charging_began(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u8 state;
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ chip->start_soc = chip->last_soc;
+
+ /*
+ * reset ocv_at_100 to -EINVAL to indicate
+ * start of charging.
+ */
+ chip->ocv_at_100 = -EINVAL;
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ /*
+ * If the BMS state is not in S2, force it in S2. Such
+ * a condition can only occur if we are coming out of
+ * suspend.
+ */
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc)
+ pr_err("Unable to get FSM state rc=%d\n", rc);
+ if (rc || (state != S2_STATE)) {
+ pr_debug("Forcing S2 state\n");
+ rc = force_fsm_state(chip, S2_STATE);
+ if (rc)
+ pr_err("Unable to set FSM state rc=%d\n", rc);
+ }
+ mutex_unlock(&chip->state_change_mutex);
+}
+
+static void charging_ended(struct qpnp_bms_chip *chip)
+{
+ u8 state;
+ int rc, status = get_battery_status(chip);
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ chip->end_soc = chip->last_soc;
+
+ if (status == POWER_SUPPLY_STATUS_FULL)
+ chip->last_soc_invalid = true;
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ /*
+ * If the BMS state is not in S2, force it in S2. Such
+ * a condition can only occur if we are coming out of
+ * suspend.
+ */
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc)
+ pr_err("Unable to get FSM state rc=%d\n", rc);
+ if (rc || (state != S2_STATE)) {
+ pr_debug("Forcing S2 state\n");
+ rc = force_fsm_state(chip, S2_STATE);
+ if (rc)
+ pr_err("Unable to set FSM state rc=%d\n", rc);
+ }
+ mutex_unlock(&chip->state_change_mutex);
+
+ /* Calculate charge accumulated and update charge cycle */
+ if (chip->dt.cfg_battery_aging_comp &&
+ (chip->end_soc > chip->start_soc)) {
+ chip->charge_increase += (chip->end_soc - chip->start_soc);
+ if (chip->charge_increase > 100) {
+ chip->charge_cycles++;
+ chip->charge_increase %= 100;
+ }
+ pr_debug("start_soc=%u end_soc=%u charge_cycles=%u charge_increase=%u\n",
+ chip->start_soc, chip->end_soc,
+ chip->charge_cycles, chip->charge_increase);
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to store charge cycles rc=%d\n", rc);
+ }
+}
+
+static int estimate_ocv(struct qpnp_bms_chip *chip)
+{
+ int i, rc, vbatt = 0, vbatt_final = 0;
+
+ for (i = 0; i < 5; i++) {
+ rc = get_battery_voltage(chip, &vbatt);
+ if (rc) {
+ pr_err("Unable to read battery-voltage rc=%d\n", rc);
+ return rc;
+ }
+ /*
+ * Conservatively select the lowest vbatt to avoid reporting
+ * a higher ocv due to variations in bootup current.
+ */
+
+ if (i == 0)
+ vbatt_final = vbatt;
+ else if (vbatt < vbatt_final)
+ vbatt_final = vbatt;
+
+ msleep(20);
+ }
+
+ /*
+ * TODO: Revisit the OCV calcuations to use approximate ibatt
+ * and rbatt.
+ */
+ return vbatt_final;
+}
+
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip, int chg_time_sec,
+ int catch_up_sec, int new_soc, int prev_soc)
+{
+ int scaled_soc;
+ int numerator;
+
+ /*
+ * Don't report a high value immediately slowly scale the
+ * value from prev_soc to the new soc based on a charge time
+ * weighted average
+ */
+ pr_debug("cts=%d catch_up_sec=%d\n", chg_time_sec, catch_up_sec);
+ if (catch_up_sec == 0)
+ return new_soc;
+
+ if (chg_time_sec > catch_up_sec)
+ return new_soc;
+
+ numerator = (catch_up_sec - chg_time_sec) * prev_soc
+ + chg_time_sec * new_soc;
+ scaled_soc = numerator / catch_up_sec;
+
+ pr_debug("cts=%d new_soc=%d prev_soc=%d scaled_soc=%d\n",
+ chg_time_sec, new_soc, prev_soc, scaled_soc);
+
+ return scaled_soc;
+}
+
+static int report_eoc(struct qpnp_bms_chip *chip)
+{
+ int rc = -EINVAL;
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc) {
+ pr_err("Unable to get battery 'STATUS' rc=%d\n", rc);
+ } else if (ret.intval != POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("Report EOC to charger\n");
+ ret.intval = POWER_SUPPLY_STATUS_FULL;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc) {
+ pr_err("Unable to set 'STATUS' rc=%d\n", rc);
+ return rc;
+ }
+ chip->eoc_reported = true;
+ }
+ } else {
+ pr_err("battery psy not registered\n");
+ }
+
+ return rc;
+}
+
+static void check_recharge_condition(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ union power_supply_propval ret = {0,};
+ int status = get_battery_status(chip);
+
+ if (chip->last_soc > chip->dt.cfg_soc_resume_limit)
+ return;
+
+ if (status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ pr_debug("Unable to read battery status\n");
+ return;
+ }
+
+ /* Report recharge to charger for SOC based resume of charging */
+ if ((status != POWER_SUPPLY_STATUS_CHARGING) && chip->eoc_reported) {
+ ret.intval = POWER_SUPPLY_STATUS_CHARGING;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc < 0) {
+ pr_err("Unable to set battery property rc=%d\n", rc);
+ } else {
+ pr_info("soc dropped below resume_soc soc=%d resume_soc=%d, restart charging\n",
+ chip->last_soc,
+ chip->dt.cfg_soc_resume_limit);
+ chip->eoc_reported = false;
+ }
+ }
+}
+
+static void check_eoc_condition(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ int status = get_battery_status(chip);
+ union power_supply_propval ret = {0,};
+
+ if (status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ pr_err("Unable to read battery status\n");
+ return;
+ }
+
+ /*
+ * Check battery status:
+ * if last_soc is 100 and battery status is still charging
+ * reset ocv_at_100 and force reporting of eoc to charger.
+ */
+ if ((chip->last_soc == 100) &&
+ (status == POWER_SUPPLY_STATUS_CHARGING))
+ chip->ocv_at_100 = -EINVAL;
+
+ /*
+ * Store the OCV value at 100. If the new ocv is greater than
+ * ocv_at_100 (battery settles), update ocv_at_100. Else
+ * if the SOC drops, reset ocv_at_100.
+ */
+ if (chip->ocv_at_100 == -EINVAL) {
+ if (chip->last_soc == 100) {
+ if (chip->dt.cfg_report_charger_eoc) {
+ rc = report_eoc(chip);
+ if (!rc) {
+ /*
+ * update ocv_at_100 only if EOC is
+ * reported successfully.
+ */
+ chip->ocv_at_100 = chip->last_ocv_uv;
+ pr_debug("Battery FULL\n");
+ } else {
+ pr_err("Unable to report eoc rc=%d\n",
+ rc);
+ chip->ocv_at_100 = -EINVAL;
+ }
+ }
+ if (chip->dt.cfg_use_reported_soc) {
+ /* begin reported_soc process */
+ chip->reported_soc_in_use = true;
+ chip->charger_removed_since_full = false;
+ chip->charger_reinserted = false;
+ chip->reported_soc = 100;
+ pr_debug("Begin reported_soc process\n");
+ }
+ }
+ } else {
+ if (chip->last_ocv_uv >= chip->ocv_at_100) {
+ pr_debug("new_ocv(%d) > ocv_at_100(%d) maintaining SOC to 100\n",
+ chip->last_ocv_uv, chip->ocv_at_100);
+ chip->ocv_at_100 = chip->last_ocv_uv;
+ chip->last_soc = 100;
+ } else if (chip->last_soc != 100) {
+ /*
+ * Report that the battery is discharging.
+ * This gets called once when the SOC falls
+ * below 100.
+ */
+ if (chip->reported_soc_in_use
+ && chip->reported_soc == 100) {
+ pr_debug("reported_soc=100, last_soc=%d, do not send DISCHARING status\n",
+ chip->last_soc);
+ } else {
+ ret.intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ }
+ pr_debug("SOC dropped (%d) discarding ocv_at_100\n",
+ chip->last_soc);
+ chip->ocv_at_100 = -EINVAL;
+ }
+ }
+}
+
+static int report_voltage_based_soc(struct qpnp_bms_chip *chip)
+{
+ pr_debug("Reported voltage based soc = %d\n",
+ chip->prev_voltage_based_soc);
+ return chip->prev_voltage_based_soc;
+}
+
+static int prepare_reported_soc(struct qpnp_bms_chip *chip)
+{
+ if (chip->charger_removed_since_full == false) {
+ /*
+ * charger is not removed since full,
+ * keep reported_soc as 100 and calculate the delta soc
+ * between reported_soc and last_soc
+ */
+ chip->reported_soc = 100;
+ chip->reported_soc_delta = 100 - chip->last_soc;
+ pr_debug("Keep at reported_soc 100, reported_soc_delta=%d, last_soc=%d\n",
+ chip->reported_soc_delta,
+ chip->last_soc);
+ } else {
+ /* charger is removed since full */
+ if (chip->charger_reinserted) {
+ /*
+ * charger reinserted, keep the reported_soc
+ * until it equals to last_soc.
+ */
+ if (chip->reported_soc == chip->last_soc) {
+ chip->reported_soc_in_use = false;
+ chip->reported_soc_high_current = false;
+ pr_debug("reported_soc equals to last_soc, stop reported_soc process\n");
+ }
+ chip->reported_soc_change_sec = 0;
+ }
+ }
+ pr_debug("Reporting reported_soc=%d, last_soc=%d\n",
+ chip->reported_soc, chip->last_soc);
+ return chip->reported_soc;
+}
+
+#define SOC_CATCHUP_SEC_MAX 600
+#define SOC_CATCHUP_SEC_PER_PERCENT 60
+#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX / SOC_CATCHUP_SEC_PER_PERCENT)
+#define SOC_CHANGE_PER_SEC 5
+static int report_vm_bms_soc(struct qpnp_bms_chip *chip)
+{
+ int soc, soc_change, batt_temp, rc;
+ int time_since_last_change_sec = 0, charge_time_sec = 0;
+ unsigned long last_change_sec;
+ bool charging;
+
+ soc = chip->calculated_soc;
+
+ last_change_sec = chip->last_soc_change_sec;
+ calculate_delta_time(&last_change_sec, &time_since_last_change_sec);
+
+ charging = is_battery_charging(chip);
+
+ pr_debug("charging=%d last_soc=%d last_soc_unbound=%d\n",
+ charging, chip->last_soc, chip->last_soc_unbound);
+ /*
+ * account for charge time - limit it to SOC_CATCHUP_SEC to
+ * avoid overflows when charging continues for extended periods
+ */
+ if (charging && chip->last_soc != -EINVAL) {
+ if (chip->charge_start_tm_sec == 0 ||
+ (chip->catch_up_time_sec == 0 &&
+ (abs(soc - chip->last_soc) >= MIN_SOC_UUC))) {
+ /*
+ * calculating soc for the first time
+ * after start of chg. Initialize catchup time
+ */
+ if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
+ chip->catch_up_time_sec =
+ (soc - chip->last_soc)
+ * SOC_CATCHUP_SEC_PER_PERCENT;
+ else
+ chip->catch_up_time_sec = SOC_CATCHUP_SEC_MAX;
+
+ chip->chg_start_soc = chip->last_soc;
+
+ if (chip->catch_up_time_sec < 0)
+ chip->catch_up_time_sec = 0;
+ chip->charge_start_tm_sec = last_change_sec;
+
+ pr_debug("chg_start_soc=%d charge_start_tm_sec=%d catch_up_time_sec=%d\n",
+ chip->chg_start_soc, chip->charge_start_tm_sec,
+ chip->catch_up_time_sec);
+ }
+
+ charge_time_sec = min(SOC_CATCHUP_SEC_MAX, (int)last_change_sec
+ - chip->charge_start_tm_sec);
+
+ /* end catchup if calculated soc and last soc are same */
+ if (chip->last_soc == soc) {
+ chip->catch_up_time_sec = 0;
+ chip->chg_start_soc = chip->last_soc;
+ }
+ }
+
+ if (chip->last_soc != -EINVAL) {
+ /*
+ * last_soc < soc ... if we have not been charging at all
+ * since the last time this was called, report previous SoC.
+ * Otherwise, scale and catch up.
+ */
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc)
+ batt_temp = BMS_DEFAULT_TEMP;
+
+ if (chip->last_soc < soc && !charging)
+ soc = chip->last_soc;
+ else if (chip->last_soc < soc && soc != 100)
+ soc = scale_soc_while_chg(chip, charge_time_sec,
+ chip->catch_up_time_sec,
+ soc, chip->chg_start_soc);
+
+ /*
+ * if the battery is close to cutoff or if the batt_temp
+ * is under the low-temp threshold allow bigger change
+ */
+ if (bms_wake_active(&chip->vbms_lv_wake_source) ||
+ (batt_temp <= chip->dt.cfg_low_temp_threshold))
+ soc_change = min((int)abs(chip->last_soc - soc),
+ time_since_last_change_sec);
+ else
+ soc_change = min((int)abs(chip->last_soc - soc),
+ time_since_last_change_sec
+ / SOC_CHANGE_PER_SEC);
+
+ if (chip->last_soc_unbound) {
+ chip->last_soc_unbound = false;
+ } else {
+ /*
+ * if soc have not been unbound by resume,
+ * only change reported SoC by 1.
+ */
+ soc_change = min(1, soc_change);
+ }
+
+ if (soc < chip->last_soc && soc != 0)
+ soc = chip->last_soc - soc_change;
+ if (soc > chip->last_soc && soc != 100)
+ soc = chip->last_soc + soc_change;
+ }
+
+ if (chip->last_soc != soc && !chip->last_soc_unbound)
+ chip->last_soc_change_sec = last_change_sec;
+
+ /*
+ * Check/update eoc under following condition:
+ * if there is change in soc:
+ * soc != chip->last_soc
+ * during bootup if soc is 100:
+ */
+ soc = bound_soc(soc);
+ if ((soc != chip->last_soc) || (soc == 100)) {
+ chip->last_soc = soc;
+ check_eoc_condition(chip);
+ if ((chip->dt.cfg_soc_resume_limit > 0) && !charging)
+ check_recharge_condition(chip);
+ }
+
+ pr_debug("last_soc=%d calculated_soc=%d soc=%d time_since_last_change=%d\n",
+ chip->last_soc, chip->calculated_soc,
+ soc, time_since_last_change_sec);
+
+ /*
+ * Backup the actual ocv (last_ocv_uv) and not the
+ * last_soc-interpolated ocv. This makes sure that
+ * the BMS algorithm always uses the correct ocv and
+ * can catch up on the last_soc (across reboots).
+ * We do not want the algorithm to be based of a wrong
+ * initial OCV.
+ */
+
+ backup_ocv_soc(chip, chip->last_ocv_uv, chip->last_soc);
+
+ if (chip->reported_soc_in_use)
+ return prepare_reported_soc(chip);
+
+ pr_debug("Reported SOC=%d\n", chip->last_soc);
+
+ return chip->last_soc;
+}
+
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
+{
+ int soc;
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ if (chip->dt.cfg_use_voltage_soc)
+ soc = report_voltage_based_soc(chip);
+ else
+ soc = report_vm_bms_soc(chip);
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ return soc;
+}
+
+static void btm_notify_vbat(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_bms_chip *chip = ctx;
+ int vbat_uv;
+ int rc;
+
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc) {
+ pr_err("error reading vbat_sns adc channel=%d, rc=%d\n",
+ VBAT_SNS, rc);
+ goto out;
+ }
+
+ pr_debug("vbat is at %d, state is at %d\n", vbat_uv, state);
+
+ if (state == ADC_TM_LOW_STATE) {
+ pr_debug("low voltage btm notification triggered\n");
+ if (vbat_uv <= (chip->vbat_monitor_params.low_thr
+ + VBATT_ERROR_MARGIN)) {
+ if (!bms_wake_active(&chip->vbms_lv_wake_source))
+ bms_stay_awake(&chip->vbms_lv_wake_source);
+
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ goto out;
+ }
+ } else if (state == ADC_TM_HIGH_STATE) {
+ pr_debug("high voltage btm notification triggered\n");
+ if (vbat_uv > chip->vbat_monitor_params.high_thr) {
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ if (bms_wake_active(&chip->vbms_lv_wake_source))
+ bms_relax(&chip->vbms_lv_wake_source);
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ goto out;
+ }
+ } else {
+ pr_debug("unknown voltage notification state: %d\n", state);
+ goto out;
+ }
+
+ if (chip->bms_psy_registered)
+ power_supply_changed(chip->bms_psy);
+
+out:
+ qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->vbat_monitor_params);
+}
+
+static int reset_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_DISABLE;
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("tm disable failed: %d\n", rc);
+ return rc;
+ }
+
+ if (bms_wake_active(&chip->vbms_lv_wake_source))
+ bms_relax(&chip->vbms_lv_wake_source);
+
+ return 0;
+}
+
+static int setup_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ chip->vbat_monitor_params.low_thr =
+ chip->dt.cfg_low_voltage_threshold;
+ chip->vbat_monitor_params.high_thr =
+ chip->dt.cfg_low_voltage_threshold
+ + VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.state_request = ADC_TM_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.channel = VBAT_SNS;
+ chip->vbat_monitor_params.btm_ctx = chip;
+ chip->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->vbat_monitor_params.threshold_notification = &btm_notify_vbat;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("adc-tm setup failed: %d\n", rc);
+ return rc;
+ }
+
+ pr_debug("vbat monitoring setup complete\n");
+ return 0;
+}
+
+static void very_low_voltage_check(struct qpnp_bms_chip *chip, int vbat_uv)
+{
+ if (!bms_wake_active(&chip->vbms_lv_wake_source)
+ && (vbat_uv <= chip->dt.cfg_low_voltage_threshold)) {
+ pr_debug("voltage=%d holding low voltage ws\n", vbat_uv);
+ bms_stay_awake(&chip->vbms_lv_wake_source);
+ } else if (bms_wake_active(&chip->vbms_lv_wake_source)
+ && (vbat_uv > chip->dt.cfg_low_voltage_threshold)) {
+ pr_debug("voltage=%d releasing low voltage ws\n", vbat_uv);
+ bms_relax(&chip->vbms_lv_wake_source);
+ }
+}
+
+static void cv_voltage_check(struct qpnp_bms_chip *chip, int vbat_uv)
+{
+ if (bms_wake_active(&chip->vbms_cv_wake_source)) {
+ if ((vbat_uv < (chip->dt.cfg_max_voltage_uv -
+ VBATT_ERROR_MARGIN + CV_DROP_MARGIN))
+ && !is_battery_taper_charging(chip)) {
+ pr_debug("Fell below CV, releasing cv ws\n");
+ chip->in_cv_state = false;
+ bms_relax(&chip->vbms_cv_wake_source);
+ } else if (!is_battery_charging(chip)) {
+ pr_debug("charging stopped, releasing cv ws\n");
+ chip->in_cv_state = false;
+ bms_relax(&chip->vbms_cv_wake_source);
+ }
+ } else if (!bms_wake_active(&chip->vbms_cv_wake_source)
+ && is_battery_charging(chip)
+ && ((vbat_uv > (chip->dt.cfg_max_voltage_uv -
+ VBATT_ERROR_MARGIN))
+ || is_battery_taper_charging(chip))) {
+ pr_debug("CC_TO_CV voltage=%d holding cv ws\n", vbat_uv);
+ chip->in_cv_state = true;
+ bms_stay_awake(&chip->vbms_cv_wake_source);
+ }
+}
+
+static void low_soc_check(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ if (chip->dt.cfg_low_soc_fifo_length < 1)
+ return;
+
+ mutex_lock(&chip->state_change_mutex);
+
+ if (chip->calculated_soc <= chip->dt.cfg_low_soc_calc_threshold) {
+ if (!chip->low_soc_fifo_set) {
+ pr_debug("soc=%d (low-soc) setting fifo_length to %d\n",
+ chip->calculated_soc,
+ chip->dt.cfg_low_soc_fifo_length);
+ rc = get_fifo_length(chip, S2_STATE,
+ &chip->s2_fifo_length);
+ if (rc) {
+ pr_err("Unable to get_fifo_length rc=%d", rc);
+ goto low_soc_exit;
+ }
+ rc = set_fifo_length(chip, S2_STATE,
+ chip->dt.cfg_low_soc_fifo_length);
+ if (rc) {
+ pr_err("Unable to set_fifo_length rc=%d", rc);
+ goto low_soc_exit;
+ }
+ chip->low_soc_fifo_set = true;
+ }
+ } else {
+ if (chip->low_soc_fifo_set) {
+ pr_debug("soc=%d setting back fifo_length to %d\n",
+ chip->calculated_soc,
+ chip->s2_fifo_length);
+ rc = set_fifo_length(chip, S2_STATE,
+ chip->s2_fifo_length);
+ if (rc) {
+ pr_err("Unable to set_fifo_length rc=%d", rc);
+ goto low_soc_exit;
+ }
+ chip->low_soc_fifo_set = false;
+ }
+ }
+
+low_soc_exit:
+ mutex_unlock(&chip->state_change_mutex);
+}
+
+static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
+{
+ int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
+ int rc, vbat_uv;
+
+ /* check if we have the averaged fifo data */
+ if (chip->voltage_soc_uv) {
+ vbat_uv = chip->voltage_soc_uv;
+ } else {
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc < 0) {
+ pr_err("adc vbat failed err = %d\n", rc);
+ return rc;
+ }
+ pr_debug("instant-voltage based voltage-soc\n");
+ }
+
+ voltage_range_uv = chip->dt.cfg_max_voltage_uv -
+ chip->dt.cfg_v_cutoff_uv;
+ voltage_remaining_uv = vbat_uv - chip->dt.cfg_v_cutoff_uv;
+ voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv;
+
+ voltage_based_soc = clamp(voltage_based_soc, 0, 100);
+
+ if (chip->prev_voltage_based_soc != voltage_based_soc
+ && chip->bms_psy_registered) {
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ }
+ chip->prev_voltage_based_soc = voltage_based_soc;
+
+ pr_debug("vbat used = %duv\n", vbat_uv);
+ pr_debug("Calculated voltage based soc=%d\n", voltage_based_soc);
+
+ if (voltage_based_soc == 100)
+ if (chip->dt.cfg_report_charger_eoc)
+ report_eoc(chip);
+
+ return 0;
+}
+
+static void calculate_reported_soc(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->last_soc < 0) {
+ pr_debug("last_soc is not ready, return\n");
+ return;
+ }
+
+ if (chip->reported_soc > chip->last_soc) {
+ /*send DISCHARGING status if the reported_soc drops from 100 */
+ if (chip->reported_soc == 100) {
+ ret.intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ pr_debug("Report discharging status, reported_soc=%d, last_soc=%d\n",
+ chip->reported_soc, chip->last_soc);
+ }
+ /*
+ * reported_soc_delta is used to prevent
+ * the big change in last_soc,
+ * this is not used in high current mode
+ */
+ if (chip->reported_soc_delta > 0)
+ chip->reported_soc_delta--;
+
+ if (chip->reported_soc_high_current)
+ chip->reported_soc--;
+ else
+ chip->reported_soc = chip->last_soc
+ + chip->reported_soc_delta;
+
+ pr_debug("New reported_soc=%d, last_soc is=%d\n",
+ chip->reported_soc, chip->last_soc);
+ } else {
+ chip->reported_soc_in_use = false;
+ chip->reported_soc_high_current = false;
+ pr_debug("reported_soc equals last_soc,stop reported_soc process\n");
+ }
+ pr_debug("bms power_supply_changed\n");
+ power_supply_changed(chip->bms_psy);
+}
+
+static int clamp_soc_based_on_voltage(struct qpnp_bms_chip *chip, int soc)
+{
+ int rc, vbat_uv;
+
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc < 0) {
+ pr_err("adc vbat failed err = %d\n", rc);
+ return soc;
+ }
+
+ /* only clamp when discharging */
+ if (is_battery_charging(chip))
+ return soc;
+
+ if (soc <= 0 && vbat_uv > chip->dt.cfg_v_cutoff_uv) {
+ pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
+ vbat_uv, chip->dt.cfg_v_cutoff_uv);
+ return 1;
+ }
+ pr_debug("not clamping, using soc = %d, vbat = %d and cutoff = %d\n",
+ soc, vbat_uv, chip->dt.cfg_v_cutoff_uv);
+ return soc;
+}
+
+static void battery_voltage_check(struct qpnp_bms_chip *chip)
+{
+ int rc, vbat_uv = 0;
+
+ rc = get_battery_voltage(chip, &vbat_uv);
+ if (rc < 0) {
+ pr_err("Failed to read battery-voltage rc=%d\n", rc);
+ } else {
+ very_low_voltage_check(chip, vbat_uv);
+ cv_voltage_check(chip, vbat_uv);
+ }
+}
+
+#define UI_SOC_CATCHUP_TIME (60)
+static void monitor_soc_work(struct work_struct *work)
+{
+ struct qpnp_bms_chip *chip = container_of(work,
+ struct qpnp_bms_chip,
+ monitor_soc_work.work);
+ int rc, new_soc = 0, batt_temp;
+
+ bms_stay_awake(&chip->vbms_soc_wake_source);
+
+ calculate_delta_time(&chip->tm_sec, &chip->delta_time_s);
+ pr_debug("elapsed_time=%d\n", chip->delta_time_s);
+
+ mutex_lock(&chip->last_soc_mutex);
+
+ if (!is_battery_present(chip)) {
+ /* if battery is not preset report 100% SOC */
+ pr_debug("battery gone, reporting 100\n");
+ chip->last_soc_invalid = true;
+ chip->last_soc = -EINVAL;
+ new_soc = 100;
+ } else {
+ battery_voltage_check(chip);
+
+ if (chip->dt.cfg_use_voltage_soc) {
+ calculate_soc_from_voltage(chip);
+ } else {
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Unable to read batt temp rc=%d, using default=%d\n",
+ rc, BMS_DEFAULT_TEMP);
+ batt_temp = BMS_DEFAULT_TEMP;
+ }
+
+ if (chip->last_soc_invalid) {
+ chip->last_soc_invalid = false;
+ chip->last_soc = -EINVAL;
+ }
+ new_soc = lookup_soc_ocv(chip, chip->last_ocv_uv,
+ batt_temp);
+ /* clamp soc due to BMS hw/sw immaturities */
+ new_soc = clamp_soc_based_on_voltage(chip, new_soc);
+
+ if (chip->calculated_soc != new_soc) {
+ pr_debug("SOC changed! new_soc=%d prev_soc=%d\n",
+ new_soc, chip->calculated_soc);
+ chip->calculated_soc = new_soc;
+ /*
+ * To recalculate the catch-up time, clear it
+ * when SOC changes.
+ */
+ chip->catch_up_time_sec = 0;
+
+ if (chip->calculated_soc == 100)
+ /* update last_soc immediately */
+ report_vm_bms_soc(chip);
+
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ } else if (chip->last_soc != chip->calculated_soc) {
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ } else {
+ report_vm_bms_soc(chip);
+ }
+ }
+ /* low SOC configuration */
+ low_soc_check(chip);
+ }
+ /*
+ * schedule the work only if last_soc has not caught up with
+ * the calculated soc or if we are using voltage based soc
+ */
+ if ((chip->last_soc != chip->calculated_soc) ||
+ chip->dt.cfg_use_voltage_soc)
+ schedule_delayed_work(&chip->monitor_soc_work,
+ msecs_to_jiffies(get_calculation_delay_ms(chip)));
+
+ if (chip->reported_soc_in_use && chip->charger_removed_since_full
+ && !chip->charger_reinserted) {
+ /* record the elapsed time after last reported_soc change */
+ chip->reported_soc_change_sec += chip->delta_time_s;
+ pr_debug("reported_soc_change_sec=%d\n",
+ chip->reported_soc_change_sec);
+
+ /* above the catch up time, calculate new reported_soc */
+ if (chip->reported_soc_change_sec > UI_SOC_CATCHUP_TIME) {
+ calculate_reported_soc(chip);
+ chip->reported_soc_change_sec = 0;
+ }
+ }
+
+ mutex_unlock(&chip->last_soc_mutex);
+
+ bms_relax(&chip->vbms_soc_wake_source);
+}
+
+static void voltage_soc_timeout_work(struct work_struct *work)
+{
+ struct qpnp_bms_chip *chip = container_of(work,
+ struct qpnp_bms_chip,
+ voltage_soc_timeout_work.work);
+
+ mutex_lock(&chip->bms_device_mutex);
+ if (!chip->bms_dev_open) {
+ pr_warn("BMS device not opened, using voltage based SOC\n");
+ chip->dt.cfg_use_voltage_soc = true;
+ }
+ mutex_unlock(&chip->bms_device_mutex);
+}
+
+static int get_prop_bms_capacity(struct qpnp_bms_chip *chip)
+{
+ return report_state_of_charge(chip);
+}
+
+static bool is_hi_power_state_requested(struct qpnp_bms_chip *chip)
+{
+
+ pr_debug("hi_power_state=0x%x\n", chip->hi_power_state);
+
+ if (chip->hi_power_state & VMBMS_IGNORE_ALL_BIT)
+ return false;
+ else
+ return !!chip->hi_power_state;
+
+}
+
+static int qpnp_vm_bms_config_power_state(struct qpnp_bms_chip *chip,
+ int usecase, bool hi_power_enable)
+{
+ if (usecase < 0) {
+ pr_err("Invalid power-usecase %x\n", usecase);
+ return -EINVAL;
+ }
+
+ if (hi_power_enable)
+ chip->hi_power_state |= usecase;
+ else
+ chip->hi_power_state &= ~usecase;
+
+ pr_debug("hi_power_state=%x usecase=%x hi_power_enable=%d\n",
+ chip->hi_power_state, usecase, hi_power_enable);
+
+ return 0;
+}
+
+static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
+{
+ return chip->current_now;
+}
+
+static int get_current_cc(struct qpnp_bms_chip *chip)
+{
+ int soc, cc_full;
+ int64_t current_charge;
+
+ if (chip->batt_data == NULL)
+ return -EINVAL;
+
+ cc_full = chip->batt_data->fcc;
+ if (chip->dt.cfg_use_voltage_soc)
+ soc = chip->prev_voltage_based_soc;
+ else
+ soc = chip->last_soc;
+
+ /*
+ * Full charge capacity is in mAh and soc is in %
+ * current_charge capacity is defined in uAh
+ * Hence conversion ((mAh * pct * 1000) / 100) => (mAh * pct * 10)
+ */
+ current_charge = cc_full * soc * 10;
+
+ return current_charge;
+}
+
+static enum power_supply_property bms_power_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE,
+ POWER_SUPPLY_PROP_RESISTANCE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_OCV,
+ POWER_SUPPLY_PROP_HI_POWER,
+ POWER_SUPPLY_PROP_LOW_POWER,
+ POWER_SUPPLY_PROP_BATTERY_TYPE,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+};
+
+static int
+qpnp_vm_bms_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ case POWER_SUPPLY_PROP_HI_POWER:
+ case POWER_SUPPLY_PROP_LOW_POWER:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int qpnp_vm_bms_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct qpnp_bms_chip *chip = power_supply_get_drvdata(psy);
+ int value = 0, rc;
+
+ val->intval = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_prop_bms_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = chip->battery_status;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = get_prop_bms_rbatt(chip);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE:
+ if (chip->batt_data->rbatt_capacitive_mohm > 0)
+ val->intval = chip->batt_data->rbatt_capacitive_mohm;
+ if (chip->dt.cfg_r_conn_mohm > 0)
+ val->intval += chip->dt.cfg_r_conn_mohm;
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE_NOW:
+ rc = get_batt_therm(chip, &value);
+ if (rc < 0)
+ value = BMS_DEFAULT_TEMP;
+ val->intval = get_rbatt(chip, chip->calculated_soc, value);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = get_prop_bms_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_BATTERY_TYPE:
+ val->strval = chip->batt_data->battery_type;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ val->intval = chip->last_ocv_uv;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ rc = get_batt_therm(chip, &value);
+ if (rc < 0)
+ value = BMS_DEFAULT_TEMP;
+ val->intval = value;
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ val->intval = is_hi_power_state_requested(chip);
+ break;
+ case POWER_SUPPLY_PROP_LOW_POWER:
+ val->intval = !is_hi_power_state_requested(chip);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ if (chip->dt.cfg_battery_aging_comp)
+ val->intval = chip->charge_cycles;
+ else
+ val->intval = -EINVAL;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = get_current_cc(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int qpnp_vm_bms_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+ struct qpnp_bms_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ chip->current_now = val->intval;
+ pr_debug("IBATT = %d\n", val->intval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+ cancel_delayed_work_sync(&chip->monitor_soc_work);
+ chip->last_ocv_uv = val->intval;
+ pr_debug("OCV = %d\n", val->intval);
+ schedule_delayed_work(&chip->monitor_soc_work, 0);
+ break;
+ case POWER_SUPPLY_PROP_HI_POWER:
+ rc = qpnp_vm_bms_config_power_state(chip, val->intval, true);
+ if (rc)
+ pr_err("Unable to set power-state rc=%d\n", rc);
+ break;
+ case POWER_SUPPLY_PROP_LOW_POWER:
+ rc = qpnp_vm_bms_config_power_state(chip, val->intval, false);
+ if (rc)
+ pr_err("Unable to set power-state rc=%d\n", rc);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static void bms_new_battery_setup(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ chip->last_soc_invalid = true;
+ /*
+ * disable and re-enable the BMS hardware to reset
+ * the realtime-FIFO data and restart accumulation
+ */
+ rc = qpnp_masked_write_base(chip, chip->base + EN_CTL_REG,
+ BMS_EN_BIT, 0);
+ /* delay for the BMS hardware to reset its state */
+ msleep(200);
+ rc |= qpnp_masked_write_base(chip, chip->base + EN_CTL_REG,
+ BMS_EN_BIT, BMS_EN_BIT);
+ /* delay for the BMS hardware to re-start */
+ msleep(200);
+ if (rc)
+ pr_err("Unable to reset BMS rc=%d\n", rc);
+
+ chip->last_ocv_uv = estimate_ocv(chip);
+
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+
+ /* update the sequence number */
+ chip->bms_data.seq_num = chip->seq_num++;
+
+ /* signal the read thread */
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+
+ /* hold a wake lock until the read thread is scheduled */
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+
+ mutex_unlock(&chip->bms_data_mutex);
+
+ /* reset aging variables */
+ if (chip->dt.cfg_battery_aging_comp) {
+ chip->charge_cycles = 0;
+ chip->charge_increase = 0;
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to reset aging data rc=%d\n", rc);
+ }
+}
+
+static void battery_insertion_check(struct qpnp_bms_chip *chip)
+{
+ int present = (int)is_battery_present(chip);
+
+ if (chip->battery_present != present) {
+ pr_debug("shadow_sts=%d status=%d\n",
+ chip->battery_present, present);
+ if (chip->battery_present != -EINVAL) {
+ if (present) {
+ /* new battery inserted */
+ bms_new_battery_setup(chip);
+ setup_vbat_monitoring(chip);
+ pr_debug("New battery inserted!\n");
+ } else {
+ /* battery removed */
+ reset_vbat_monitoring(chip);
+ pr_debug("Battery removed\n");
+ }
+ }
+ chip->battery_present = present;
+ }
+}
+
+static void battery_status_check(struct qpnp_bms_chip *chip)
+{
+ int status = get_battery_status(chip);
+
+ if (chip->battery_status != status) {
+ if (status == POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging started\n");
+ charging_began(chip);
+ } else if (chip->battery_status ==
+ POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging stopped\n");
+ charging_ended(chip);
+ }
+
+ if (status == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery full\n");
+ chip->battery_full = true;
+ } else if (chip->battery_status == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery not-full anymore\n");
+ chip->battery_full = false;
+ }
+ chip->battery_status = status;
+ }
+}
+
+#define HIGH_CURRENT_TH 2
+static void reported_soc_check_status(struct qpnp_bms_chip *chip)
+{
+ u8 present;
+
+ present = is_charger_present(chip);
+ pr_debug("usb_present=%d\n", present);
+
+ if (!present && !chip->charger_removed_since_full) {
+ chip->charger_removed_since_full = true;
+ pr_debug("reported_soc: charger removed since full\n");
+ return;
+ }
+ if (chip->reported_soc_high_current) {
+ pr_debug("reported_soc in high current mode, return\n");
+ return;
+ }
+ if ((chip->reported_soc - chip->last_soc) >
+ (100 - chip->dt.cfg_soc_resume_limit
+ + HIGH_CURRENT_TH)) {
+ chip->reported_soc_high_current = true;
+ chip->charger_removed_since_full = true;
+ chip->charger_reinserted = false;
+ pr_debug("reported_soc enters high current mode\n");
+ return;
+ }
+ if (present && chip->charger_removed_since_full) {
+ chip->charger_reinserted = true;
+ pr_debug("reported_soc: charger reinserted\n");
+ }
+ if (!present && chip->charger_removed_since_full) {
+ chip->charger_reinserted = false;
+ pr_debug("reported_soc: charger removed again\n");
+ }
+}
+
+static void qpnp_vm_bms_ext_power_changed(struct power_supply *psy)
+{
+ struct qpnp_bms_chip *chip = power_supply_get_drvdata(psy);
+
+ pr_debug("Triggered!\n");
+ battery_status_check(chip);
+ battery_insertion_check(chip);
+
+ mutex_lock(&chip->last_soc_mutex);
+ battery_voltage_check(chip);
+ mutex_unlock(&chip->last_soc_mutex);
+
+ if (chip->reported_soc_in_use)
+ reported_soc_check_status(chip);
+}
+
+
+static void dump_bms_data(const char *func, struct qpnp_bms_chip *chip)
+{
+ int i;
+
+ pr_debug("%s: fifo_count=%d acc_count=%d seq_num=%d\n",
+ func, chip->bms_data.num_fifo,
+ chip->bms_data.acc_count,
+ chip->bms_data.seq_num);
+
+ for (i = 0; i < chip->bms_data.num_fifo; i++)
+ pr_debug("fifo=%d fifo_uv=%d sample_interval=%d sample_count=%d\n",
+ i, chip->bms_data.fifo_uv[i],
+ chip->bms_data.sample_interval_ms,
+ chip->bms_data.sample_count);
+ pr_debug("avg_acc_data=%d\n", chip->bms_data.acc_uv);
+}
+
+static int read_and_populate_fifo_data(struct qpnp_bms_chip *chip)
+{
+ u8 fifo_count = 0, val = 0;
+ u8 fifo_data_raw[MAX_FIFO_REGS * 2];
+ u16 fifo_data;
+ int rc, i, j;
+ int64_t voltage_soc_avg = 0;
+
+ /* read the completed FIFO count */
+ rc = qpnp_read_wrapper(chip, &val, chip->base + STATUS2_REG, 1);
+ if (rc) {
+ pr_err("Unable to read STATUS2 register rc=%d\n", rc);
+ return rc;
+ }
+ fifo_count = (val & FIFO_CNT_SD_MASK) >> FIFO_CNT_SD_SHIFT;
+ pr_debug("fifo_count=%d\n", fifo_count);
+ if (!fifo_count) {
+ pr_debug("No data in FIFO\n");
+ return 0;
+ } else if (fifo_count > MAX_FIFO_REGS) {
+ pr_err("Invalid fifo-length %d rejecting data\n", fifo_count);
+ chip->bms_data.num_fifo = 0;
+ return 0;
+ }
+
+ /* read the FIFO data */
+ for (i = 0; i < fifo_count * 2; i++) {
+ rc = qpnp_read_wrapper(chip, &fifo_data_raw[i],
+ chip->base + FIFO_0_LSB_REG + i, 1);
+ if (rc) {
+ pr_err("Unable to read FIFO register(%d) rc=%d\n",
+ i, rc);
+ return rc;
+ }
+ }
+
+ /* populate the structure */
+ chip->bms_data.num_fifo = fifo_count;
+
+ rc = get_sample_interval(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_interval_ms);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_interval rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ rc = get_sample_count(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_count);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_count rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ for (i = 0, j = 0; i < fifo_count * 2; i = i + 2, j++) {
+ fifo_data = fifo_data_raw[i] | (fifo_data_raw[i + 1] << 8);
+ chip->bms_data.fifo_uv[j] = convert_vbatt_raw_to_uv(chip,
+ fifo_data, 0);
+ voltage_soc_avg += chip->bms_data.fifo_uv[j];
+ }
+ /* store the fifo average for voltage-based-soc */
+ chip->voltage_soc_uv = div_u64(voltage_soc_avg, fifo_count);
+
+ return 0;
+}
+
+static int read_and_populate_acc_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u32 acc_data_sd = 0, acc_count_sd = 0, avg_acc_data = 0;
+
+ /* read ACC SD count */
+ rc = qpnp_read_wrapper(chip, (u8 *)&acc_count_sd,
+ chip->base + ACC_CNT_SD_REG, 1);
+ if (rc) {
+ pr_err("Unable to read ACC_CNT_SD_REG rc=%d\n", rc);
+ return rc;
+ }
+ if (!acc_count_sd) {
+ pr_debug("No data in accumulator\n");
+ return 0;
+ }
+ /* read ACC SD data */
+ rc = qpnp_read_wrapper(chip, (u8 *)&acc_data_sd,
+ chip->base + ACC_DATA0_SD_REG, 3);
+ if (rc) {
+ pr_err("Unable to read ACC_DATA0_SD_REG rc=%d\n", rc);
+ return rc;
+ }
+ avg_acc_data = div_u64(acc_data_sd, acc_count_sd);
+
+ chip->bms_data.acc_uv = convert_vbatt_raw_to_uv(chip,
+ avg_acc_data, 0);
+ chip->bms_data.acc_count = acc_count_sd;
+
+ rc = get_sample_interval(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_interval_ms);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_interval rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ rc = get_sample_count(chip, chip->current_fsm_state,
+ &chip->bms_data.sample_count);
+ if (rc) {
+ pr_err("Unable to read state=%d sample_count rc=%d\n",
+ chip->current_fsm_state, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int clear_fifo_acc_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ reg = FIFO_CNT_SD_CLR_BIT | ACC_DATA_SD_CLR_BIT | ACC_CNT_SD_CLR_BIT;
+ rc = qpnp_masked_write_base(chip, chip->base + DATA_CTL2_REG, reg, reg);
+ if (rc)
+ pr_err("Unable to write DATA_CTL2_REG rc=%d\n", rc);
+
+ return rc;
+}
+
+static irqreturn_t bms_fifo_update_done_irq_handler(int irq, void *_chip)
+{
+ int rc;
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("fifo_update_done triggered\n");
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ if (chip->suspend_data_valid) {
+ pr_debug("Suspend data not processed yet\n");
+ goto fail_fifo;
+ }
+
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Unable to calibrate vadc rc=%d\n", rc);
+
+ /* clear old data */
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+ /*
+ * 1. Read FIFO and populate the bms_data
+ * 2. Clear FIFO data
+ * 3. Notify userspace
+ */
+ rc = update_fsm_state(chip);
+ if (rc) {
+ pr_err("Unable to read FSM state rc=%d\n", rc);
+ goto fail_fifo;
+ }
+ pr_debug("fsm_state=%d\n", chip->current_fsm_state);
+
+ rc = read_and_populate_fifo_data(chip);
+ if (rc) {
+ pr_err("Unable to read FIFO data rc=%d\n", rc);
+ goto fail_fifo;
+ }
+
+ rc = clear_fifo_acc_data(chip);
+ if (rc)
+ pr_err("Unable to clear FIFO/ACC data rc=%d\n", rc);
+
+ /* update the sequence number */
+ chip->bms_data.seq_num = chip->seq_num++;
+
+ dump_bms_data(__func__, chip);
+
+ /* signal the read thread */
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+
+ /* hold a wake lock until the read thread is scheduled */
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+fail_fifo:
+ mutex_unlock(&chip->bms_data_mutex);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bms_fsm_state_change_irq_handler(int irq, void *_chip)
+{
+ int rc;
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("fsm_state_changed triggered\n");
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ if (chip->suspend_data_valid) {
+ pr_debug("Suspend data not processed yet\n");
+ goto fail_state;
+ }
+
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Unable to calibrate vadc rc=%d\n", rc);
+
+ /* clear old data */
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+ /*
+ * 1. Read FIFO and ACC_DATA and populate the bms_data
+ * 2. Clear FIFO & ACC data
+ * 3. Notify userspace
+ */
+ pr_debug("prev_fsm_state=%d\n", chip->current_fsm_state);
+
+ rc = read_and_populate_fifo_data(chip);
+ if (rc) {
+ pr_err("Unable to read FIFO data rc=%d\n", rc);
+ goto fail_state;
+ }
+
+ /* read accumulator data */
+ rc = read_and_populate_acc_data(chip);
+ if (rc) {
+ pr_err("Unable to read ACC_SD data rc=%d\n", rc);
+ goto fail_state;
+ }
+
+ rc = update_fsm_state(chip);
+ if (rc) {
+ pr_err("Unable to read FSM state rc=%d\n", rc);
+ goto fail_state;
+ }
+
+ rc = clear_fifo_acc_data(chip);
+ if (rc)
+ pr_err("Unable to clear FIFO/ACC data rc=%d\n", rc);
+
+ /* update the sequence number */
+ chip->bms_data.seq_num = chip->seq_num++;
+
+ dump_bms_data(__func__, chip);
+
+ /* signal the read thread */
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+
+ /* hold a wake lock until the read thread is scheduled */
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+fail_state:
+ mutex_unlock(&chip->bms_data_mutex);
+ return IRQ_HANDLED;
+}
+
+static int read_shutdown_ocv_soc(struct qpnp_bms_chip *chip)
+{
+ u8 stored_soc = 0;
+ u16 stored_ocv = 0;
+ int rc;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&stored_ocv,
+ chip->base + BMS_OCV_REG, 2);
+ if (rc) {
+ pr_err("failed to read addr = %d %d\n",
+ chip->base + BMS_OCV_REG, rc);
+ return -EINVAL;
+ }
+
+ /* if shutdwon ocv is invalid, reject shutdown soc too */
+ if (!stored_ocv || (stored_ocv == OCV_INVALID)) {
+ pr_debug("shutdown OCV %d - invalid\n", stored_ocv);
+ chip->shutdown_ocv = OCV_INVALID;
+ chip->shutdown_soc = SOC_INVALID;
+ return -EINVAL;
+ }
+ chip->shutdown_ocv = stored_ocv * 1000;
+
+ /*
+ * The previous SOC is stored in the first 7 bits of the register as
+ * (Shutdown SOC + 1). This allows for register reset values of both
+ * 0x00 and 0xFF.
+ */
+ rc = qpnp_read_wrapper(chip, &stored_soc, chip->base + BMS_SOC_REG, 1);
+ if (rc) {
+ pr_err("failed to read addr = %d %d\n",
+ chip->base + BMS_SOC_REG, rc);
+ return -EINVAL;
+ }
+
+ if (!stored_soc || stored_soc == SOC_INVALID) {
+ chip->shutdown_soc = SOC_INVALID;
+ chip->shutdown_ocv = OCV_INVALID;
+ return -EINVAL;
+ }
+ chip->shutdown_soc = (stored_soc >> 1) - 1;
+
+ pr_debug("shutdown_ocv=%d shutdown_soc=%d\n",
+ chip->shutdown_ocv, chip->shutdown_soc);
+
+ return 0;
+}
+
+static int interpolate_current_comp(int die_temp)
+{
+ int i;
+ int num_rows = ARRAY_SIZE(temp_curr_comp_lut);
+
+ if (die_temp <= (temp_curr_comp_lut[0].temp_decideg))
+ return temp_curr_comp_lut[0].current_ma;
+
+ if (die_temp >= (temp_curr_comp_lut[num_rows - 1].temp_decideg))
+ return temp_curr_comp_lut[num_rows - 1].current_ma;
+
+ for (i = 0; i < num_rows - 1; i++)
+ if (die_temp <= (temp_curr_comp_lut[i].temp_decideg))
+ break;
+
+ if (die_temp == (temp_curr_comp_lut[i].temp_decideg))
+ return temp_curr_comp_lut[i].current_ma;
+
+ return linear_interpolate(
+ temp_curr_comp_lut[i - 1].current_ma,
+ temp_curr_comp_lut[i - 1].temp_decideg,
+ temp_curr_comp_lut[i].current_ma,
+ temp_curr_comp_lut[i].temp_decideg,
+ die_temp);
+}
+
+static void adjust_pon_ocv(struct qpnp_bms_chip *chip, int batt_temp)
+{
+ int rc, current_ma, rbatt_mohm, die_temp, delta_uv, pc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, DIE_TEMP, &result);
+ if (rc) {
+ pr_err("error reading adc channel=%d, rc=%d\n", DIE_TEMP, rc);
+ } else {
+ pc = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
+ batt_temp, chip->last_ocv_uv / 1000);
+ /*
+ * For pc < 2, use the rbatt of pc = 2. This is to avoid
+ * the huge rbatt values at pc < 2 which can disrupt the pon_ocv
+ * calculations.
+ */
+ if (pc < 2)
+ pc = 2;
+ rbatt_mohm = get_rbatt(chip, pc, batt_temp);
+ /* convert die_temp to DECIDEGC */
+ die_temp = (int)result.physical / 100;
+ current_ma = interpolate_current_comp(die_temp);
+ delta_uv = rbatt_mohm * current_ma;
+ pr_debug("PON OCV changed from %d to %d pc=%d rbatt=%d current_ma=%d die_temp=%d batt_temp=%d delta_uv=%d\n",
+ chip->last_ocv_uv, chip->last_ocv_uv + delta_uv, pc,
+ rbatt_mohm, current_ma, die_temp, batt_temp, delta_uv);
+
+ chip->last_ocv_uv += delta_uv;
+ }
+}
+
+static int calculate_initial_soc(struct qpnp_bms_chip *chip)
+{
+ int rc, batt_temp = 0, est_ocv = 0;
+
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Unable to read batt temp, using default=%d\n",
+ BMS_DEFAULT_TEMP);
+ batt_temp = BMS_DEFAULT_TEMP;
+ }
+
+ rc = read_and_update_ocv(chip, batt_temp, true);
+ if (rc) {
+ pr_err("Unable to read PON OCV rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = read_shutdown_ocv_soc(chip);
+ if (rc < 0 || chip->dt.cfg_ignore_shutdown_soc)
+ chip->shutdown_soc_invalid = true;
+
+ if (chip->warm_reset) {
+ /*
+ * if we have powered on from warm reset -
+ * Always use shutdown SOC. If shudown SOC is invalid then
+ * estimate OCV
+ */
+ if (chip->shutdown_soc_invalid) {
+ pr_debug("Estimate OCV\n");
+ est_ocv = estimate_ocv(chip);
+ if (est_ocv <= 0) {
+ pr_err("Unable to estimate OCV rc=%d\n",
+ est_ocv);
+ return -EINVAL;
+ }
+ chip->last_ocv_uv = est_ocv;
+ chip->calculated_soc = lookup_soc_ocv(chip, est_ocv,
+ batt_temp);
+ } else {
+ chip->last_ocv_uv = chip->shutdown_ocv;
+ chip->last_soc = chip->shutdown_soc;
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->shutdown_ocv, batt_temp);
+ pr_debug("Using shutdown SOC\n");
+ }
+ } else {
+ /*
+ * In PM8916 2.0 PON OCV calculation is delayed due to
+ * change in the ordering of power-on sequence of LDO6.
+ * Adjust PON OCV to include current during PON.
+ */
+ if (chip->workaround_flag & WRKARND_PON_OCV_COMP)
+ adjust_pon_ocv(chip, batt_temp);
+
+ /* !warm_reset use PON OCV only if shutdown SOC is invalid */
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->last_ocv_uv, batt_temp);
+ if (!chip->shutdown_soc_invalid &&
+ (abs(chip->shutdown_soc - chip->calculated_soc) <
+ chip->dt.cfg_shutdown_soc_valid_limit)) {
+ chip->last_ocv_uv = chip->shutdown_ocv;
+ chip->last_soc = chip->shutdown_soc;
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->shutdown_ocv, batt_temp);
+ pr_debug("Using shutdown SOC\n");
+ } else {
+ chip->shutdown_soc_invalid = true;
+ pr_debug("Using PON SOC\n");
+ }
+ }
+ /* store the start-up OCV for voltage-based-soc */
+ chip->voltage_soc_uv = chip->last_ocv_uv;
+
+ pr_info("warm_reset=%d est_ocv=%d shutdown_soc_invalid=%d shutdown_ocv=%d shutdown_soc=%d last_soc=%d calculated_soc=%d last_ocv_uv=%d\n",
+ chip->warm_reset, est_ocv, chip->shutdown_soc_invalid,
+ chip->shutdown_ocv, chip->shutdown_soc, chip->last_soc,
+ chip->calculated_soc, chip->last_ocv_uv);
+
+ return 0;
+}
+
+static int calculate_initial_aging_comp(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ bool battery_removed = is_battery_replaced_in_offmode(chip);
+
+ if (battery_removed || chip->shutdown_soc_invalid) {
+ pr_info("Clearing aging data battery_removed=%d shutdown_soc_invalid=%d\n",
+ battery_removed, chip->shutdown_soc_invalid);
+ chip->charge_cycles = 0;
+ chip->charge_increase = 0;
+ rc = backup_charge_cycle(chip);
+ if (rc)
+ pr_err("Unable to reset aging data rc=%d\n", rc);
+ } else {
+ rc = read_chgcycle_data_from_backup(chip);
+ if (rc)
+ pr_err("Unable to read aging data rc=%d\n", rc);
+ }
+
+ pr_debug("Initial aging data charge_cycles=%u charge_increase=%u\n",
+ chip->charge_cycles, chip->charge_increase);
+ return rc;
+}
+
+static int bms_load_hw_defaults(struct qpnp_bms_chip *chip)
+{
+ u8 val, bms_en = 0;
+ u32 interval[2], count[2], fifo[2];
+ int rc;
+
+ /* S3 OCV tolerence threshold */
+ if (chip->dt.cfg_s3_ocv_tol_uv >= 0 &&
+ chip->dt.cfg_s3_ocv_tol_uv <= MAX_OCV_TOL_THRESHOLD) {
+ val = chip->dt.cfg_s3_ocv_tol_uv / OCV_TOL_LSB_UV;
+ rc = qpnp_masked_write_base(chip,
+ chip->base + S3_OCV_TOL_CTL_REG, 0xFF, val);
+ if (rc) {
+ pr_err("Unable to write s3_ocv_tol_threshold rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* S1 accumulator threshold */
+ if (chip->dt.cfg_s1_sample_count >= 1 &&
+ chip->dt.cfg_s1_sample_count <= MAX_SAMPLE_COUNT) {
+ val = (chip->dt.cfg_s1_sample_count > 1) ?
+ (ilog2(chip->dt.cfg_s1_sample_count) - 1) : 0;
+ rc = qpnp_masked_write_base(chip,
+ chip->base + S1_ACC_CNT_REG,
+ ACC_CNT_MASK, val);
+ if (rc) {
+ pr_err("Unable to write s1 sample count rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* S2 accumulator threshold */
+ if (chip->dt.cfg_s2_sample_count >= 1 &&
+ chip->dt.cfg_s2_sample_count <= MAX_SAMPLE_COUNT) {
+ val = (chip->dt.cfg_s2_sample_count > 1) ?
+ (ilog2(chip->dt.cfg_s2_sample_count) - 1) : 0;
+ rc = qpnp_masked_write_base(chip,
+ chip->base + S2_ACC_CNT_REG,
+ ACC_CNT_MASK, val);
+ if (rc) {
+ pr_err("Unable to write s2 sample count rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s1_sample_interval_ms >= 0 &&
+ chip->dt.cfg_s1_sample_interval_ms <= MAX_SAMPLE_INTERVAL) {
+ val = chip->dt.cfg_s1_sample_interval_ms / 10;
+ rc = qpnp_write_wrapper(chip, &val,
+ chip->base + S1_SAMPLE_INTVL_REG, 1);
+ if (rc) {
+ pr_err("Unable to write s1 sample inteval rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s2_sample_interval_ms >= 0 &&
+ chip->dt.cfg_s2_sample_interval_ms <= MAX_SAMPLE_INTERVAL) {
+ val = chip->dt.cfg_s2_sample_interval_ms / 10;
+ rc = qpnp_write_wrapper(chip, &val,
+ chip->base + S2_SAMPLE_INTVL_REG, 1);
+ if (rc) {
+ pr_err("Unable to write s2 sample inteval rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s1_fifo_length >= 0 &&
+ chip->dt.cfg_s1_fifo_length <= MAX_FIFO_REGS) {
+ rc = qpnp_masked_write_base(chip, chip->base + FIFO_LENGTH_REG,
+ S1_FIFO_LENGTH_MASK,
+ chip->dt.cfg_s1_fifo_length);
+ if (rc) {
+ pr_err("Unable to write s1 fifo length rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->dt.cfg_s2_fifo_length >= 0 &&
+ chip->dt.cfg_s2_fifo_length <= MAX_FIFO_REGS) {
+ rc = qpnp_masked_write_base(chip, chip->base +
+ FIFO_LENGTH_REG, S2_FIFO_LENGTH_MASK,
+ chip->dt.cfg_s2_fifo_length
+ << S2_FIFO_LENGTH_SHIFT);
+ if (rc) {
+ pr_err("Unable to write s2 fifo length rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ get_sample_interval(chip, S1_STATE, &interval[0]);
+ get_sample_interval(chip, S2_STATE, &interval[1]);
+ get_sample_count(chip, S1_STATE, &count[0]);
+ get_sample_count(chip, S2_STATE, &count[1]);
+ get_fifo_length(chip, S1_STATE, &fifo[0]);
+ get_fifo_length(chip, S2_STATE, &fifo[1]);
+
+ /* Force the BMS state to S2 at boot-up */
+ rc = force_fsm_state(chip, S2_STATE);
+ if (rc) {
+ pr_err("Unable to force S2 state rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_read_wrapper(chip, &bms_en, chip->base + EN_CTL_REG, 1);
+ if (rc) {
+ pr_err("Unable to read BMS_EN state rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = update_fsm_state(chip);
+ if (rc) {
+ pr_err("Unable to read FSM state rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_info("BMS_EN=%d Sample_Interval-S1=[%d]S2=[%d] Sample_Count-S1=[%d]S2=[%d] Fifo_Length-S1=[%d]S2=[%d] FSM_state=%d\n",
+ !!bms_en, interval[0], interval[1], count[0],
+ count[1], fifo[0], fifo[1],
+ chip->current_fsm_state);
+
+ return 0;
+}
+
+static ssize_t vm_bms_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int rc;
+ struct qpnp_bms_chip *chip = file->private_data;
+
+ if (!chip->data_ready && (file->f_flags & O_NONBLOCK)) {
+ rc = -EAGAIN;
+ goto fail_read;
+ }
+
+ rc = wait_event_interruptible(chip->bms_wait_q, chip->data_ready);
+ if (rc) {
+ pr_debug("wait failed! rc=%d\n", rc);
+ goto fail_read;
+ }
+
+ if (!chip->data_ready) {
+ pr_debug("No Data, false wakeup\n");
+ rc = -EFAULT;
+ goto fail_read;
+ }
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ if (copy_to_user(buf, &chip->bms_data, sizeof(chip->bms_data))) {
+ pr_err("Failed in copy_to_user\n");
+ mutex_unlock(&chip->bms_data_mutex);
+ rc = -EFAULT;
+ goto fail_read;
+ }
+ pr_debug("Data copied!!\n");
+ chip->data_ready = 0;
+
+ mutex_unlock(&chip->bms_data_mutex);
+ /* wakelock-timeout for userspace to pick up */
+ pm_wakeup_event(chip->dev, BMS_READ_TIMEOUT);
+
+ return sizeof(chip->bms_data);
+
+fail_read:
+ pm_relax(chip->dev);
+ return rc;
+}
+
+static int vm_bms_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = container_of(inode->i_cdev,
+ struct qpnp_bms_chip, bms_cdev);
+
+ mutex_lock(&chip->bms_device_mutex);
+
+ if (chip->bms_dev_open) {
+ pr_debug("BMS device already open\n");
+ mutex_unlock(&chip->bms_device_mutex);
+ return -EBUSY;
+ }
+
+ chip->bms_dev_open = true;
+ file->private_data = chip;
+ pr_debug("BMS device opened\n");
+
+ mutex_unlock(&chip->bms_device_mutex);
+
+ return 0;
+}
+
+static int vm_bms_release(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = container_of(inode->i_cdev,
+ struct qpnp_bms_chip, bms_cdev);
+
+ mutex_lock(&chip->bms_device_mutex);
+
+ chip->bms_dev_open = false;
+ pm_relax(chip->dev);
+ pr_debug("BMS device closed\n");
+
+ mutex_unlock(&chip->bms_device_mutex);
+
+ return 0;
+}
+
+static const struct file_operations bms_fops = {
+ .owner = THIS_MODULE,
+ .open = vm_bms_open,
+ .read = vm_bms_read,
+ .release = vm_bms_release,
+};
+
+static void bms_init_defaults(struct qpnp_bms_chip *chip)
+{
+ chip->data_ready = 0;
+ chip->last_ocv_raw = OCV_UNINITIALIZED;
+ chip->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ chip->battery_present = -EINVAL;
+ chip->calculated_soc = -EINVAL;
+ chip->last_soc = -EINVAL;
+ chip->vbms_lv_wake_source.disabled = 1;
+ chip->vbms_cv_wake_source.disabled = 1;
+ chip->vbms_soc_wake_source.disabled = 1;
+ chip->ocv_at_100 = -EINVAL;
+ chip->prev_soc_uuc = -EINVAL;
+ chip->charge_cycles = 0;
+ chip->start_soc = 0;
+ chip->end_soc = 0;
+ chip->charge_increase = 0;
+}
+
+#define REQUEST_IRQ(chip, rc, irq_name) \
+do { \
+ rc = devm_request_threaded_irq(chip->dev, \
+ chip->irq_name##_irq.irq, NULL, \
+ bms_##irq_name##_irq_handler, \
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, \
+ #irq_name, chip); \
+ if (rc < 0) \
+ pr_err("Unable to request " #irq_name " irq: %d\n", rc);\
+} while (0)
+
+#define FIND_IRQ(chip, pdev, irq_name, rc) \
+do { \
+ chip->irq_name##_irq.irq = of_irq_get_byname(child, \
+ #irq_name); \
+ if (chip->irq_name##_irq.irq < 0) { \
+ rc = chip->irq_name##_irq.irq; \
+ pr_err("Unable to get " #irq_name " irq rc=%d\n", rc); \
+ } \
+} while (0)
+
+static int bms_request_irqs(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ REQUEST_IRQ(chip, rc, fifo_update_done);
+ if (rc < 0)
+ return rc;
+
+ REQUEST_IRQ(chip, rc, fsm_state_change);
+ if (rc < 0)
+ return rc;
+
+ /* Disable the state change IRQ */
+ disable_bms_irq(&chip->fsm_state_change_irq);
+ enable_irq_wake(chip->fifo_update_done_irq.irq);
+
+ return 0;
+}
+
+static int bms_find_irqs(struct qpnp_bms_chip *chip, struct device_node *child)
+{
+ int rc = 0;
+
+ FIND_IRQ(chip, child, fifo_update_done, rc);
+ if (rc < 0)
+ return rc;
+ FIND_IRQ(chip, child, fsm_state_change, rc);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+
+static int64_t read_battery_id(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result);
+ if (rc) {
+ pr_err("error reading batt id channel = %d, rc = %d\n",
+ LR_MUX2_BAT_ID, rc);
+ return rc;
+ }
+
+ return result.physical;
+}
+
+static int show_bms_config(struct seq_file *m, void *data)
+{
+ struct qpnp_bms_chip *chip = m->private;
+ int s1_sample_interval, s2_sample_interval;
+ int s1_sample_count, s2_sample_count;
+ int s1_fifo_length, s2_fifo_length;
+
+ get_sample_interval(chip, S1_STATE, &s1_sample_interval);
+ get_sample_interval(chip, S2_STATE, &s2_sample_interval);
+ get_sample_count(chip, S1_STATE, &s1_sample_count);
+ get_sample_count(chip, S2_STATE, &s2_sample_count);
+ get_fifo_length(chip, S1_STATE, &s1_fifo_length);
+ get_fifo_length(chip, S2_STATE, &s2_fifo_length);
+
+ seq_printf(m, "r_conn_mohm\t=\t%d\n"
+ "v_cutoff_uv\t=\t%d\n"
+ "max_voltage_uv\t=\t%d\n"
+ "use_voltage_soc\t=\t%d\n"
+ "low_soc_calc_threshold\t=\t%d\n"
+ "low_soc_calculate_soc_ms\t=\t%d\n"
+ "low_voltage_threshold\t=\t%d\n"
+ "low_voltage_calculate_soc_ms\t=\t%d\n"
+ "calculate_soc_ms\t=\t%d\n"
+ "voltage_soc_timeout_ms\t=\t%d\n"
+ "ignore_shutdown_soc\t=\t%d\n"
+ "shutdown_soc_valid_limit\t=\t%d\n"
+ "force_s3_on_suspend\t=\t%d\n"
+ "report_charger_eoc\t=\t%d\n"
+ "aging_compensation\t=\t%d\n"
+ "use_reported_soc\t=\t%d\n"
+ "s1_sample_interval_ms\t=\t%d\n"
+ "s2_sample_interval_ms\t=\t%d\n"
+ "s1_sample_count\t=\t%d\n"
+ "s2_sample_count\t=\t%d\n"
+ "s1_fifo_length\t=\t%d\n"
+ "s2_fifo_length\t=\t%d\n",
+ chip->dt.cfg_r_conn_mohm,
+ chip->dt.cfg_v_cutoff_uv,
+ chip->dt.cfg_max_voltage_uv,
+ chip->dt.cfg_use_voltage_soc,
+ chip->dt.cfg_low_soc_calc_threshold,
+ chip->dt.cfg_low_soc_calculate_soc_ms,
+ chip->dt.cfg_low_voltage_threshold,
+ chip->dt.cfg_low_voltage_calculate_soc_ms,
+ chip->dt.cfg_calculate_soc_ms,
+ chip->dt.cfg_voltage_soc_timeout_ms,
+ chip->dt.cfg_ignore_shutdown_soc,
+ chip->dt.cfg_shutdown_soc_valid_limit,
+ chip->dt.cfg_force_s3_on_suspend,
+ chip->dt.cfg_report_charger_eoc,
+ chip->dt.cfg_battery_aging_comp,
+ chip->dt.cfg_use_reported_soc,
+ s1_sample_interval,
+ s2_sample_interval,
+ s1_sample_count,
+ s2_sample_count,
+ s1_fifo_length,
+ s2_fifo_length);
+
+ return 0;
+}
+
+static int bms_config_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = inode->i_private;
+
+ return single_open(file, show_bms_config, chip);
+}
+
+static const struct file_operations bms_config_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = bms_config_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int show_bms_status(struct seq_file *m, void *data)
+{
+ struct qpnp_bms_chip *chip = m->private;
+
+ seq_printf(m, "bms_psy_registered\t=\t%d\n"
+ "bms_dev_open\t=\t%d\n"
+ "warm_reset\t=\t%d\n"
+ "battery_status\t=\t%d\n"
+ "battery_present\t=\t%d\n"
+ "in_cv_state\t=\t%d\n"
+ "calculated_soc\t=\t%d\n"
+ "last_soc\t=\t%d\n"
+ "last_ocv_uv\t=\t%d\n"
+ "last_ocv_raw\t=\t%d\n"
+ "last_soc_unbound\t=\t%d\n"
+ "current_fsm_state\t=\t%d\n"
+ "current_now\t=\t%d\n"
+ "ocv_at_100\t=\t%d\n"
+ "low_voltage_ws_active\t=\t%d\n"
+ "cv_ws_active\t=\t%d\n",
+ chip->bms_psy_registered,
+ chip->bms_dev_open,
+ chip->warm_reset,
+ chip->battery_status,
+ chip->battery_present,
+ chip->in_cv_state,
+ chip->calculated_soc,
+ chip->last_soc,
+ chip->last_ocv_uv,
+ chip->last_ocv_raw,
+ chip->last_soc_unbound,
+ chip->current_fsm_state,
+ chip->current_now,
+ chip->ocv_at_100,
+ bms_wake_active(&chip->vbms_lv_wake_source),
+ bms_wake_active(&chip->vbms_cv_wake_source));
+ return 0;
+}
+
+static int bms_status_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = inode->i_private;
+
+ return single_open(file, show_bms_status, chip);
+}
+
+static const struct file_operations bms_status_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = bms_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int show_bms_data(struct seq_file *m, void *data)
+{
+ struct qpnp_bms_chip *chip = m->private;
+ int i;
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ seq_printf(m, "seq_num=%d\n", chip->bms_data.seq_num);
+ for (i = 0; i < chip->bms_data.num_fifo; i++)
+ seq_printf(m, "fifo_uv[%d]=%d sample_count=%d interval_ms=%d\n",
+ i, chip->bms_data.fifo_uv[i],
+ chip->bms_data.sample_count,
+ chip->bms_data.sample_interval_ms);
+ seq_printf(m, "acc_uv=%d sample_count=%d sample_interval=%d\n",
+ chip->bms_data.acc_uv, chip->bms_data.acc_count,
+ chip->bms_data.sample_interval_ms);
+
+ mutex_unlock(&chip->bms_data_mutex);
+
+ return 0;
+}
+
+static int bms_data_open(struct inode *inode, struct file *file)
+{
+ struct qpnp_bms_chip *chip = inode->i_private;
+
+ return single_open(file, show_bms_data, chip);
+}
+
+static const struct file_operations bms_data_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = bms_data_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int set_battery_data(struct qpnp_bms_chip *chip)
+{
+ int64_t battery_id;
+ int rc = 0;
+ struct bms_battery_data *batt_data;
+ struct device_node *node;
+
+ battery_id = read_battery_id(chip);
+ if (battery_id < 0) {
+ pr_err("cannot read battery id err = %lld\n", battery_id);
+ return battery_id;
+ }
+ node = of_find_node_by_name(chip->pdev->dev.of_node,
+ "qcom,battery-data");
+ if (!node) {
+ pr_err("No available batterydata\n");
+ return -EINVAL;
+ }
+
+ batt_data = devm_kzalloc(chip->dev,
+ sizeof(struct bms_battery_data), GFP_KERNEL);
+ if (!batt_data)
+ return -EINVAL;
+
+ batt_data->fcc_temp_lut = devm_kzalloc(chip->dev,
+ sizeof(struct single_row_lut), GFP_KERNEL);
+ batt_data->pc_temp_ocv_lut = devm_kzalloc(chip->dev,
+ sizeof(struct pc_temp_ocv_lut), GFP_KERNEL);
+ batt_data->rbatt_sf_lut = devm_kzalloc(chip->dev,
+ sizeof(struct sf_lut), GFP_KERNEL);
+ batt_data->ibat_acc_lut = devm_kzalloc(chip->dev,
+ sizeof(struct ibat_temp_acc_lut), GFP_KERNEL);
+
+ batt_data->max_voltage_uv = -1;
+ batt_data->cutoff_uv = -1;
+ batt_data->iterm_ua = -1;
+
+ /*
+ * if the alloced luts are 0s, of_batterydata_read_data ignores
+ * them.
+ */
+ rc = of_batterydata_read_data(node, batt_data, battery_id);
+ if (rc || !batt_data->pc_temp_ocv_lut
+ || !batt_data->fcc_temp_lut
+ || !batt_data->rbatt_sf_lut
+ || !batt_data->ibat_acc_lut) {
+ pr_err("battery data load failed\n");
+ devm_kfree(chip->dev, batt_data->fcc_temp_lut);
+ devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut);
+ devm_kfree(chip->dev, batt_data->rbatt_sf_lut);
+ devm_kfree(chip->dev, batt_data->ibat_acc_lut);
+ devm_kfree(chip->dev, batt_data);
+ return rc;
+ }
+
+ if (batt_data->pc_temp_ocv_lut == NULL) {
+ pr_err("temp ocv lut table has not been loaded\n");
+ devm_kfree(chip->dev, batt_data->fcc_temp_lut);
+ devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut);
+ devm_kfree(chip->dev, batt_data->rbatt_sf_lut);
+ devm_kfree(chip->dev, batt_data->ibat_acc_lut);
+ devm_kfree(chip->dev, batt_data);
+
+ return -EINVAL;
+ }
+
+ /* check if ibat_acc_lut is valid */
+ if (!batt_data->ibat_acc_lut->rows) {
+ pr_info("ibat_acc_lut not present\n");
+ devm_kfree(chip->dev, batt_data->ibat_acc_lut);
+ batt_data->ibat_acc_lut = NULL;
+ }
+
+ /* Override battery properties if specified in the battery profile */
+ if (batt_data->max_voltage_uv >= 0)
+ chip->dt.cfg_max_voltage_uv = batt_data->max_voltage_uv;
+ if (batt_data->cutoff_uv >= 0)
+ chip->dt.cfg_v_cutoff_uv = batt_data->cutoff_uv;
+
+ chip->batt_data = batt_data;
+
+ return 0;
+}
+
+static int parse_pdev_dt_properties(struct qpnp_bms_chip *chip,
+ struct platform_device *pdev)
+{
+ struct device_node *child;
+ int rc;
+ unsigned int base;
+
+ chip->dev = &(pdev->dev);
+ chip->pdev = pdev;
+
+ if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+ pr_err("no child nodes found\n");
+ return -ENXIO;
+ }
+
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ rc = of_property_read_u32(child, "reg", &base);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ child->full_name, rc);
+ return -ENXIO;
+ }
+
+ pr_debug("Node name = %s\n", child->name);
+
+ if (strcmp("qcom,batt-pres-status",
+ child->name) == 0) {
+ chip->batt_pres_addr = base;
+ continue;
+ }
+
+ if (strcmp("qcom,qpnp-chg-pres",
+ child->name) == 0) {
+ chip->chg_pres_addr = base;
+ continue;
+ }
+
+ chip->base = base;
+ rc = bms_find_irqs(chip, child);
+ if (rc) {
+ pr_err("Could not find irqs rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->base == 0) {
+ dev_err(&pdev->dev, "BMS peripheral was not registered\n");
+ return -EINVAL;
+ }
+
+ pr_debug("bms-base=0x%04x bat-pres-reg=0x%04x qpnp-chg-pres=0x%04x\n",
+ chip->base, chip->batt_pres_addr, chip->chg_pres_addr);
+
+ return 0;
+}
+
+#define PROP_READ(chip_prop, qpnp_pdev_property, retval) \
+do { \
+ if (retval) \
+ break; \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," qpnp_pdev_property, \
+ &chip->dt.chip_prop); \
+ if (retval) { \
+ pr_err("Error reading " #qpnp_pdev_property \
+ " property %d\n", retval); \
+ } \
+} while (0)
+
+#define PROP_READ_OPTIONAL(chip_prop, qpnp_pdev_property, retval) \
+do { \
+ retval = of_property_read_u32(chip->pdev->dev.of_node, \
+ "qcom," qpnp_pdev_property, \
+ &chip->dt.chip_prop); \
+ if (retval) \
+ chip->dt.chip_prop = -EINVAL; \
+} while (0)
+
+static int parse_bms_dt_properties(struct qpnp_bms_chip *chip)
+{
+ int rc = 0;
+
+ PROP_READ(cfg_v_cutoff_uv, "v-cutoff-uv", rc);
+ PROP_READ(cfg_max_voltage_uv, "max-voltage-uv", rc);
+ PROP_READ(cfg_r_conn_mohm, "r-conn-mohm", rc);
+ PROP_READ(cfg_shutdown_soc_valid_limit,
+ "shutdown-soc-valid-limit", rc);
+ PROP_READ(cfg_low_soc_calc_threshold,
+ "low-soc-calculate-soc-threshold", rc);
+ PROP_READ(cfg_low_soc_calculate_soc_ms,
+ "low-soc-calculate-soc-ms", rc);
+ PROP_READ(cfg_low_voltage_calculate_soc_ms,
+ "low-voltage-calculate-soc-ms", rc);
+ PROP_READ(cfg_calculate_soc_ms, "calculate-soc-ms", rc);
+ PROP_READ(cfg_low_voltage_threshold, "low-voltage-threshold", rc);
+ PROP_READ(cfg_voltage_soc_timeout_ms,
+ "volatge-soc-timeout-ms", rc);
+
+ if (rc) {
+ pr_err("Missing required properties rc=%d\n", rc);
+ return rc;
+ }
+
+ PROP_READ_OPTIONAL(cfg_s1_sample_interval_ms,
+ "s1-sample-interval-ms", rc);
+ PROP_READ_OPTIONAL(cfg_s2_sample_interval_ms,
+ "s2-sample-interval-ms", rc);
+ PROP_READ_OPTIONAL(cfg_s1_sample_count, "s1-sample-count", rc);
+ PROP_READ_OPTIONAL(cfg_s2_sample_count, "s2-sample-count", rc);
+ PROP_READ_OPTIONAL(cfg_s1_fifo_length, "s1-fifo-length", rc);
+ PROP_READ_OPTIONAL(cfg_s2_fifo_length, "s2-fifo-length", rc);
+ PROP_READ_OPTIONAL(cfg_s3_ocv_tol_uv, "s3-ocv-tolerence-uv", rc);
+ PROP_READ_OPTIONAL(cfg_low_soc_fifo_length,
+ "low-soc-fifo-length", rc);
+ PROP_READ_OPTIONAL(cfg_soc_resume_limit, "resume-soc", rc);
+ PROP_READ_OPTIONAL(cfg_low_temp_threshold,
+ "low-temp-threshold", rc);
+ if (rc)
+ chip->dt.cfg_low_temp_threshold = 0;
+
+ PROP_READ_OPTIONAL(cfg_ibat_avg_samples,
+ "ibat-avg-samples", rc);
+ if (rc || (chip->dt.cfg_ibat_avg_samples <= 0) ||
+ (chip->dt.cfg_ibat_avg_samples > IAVG_SAMPLES))
+ chip->dt.cfg_ibat_avg_samples = IAVG_SAMPLES;
+
+ chip->dt.cfg_ignore_shutdown_soc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,ignore-shutdown-soc");
+ chip->dt.cfg_use_voltage_soc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,use-voltage-soc");
+ chip->dt.cfg_force_s3_on_suspend = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,force-s3-on-suspend");
+ chip->dt.cfg_report_charger_eoc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,report-charger-eoc");
+ chip->dt.cfg_disable_bms = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,disable-bms");
+ chip->dt.cfg_force_bms_active_on_charger = of_property_read_bool(
+ chip->pdev->dev.of_node,
+ "qcom,force-bms-active-on-charger");
+ chip->dt.cfg_battery_aging_comp = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,batt-aging-comp");
+ chip->dt.cfg_use_reported_soc = of_property_read_bool(
+ chip->pdev->dev.of_node, "qcom,use-reported-soc");
+ pr_debug("v_cutoff_uv=%d, max_v=%d\n", chip->dt.cfg_v_cutoff_uv,
+ chip->dt.cfg_max_voltage_uv);
+ pr_debug("r_conn=%d shutdown_soc_valid_limit=%d low_temp_threshold=%d ibat_avg_samples=%d\n",
+ chip->dt.cfg_r_conn_mohm,
+ chip->dt.cfg_shutdown_soc_valid_limit,
+ chip->dt.cfg_low_temp_threshold,
+ chip->dt.cfg_ibat_avg_samples);
+ pr_debug("ignore_shutdown_soc=%d, use_voltage_soc=%d low_soc_fifo_length=%d\n",
+ chip->dt.cfg_ignore_shutdown_soc,
+ chip->dt.cfg_use_voltage_soc,
+ chip->dt.cfg_low_soc_fifo_length);
+ pr_debug("force-s3-on-suspend=%d report-charger-eoc=%d disable-bms=%d disable-suspend-on-usb=%d aging_compensation=%d\n",
+ chip->dt.cfg_force_s3_on_suspend,
+ chip->dt.cfg_report_charger_eoc,
+ chip->dt.cfg_disable_bms,
+ chip->dt.cfg_force_bms_active_on_charger,
+ chip->dt.cfg_battery_aging_comp);
+ pr_debug("use-reported-soc is %d\n",
+ chip->dt.cfg_use_reported_soc);
+
+ return 0;
+}
+
+static int bms_get_adc(struct qpnp_bms_chip *chip,
+ struct platform_device *pdev)
+{
+ int rc = 0;
+
+ chip->vadc_dev = qpnp_get_vadc(&pdev->dev, "bms");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("vadc not found - defer probe rc=%d\n", rc);
+ else
+ pr_err("vadc property missing, rc=%d\n", rc);
+
+ return rc;
+ }
+
+ chip->adc_tm_dev = qpnp_get_adc_tm(&pdev->dev, "bms");
+ if (IS_ERR(chip->adc_tm_dev)) {
+ rc = PTR_ERR(chip->adc_tm_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("adc-tm not found - defer probe rc=%d\n", rc);
+ else
+ pr_err("adc-tm property missing, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int register_bms_char_device(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ rc = alloc_chrdev_region(&chip->dev_no, 0, 1, "vm_bms");
+ if (rc) {
+ pr_err("Unable to allocate chrdev rc=%d\n", rc);
+ return rc;
+ }
+ cdev_init(&chip->bms_cdev, &bms_fops);
+ rc = cdev_add(&chip->bms_cdev, chip->dev_no, 1);
+ if (rc) {
+ pr_err("Unable to add bms_cdev rc=%d\n", rc);
+ goto unregister_chrdev;
+ }
+
+ chip->bms_class = class_create(THIS_MODULE, "vm_bms");
+ if (IS_ERR_OR_NULL(chip->bms_class)) {
+ pr_err("Fail to create bms class\n");
+ rc = -EINVAL;
+ goto delete_cdev;
+ }
+ chip->bms_device = device_create(chip->bms_class,
+ NULL, chip->dev_no,
+ NULL, "vm_bms");
+ if (IS_ERR(chip->bms_device)) {
+ pr_err("Fail to create bms_device device\n");
+ rc = -EINVAL;
+ goto delete_cdev;
+ }
+
+ return 0;
+
+delete_cdev:
+ cdev_del(&chip->bms_cdev);
+unregister_chrdev:
+ unregister_chrdev_region(chip->dev_no, 1);
+ return rc;
+}
+
+static int qpnp_vm_bms_probe(struct platform_device *pdev)
+{
+ struct qpnp_bms_chip *chip;
+ struct device_node *revid_dev_node;
+ struct power_supply_config bms_psy_cfg;
+ int rc, vbatt = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ rc = bms_get_adc(chip, pdev);
+ if (rc < 0) {
+ pr_err("Failed to get adc rc=%d\n", rc);
+ return rc;
+ }
+
+ revid_dev_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property\n");
+ return -EINVAL;
+ }
+
+ chip->revid_data = get_revid_data(revid_dev_node);
+ if (IS_ERR(chip->revid_data)) {
+ pr_err("revid error rc = %ld\n", PTR_ERR(chip->revid_data));
+ return -EINVAL;
+ }
+ if ((chip->revid_data->pmic_subtype == PM8916_SUBTYPE) &&
+ chip->revid_data->rev4 == PM8916_V2P0_REV4)
+ chip->workaround_flag |= WRKARND_PON_OCV_COMP;
+
+ rc = qpnp_pon_is_warm_reset();
+ if (rc < 0) {
+ pr_err("Error reading warm reset status rc=%d\n", rc);
+ return rc;
+ }
+ chip->warm_reset = !!rc;
+
+ rc = parse_pdev_dt_properties(chip, pdev);
+ if (rc) {
+ pr_err("Error registering pdev resource rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = parse_bms_dt_properties(chip);
+ if (rc) {
+ pr_err("Unable to read all bms properties, rc = %d\n", rc);
+ return rc;
+ }
+
+ if (chip->dt.cfg_disable_bms) {
+ pr_info("VMBMS disabled (disable-bms = 1)\n");
+ rc = qpnp_masked_write_base(chip, chip->base + EN_CTL_REG,
+ BMS_EN_BIT, 0);
+ if (rc)
+ pr_err("Unable to disable VMBMS rc=%d\n", rc);
+ return -ENODEV;
+ }
+
+ rc = qpnp_read_wrapper(chip, chip->revision,
+ chip->base + REVISION1_REG, 2);
+ if (rc) {
+ pr_err("Error reading version register rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("BMS version: %hhu.%hhu\n",
+ chip->revision[1], chip->revision[0]);
+
+ dev_set_drvdata(&pdev->dev, chip);
+ device_init_wakeup(&pdev->dev, 1);
+ mutex_init(&chip->bms_data_mutex);
+ mutex_init(&chip->bms_device_mutex);
+ mutex_init(&chip->last_soc_mutex);
+ mutex_init(&chip->state_change_mutex);
+ init_waitqueue_head(&chip->bms_wait_q);
+
+ /* read battery-id and select the battery profile */
+ rc = set_battery_data(chip);
+ if (rc) {
+ pr_err("Unable to read battery data %d\n", rc);
+ goto fail_init;
+ }
+
+ /* set the battery profile */
+ rc = config_battery_data(chip->batt_data);
+ if (rc) {
+ pr_err("Unable to config battery data %d\n", rc);
+ goto fail_init;
+ }
+
+ wakeup_source_init(&chip->vbms_lv_wake_source.source, "vbms_lv_wake");
+ wakeup_source_init(&chip->vbms_cv_wake_source.source, "vbms_cv_wake");
+ wakeup_source_init(&chip->vbms_soc_wake_source.source, "vbms_soc_wake");
+ INIT_DELAYED_WORK(&chip->monitor_soc_work, monitor_soc_work);
+ INIT_DELAYED_WORK(&chip->voltage_soc_timeout_work,
+ voltage_soc_timeout_work);
+
+ bms_init_defaults(chip);
+ bms_load_hw_defaults(chip);
+
+ if (is_battery_present(chip)) {
+ rc = setup_vbat_monitoring(chip);
+ if (rc) {
+ pr_err("fail to configure vbat monitoring rc=%d\n",
+ rc);
+ goto fail_setup;
+ }
+ }
+
+ rc = bms_request_irqs(chip);
+ if (rc) {
+ pr_err("error requesting bms irqs, rc = %d\n", rc);
+ goto fail_irq;
+ }
+
+ battery_insertion_check(chip);
+ battery_status_check(chip);
+
+ /* character device to pass data to the userspace */
+ rc = register_bms_char_device(chip);
+ if (rc) {
+ pr_err("Unable to regiter '/dev/vm_bms' rc=%d\n", rc);
+ goto fail_bms_device;
+ }
+
+ the_chip = chip;
+ calculate_initial_soc(chip);
+ if (chip->dt.cfg_battery_aging_comp) {
+ rc = calculate_initial_aging_comp(chip);
+ if (rc)
+ pr_err("Unable to calculate initial aging data rc=%d\n",
+ rc);
+ }
+
+ /* setup & register the battery power supply */
+ chip->bms_psy_d.name = "bms";
+ chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS;
+ chip->bms_psy_d.properties = bms_power_props;
+ chip->bms_psy_d.num_properties = ARRAY_SIZE(bms_power_props);
+ chip->bms_psy_d.get_property = qpnp_vm_bms_power_get_property;
+ chip->bms_psy_d.set_property = qpnp_vm_bms_power_set_property;
+ chip->bms_psy_d.external_power_changed = qpnp_vm_bms_ext_power_changed;
+ chip->bms_psy_d.property_is_writeable =
+ qpnp_vm_bms_property_is_writeable;
+
+ bms_psy_cfg.supplied_to = qpnp_vm_bms_supplicants;
+ bms_psy_cfg.num_supplicants = ARRAY_SIZE(qpnp_vm_bms_supplicants);
+ bms_psy_cfg.drv_data = chip;
+ bms_psy_cfg.of_node = NULL;
+
+ chip->bms_psy = devm_power_supply_register(chip->dev,
+ &chip->bms_psy_d,
+ &bms_psy_cfg);
+ if (IS_ERR(chip->bms_psy)) {
+ pr_err("power_supply_register bms failed rc = %ld\n",
+ PTR_ERR(chip->bms_psy));
+ goto fail_psy;
+ }
+ chip->bms_psy_registered = true;
+
+ rc = get_battery_voltage(chip, &vbatt);
+ if (rc) {
+ pr_err("error reading vbat_sns adc channel=%d, rc=%d\n",
+ VBAT_SNS, rc);
+ goto fail_get_vtg;
+ }
+
+ chip->debug_root = debugfs_create_dir("qpnp_vmbms", NULL);
+ if (!chip->debug_root)
+ pr_err("Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("bms_data", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &bms_data_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create bms_data debug file\n");
+
+ ent = debugfs_create_file("bms_config", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &bms_config_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create bms_config debug file\n");
+
+ ent = debugfs_create_file("bms_status", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &bms_status_debugfs_ops);
+ if (!ent)
+ pr_err("Couldn't create bms_status debug file\n");
+ }
+
+ schedule_delayed_work(&chip->monitor_soc_work, 0);
+
+ /*
+ * schedule a work to check if the userspace vmbms module
+ * has registered. Fall-back to voltage-based-soc reporting
+ * if it has not.
+ */
+ schedule_delayed_work(&chip->voltage_soc_timeout_work,
+ msecs_to_jiffies(chip->dt.cfg_voltage_soc_timeout_ms));
+
+ pr_info("probe success: soc=%d vbatt=%d ocv=%d warm_reset=%d\n",
+ get_prop_bms_capacity(chip), vbatt,
+ chip->last_ocv_uv, chip->warm_reset);
+
+ return rc;
+
+fail_get_vtg:
+ power_supply_unregister(chip->bms_psy);
+fail_psy:
+ device_destroy(chip->bms_class, chip->dev_no);
+ cdev_del(&chip->bms_cdev);
+ unregister_chrdev_region(chip->dev_no, 1);
+fail_bms_device:
+ chip->bms_psy_registered = false;
+fail_irq:
+ reset_vbat_monitoring(chip);
+fail_setup:
+ wakeup_source_trash(&chip->vbms_lv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_cv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_soc_wake_source.source);
+fail_init:
+ mutex_destroy(&chip->bms_data_mutex);
+ mutex_destroy(&chip->last_soc_mutex);
+ mutex_destroy(&chip->state_change_mutex);
+ mutex_destroy(&chip->bms_device_mutex);
+ the_chip = NULL;
+
+ return rc;
+}
+
+static int qpnp_vm_bms_remove(struct platform_device *pdev)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ cancel_delayed_work_sync(&chip->monitor_soc_work);
+ debugfs_remove_recursive(chip->debug_root);
+ device_destroy(chip->bms_class, chip->dev_no);
+ cdev_del(&chip->bms_cdev);
+ unregister_chrdev_region(chip->dev_no, 1);
+ reset_vbat_monitoring(chip);
+ wakeup_source_trash(&chip->vbms_lv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_cv_wake_source.source);
+ wakeup_source_trash(&chip->vbms_soc_wake_source.source);
+ mutex_destroy(&chip->bms_data_mutex);
+ mutex_destroy(&chip->last_soc_mutex);
+ mutex_destroy(&chip->state_change_mutex);
+ mutex_destroy(&chip->bms_device_mutex);
+ power_supply_unregister(chip->bms_psy);
+ dev_set_drvdata(&pdev->dev, NULL);
+ the_chip = NULL;
+
+ return 0;
+}
+
+static void process_suspend_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ mutex_lock(&chip->bms_data_mutex);
+
+ chip->suspend_data_valid = false;
+
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+
+ rc = read_and_populate_fifo_data(chip);
+ if (rc)
+ pr_err("Unable to read FIFO data rc=%d\n", rc);
+
+ rc = read_and_populate_acc_data(chip);
+ if (rc)
+ pr_err("Unable to read ACC_SD data rc=%d\n", rc);
+
+ rc = clear_fifo_acc_data(chip);
+ if (rc)
+ pr_err("Unable to clear FIFO/ACC data rc=%d\n", rc);
+
+ if (chip->bms_data.num_fifo || chip->bms_data.acc_count) {
+ pr_debug("suspend data valid\n");
+ chip->suspend_data_valid = true;
+ }
+
+ mutex_unlock(&chip->bms_data_mutex);
+}
+
+static void process_resume_data(struct qpnp_bms_chip *chip)
+{
+ int rc, batt_temp = 0;
+ int old_ocv = 0;
+ bool ocv_updated = false;
+
+ rc = get_batt_therm(chip, &batt_temp);
+ if (rc < 0) {
+ pr_err("Unable to read batt temp, using default=%d\n",
+ BMS_DEFAULT_TEMP);
+ batt_temp = BMS_DEFAULT_TEMP;
+ }
+
+ mutex_lock(&chip->bms_data_mutex);
+ /*
+ * We can get a h/w OCV update when the sleep_b
+ * is low, which is possible when APPS is suspended.
+ * So check for an OCV update only in bms_resume
+ */
+ old_ocv = chip->last_ocv_uv;
+ rc = read_and_update_ocv(chip, batt_temp, false);
+ if (rc)
+ pr_err("Unable to read/upadate OCV rc=%d\n", rc);
+
+ if (old_ocv != chip->last_ocv_uv) {
+ ocv_updated = true;
+ /* new OCV, clear suspended data */
+ chip->suspend_data_valid = false;
+ memset(&chip->bms_data, 0, sizeof(chip->bms_data));
+ chip->calculated_soc = lookup_soc_ocv(chip,
+ chip->last_ocv_uv, batt_temp);
+ pr_debug("OCV in sleep SOC=%d\n", chip->calculated_soc);
+ chip->last_soc_unbound = true;
+ chip->voltage_soc_uv = chip->last_ocv_uv;
+ pr_debug("update bms_psy\n");
+ power_supply_changed(chip->bms_psy);
+ }
+
+ if (ocv_updated || chip->suspend_data_valid) {
+ /* there is data to be sent */
+ pr_debug("ocv_updated=%d suspend_data_valid=%d\n",
+ ocv_updated, chip->suspend_data_valid);
+ chip->bms_data.seq_num = chip->seq_num++;
+ dump_bms_data(__func__, chip);
+
+ chip->data_ready = 1;
+ wake_up_interruptible(&chip->bms_wait_q);
+ if (chip->bms_dev_open)
+ pm_stay_awake(chip->dev);
+
+ }
+ chip->suspend_data_valid = false;
+ mutex_unlock(&chip->bms_data_mutex);
+}
+
+static int bms_suspend(struct device *dev)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+ bool battery_charging = is_battery_charging(chip);
+ bool hi_power_state = is_hi_power_state_requested(chip);
+ bool charger_present = is_charger_present(chip);
+ bool bms_suspend_config;
+
+ /*
+ * Keep BMS FSM active if 'cfg_force_bms_active_on_charger' property
+ * is present and charger inserted. This ensures that recharge
+ * starts once battery SOC falls below resume_soc.
+ */
+ bms_suspend_config = chip->dt.cfg_force_bms_active_on_charger
+ && charger_present;
+
+ chip->apply_suspend_config = false;
+ if (!battery_charging && !hi_power_state && !bms_suspend_config)
+ chip->apply_suspend_config = true;
+
+ pr_debug("battery_charging=%d power_state=%s hi_power_state=0x%x apply_suspend_config=%d bms_suspend_config=%d usb_present=%d\n",
+ battery_charging, hi_power_state ? "hi" : "low",
+ chip->hi_power_state,
+ chip->apply_suspend_config, bms_suspend_config,
+ charger_present);
+
+ if (chip->apply_suspend_config) {
+ if (chip->dt.cfg_force_s3_on_suspend) {
+ disable_bms_irq(&chip->fifo_update_done_irq);
+ pr_debug("Forcing S3 state\n");
+ mutex_lock(&chip->state_change_mutex);
+ force_fsm_state(chip, S3_STATE);
+ mutex_unlock(&chip->state_change_mutex);
+ /* Store accumulated data if any */
+ process_suspend_data(chip);
+ }
+ }
+
+ cancel_delayed_work_sync(&chip->monitor_soc_work);
+
+ return 0;
+}
+
+static int bms_resume(struct device *dev)
+{
+ u8 state = 0;
+ int rc, monitor_soc_delay = 0;
+ unsigned long tm_now_sec;
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ if (chip->apply_suspend_config) {
+ if (chip->dt.cfg_force_s3_on_suspend) {
+ /*
+ * Update the state to S2 only if we are in S3. There is
+ * a possibility of being in S2 if we resumed on
+ * a charger insertion
+ */
+ mutex_lock(&chip->state_change_mutex);
+ rc = get_fsm_state(chip, &state);
+ if (rc)
+ pr_err("Unable to get FSM state rc=%d\n", rc);
+ if (rc || (state == S3_STATE)) {
+ pr_debug("Unforcing S3 state, setting S2 state\n");
+ force_fsm_state(chip, S2_STATE);
+ }
+ mutex_unlock(&chip->state_change_mutex);
+ enable_bms_irq(&chip->fifo_update_done_irq);
+ /*
+ * if we were charging while suspended, we will
+ * be woken up by the fifo done interrupt and no
+ * additional processing is needed.
+ */
+ process_resume_data(chip);
+ }
+ }
+
+ /* Start monitor_soc_work based on when it last executed */
+ rc = get_current_time(&tm_now_sec);
+ if (rc) {
+ pr_err("Could not read current time: %d\n", rc);
+ } else {
+ monitor_soc_delay = get_calculation_delay_ms(chip) -
+ ((tm_now_sec - chip->tm_sec) * 1000);
+ monitor_soc_delay = max(0, monitor_soc_delay);
+ }
+ pr_debug("monitor_soc_delay_sec=%d tm_now_sec=%ld chip->tm_sec=%ld\n",
+ monitor_soc_delay / 1000, tm_now_sec, chip->tm_sec);
+ schedule_delayed_work(&chip->monitor_soc_work,
+ msecs_to_jiffies(monitor_soc_delay));
+
+ return 0;
+}
+
+static const struct dev_pm_ops qpnp_vm_bms_pm_ops = {
+ .suspend = bms_suspend,
+ .resume = bms_resume,
+};
+
+static const struct of_device_id qpnp_vm_bms_match_table[] = {
+ { .compatible = QPNP_VM_BMS_DEV_NAME },
+ {}
+};
+
+static struct platform_driver qpnp_vm_bms_driver = {
+ .probe = qpnp_vm_bms_probe,
+ .remove = qpnp_vm_bms_remove,
+ .driver = {
+ .name = QPNP_VM_BMS_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_vm_bms_match_table,
+ .pm = &qpnp_vm_bms_pm_ops,
+ },
+};
+module_platform_driver(qpnp_vm_bms_driver);
+
+MODULE_DESCRIPTION("QPNP VM-BMS Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_VM_BMS_DEV_NAME);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 39005f6..612c3dd 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -1908,7 +1908,8 @@
stat);
if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
- rc = smblib_get_prop_batt_voltage_now(chg, &pval);
+ rc = smblib_get_prop_from_bms(chg,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval);
if (!rc) {
/*
* If Vbatt is within 40mV above Vfloat, then don't
@@ -1973,45 +1974,6 @@
return 0;
}
-int smblib_get_prop_batt_voltage_now(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- int rc;
-
- if (!chg->bms_psy)
- return -EINVAL;
-
- rc = power_supply_get_property(chg->bms_psy,
- POWER_SUPPLY_PROP_VOLTAGE_NOW, val);
- return rc;
-}
-
-int smblib_get_prop_batt_current_now(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- int rc;
-
- if (!chg->bms_psy)
- return -EINVAL;
-
- rc = power_supply_get_property(chg->bms_psy,
- POWER_SUPPLY_PROP_CURRENT_NOW, val);
- return rc;
-}
-
-int smblib_get_prop_batt_temp(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- int rc;
-
- if (!chg->bms_psy)
- return -EINVAL;
-
- rc = power_supply_get_property(chg->bms_psy,
- POWER_SUPPLY_PROP_TEMP, val);
- return rc;
-}
-
int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
union power_supply_propval *val)
{
@@ -2047,16 +2009,17 @@
return 0;
}
-int smblib_get_prop_batt_charge_counter(struct smb_charger *chg,
- union power_supply_propval *val)
+int smblib_get_prop_from_bms(struct smb_charger *chg,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
{
int rc;
if (!chg->bms_psy)
return -EINVAL;
- rc = power_supply_get_property(chg->bms_psy,
- POWER_SUPPLY_PROP_CHARGE_COUNTER, val);
+ rc = power_supply_get_property(chg->bms_psy, psp, val);
+
return rc;
}
@@ -2452,6 +2415,28 @@
switch (chg->real_charger_type) {
case POWER_SUPPLY_TYPE_USB_HVDCP:
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
+ if (chg->smb_version == PM660_SUBTYPE)
+ val->intval = MICRO_9V;
+ else
+ val->intval = MICRO_12V;
+ break;
+ case POWER_SUPPLY_TYPE_USB_PD:
+ val->intval = chg->voltage_max_uv;
+ break;
+ default:
+ val->intval = MICRO_5V;
+ break;
+ }
+
+ return 0;
+}
+
+int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ switch (chg->real_charger_type) {
+ case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3:
case POWER_SUPPLY_TYPE_USB_PD:
if (chg->smb_version == PM660_SUBTYPE)
val->intval = MICRO_9V;
@@ -3445,6 +3430,9 @@
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
struct storm_watch *wdata;
+ const struct apsd_result *apsd = smblib_get_apsd_result(chg);
+ int rc;
+ u8 stat = 0, max_pulses = 0;
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
if (!chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data)
@@ -3452,6 +3440,46 @@
wdata = &chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data->storm_data;
reset_storm_count(wdata);
+
+ if (!chg->non_compliant_chg_detected &&
+ apsd->pst == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't read CHANGE_STATUS_REG rc=%d\n", rc);
+
+ if (stat & QC_5V_BIT)
+ return IRQ_HANDLED;
+
+ rc = smblib_read(chg, HVDCP_PULSE_COUNT_MAX_REG, &max_pulses);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't read QC2 max pulses rc=%d\n", rc);
+
+ chg->non_compliant_chg_detected = true;
+ chg->qc2_max_pulses = (max_pulses &
+ HVDCP_PULSE_COUNT_MAX_QC2_MASK);
+
+ if (stat & QC_12V_BIT) {
+ rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
+ HVDCP_PULSE_COUNT_MAX_QC2_MASK,
+ HVDCP_PULSE_COUNT_MAX_QC2_9V);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't force max pulses to 9V rc=%d\n",
+ rc);
+
+ } else if (stat & QC_9V_BIT) {
+ rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
+ HVDCP_PULSE_COUNT_MAX_QC2_MASK,
+ HVDCP_PULSE_COUNT_MAX_QC2_5V);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't force max pulses to 5V rc=%d\n",
+ rc);
+
+ }
+ smblib_rerun_apsd(chg);
+ }
+
return IRQ_HANDLED;
}
@@ -4270,6 +4298,17 @@
if (rc < 0)
smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc);
+ /* if non-compliant charger caused UV, restore original max pulses */
+ if (chg->non_compliant_chg_detected) {
+ rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
+ HVDCP_PULSE_COUNT_MAX_QC2_MASK,
+ chg->qc2_max_pulses);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't restore max pulses rc=%d\n",
+ rc);
+ chg->non_compliant_chg_detected = false;
+ }
+
/* enable APSD CC trigger for next insertion */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT);
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index dc8cbc7..3b8bc1f 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -353,6 +353,7 @@
bool use_extcon;
bool otg_present;
bool is_audio_adapter;
+ bool disable_stat_sw_override;
/* workaround flag */
u32 wa_flags;
@@ -361,6 +362,8 @@
bool try_sink_active;
int boost_current_ua;
int temp_speed_reading_count;
+ int qc2_max_pulses;
+ bool non_compliant_chg_detected;
bool fake_usb_insertion;
/* extcon for VBUS / ID notification to USB for uUSB */
@@ -444,14 +447,6 @@
union power_supply_propval *val);
int smblib_get_prop_input_current_limited(struct smb_charger *chg,
union power_supply_propval *val);
-int smblib_get_prop_batt_voltage_now(struct smb_charger *chg,
- union power_supply_propval *val);
-int smblib_get_prop_batt_current_now(struct smb_charger *chg,
- union power_supply_propval *val);
-int smblib_get_prop_batt_temp(struct smb_charger *chg,
- union power_supply_propval *val);
-int smblib_get_prop_batt_charge_counter(struct smb_charger *chg,
- union power_supply_propval *val);
int smblib_set_prop_input_suspend(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_batt_capacity(struct smb_charger *chg,
@@ -480,6 +475,8 @@
union power_supply_propval *val);
int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_usb_current_now(struct smb_charger *chg,
@@ -541,6 +538,9 @@
int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua);
int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_from_bms(struct smb_charger *chg,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override);
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index d40d6fd..449d974 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -587,6 +587,15 @@
#define EN_LEGACY_CABLE_DETECTION_BIT BIT(1)
#define ALLOW_PD_DRING_UFP_TCCDB_BIT BIT(0)
+#define HVDCP_PULSE_COUNT_MAX_REG (USBIN_BASE + 0x5B)
+#define HVDCP_PULSE_COUNT_MAX_QC2_MASK GENMASK(7, 6)
+enum {
+ HVDCP_PULSE_COUNT_MAX_QC2_5V,
+ HVDCP_PULSE_COUNT_MAX_QC2_9V,
+ HVDCP_PULSE_COUNT_MAX_QC2_12V,
+ HVDCP_PULSE_COUNT_MAX_QC2_INVALID
+};
+
#define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60)
#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0)
enum {
diff --git a/drivers/power/supply/qcom/smb1355-charger.c b/drivers/power/supply/qcom/smb1355-charger.c
index 327ae63..1d99ccb 100644
--- a/drivers/power/supply/qcom/smb1355-charger.c
+++ b/drivers/power/supply/qcom/smb1355-charger.c
@@ -108,6 +108,9 @@
#define BARK_BITE_WDOG_PET_REG (MISC_BASE + 0x43)
#define BARK_BITE_WDOG_PET_BIT BIT(0)
+#define CLOCK_REQUEST_REG (MISC_BASE + 0x44)
+#define CLOCK_REQUEST_CMD_BIT BIT(0)
+
#define WD_CFG_REG (MISC_BASE + 0x51)
#define WATCHDOG_TRIGGER_AFP_EN_BIT BIT(7)
#define BARK_WDOG_INT_EN_BIT BIT(6)
@@ -249,6 +252,9 @@
static bool is_secure(struct smb1355 *chip, int addr)
{
+ if (addr == CLOCK_REQUEST_REG)
+ return true;
+
/* assume everything above 0xA0 is secure */
return (addr & 0xFF) >= 0xA0;
}
@@ -265,6 +271,25 @@
return rc;
}
+static int smb1355_masked_force_write(struct smb1355 *chip, u16 addr, u8 mask,
+ u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->write_lock);
+ if (is_secure(chip, addr)) {
+ rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
+ if (rc < 0)
+ goto unlock;
+ }
+
+ rc = regmap_write_bits(chip->regmap, addr, mask, val);
+
+unlock:
+ mutex_unlock(&chip->write_lock);
+ return rc;
+}
+
static int smb1355_masked_write(struct smb1355 *chip, u16 addr, u8 mask, u8 val)
{
int rc;
@@ -494,6 +519,7 @@
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_MIN_ICL,
POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_SET_SHIP_MODE,
};
static int smb1355_get_prop_batt_charge_type(struct smb1355 *chip,
@@ -635,6 +661,10 @@
case POWER_SUPPLY_PROP_PARALLEL_FCC_MAX:
val->intval = chip->max_fcc;
break;
+ case POWER_SUPPLY_PROP_SET_SHIP_MODE:
+ /* Not in ship mode as long as device is active */
+ val->intval = 0;
+ break;
default:
pr_err_ratelimited("parallel psy get prop %d not supported\n",
prop);
@@ -726,6 +756,20 @@
return rc;
}
+static int smb1355_clk_request(struct smb1355 *chip, bool enable)
+{
+ int rc;
+
+ rc = smb1355_masked_force_write(chip, CLOCK_REQUEST_REG,
+ CLOCK_REQUEST_CMD_BIT,
+ enable ? CLOCK_REQUEST_CMD_BIT : 0);
+ if (rc < 0)
+ pr_err("Couldn't %s clock rc=%d\n",
+ enable ? "enable" : "disable", rc);
+
+ return rc;
+}
+
static int smb1355_parallel_set_prop(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
@@ -752,6 +796,11 @@
chip->c_health = val->intval;
power_supply_changed(chip->parallel_psy);
break;
+ case POWER_SUPPLY_PROP_SET_SHIP_MODE:
+ if (!val->intval)
+ break;
+ rc = smb1355_clk_request(chip, false);
+ break;
default:
pr_debug("parallel power supply set prop %d not supported\n",
prop);
@@ -930,6 +979,11 @@
{
int rc;
+ /* request clock always on */
+ rc = smb1355_clk_request(chip, true);
+ if (rc < 0)
+ return rc;
+
/* enable watchdog bark and bite interrupts, and disable the watchdog */
rc = smb1355_masked_write(chip, WD_CFG_REG, WDOG_TIMER_EN_BIT
| WDOG_TIMER_EN_ON_PLUGIN_BIT | BITE_WDOG_INT_EN_BIT
@@ -1340,6 +1394,8 @@
rc = smb1355_set_parallel_charging(chip, true);
if (rc < 0)
pr_err("Couldn't disable parallel path rc=%d\n", rc);
+
+ smb1355_clk_request(chip, false);
}
static struct platform_driver smb1355_driver = {
diff --git a/drivers/power/supply/qcom/smb1360-charger-fg.c b/drivers/power/supply/qcom/smb1360-charger-fg.c
new file mode 100644
index 0000000..ed9c610
--- /dev/null
+++ b/drivers/power/supply/qcom/smb1360-charger-fg.c
@@ -0,0 +1,5372 @@
+/* Copyright (c) 2013-2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "SMB:%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/extcon.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/bitops.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/completion.h>
+#include <linux/pm_wakeup.h>
+
+#define _SMB1360_MASK(BITS, POS) \
+ ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
+#define SMB1360_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
+ _SMB1360_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
+ (RIGHT_BIT_POS))
+
+/* Charger Registers */
+#define CFG_BATT_CHG_REG 0x00
+#define CHG_ITERM_MASK SMB1360_MASK(2, 0)
+#define CHG_ITERM_25MA 0x0
+#define CHG_ITERM_200MA 0x7
+#define RECHG_MV_MASK SMB1360_MASK(6, 5)
+#define RECHG_MV_SHIFT 5
+#define OTG_CURRENT_MASK SMB1360_MASK(4, 3)
+#define OTG_CURRENT_SHIFT 3
+
+#define CFG_BATT_CHG_ICL_REG 0x05
+#define AC_INPUT_ICL_PIN_BIT BIT(7)
+#define AC_INPUT_PIN_HIGH_BIT BIT(6)
+#define RESET_STATE_USB_500 BIT(5)
+#define INPUT_CURR_LIM_MASK SMB1360_MASK(3, 0)
+#define INPUT_CURR_LIM_300MA 0x0
+
+#define CFG_GLITCH_FLT_REG 0x06
+#define AICL_ENABLED_BIT BIT(0)
+#define INPUT_UV_GLITCH_FLT_20MS_BIT BIT(7)
+
+#define CFG_CHG_MISC_REG 0x7
+#define CHG_EN_BY_PIN_BIT BIT(7)
+#define CHG_EN_ACTIVE_LOW_BIT BIT(6)
+#define PRE_TO_FAST_REQ_CMD_BIT BIT(5)
+#define CFG_BAT_OV_ENDS_CHG_CYC BIT(4)
+#define CHG_CURR_TERM_DIS_BIT BIT(3)
+#define CFG_AUTO_RECHG_DIS_BIT BIT(2)
+#define CFG_CHG_INHIBIT_EN_BIT BIT(0)
+
+#define CFG_CHG_FUNC_CTRL_REG 0x08
+#define CHG_RECHG_THRESH_FG_SRC_BIT BIT(1)
+
+#define CFG_STAT_CTRL_REG 0x09
+#define CHG_STAT_IRQ_ONLY_BIT BIT(4)
+#define CHG_TEMP_CHG_ERR_BLINK_BIT BIT(3)
+#define CHG_STAT_ACTIVE_HIGH_BIT BIT(1)
+#define CHG_STAT_DISABLE_BIT BIT(0)
+
+#define CFG_SFY_TIMER_CTRL_REG 0x0A
+#define SAFETY_TIME_DISABLE_BIT BIT(5)
+#define SAFETY_TIME_MINUTES_SHIFT 2
+#define SAFETY_TIME_MINUTES_MASK SMB1360_MASK(3, 2)
+
+#define CFG_BATT_MISSING_REG 0x0D
+#define BATT_MISSING_SRC_THERM_BIT BIT(1)
+
+#define CFG_FG_BATT_CTRL_REG 0x0E
+#define CFG_FG_OTP_BACK_UP_ENABLE BIT(7)
+#define BATT_ID_ENABLED_BIT BIT(5)
+#define CHG_BATT_ID_FAIL BIT(4)
+#define BATT_ID_FAIL_SELECT_PROFILE BIT(3)
+#define BATT_PROFILE_SELECT_MASK SMB1360_MASK(3, 0)
+#define BATT_PROFILEA_MASK 0x0
+#define BATT_PROFILEB_MASK 0xF
+
+#define IRQ_CFG_REG 0x0F
+#define IRQ_BAT_HOT_COLD_HARD_BIT BIT(7)
+#define IRQ_BAT_HOT_COLD_SOFT_BIT BIT(6)
+#define IRQ_DCIN_UV_BIT BIT(2)
+#define IRQ_AICL_DONE_BIT BIT(1)
+#define IRQ_INTERNAL_TEMPERATURE_BIT BIT(0)
+
+#define IRQ2_CFG_REG 0x10
+#define IRQ2_SAFETY_TIMER_BIT BIT(7)
+#define IRQ2_CHG_ERR_BIT BIT(6)
+#define IRQ2_CHG_PHASE_CHANGE_BIT BIT(4)
+#define IRQ2_POWER_OK_BIT BIT(2)
+#define IRQ2_BATT_MISSING_BIT BIT(1)
+#define IRQ2_VBAT_LOW_BIT BIT(0)
+
+#define IRQ3_CFG_REG 0x11
+#define IRQ3_FG_ACCESS_OK_BIT BIT(6)
+#define IRQ3_SOC_CHANGE_BIT BIT(4)
+#define IRQ3_SOC_MIN_BIT BIT(3)
+#define IRQ3_SOC_MAX_BIT BIT(2)
+#define IRQ3_SOC_EMPTY_BIT BIT(1)
+#define IRQ3_SOC_FULL_BIT BIT(0)
+
+#define CHG_CURRENT_REG 0x13
+#define FASTCHG_CURR_MASK SMB1360_MASK(4, 2)
+#define FASTCHG_CURR_SHIFT 2
+
+#define CHG_CMP_CFG 0x14
+#define JEITA_COMP_CURR_MASK SMB1360_MASK(3, 0)
+#define JEITA_COMP_EN_MASK SMB1360_MASK(7, 4)
+#define JEITA_COMP_EN_SHIFT 4
+#define JEITA_COMP_EN_BIT SMB1360_MASK(7, 4)
+#define BATT_CHG_FLT_VTG_REG 0x15
+#define VFLOAT_MASK SMB1360_MASK(6, 0)
+#define CFG_FVC_REG 0x16
+#define FLT_VTG_COMP_MASK SMB1360_MASK(6, 0)
+
+#define SHDN_CTRL_REG 0x1A
+#define SHDN_CMD_USE_BIT BIT(1)
+#define SHDN_CMD_POLARITY_BIT BIT(2)
+
+#define CURRENT_GAIN_LSB_REG 0x1D
+#define CURRENT_GAIN_MSB_REG 0x1E
+
+/* Command Registers */
+#define CMD_I2C_REG 0x40
+#define ALLOW_VOLATILE_BIT BIT(6)
+#define FG_ACCESS_ENABLED_BIT BIT(5)
+#define FG_RESET_BIT BIT(4)
+#define CYCLE_STRETCH_CLEAR_BIT BIT(3)
+
+#define CMD_IL_REG 0x41
+#define USB_CTRL_MASK SMB1360_MASK(1, 0)
+#define USB_100_BIT 0x01
+#define USB_500_BIT 0x00
+#define USB_AC_BIT 0x02
+#define SHDN_CMD_BIT BIT(7)
+
+#define CMD_CHG_REG 0x42
+#define CMD_CHG_EN BIT(1)
+#define CMD_OTG_EN_BIT BIT(0)
+
+/* Status Registers */
+#define STATUS_1_REG 0x48
+#define AICL_CURRENT_STATUS_MASK SMB1360_MASK(6, 0)
+#define AICL_LIMIT_1500MA 0xF
+
+#define STATUS_3_REG 0x4B
+#define CHG_HOLD_OFF_BIT BIT(3)
+#define CHG_TYPE_MASK SMB1360_MASK(2, 1)
+#define CHG_TYPE_SHIFT 1
+#define BATT_NOT_CHG_VAL 0x0
+#define BATT_PRE_CHG_VAL 0x1
+#define BATT_FAST_CHG_VAL 0x2
+#define BATT_TAPER_CHG_VAL 0x3
+#define CHG_EN_BIT BIT(0)
+
+#define STATUS_4_REG 0x4C
+#define CYCLE_STRETCH_ACTIVE_BIT BIT(5)
+
+#define REVISION_CTRL_REG 0x4F
+#define DEVICE_REV_MASK SMB1360_MASK(3, 0)
+
+/* IRQ Status Registers */
+#define IRQ_A_REG 0x50
+#define IRQ_A_HOT_HARD_BIT BIT(6)
+#define IRQ_A_COLD_HARD_BIT BIT(4)
+#define IRQ_A_HOT_SOFT_BIT BIT(2)
+#define IRQ_A_COLD_SOFT_BIT BIT(0)
+
+#define IRQ_B_REG 0x51
+#define IRQ_B_BATT_TERMINAL_BIT BIT(6)
+#define IRQ_B_BATT_MISSING_BIT BIT(4)
+
+#define IRQ_C_REG 0x52
+#define IRQ_C_CHG_TERM BIT(0)
+
+#define IRQ_D_REG 0x53
+#define IRQ_E_REG 0x54
+#define IRQ_E_USBIN_UV_BIT BIT(0)
+
+#define IRQ_F_REG 0x55
+
+#define IRQ_G_REG 0x56
+
+#define IRQ_H_REG 0x57
+#define IRQ_I_REG 0x58
+#define FG_ACCESS_ALLOWED_BIT BIT(0)
+#define BATT_ID_RESULT_BIT SMB1360_MASK(6, 4)
+#define BATT_ID_SHIFT 4
+
+/* FG registers - IRQ config register */
+#define SOC_MAX_REG 0x24
+#define SOC_MIN_REG 0x25
+#define VTG_EMPTY_REG 0x26
+#define SOC_DELTA_REG 0x28
+#define JEITA_SOFT_COLD_REG 0x29
+#define JEITA_SOFT_HOT_REG 0x2A
+#define VTG_MIN_REG 0x2B
+
+/* FG SHADOW registers */
+#define SHDW_FG_ESR_ACTUAL 0x20
+#define SHDW_FG_BATT_STATUS 0x60
+#define BATTERY_PROFILE_BIT BIT(0)
+
+#define SHDW_FG_MSYS_SOC 0x61
+#define SHDW_FG_CAPACITY 0x62
+#define SHDW_FG_VTG_NOW 0x69
+#define SHDW_FG_CURR_NOW 0x6B
+#define SHDW_FG_BATT_TEMP 0x6D
+
+#define VOLTAGE_PREDICTED_REG 0x80
+#define CC_TO_SOC_COEFF 0xBA
+#define NOMINAL_CAPACITY_REG 0xBC
+#define ACTUAL_CAPACITY_REG 0xBE
+#define FG_AUTO_RECHARGE_SOC 0xD2
+#define FG_SYS_CUTOFF_V_REG 0xD3
+#define FG_CC_TO_CV_V_REG 0xD5
+#define FG_ITERM_REG 0xD9
+#define FG_THERM_C1_COEFF_REG 0xDB
+#define FG_IBATT_STANDBY_REG 0xCF
+
+#define FG_I2C_CFG_MASK SMB1360_MASK(2, 1)
+#define FG_CFG_I2C_ADDR 0x2
+#define FG_PROFILE_A_ADDR 0x4
+#define FG_PROFILE_B_ADDR 0x6
+
+/* Constants */
+#define CURRENT_100_MA 100
+#define CURRENT_500_MA 500
+#define MAX_8_BITS 255
+#define JEITA_WORK_MS 3000
+
+#define FG_RESET_THRESHOLD_MV 15
+#define SMB1360_REV_1 0x01
+
+#define SMB1360_POWERON_DELAY_MS 2000
+#define SMB1360_FG_RESET_DELAY_MS 1500
+
+enum {
+ WRKRND_FG_CONFIG_FAIL = BIT(0),
+ WRKRND_BATT_DET_FAIL = BIT(1),
+ WRKRND_USB100_FAIL = BIT(2),
+ WRKRND_HARD_JEITA = BIT(3),
+};
+
+enum {
+ USER = BIT(0),
+};
+
+enum {
+ PARALLEL_USER = BIT(0),
+ PARALLEL_CURRENT = BIT(1),
+ PARALLEL_JEITA_SOFT = BIT(2),
+ PARALLEL_JEITA_HARD = BIT(3),
+ PARALLEL_EOC = BIT(4),
+};
+
+enum fg_i2c_access_type {
+ FG_ACCESS_CFG = 0x1,
+ FG_ACCESS_PROFILE_A = 0x2,
+ FG_ACCESS_PROFILE_B = 0x3
+};
+
+enum {
+ BATTERY_PROFILE_A,
+ BATTERY_PROFILE_B,
+ BATTERY_PROFILE_MAX,
+};
+
+static int otg_curr_ma[] = {350, 550, 950, 1500};
+
+struct otp_backup_pool {
+ u8 reg_start;
+ u8 reg_end;
+ u8 start_now;
+ u16 alg_bitmap;
+ bool initialized;
+ struct mutex lock;
+};
+
+enum otp_backup_alg {
+ OTP_BACKUP_NOT_USE = 0,
+ OTP_BACKUP_FG_USE,
+ OTP_BACKUP_PROF_A_USE,
+ OTP_BACKUP_PROF_B_USE,
+};
+
+struct smb1360_otg_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+};
+
+enum wakeup_src {
+ WAKEUP_SRC_FG_ACCESS = 0,
+ WAKEUP_SRC_JEITA_SOFT,
+ WAKEUP_SRC_PARALLEL,
+ WAKEUP_SRC_MIN_SOC,
+ WAKEUP_SRC_EMPTY_SOC,
+ WAKEUP_SRC_JEITA_HYSTERSIS,
+ WAKEUP_SRC_MAX,
+};
+#define WAKEUP_SRC_MASK (~(~0 << WAKEUP_SRC_MAX))
+
+struct smb1360_wakeup_source {
+ struct wakeup_source source;
+ unsigned long enabled_bitmap;
+ spinlock_t ws_lock;
+};
+
+static const unsigned int smb1360_extcon_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+struct smb1360_chip {
+ struct i2c_client *client;
+ struct device *dev;
+ u8 revision;
+ u8 soft_hot_rt_stat;
+ u8 soft_cold_rt_stat;
+ struct delayed_work jeita_work;
+ struct delayed_work delayed_init_work;
+ unsigned short default_i2c_addr;
+ unsigned short fg_i2c_addr;
+ bool pulsed_irq;
+ struct completion fg_mem_access_granted;
+
+ /* wakeup source */
+ struct smb1360_wakeup_source smb1360_ws;
+
+ /* configuration data - charger */
+ int fake_battery_soc;
+ bool batt_id_disabled;
+ bool charging_disabled;
+ bool recharge_disabled;
+ bool chg_inhibit_disabled;
+ bool iterm_disabled;
+ bool shdn_after_pwroff;
+ bool config_hard_thresholds;
+ bool soft_jeita_supported;
+ bool ov_ends_chg_cycle_disabled;
+ int iterm_ma;
+ int vfloat_mv;
+ int safety_time;
+ int resume_delta_mv;
+ u32 default_batt_profile;
+ unsigned int thermal_levels;
+ unsigned int therm_lvl_sel;
+ unsigned int *thermal_mitigation;
+ int otg_batt_curr_limit;
+ bool min_icl_usb100;
+ int cold_bat_decidegc;
+ int hot_bat_decidegc;
+ int cool_bat_decidegc;
+ int warm_bat_decidegc;
+ int cool_bat_mv;
+ int warm_bat_mv;
+ int cool_bat_ma;
+ int warm_bat_ma;
+ int soft_cold_thresh;
+ int soft_hot_thresh;
+
+ /* parallel-chg params */
+ int fastchg_current;
+ int parallel_chg_disable_status;
+ int max_parallel_chg_current;
+ bool parallel_charging;
+
+ /* configuration data - fg */
+ int soc_max;
+ int soc_min;
+ int delta_soc;
+ int voltage_min_mv;
+ int voltage_empty_mv;
+ int batt_capacity_mah;
+ int cc_soc_coeff;
+ int v_cutoff_mv;
+ int fg_iterm_ma;
+ int fg_ibatt_standby_ma;
+ int fg_thermistor_c1_coeff;
+ int fg_cc_to_cv_mv;
+ int fg_auto_recharge_soc;
+ bool empty_soc_disabled;
+ int fg_reset_threshold_mv;
+ bool fg_reset_at_pon;
+ bool rsense_10mohm;
+ bool otg_fet_present;
+ bool fet_gain_enabled;
+ int otg_fet_enable_gpio;
+
+ /* status tracking */
+ int voltage_now;
+ int current_now;
+ int resistance_now;
+ int temp_now;
+ int soc_now;
+ int fcc_mah;
+ bool usb_present;
+ bool batt_present;
+ bool batt_hot;
+ bool batt_cold;
+ bool batt_warm;
+ bool batt_cool;
+ bool batt_full;
+ bool resume_completed;
+ bool irq_waiting;
+ bool irq_disabled;
+ bool empty_soc;
+ bool awake_min_soc;
+ int workaround_flags;
+ u8 irq_cfg_mask[3];
+ int usb_psy_ma;
+ int charging_disabled_status;
+ u32 connected_rid;
+ u32 profile_rid[BATTERY_PROFILE_MAX];
+
+ u32 peek_poke_address;
+ u32 fg_access_type;
+ u32 fg_peek_poke_address;
+ int skip_writes;
+ int skip_reads;
+ enum power_supply_type usb_supply_type;
+ struct dentry *debug_root;
+
+ struct qpnp_vadc_chip *vadc_dev;
+ struct power_supply *parallel_psy;
+ struct power_supply_desc parallel_psy_d;
+ struct power_supply *usb_psy;
+ struct power_supply_desc usb_psy_d;
+ struct power_supply *batt_psy;
+ struct power_supply_desc batt_psy_d;
+ struct smb1360_otg_regulator otg_vreg;
+ struct mutex irq_complete;
+ struct mutex charging_disable_lock;
+ struct mutex current_change_lock;
+ struct mutex read_write_lock;
+ struct mutex parallel_chg_lock;
+ struct work_struct parallel_work;
+ struct mutex otp_gain_lock;
+ struct mutex fg_access_request_lock;
+ struct otp_backup_pool otp_backup;
+ u8 current_gain_otp_reg;
+ bool otp_hard_jeita_config;
+ int otp_cold_bat_decidegc;
+ int otp_hot_bat_decidegc;
+ u8 hard_jeita_otp_reg;
+ struct work_struct jeita_hysteresis_work;
+ int cold_hysteresis;
+ int hot_hysteresis;
+ struct extcon_dev *extcon;
+};
+
+static int chg_time[] = {
+ 192,
+ 384,
+ 768,
+ 1536,
+};
+
+static int input_current_limit[] = {
+ 300, 400, 450, 500, 600, 700, 800, 850, 900,
+ 950, 1000, 1100, 1200, 1300, 1400, 1500,
+};
+
+static int fastchg_current[] = {
+ 450, 600, 750, 900, 1050, 1200, 1350, 1500,
+};
+
+static void smb1360_stay_awake(struct smb1360_wakeup_source *source,
+ enum wakeup_src wk_src)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&source->ws_lock, flags);
+
+ if (!__test_and_set_bit(wk_src, &source->enabled_bitmap)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s, wakeup_src %d\n",
+ source->source.name, wk_src);
+ }
+ spin_unlock_irqrestore(&source->ws_lock, flags);
+}
+
+static void smb1360_relax(struct smb1360_wakeup_source *source,
+ enum wakeup_src wk_src)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&source->ws_lock, flags);
+ if (__test_and_clear_bit(wk_src, &source->enabled_bitmap) &&
+ !(source->enabled_bitmap & WAKEUP_SRC_MASK)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+ spin_unlock_irqrestore(&source->ws_lock, flags);
+
+ pr_debug("relax source %s, wakeup_src %d\n",
+ source->source.name, wk_src);
+}
+
+static void smb1360_wakeup_src_init(struct smb1360_chip *chip)
+{
+ spin_lock_init(&chip->smb1360_ws.ws_lock);
+ wakeup_source_init(&chip->smb1360_ws.source, "smb1360");
+}
+
+static int is_between(int value, int left, int right)
+{
+ if (left >= right && left >= value && value >= right)
+ return 1;
+ if (left <= right && left <= value && value <= right)
+ return 1;
+
+ return 0;
+}
+
+static int bound(int val, int min, int max)
+{
+ if (val < min)
+ return min;
+ if (val > max)
+ return max;
+
+ return val;
+}
+
+static int __smb1360_read(struct smb1360_chip *chip, int reg,
+ u8 *val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "i2c read fail: can't read from %02x: %d\n", reg, ret);
+ return ret;
+ }
+ *val = ret;
+ pr_debug("Reading 0x%02x=0x%02x\n", reg, *val);
+
+ return 0;
+}
+
+static int __smb1360_write(struct smb1360_chip *chip, int reg,
+ u8 val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_write_byte_data(chip->client, reg, val);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, ret);
+ return ret;
+ }
+ pr_debug("Writing 0x%02x=0x%02x\n", reg, val);
+ return 0;
+}
+
+static int smb1360_read(struct smb1360_chip *chip, int reg,
+ u8 *val)
+{
+ int rc;
+
+ if (chip->skip_reads) {
+ *val = 0;
+ return 0;
+ }
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb1360_read(chip, reg, val);
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_write(struct smb1360_chip *chip, int reg,
+ u8 val)
+{
+ int rc;
+
+ if (chip->skip_writes)
+ return 0;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb1360_write(chip, reg, val);
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_fg_read(struct smb1360_chip *chip, int reg,
+ u8 *val)
+{
+ int rc;
+
+ if (chip->skip_reads) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ chip->client->addr = chip->fg_i2c_addr;
+ rc = __smb1360_read(chip, reg, val);
+ chip->client->addr = chip->default_i2c_addr;
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_fg_write(struct smb1360_chip *chip, int reg,
+ u8 val)
+{
+ int rc;
+
+ if (chip->skip_writes)
+ return 0;
+
+ mutex_lock(&chip->read_write_lock);
+ chip->client->addr = chip->fg_i2c_addr;
+ rc = __smb1360_write(chip, reg, val);
+ chip->client->addr = chip->default_i2c_addr;
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb1360_read_bytes(struct smb1360_chip *chip, int reg,
+ u8 *val, u8 bytes)
+{
+ s32 rc;
+
+ if (chip->skip_reads) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ rc = i2c_smbus_read_i2c_block_data(chip->client, reg, bytes, val);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "i2c read fail: can't read %d bytes from %02x: %d\n",
+ bytes, reg, rc);
+ mutex_unlock(&chip->read_write_lock);
+
+ return (rc < 0) ? rc : 0;
+}
+
+static int smb1360_write_bytes(struct smb1360_chip *chip, int reg,
+ u8 *val, u8 bytes)
+{
+ s32 rc;
+
+ if (chip->skip_writes) {
+ *val = 0;
+ return 0;
+ }
+
+ mutex_lock(&chip->read_write_lock);
+ rc = i2c_smbus_write_i2c_block_data(chip->client, reg, bytes, val);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "i2c write fail: can't read %d bytes from %02x: %d\n",
+ bytes, reg, rc);
+ mutex_unlock(&chip->read_write_lock);
+
+ return (rc < 0) ? rc : 0;
+}
+
+static int smb1360_masked_write(struct smb1360_chip *chip, int reg,
+ u8 mask, u8 val)
+{
+ s32 rc;
+ u8 temp;
+
+ if (chip->skip_writes || chip->skip_reads)
+ return 0;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb1360_read(chip, reg, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev, "read failed: reg=%03X, rc=%d\n", reg, rc);
+ goto out;
+ }
+ temp &= ~mask;
+ temp |= val & mask;
+ rc = __smb1360_write(chip, reg, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "write failed: reg=%03X, rc=%d\n", reg, rc);
+ }
+out:
+ mutex_unlock(&chip->read_write_lock);
+ return rc;
+}
+
+static int smb1360_select_fg_i2c_address(struct smb1360_chip *chip)
+{
+ unsigned short addr = chip->default_i2c_addr << 0x1;
+
+ switch (chip->fg_access_type) {
+ case FG_ACCESS_CFG:
+ addr = (addr & ~FG_I2C_CFG_MASK) | FG_CFG_I2C_ADDR;
+ break;
+ case FG_ACCESS_PROFILE_A:
+ addr = (addr & ~FG_I2C_CFG_MASK) | FG_PROFILE_A_ADDR;
+ break;
+ case FG_ACCESS_PROFILE_B:
+ addr = (addr & ~FG_I2C_CFG_MASK) | FG_PROFILE_B_ADDR;
+ break;
+ default:
+ pr_err("Invalid FG access type=%d\n", chip->fg_access_type);
+ return -EINVAL;
+ }
+
+ chip->fg_i2c_addr = addr >> 0x1;
+ pr_debug("FG_access_type=%d fg_i2c_addr=%x\n", chip->fg_access_type,
+ chip->fg_i2c_addr);
+
+ return 0;
+}
+
+#define EXPONENT_MASK 0xF800
+#define MANTISSA_MASK 0x3FF
+#define SIGN_MASK 0x400
+#define EXPONENT_SHIFT 11
+#define SIGN_SHIFT 10
+#define MICRO_UNIT 1000000ULL
+static int64_t float_decode(u16 reg)
+{
+ int64_t final_val, exponent_val, mantissa_val;
+ int exponent, mantissa, n;
+ bool sign;
+
+ exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
+ mantissa = (reg & MANTISSA_MASK);
+ sign = !!(reg & SIGN_MASK);
+
+ pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
+
+ mantissa_val = mantissa * MICRO_UNIT;
+
+ n = exponent - 15;
+ if (n < 0)
+ exponent_val = MICRO_UNIT >> -n;
+ else
+ exponent_val = MICRO_UNIT << n;
+
+ n = n - 10;
+ if (n < 0)
+ mantissa_val >>= -n;
+ else
+ mantissa_val <<= n;
+
+ final_val = exponent_val + mantissa_val;
+
+ if (sign)
+ final_val *= -1;
+
+ return final_val;
+}
+
+#define MAX_MANTISSA (1023 * 1000000ULL)
+static unsigned int float_encode(int64_t float_val)
+{
+ int exponent = 0, sign = 0;
+ unsigned int final_val = 0;
+
+ if (float_val == 0)
+ return 0;
+
+ if (float_val < 0) {
+ sign = 1;
+ float_val = -float_val;
+ }
+
+ /* Reduce large mantissa until it fits into 10 bit */
+ while (float_val >= MAX_MANTISSA) {
+ exponent++;
+ float_val >>= 1;
+ }
+
+ /* Increase small mantissa to improve precision */
+ while (float_val < MAX_MANTISSA && exponent > -25) {
+ exponent--;
+ float_val <<= 1;
+ }
+
+ exponent = exponent + 25;
+
+ /* Convert mantissa from micro-units to units */
+ float_val = div_s64((float_val + MICRO_UNIT), (int)MICRO_UNIT);
+
+ if (float_val == 1024) {
+ exponent--;
+ float_val <<= 1;
+ }
+
+ float_val -= 1024;
+
+ /* Ensure that resulting number is within range */
+ if (float_val > MANTISSA_MASK)
+ float_val = MANTISSA_MASK;
+
+ /* Convert to 5 bit exponent, 11 bit mantissa */
+ final_val = (float_val & MANTISSA_MASK) | (sign << SIGN_SHIFT) |
+ ((exponent << EXPONENT_SHIFT) & EXPONENT_MASK);
+
+ return final_val;
+}
+
+/* FG reset could only be done after FG access being granted */
+static int smb1360_force_fg_reset(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT,
+ FG_RESET_BIT);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ return rc;
+ }
+
+ msleep(SMB1360_FG_RESET_DELAY_MS);
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_RESET_BIT, 0);
+ if (rc)
+ pr_err("Couldn't un-reset FG rc=%d\n", rc);
+
+ return rc;
+}
+
+/*
+ * Requesting FG access relys on the FG_ACCESS_ALLOWED IRQ.
+ * This function can only be called after interrupt handler
+ * being installed successfully.
+ */
+#define SMB1360_FG_ACCESS_TIMEOUT_MS 5000
+#define SMB1360_FG_ACCESS_RETRY_COUNT 3
+static int smb1360_enable_fg_access(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ u8 reg, retry = SMB1360_FG_ACCESS_RETRY_COUNT;
+
+ pr_debug("request FG memory access\n");
+ /*
+ * read the ACCESS_ALLOW status bit firstly to
+ * check if the access was granted before
+ */
+ mutex_lock(&chip->fg_access_request_lock);
+ smb1360_stay_awake(&chip->smb1360_ws, WAKEUP_SRC_FG_ACCESS);
+ rc = smb1360_read(chip, IRQ_I_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read IRQ_I_REG, rc=%d\n", rc);
+ goto bail_i2c;
+ } else if (reg & FG_ACCESS_ALLOWED_BIT) {
+ pr_debug("FG access was granted\n");
+ goto bail_i2c;
+ }
+
+ /* request FG access */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT,
+ FG_ACCESS_ENABLED_BIT);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ goto bail_i2c;
+ }
+
+ while (retry--) {
+ rc = wait_for_completion_interruptible_timeout(
+ &chip->fg_mem_access_granted,
+ msecs_to_jiffies(SMB1360_FG_ACCESS_TIMEOUT_MS));
+ if (rc <= 0)
+ pr_debug("FG access timeout, retry: %d\n", retry);
+ else
+ break;
+ }
+ if (rc == 0) /* timed out */
+ rc = -ETIMEDOUT;
+ else if (rc > 0) /* completed */
+ rc = 0;
+
+ /* Clear the FG access bit if request failed */
+ if (rc < 0) {
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ FG_ACCESS_ENABLED_BIT, 0);
+ if (rc)
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+ }
+
+bail_i2c:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_FG_ACCESS);
+ mutex_unlock(&chip->fg_access_request_lock);
+ return rc;
+}
+
+static inline bool is_device_suspended(struct smb1360_chip *chip)
+{
+ return !chip->resume_completed;
+}
+
+static int smb1360_disable_fg_access(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG, FG_ACCESS_ENABLED_BIT, 0);
+ if (rc)
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+
+ init_completion(&chip->fg_mem_access_granted);
+
+ return rc;
+}
+
+static int smb1360_enable_volatile_writes(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ ALLOW_VOLATILE_BIT, ALLOW_VOLATILE_BIT);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't set VOLATILE_W_PERM_BIT rc=%d\n", rc);
+
+ return rc;
+}
+
+static void smb1360_otp_backup_pool_init(struct smb1360_chip *chip)
+{
+ struct otp_backup_pool *pool = &chip->otp_backup;
+
+ pool->reg_start = 0xE0;
+ pool->reg_end = 0xEF;
+ pool->start_now = pool->reg_start;
+ mutex_init(&pool->lock);
+}
+
+static int smb1360_alloc_otp_backup_register(struct smb1360_chip *chip,
+ u8 size, int usage)
+{
+ int rc = 0, i;
+ u8 inv_pos;
+ struct otp_backup_pool *pool = &chip->otp_backup;
+
+ if (size % 2) {
+ pr_err("Must be allocated with pairs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pool->lock);
+ if (pool->start_now + size > pool->reg_end) {
+ pr_err("Allocation fail: start = 0x%x, size = %d\n",
+ pool->start_now, size);
+ mutex_unlock(&pool->lock);
+ return -EBUSY;
+ }
+ rc = pool->start_now;
+ inv_pos = pool->reg_end - pool->start_now + 1;
+ for (i = 0; i < size; i = i + 2) {
+ inv_pos -= (i ? 2 : 0);
+ pool->alg_bitmap |= usage << (inv_pos - 2);
+ }
+ pr_debug("Allocation success, start = 0x%x, size = %d, alg_bitmap = 0x%x\n",
+ rc, size, pool->alg_bitmap);
+ pool->start_now += size;
+ mutex_unlock(&pool->lock);
+
+ return rc;
+}
+
+#define OTP_BACKUP_WA_ALG_1 0xF0
+#define OTP_BACKUP_WA_ALG_2 0xF1
+static int smb1360_otp_backup_alg_update(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ struct otp_backup_pool *pool = &chip->otp_backup;
+
+ mutex_lock(&pool->lock);
+ rc = smb1360_fg_write(chip, OTP_BACKUP_WA_ALG_1,
+ (u8)(pool->alg_bitmap >> 8));
+ rc |= smb1360_fg_write(chip, OTP_BACKUP_WA_ALG_2,
+ (u8)(pool->alg_bitmap));
+ if (rc)
+ pr_err("Write FG address F0/F1 failed, rc = %d\n", rc);
+ mutex_unlock(&pool->lock);
+
+ return rc;
+}
+
+#define TRIM_1C_REG 0x1C
+#define CHECK_USB100_GOOD_BIT BIT(6)
+static bool is_usb100_broken(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = smb1360_read(chip, TRIM_1C_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read trim 1C reg rc = %d\n", rc);
+ return rc;
+ }
+ return !!(reg & CHECK_USB100_GOOD_BIT);
+}
+
+static int read_revision(struct smb1360_chip *chip, u8 *revision)
+{
+ int rc;
+
+ *revision = 0;
+ rc = smb1360_read(chip, REVISION_CTRL_REG, revision);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read REVISION_CTRL_REG rc=%d", rc);
+
+ *revision &= DEVICE_REV_MASK;
+
+ return rc;
+}
+
+#define MIN_FLOAT_MV 3460
+#define MAX_FLOAT_MV 4730
+#define VFLOAT_STEP_MV 10
+static int smb1360_float_voltage_set(struct smb1360_chip *chip, int vfloat_mv)
+{
+ u8 temp;
+
+ if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
+ dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
+ vfloat_mv);
+ return -EINVAL;
+ }
+
+ temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV;
+
+ return smb1360_masked_write(chip, BATT_CHG_FLT_VTG_REG,
+ VFLOAT_MASK, temp);
+}
+
+#define MIN_RECHG_MV 50
+#define MAX_RECHG_MV 300
+static int smb1360_recharge_threshold_set(struct smb1360_chip *chip,
+ int resume_mv)
+{
+ u8 temp;
+
+ if ((resume_mv < MIN_RECHG_MV) || (resume_mv > MAX_RECHG_MV)) {
+ dev_err(chip->dev, "bad rechg_thrsh =%d asked to set\n",
+ resume_mv);
+ return -EINVAL;
+ }
+
+ temp = resume_mv / 100;
+
+ return smb1360_masked_write(chip, CFG_BATT_CHG_REG,
+ RECHG_MV_MASK, temp << RECHG_MV_SHIFT);
+}
+
+static int __smb1360_charging_disable(struct smb1360_chip *chip, bool disable)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_CHG_REG,
+ CMD_CHG_EN, disable ? 0 : CMD_CHG_EN);
+ if (rc < 0)
+ pr_err("Couldn't set CHG_ENABLE_BIT disable=%d rc = %d\n",
+ disable, rc);
+ else
+ pr_debug("CHG_EN status=%d\n", !disable);
+
+ return rc;
+}
+
+static int smb1360_charging_disable(struct smb1360_chip *chip, int reason,
+ int disable)
+{
+ int rc = 0;
+ int disabled;
+
+ mutex_lock(&chip->charging_disable_lock);
+
+ disabled = chip->charging_disabled_status;
+
+ pr_debug("reason=%d requested_disable=%d disabled_status=%d\n",
+ reason, disable, disabled);
+
+ if (disable == true)
+ disabled |= reason;
+ else
+ disabled &= ~reason;
+
+ if (disabled)
+ rc = __smb1360_charging_disable(chip, true);
+ else
+ rc = __smb1360_charging_disable(chip, false);
+
+ if (rc)
+ pr_err("Couldn't disable charging for reason=%d rc=%d\n",
+ rc, reason);
+ else
+ chip->charging_disabled_status = disabled;
+
+ mutex_unlock(&chip->charging_disable_lock);
+
+ return rc;
+}
+
+static int smb1360_soft_jeita_comp_enable(struct smb1360_chip *chip,
+ bool enable)
+{
+ int rc = 0;
+
+ rc = smb1360_masked_write(chip, CHG_CMP_CFG, JEITA_COMP_EN_MASK,
+ enable ? JEITA_COMP_EN_BIT : 0);
+ if (rc)
+ pr_err("Couldn't %s JEITA compensation\n", enable ?
+ "enable" : "disable");
+
+ return rc;
+}
+
+static enum power_supply_property smb1360_battery_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+};
+
+static int smb1360_get_prop_batt_present(struct smb1360_chip *chip)
+{
+ return chip->batt_present;
+}
+
+static int smb1360_get_prop_batt_status(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0, chg_type;
+
+ if (is_device_suspended(chip))
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+
+ if (chip->batt_full)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ rc = smb1360_read(chip, STATUS_3_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read STATUS_3_REG rc=%d\n", rc);
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ pr_debug("STATUS_3_REG = %x\n", reg);
+
+ if (reg & CHG_HOLD_OFF_BIT)
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+
+ if (chg_type == BATT_NOT_CHG_VAL)
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ return POWER_SUPPLY_STATUS_CHARGING;
+}
+
+static int smb1360_get_prop_charge_type(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+ u8 chg_type;
+
+ if (is_device_suspended(chip))
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+
+ rc = smb1360_read(chip, STATUS_3_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read STATUS_3_REG rc=%d\n", rc);
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+
+ chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+ if (chg_type == BATT_NOT_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else if ((chg_type == BATT_FAST_CHG_VAL) ||
+ (chg_type == BATT_TAPER_CHG_VAL))
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (chg_type == BATT_PRE_CHG_VAL)
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int smb1360_get_prop_batt_health(struct smb1360_chip *chip)
+{
+ union power_supply_propval ret = {0, };
+
+ if (chip->batt_hot)
+ ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (chip->batt_cold)
+ ret.intval = POWER_SUPPLY_HEALTH_COLD;
+ else if (chip->batt_warm)
+ ret.intval = POWER_SUPPLY_HEALTH_WARM;
+ else if (chip->batt_cool)
+ ret.intval = POWER_SUPPLY_HEALTH_COOL;
+ else
+ ret.intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ return ret.intval;
+}
+
+static int smb1360_get_prop_batt_capacity(struct smb1360_chip *chip)
+{
+ u8 reg;
+ u32 temp = 0;
+ int rc, soc = 0;
+
+ if (chip->fake_battery_soc >= 0)
+ return chip->fake_battery_soc;
+
+ if (chip->empty_soc) {
+ pr_debug("empty_soc\n");
+ return 0;
+ }
+
+ if (is_device_suspended(chip))
+ return chip->soc_now;
+
+ rc = smb1360_read(chip, SHDW_FG_MSYS_SOC, ®);
+ if (rc) {
+ pr_err("Failed to read FG_MSYS_SOC rc=%d\n", rc);
+ return rc;
+ }
+ soc = (100 * reg) / MAX_8_BITS;
+
+ temp = (100 * reg) % MAX_8_BITS;
+ if (temp > (MAX_8_BITS / 2))
+ soc += 1;
+
+ pr_debug("msys_soc_reg=0x%02x, fg_soc=%d batt_full = %d\n", reg,
+ soc, chip->batt_full);
+
+ chip->soc_now = (chip->batt_full ? 100 : bound(soc, 0, 100));
+
+ return chip->soc_now;
+}
+
+static int smb1360_get_prop_chg_full_design(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, fcc_mah = 0;
+
+ if (is_device_suspended(chip))
+ return chip->fcc_mah;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_CAPACITY, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_CAPACITY rc=%d\n", rc);
+ return rc;
+ }
+ fcc_mah = (reg[1] << 8) | reg[0];
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x fcc_mah=%d\n",
+ reg[0], reg[1], fcc_mah);
+
+ chip->fcc_mah = fcc_mah * 1000;
+
+ return chip->fcc_mah;
+}
+
+static int smb1360_get_prop_batt_temp(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, temp = 0;
+
+ if (is_device_suspended(chip))
+ return chip->temp_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_BATT_TEMP, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_BATT_TEMP rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = (reg[1] << 8) | reg[0];
+ temp = div_u64(temp * 625, 10000UL); /* temperature in kelvin */
+ temp = (temp - 273) * 10; /* temperature in decideg */
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x temperature=%d\n",
+ reg[0], reg[1], temp);
+
+ chip->temp_now = temp;
+
+ return chip->temp_now;
+}
+
+static int smb1360_get_prop_voltage_now(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, temp = 0;
+
+ if (is_device_suspended(chip))
+ return chip->voltage_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_VTG_NOW, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_VTG_NOW rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = (reg[1] << 8) | reg[0];
+ temp = div_u64(temp * 5000, 0x7FFF);
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x voltage=%d\n",
+ reg[0], reg[1], temp * 1000);
+
+ chip->voltage_now = temp * 1000;
+
+ return chip->voltage_now;
+}
+
+static int smb1360_get_prop_batt_resistance(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ u16 temp;
+ int rc;
+ int64_t resistance;
+
+ if (is_device_suspended(chip))
+ return chip->resistance_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_ESR_ACTUAL, reg, 2);
+ if (rc) {
+ pr_err("Failed to read FG_ESR_ACTUAL rc=%d\n", rc);
+ return rc;
+ }
+ temp = (reg[1] << 8) | reg[0];
+
+ resistance = float_decode(temp) * 2;
+
+ pr_debug("reg=0x%02x resistance=%lld\n", temp, resistance);
+
+ /* resistance in uohms */
+ chip->resistance_now = resistance;
+
+ return chip->resistance_now;
+}
+
+static int smb1360_get_prop_current_now(struct smb1360_chip *chip)
+{
+ u8 reg[2];
+ int rc, temp = 0;
+
+ if (is_device_suspended(chip))
+ return chip->current_now;
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_CURR_NOW, reg, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_CURR_NOW rc=%d\n", rc);
+ return rc;
+ }
+
+ temp = ((s8)reg[1] << 8) | reg[0];
+ temp = div_s64(temp * 2500, 0x7FFF);
+
+ pr_debug("reg[0]=0x%02x reg[1]=0x%02x current=%d\n",
+ reg[0], reg[1], temp * 1000);
+
+ chip->current_now = temp * 1000;
+
+ return chip->current_now;
+}
+
+static int smb1360_set_minimum_usb_current(struct smb1360_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->min_icl_usb100) {
+ pr_debug("USB min current set to 100mA\n");
+ /* set input current limit to minimum (300mA) */
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ INPUT_CURR_LIM_MASK,
+ INPUT_CURR_LIM_300MA);
+ if (rc)
+ pr_err("Couldn't set ICL mA rc=%d\n", rc);
+
+ if (!(chip->workaround_flags & WRKRND_USB100_FAIL))
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_100_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n",
+ rc);
+ } else {
+ pr_debug("USB min current set to 500mA\n");
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_500_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n",
+ rc);
+ }
+
+ return rc;
+}
+
+static struct power_supply *get_parallel_psy(struct smb1360_chip *chip)
+{
+ if (chip->parallel_psy)
+ return chip->parallel_psy;
+ chip->parallel_psy = power_supply_get_by_name("usb-parallel");
+ if (!chip->parallel_psy)
+ pr_debug("parallel charger not found\n");
+ return chip->parallel_psy;
+}
+
+static int __smb1360_parallel_charger_enable(struct smb1360_chip *chip,
+ bool enable)
+{
+ struct power_supply *parallel_psy = get_parallel_psy(chip);
+ union power_supply_propval pval = {0, };
+
+ if (!parallel_psy)
+ return 0;
+
+ pval.intval = (enable ? (chip->max_parallel_chg_current * 1000) : 0);
+ chip->parallel_psy_d.set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+ pval.intval = (enable ? 1 : 0);
+ chip->parallel_psy_d.set_property(parallel_psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
+
+ pr_debug("Parallel-charger %s max_chg_current=%d\n",
+ enable ? "enabled" : "disabled",
+ enable ? (chip->max_parallel_chg_current * 1000) : 0);
+
+ return 0;
+}
+
+static int smb1360_parallel_charger_enable(struct smb1360_chip *chip,
+ int reason, bool enable)
+{
+ int disabled, *disabled_status;
+
+ mutex_lock(&chip->parallel_chg_lock);
+
+ disabled = chip->parallel_chg_disable_status;
+ disabled_status = &chip->parallel_chg_disable_status;
+
+ pr_debug("reason=0x%x requested=%s disabled_status=0x%x\n",
+ reason, enable ? "enable" : "disable", disabled);
+
+ if (enable == true)
+ disabled &= ~reason;
+ else
+ disabled |= reason;
+
+ if (*disabled_status && !disabled)
+ __smb1360_parallel_charger_enable(chip, true);
+
+ if (!(*disabled_status) && disabled)
+ __smb1360_parallel_charger_enable(chip, false);
+
+ *disabled_status = disabled;
+
+ pr_debug("disabled_status = %x\n", *disabled_status);
+
+ mutex_unlock(&chip->parallel_chg_lock);
+
+ return 0;
+}
+
+static void smb1360_parallel_work(struct work_struct *work)
+{
+ u8 reg;
+ int rc, i;
+ struct smb1360_chip *chip = container_of(work,
+ struct smb1360_chip, parallel_work);
+
+ /* check the AICL settled value */
+ rc = smb1360_read(chip, STATUS_1_REG, ®);
+ if (rc) {
+ pr_debug("Unable to read AICL status rc=%d\n", rc);
+ goto exit_work;
+ }
+ pr_debug("STATUS_1 (aicl status)=0x%x\n", reg);
+ if ((reg & AICL_CURRENT_STATUS_MASK) == AICL_LIMIT_1500MA) {
+ /* Strong Charger - Enable parallel path */
+ /* find the new fastchg current */
+ chip->fastchg_current += (chip->max_parallel_chg_current / 2);
+ for (i = 0; i < ARRAY_SIZE(fastchg_current) - 1; i++) {
+ if (fastchg_current[i] >= chip->fastchg_current)
+ break;
+ }
+ if (i == ARRAY_SIZE(fastchg_current))
+ i--;
+
+ rc = smb1360_masked_write(chip, CHG_CURRENT_REG,
+ FASTCHG_CURR_MASK, i << FASTCHG_CURR_SHIFT);
+ if (rc)
+ pr_err("Couldn't set fastchg mA rc=%d\n", rc);
+
+ pr_debug("fast-chg (parallel-mode) current set to = %d\n",
+ fastchg_current[i]);
+
+ smb1360_parallel_charger_enable(chip, PARALLEL_CURRENT, true);
+ } else {
+ /* Weak-charger - Disable parallel path */
+ smb1360_parallel_charger_enable(chip, PARALLEL_CURRENT, false);
+ }
+
+exit_work:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_PARALLEL);
+}
+
+static int smb1360_set_appropriate_usb_current(struct smb1360_chip *chip)
+{
+ int rc = 0, i, therm_ma, current_ma;
+ int path_current = chip->usb_psy_ma;
+
+ /*
+ * If battery is absent do not modify the current at all, these
+ * would be some appropriate values set by the bootloader or default
+ * configuration and since it is the only source of power we should
+ * not change it
+ */
+ if (!chip->batt_present) {
+ pr_debug("ignoring current request since battery is absent\n");
+ return 0;
+ }
+
+ if (chip->therm_lvl_sel > 0
+ && chip->therm_lvl_sel < (chip->thermal_levels - 1))
+ /*
+ * consider thermal limit only when it is active and not at
+ * the highest level
+ */
+ therm_ma = chip->thermal_mitigation[chip->therm_lvl_sel];
+ else
+ therm_ma = path_current;
+
+ current_ma = min(therm_ma, path_current);
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ if (chip->batt_warm)
+ current_ma = min(current_ma, chip->warm_bat_ma);
+ else if (chip->batt_cool)
+ current_ma = min(current_ma, chip->cool_bat_ma);
+ }
+
+ if (current_ma <= 2) {
+ /*
+ * SMB1360 does not support USB suspend -
+ * so set the current-limit to minimum in suspend.
+ */
+ pr_debug("current_ma=%d <= 2 set USB current to minimum\n",
+ current_ma);
+ rc = smb1360_set_minimum_usb_current(chip);
+ if (rc < 0)
+ pr_err("Couldn't to set minimum USB current rc = %d\n",
+ rc);
+ /* disable parallel charger */
+ if (chip->parallel_charging)
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_CURRENT, false);
+
+ return rc;
+ }
+
+ for (i = ARRAY_SIZE(input_current_limit) - 1; i >= 0; i--) {
+ if (input_current_limit[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ pr_debug("Couldn't find ICL mA rc=%d\n", rc);
+ i = 0;
+ }
+ /* set input current limit */
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ INPUT_CURR_LIM_MASK, i);
+ if (rc)
+ pr_err("Couldn't set ICL mA rc=%d\n", rc);
+
+ pr_debug("ICL set to = %d\n", input_current_limit[i]);
+
+ if ((current_ma <= CURRENT_100_MA) &&
+ ((chip->workaround_flags & WRKRND_USB100_FAIL) ||
+ !chip->min_icl_usb100)) {
+ pr_debug("usb100 not supported: usb100_wrkrnd=%d min_icl_100=%d\n",
+ !!(chip->workaround_flags & WRKRND_USB100_FAIL),
+ chip->min_icl_usb100);
+ current_ma = CURRENT_500_MA;
+ }
+
+ if (current_ma <= CURRENT_100_MA) {
+ /* USB 100 */
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_100_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB100 rc=%d\n", rc);
+ pr_debug("Setting USB 100\n");
+ } else if (current_ma <= CURRENT_500_MA) {
+ /* USB 500 */
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_500_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB500 rc=%d\n", rc);
+ pr_debug("Setting USB 500\n");
+ } else {
+ /* USB AC */
+ if (chip->rsense_10mohm)
+ current_ma /= 2;
+
+ for (i = ARRAY_SIZE(fastchg_current) - 1; i >= 0; i--) {
+ if (fastchg_current[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ pr_debug("Couldn't find fastchg mA rc=%d\n", rc);
+ i = 0;
+ }
+
+ chip->fastchg_current = fastchg_current[i];
+
+ /* set fastchg limit */
+ rc = smb1360_masked_write(chip, CHG_CURRENT_REG,
+ FASTCHG_CURR_MASK, i << FASTCHG_CURR_SHIFT);
+ if (rc)
+ pr_err("Couldn't set fastchg mA rc=%d\n", rc);
+
+ /*
+ * To move to a new (higher) input-current setting,
+ * first set USB500 and then USBAC. This makes sure
+ * that the new ICL setting takes affect.
+ */
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_500_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB500 rc=%d\n", rc);
+
+ rc = smb1360_masked_write(chip, CMD_IL_REG,
+ USB_CTRL_MASK, USB_AC_BIT);
+ if (rc)
+ pr_err("Couldn't configure for USB AC rc=%d\n", rc);
+
+ pr_debug("fast-chg current set to = %d\n", fastchg_current[i]);
+ }
+
+ return rc;
+}
+
+static int smb1360_set_jeita_comp_curr(struct smb1360_chip *chip,
+ int current_ma)
+{
+ int i;
+ int rc = 0;
+
+ for (i = ARRAY_SIZE(fastchg_current) - 1; i >= 0; i--) {
+ if (fastchg_current[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ pr_debug("Couldn't find fastchg_current %dmA\n", current_ma);
+ i = 0;
+ }
+
+ rc = smb1360_masked_write(chip, CHG_CMP_CFG,
+ JEITA_COMP_CURR_MASK, i);
+ if (rc)
+ pr_err("Couldn't configure for Icomp, rc = %d\n", rc);
+
+ return rc;
+}
+
+#define TEMP_THRE_SET(x) ((x + 300) / 10)
+#define TEMP_THRE_GET(x) ((x * 10) - 300)
+static int smb1360_set_soft_jeita_threshold(struct smb1360_chip *chip,
+ int cold_threshold, int hot_threshold)
+{
+ int rc = 0;
+
+ rc = smb1360_write(chip, JEITA_SOFT_COLD_REG,
+ TEMP_THRE_SET(cold_threshold));
+ if (rc) {
+ pr_err("Couldn't set soft cold threshold, rc = %d\n", rc);
+ return rc;
+ }
+ chip->soft_cold_thresh = cold_threshold;
+
+ rc = smb1360_write(chip, JEITA_SOFT_HOT_REG,
+ TEMP_THRE_SET(hot_threshold));
+ if (rc) {
+ pr_err("Couldn't set soft hot threshold, rc = %d\n", rc);
+ return rc;
+ }
+ chip->soft_hot_thresh = hot_threshold;
+
+ return rc;
+}
+
+static int smb1360_get_soft_jeita_threshold(struct smb1360_chip *chip,
+ int *cold_threshold, int *hot_threshold)
+{
+ int rc = 0;
+ u8 value;
+
+ rc = smb1360_read(chip, JEITA_SOFT_COLD_REG, &value);
+ if (rc) {
+ pr_err("Couldn't get soft cold threshold, rc = %d\n", rc);
+ return rc;
+ }
+ *cold_threshold = TEMP_THRE_GET(value);
+
+ rc = smb1360_read(chip, JEITA_SOFT_HOT_REG, &value);
+ if (rc) {
+ pr_err("Couldn't get soft hot threshold, rc = %d\n", rc);
+ return rc;
+ }
+ *hot_threshold = TEMP_THRE_GET(value);
+
+ return rc;
+}
+
+#define OTP_HARD_COLD_REG_ADDR 0x12
+#define OTP_HARD_HOT_REG_ADDR 0x13
+static int smb1360_set_otp_hard_jeita_threshold(struct smb1360_chip *chip,
+ int cold_threshold, int hot_threshold)
+{
+ int rc = 0, i;
+ u8 reg[4] = { 0 };
+ u8 otp_reg = 0;
+ int temp_code;
+
+ if (cold_threshold > chip->cool_bat_decidegc ||
+ chip->cool_bat_decidegc >= chip->warm_bat_decidegc ||
+ chip->warm_bat_decidegc > hot_threshold) {
+ pr_err("cold:%d, cool:%d, warm:%d, hot:%d should be ordered in size\n",
+ cold_threshold, chip->cool_bat_decidegc,
+ chip->warm_bat_decidegc, hot_threshold);
+ return -EINVAL;
+ }
+ pr_debug("cold:%d, cool:%d, warm:%d, hot:%d\n",
+ cold_threshold, chip->cool_bat_decidegc,
+ chip->warm_bat_decidegc, hot_threshold);
+ if (!chip->hard_jeita_otp_reg) {
+ otp_reg = smb1360_alloc_otp_backup_register(chip,
+ ARRAY_SIZE(reg), OTP_BACKUP_FG_USE);
+ if (otp_reg <= 0) {
+ pr_err("OTP reg allocation failed for hard JEITA\n");
+ return otp_reg;
+ }
+
+ chip->hard_jeita_otp_reg = otp_reg;
+ } else {
+ otp_reg = chip->hard_jeita_otp_reg;
+ }
+ pr_debug("hard_jeita_otp_reg = 0x%x\n", chip->hard_jeita_otp_reg);
+
+ reg[0] = (u8)OTP_HARD_HOT_REG_ADDR;
+ temp_code = TEMP_THRE_SET(hot_threshold);
+ if (temp_code < 0) {
+ pr_err("hard hot temp encode failed\n");
+ return temp_code;
+ }
+ reg[1] = (u8)temp_code;
+ reg[2] = (u8)OTP_HARD_COLD_REG_ADDR;
+ temp_code = TEMP_THRE_SET(cold_threshold);
+ if (temp_code < 0) {
+ pr_err("hard cold temp encode failed\n");
+ return temp_code;
+ }
+ reg[3] = (u8)temp_code;
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc = %d\n", rc);
+ return rc;
+ }
+ chip->fg_access_type = FG_ACCESS_CFG;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ goto restore_fg;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg); i++) {
+ rc = smb1360_fg_write(chip, (otp_reg + i), reg[i]);
+ if (rc) {
+ pr_err("Write FG address 0x%x: 0x%x failed, rc = %d\n",
+ otp_reg + i, reg[i], rc);
+ goto restore_fg;
+ }
+ pr_debug("Write FG addr=0x%x, value=0x%x\n",
+ otp_reg + i, reg[i]);
+ }
+ rc = smb1360_otp_backup_alg_update(chip);
+ if (rc) {
+ pr_err("Update OTP backup algorithm failed\n");
+ goto restore_fg;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ CFG_FG_OTP_BACK_UP_ENABLE, CFG_FG_OTP_BACK_UP_ENABLE);
+ if (rc) {
+ pr_err("Write reg 0x0E failed, rc = %d\n", rc);
+ goto restore_fg;
+ }
+
+restore_fg:
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1360_hard_jeita_otp_init(struct smb1360_chip *chip)
+{
+ int rc = 0;
+
+ if (!chip->otp_hard_jeita_config)
+ return rc;
+
+ rc = smb1360_set_otp_hard_jeita_threshold(chip,
+ chip->otp_cold_bat_decidegc, chip->otp_hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set OTP hard jeita threshold,rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1360_system_temp_level_set(struct smb1360_chip *chip,
+ int lvl_sel)
+{
+ int rc = 0;
+ int prev_therm_lvl;
+
+ if (!chip->thermal_mitigation) {
+ pr_err("Thermal mitigation not supported\n");
+ return -EINVAL;
+ }
+
+ if (lvl_sel < 0) {
+ pr_err("Unsupported level selected %d\n", lvl_sel);
+ return -EINVAL;
+ }
+
+ if (lvl_sel >= chip->thermal_levels) {
+ pr_err("Unsupported level selected %d forcing %d\n", lvl_sel,
+ chip->thermal_levels - 1);
+ lvl_sel = chip->thermal_levels - 1;
+ }
+
+ if (lvl_sel == chip->therm_lvl_sel)
+ return 0;
+
+ mutex_lock(&chip->current_change_lock);
+ prev_therm_lvl = chip->therm_lvl_sel;
+ chip->therm_lvl_sel = lvl_sel;
+
+ if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
+ rc = smb1360_set_minimum_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current to minimum rc = %d\n",
+ rc);
+ } else {
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current rc = %d\n", rc);
+ }
+
+ mutex_unlock(&chip->current_change_lock);
+ return rc;
+}
+
+static enum power_supply_property smb1360_usb_properties[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_REAL_TYPE,
+ POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+};
+
+static int smb1360_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int is_battery_charging = 0;
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = chip->usb_psy_ma * 1000;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = chip->usb_present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ is_battery_charging = smb1360_get_prop_batt_status(chip);
+ val->intval = chip->usb_present &&
+ (is_battery_charging == POWER_SUPPLY_STATUS_CHARGING);
+ break;
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_UNKNOWN;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ if (chip->usb_present &&
+ (chip->usb_supply_type != POWER_SUPPLY_TYPE_UNKNOWN))
+ val->intval = chip->usb_supply_type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int smb1360_usb_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ chip->usb_psy_ma = val->intval / 1000;
+ rc = smb1360_set_appropriate_usb_current(chip);
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ case POWER_SUPPLY_PROP_REAL_TYPE:
+ chip->usb_supply_type = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(psy);
+ return 0;
+}
+
+static int smb1360_usb_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int smb1360_battery_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ smb1360_charging_disable(chip, USER, !val->intval);
+ if (chip->parallel_charging)
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_USER, val->intval);
+ power_supply_changed(chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ chip->fake_battery_soc = val->intval;
+ pr_info("fake_soc set to %d\n", chip->fake_battery_soc);
+ power_supply_changed(chip->batt_psy);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ smb1360_system_temp_level_set(chip, val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smb1360_battery_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ int rc;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ rc = 1;
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+static int smb1360_battery_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb1360_chip *chip = power_supply_get_drvdata(psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = smb1360_get_prop_batt_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = smb1360_get_prop_batt_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = smb1360_get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !chip->charging_disabled_status;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = smb1360_get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = smb1360_get_prop_batt_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = smb1360_get_prop_chg_full_design(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = smb1360_get_prop_voltage_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = smb1360_get_prop_current_now(chip);
+ break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = smb1360_get_prop_batt_resistance(chip);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = smb1360_get_prop_batt_temp(chip);
+ break;
+ case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+ val->intval = chip->therm_lvl_sel;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hot_hard_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_hot = !!rt_stat;
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_hot ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_HARD, !chip->batt_hot);
+ }
+ if (chip->hot_hysteresis) {
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_HYSTERSIS);
+ schedule_work(&chip->jeita_hysteresis_work);
+ }
+
+ return 0;
+}
+
+static int cold_hard_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_cold = !!rt_stat;
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_cold ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_HARD, !chip->batt_cold);
+ }
+ if (chip->cold_hysteresis) {
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_HYSTERSIS);
+ schedule_work(&chip->jeita_hysteresis_work);
+ }
+
+ return 0;
+}
+
+static void smb1360_jeita_hysteresis_work(struct work_struct *work)
+{
+ int rc = 0;
+ int hard_hot, hard_cold;
+ struct smb1360_chip *chip = container_of(work,
+ struct smb1360_chip, jeita_hysteresis_work);
+
+ /* disable hard JEITA IRQ first */
+ rc = smb1360_masked_write(chip, IRQ_CFG_REG,
+ IRQ_BAT_HOT_COLD_HARD_BIT, 0);
+ if (rc) {
+ pr_err("disable hard JEITA IRQ failed, rc = %d\n", rc);
+ goto exit_worker;
+ }
+ hard_hot = chip->otp_hot_bat_decidegc;
+ hard_cold = chip->otp_cold_bat_decidegc;
+ if (chip->batt_hot)
+ hard_hot -= chip->hot_hysteresis;
+ else if (chip->batt_cold)
+ hard_cold += chip->cold_hysteresis;
+
+ rc = smb1360_set_otp_hard_jeita_threshold(chip, hard_cold, hard_hot);
+ if (rc) {
+ pr_err("set hard JEITA threshold failed\n");
+ goto exit_worker;
+ }
+ pr_debug("hard cold: %d, hard hot: %d reprogramed\n",
+ hard_cold, hard_hot);
+ /* enable hard JEITA IRQ at the end */
+ rc = smb1360_masked_write(chip, IRQ_CFG_REG,
+ IRQ_BAT_HOT_COLD_HARD_BIT, IRQ_BAT_HOT_COLD_HARD_BIT);
+ if (rc)
+ pr_err("enable hard JEITA IRQ failed\n");
+exit_worker:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_JEITA_HYSTERSIS);
+}
+
+/*
+ * This worker thread should only be called when WRKRND_HARD_JEITA
+ * is set.
+ * It is needed to re-program JEITA soft thresholds, compensate
+ * target voltage and charging current manually.
+ * The function is required as JEITA hard thresholds can't be programmed.
+ */
+static void smb1360_jeita_work_fn(struct work_struct *work)
+{
+ int temp;
+ int rc = 0;
+ struct smb1360_chip *chip = container_of(work,
+ struct smb1360_chip, jeita_work.work);
+
+ temp = smb1360_get_prop_batt_temp(chip);
+
+ if (temp > chip->hot_bat_decidegc) {
+ /* battery status is hot, only config thresholds */
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->warm_bat_decidegc, chip->hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else if (temp > chip->warm_bat_decidegc ||
+ (temp == chip->warm_bat_decidegc && !!chip->soft_hot_rt_stat)) {
+ /* battery status is warm, do compensation manually */
+ chip->batt_warm = true;
+ chip->batt_cool = false;
+ rc = smb1360_float_voltage_set(chip, chip->warm_bat_mv);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set float voltage\n");
+ goto end;
+ }
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current\n");
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->warm_bat_decidegc, chip->hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else if (temp > chip->cool_bat_decidegc ||
+ (temp == chip->cool_bat_decidegc && !chip->soft_cold_rt_stat)) {
+ /* battery status is good, do the normal charging */
+ chip->batt_warm = false;
+ chip->batt_cool = false;
+ rc = smb1360_float_voltage_set(chip, chip->vfloat_mv);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set float voltage\n");
+ goto end;
+ }
+ rc = smb1360_set_appropriate_usb_current(chip);
+ if (rc)
+ pr_err("Couldn't set USB current\n");
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cool_bat_decidegc, chip->warm_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else if (temp > chip->cold_bat_decidegc) {
+ /* battery status is cool, do compensation manually */
+ chip->batt_cool = true;
+ chip->batt_warm = false;
+ rc = smb1360_float_voltage_set(chip, chip->cool_bat_mv);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set float voltage\n");
+ goto end;
+ }
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cold_bat_decidegc, chip->cool_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ } else {
+ /* battery status is cold, only config thresholds */
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cold_bat_decidegc, chip->cool_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set jeita threshold\n");
+ goto end;
+ }
+ }
+
+ pr_debug("warm %d, cool %d, soft_cold_rt_sts %d, soft_hot_rt_sts %d, jeita supported %d, threshold_now %d %d\n",
+ chip->batt_warm, chip->batt_cool, !!chip->soft_cold_rt_stat,
+ !!chip->soft_hot_rt_stat, chip->soft_jeita_supported,
+ chip->soft_cold_thresh, chip->soft_hot_thresh);
+end:
+ smb1360_relax(&chip->smb1360_ws, WAKEUP_SRC_JEITA_SOFT);
+}
+
+static int hot_soft_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ chip->soft_hot_rt_stat = rt_stat;
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ if (!chip->config_hard_thresholds)
+ chip->batt_warm = !!rt_stat;
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ cancel_delayed_work_sync(&chip->jeita_work);
+ schedule_delayed_work(&chip->jeita_work,
+ msecs_to_jiffies(JEITA_WORK_MS));
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_SOFT);
+ }
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_warm ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_SOFT, !chip->batt_warm);
+ }
+ return 0;
+}
+
+static int cold_soft_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ chip->soft_cold_rt_stat = rt_stat;
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ if (!chip->config_hard_thresholds)
+ chip->batt_cool = !!rt_stat;
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ cancel_delayed_work_sync(&chip->jeita_work);
+ schedule_delayed_work(&chip->jeita_work,
+ msecs_to_jiffies(JEITA_WORK_MS));
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_JEITA_SOFT);
+ }
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_cool ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_JEITA_SOFT, !chip->batt_cool);
+ }
+
+ return 0;
+}
+
+static int battery_missing_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_present = !rt_stat;
+ return 0;
+}
+
+static int vbat_low_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("vbat low\n");
+
+ return 0;
+}
+
+static int chg_hot_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_warn_ratelimited("chg hot\n");
+ return 0;
+}
+
+static int chg_term_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_full = !!rt_stat;
+
+ if (chip->parallel_charging) {
+ pr_debug("%s parallel-charging\n", chip->batt_full ?
+ "Disable" : "Enable");
+ smb1360_parallel_charger_enable(chip,
+ PARALLEL_EOC, !chip->batt_full);
+ }
+
+ return 0;
+}
+
+static int chg_fastchg_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+
+ return 0;
+}
+
+static int usbin_uv_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ bool usb_present = !rt_stat;
+
+ pr_debug("chip->usb_present = %d usb_present = %d\n",
+ chip->usb_present, usb_present);
+ if (chip->usb_present && !usb_present) {
+ /* USB removed */
+ chip->usb_present = usb_present;
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+
+ if (!chip->usb_present && usb_present) {
+ /* USB inserted */
+ chip->usb_present = usb_present;
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+ }
+ power_supply_changed(chip->usb_psy);
+
+ return 0;
+}
+
+static int aicl_done_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ bool aicl_done = !!rt_stat;
+
+ pr_debug("AICL done=%d\n", aicl_done);
+
+ if (chip->parallel_charging && aicl_done) {
+ cancel_work_sync(&chip->parallel_work);
+ smb1360_stay_awake(&chip->smb1360_ws, WAKEUP_SRC_PARALLEL);
+ schedule_work(&chip->parallel_work);
+ }
+
+ return 0;
+}
+
+static int chg_inhibit_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ /*
+ * charger is inserted when the battery voltage is high
+ * so h/w won't start charging just yet. Treat this as
+ * battery full
+ */
+ pr_debug("rt_stat = 0x%02x\n", rt_stat);
+ chip->batt_full = !!rt_stat;
+ return 0;
+}
+
+static int delta_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("SOC changed! - rt_stat = 0x%02x\n", rt_stat);
+
+ return 0;
+}
+
+static int min_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("SOC dropped below min SOC, rt_stat = 0x%02x\n", rt_stat);
+
+ if (chip->awake_min_soc)
+ rt_stat ? smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_MIN_SOC) :
+ smb1360_relax(&chip->smb1360_ws,
+ WAKEUP_SRC_MIN_SOC);
+
+ return 0;
+}
+
+static int empty_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("SOC empty! rt_stat = 0x%02x\n", rt_stat);
+
+ if (!chip->empty_soc_disabled) {
+ if (rt_stat) {
+ chip->empty_soc = true;
+ smb1360_stay_awake(&chip->smb1360_ws,
+ WAKEUP_SRC_EMPTY_SOC);
+ pr_warn_ratelimited("SOC is 0\n");
+ } else {
+ chip->empty_soc = false;
+ smb1360_relax(&chip->smb1360_ws,
+ WAKEUP_SRC_EMPTY_SOC);
+ }
+ }
+
+ return 0;
+}
+
+static int full_soc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ if (rt_stat)
+ pr_debug("SOC is 100\n");
+
+ return 0;
+}
+
+static int fg_access_allowed_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("stat=%d\n", !!rt_stat);
+
+ if (rt_stat & FG_ACCESS_ALLOWED_BIT) {
+ pr_debug("FG access granted\n");
+ complete_all(&chip->fg_mem_access_granted);
+ }
+
+ return 0;
+}
+
+static int batt_id_complete_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ pr_debug("batt_id = %x\n", (rt_stat & BATT_ID_RESULT_BIT)
+ >> BATT_ID_SHIFT);
+
+ return 0;
+}
+
+static int smb1360_adjust_current_gain(struct smb1360_chip *chip,
+ int gain_factor)
+{
+ int i, rc;
+ int64_t current_gain, new_current_gain;
+ u16 reg_value1 = 0, reg_value2 = 0;
+ u8 reg[4] = {0x1D, 0x00, 0x1E, 0x00};
+ u8 otp_reg = 0;
+
+ if (!chip->current_gain_otp_reg) {
+ otp_reg = smb1360_alloc_otp_backup_register(chip,
+ ARRAY_SIZE(reg), OTP_BACKUP_FG_USE);
+ if (otp_reg <= 0) {
+ pr_err("OTP reg allocation fail for adjusting current gain\n");
+ return otp_reg;
+ }
+ chip->current_gain_otp_reg = otp_reg;
+ } else {
+ otp_reg = chip->current_gain_otp_reg;
+ }
+ pr_debug("current_gain_otp_reg = 0x%x\n", chip->current_gain_otp_reg);
+
+ if (gain_factor) {
+ rc = smb1360_fg_read(chip, CURRENT_GAIN_LSB_REG, ®[1]);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_fg_read(chip, CURRENT_GAIN_MSB_REG, ®[3]);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ reg_value1 = (reg[3] << 8) | reg[1];
+ current_gain = float_decode(reg_value1);
+ new_current_gain = MICRO_UNIT + (gain_factor * current_gain);
+ reg_value2 = float_encode(new_current_gain);
+ reg[1] = reg_value2 & 0xFF;
+ reg[3] = (reg_value2 & 0xFF00) >> 8;
+ pr_debug("current_gain_reg=0x%x current_gain_decoded=%lld new_current_gain_decoded=%lld new_current_gain_reg=0x%x\n",
+ reg_value1, current_gain, new_current_gain, reg_value2);
+
+ for (i = 0; i < ARRAY_SIZE(reg); i++) {
+ pr_debug("Writing reg_add=%x value=%x\n",
+ otp_reg + i, reg[i]);
+
+ rc = smb1360_fg_write(chip, (otp_reg + i), reg[i]);
+ if (rc) {
+ pr_err("Write FG address 0x%x failed, rc = %d\n",
+ otp_reg + i, rc);
+ return rc;
+ }
+ }
+ rc = smb1360_otp_backup_alg_update(chip);
+ if (rc) {
+ pr_err("Update OTP backup algorithm failed\n");
+ return rc;
+ }
+ } else {
+ pr_debug("Disabling gain correction\n");
+ rc = smb1360_fg_write(chip, 0xF0, 0x00);
+ if (rc) {
+ pr_err("Write fg address 0x%x failed, rc = %d\n",
+ 0xF0, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int smb1360_otp_gain_config(struct smb1360_chip *chip, int gain_factor)
+{
+ int rc = 0;
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc = %d\n", rc);
+ return rc;
+ }
+ chip->fg_access_type = FG_ACCESS_CFG;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ goto restore_fg;
+ }
+
+ rc = smb1360_adjust_current_gain(chip, gain_factor);
+ if (rc) {
+ pr_err("Unable to modify current gain rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ CFG_FG_OTP_BACK_UP_ENABLE, CFG_FG_OTP_BACK_UP_ENABLE);
+ if (rc) {
+ pr_err("Write reg 0x0E failed, rc = %d\n", rc);
+ goto restore_fg;
+ }
+
+restore_fg:
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smb1360_otg_disable(struct smb1360_chip *chip)
+{
+ int rc;
+
+ rc = smb1360_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT, 0);
+ if (rc) {
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+ return rc;
+ }
+
+ mutex_lock(&chip->otp_gain_lock);
+ /* Disable current gain configuration */
+ if (chip->otg_fet_present && chip->fet_gain_enabled) {
+ /* Disable FET */
+ gpio_set_value(chip->otg_fet_enable_gpio, 1);
+ rc = smb1360_otp_gain_config(chip, 0);
+ if (rc < 0)
+ pr_err("Couldn't config OTP gain config rc=%d\n", rc);
+ else
+ chip->fet_gain_enabled = false;
+ }
+ mutex_unlock(&chip->otp_gain_lock);
+
+ return rc;
+}
+
+static int otg_fail_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ int rc;
+
+ pr_debug("OTG Failed stat=%d\n", rt_stat);
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+
+ return 0;
+}
+
+static int otg_oc_handler(struct smb1360_chip *chip, u8 rt_stat)
+{
+ int rc;
+
+ pr_debug("OTG over-current stat=%d\n", rt_stat);
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+
+ return 0;
+}
+
+struct smb_irq_info {
+ const char *name;
+ int (*smb_irq)(struct smb1360_chip *chip,
+ u8 rt_stat);
+ int high;
+ int low;
+};
+
+struct irq_handler_info {
+ u8 stat_reg;
+ u8 val;
+ u8 prev_val;
+ struct smb_irq_info irq_info[4];
+};
+
+static struct irq_handler_info handlers[] = {
+ {IRQ_A_REG, 0, 0,
+ {
+ {
+ .name = "cold_soft",
+ .smb_irq = cold_soft_handler,
+ },
+ {
+ .name = "hot_soft",
+ .smb_irq = hot_soft_handler,
+ },
+ {
+ .name = "cold_hard",
+ .smb_irq = cold_hard_handler,
+ },
+ {
+ .name = "hot_hard",
+ .smb_irq = hot_hard_handler,
+ },
+ },
+ },
+ {IRQ_B_REG, 0, 0,
+ {
+ {
+ .name = "chg_hot",
+ .smb_irq = chg_hot_handler,
+ },
+ {
+ .name = "vbat_low",
+ .smb_irq = vbat_low_handler,
+ },
+ {
+ .name = "battery_missing",
+ .smb_irq = battery_missing_handler,
+ },
+ {
+ .name = "battery_missing",
+ .smb_irq = battery_missing_handler,
+ },
+ },
+ },
+ {IRQ_C_REG, 0, 0,
+ {
+ {
+ .name = "chg_term",
+ .smb_irq = chg_term_handler,
+ },
+ {
+ .name = "taper",
+ },
+ {
+ .name = "recharge",
+ },
+ {
+ .name = "fast_chg",
+ .smb_irq = chg_fastchg_handler,
+ },
+ },
+ },
+ {IRQ_D_REG, 0, 0,
+ {
+ {
+ .name = "prechg_timeout",
+ },
+ {
+ .name = "safety_timeout",
+ },
+ {
+ .name = "aicl_done",
+ .smb_irq = aicl_done_handler,
+ },
+ {
+ .name = "battery_ov",
+ },
+ },
+ },
+ {IRQ_E_REG, 0, 0,
+ {
+ {
+ .name = "usbin_uv",
+ .smb_irq = usbin_uv_handler,
+ },
+ {
+ .name = "usbin_ov",
+ },
+ {
+ .name = "unused",
+ },
+ {
+ .name = "chg_inhibit",
+ .smb_irq = chg_inhibit_handler,
+ },
+ },
+ },
+ {IRQ_F_REG, 0, 0,
+ {
+ {
+ .name = "power_ok",
+ },
+ {
+ .name = "unused",
+ },
+ {
+ .name = "otg_fail",
+ .smb_irq = otg_fail_handler,
+ },
+ {
+ .name = "otg_oc",
+ .smb_irq = otg_oc_handler,
+ },
+ },
+ },
+ {IRQ_G_REG, 0, 0,
+ {
+ {
+ .name = "delta_soc",
+ .smb_irq = delta_soc_handler,
+ },
+ {
+ .name = "chg_error",
+ },
+ {
+ .name = "wd_timeout",
+ },
+ {
+ .name = "unused",
+ },
+ },
+ },
+ {IRQ_H_REG, 0, 0,
+ {
+ {
+ .name = "min_soc",
+ .smb_irq = min_soc_handler,
+ },
+ {
+ .name = "max_soc",
+ },
+ {
+ .name = "empty_soc",
+ .smb_irq = empty_soc_handler,
+ },
+ {
+ .name = "full_soc",
+ .smb_irq = full_soc_handler,
+ },
+ },
+ },
+ {IRQ_I_REG, 0, 0,
+ {
+ {
+ .name = "fg_access_allowed",
+ .smb_irq = fg_access_allowed_handler,
+ },
+ {
+ .name = "fg_data_recovery",
+ },
+ {
+ .name = "batt_id_complete",
+ .smb_irq = batt_id_complete_handler,
+ },
+ },
+ },
+};
+
+#define IRQ_LATCHED_MASK 0x02
+#define IRQ_STATUS_MASK 0x01
+#define BATT_ID_LATCHED_MASK 0x08
+#define BATT_ID_STATUS_MASK 0x07
+#define BITS_PER_IRQ 2
+static irqreturn_t smb1360_stat_handler(int irq, void *dev_id)
+{
+ struct smb1360_chip *chip = dev_id;
+ int i, j;
+ u8 triggered;
+ u8 changed;
+ u8 rt_stat, prev_rt_stat, irq_latched_mask, irq_status_mask;
+ int rc;
+ int handler_count = 0;
+
+ mutex_lock(&chip->irq_complete);
+ chip->irq_waiting = true;
+ if (!chip->resume_completed) {
+ dev_dbg(chip->dev, "IRQ triggered before device-resume\n");
+ if (!chip->irq_disabled) {
+ disable_irq_nosync(irq);
+ chip->irq_disabled = true;
+ }
+ mutex_unlock(&chip->irq_complete);
+ return IRQ_HANDLED;
+ }
+ chip->irq_waiting = false;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ rc = smb1360_read(chip, handlers[i].stat_reg,
+ &handlers[i].val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read %d rc = %d\n",
+ handlers[i].stat_reg, rc);
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) {
+ if (handlers[i].stat_reg == IRQ_I_REG && j == 2) {
+ irq_latched_mask = BATT_ID_LATCHED_MASK;
+ irq_status_mask = BATT_ID_STATUS_MASK;
+ } else {
+ irq_latched_mask = IRQ_LATCHED_MASK;
+ irq_status_mask = IRQ_STATUS_MASK;
+ }
+ triggered = handlers[i].val
+ & (irq_latched_mask << (j * BITS_PER_IRQ));
+ rt_stat = handlers[i].val
+ & (irq_status_mask << (j * BITS_PER_IRQ));
+ prev_rt_stat = handlers[i].prev_val
+ & (irq_status_mask << (j * BITS_PER_IRQ));
+ changed = prev_rt_stat ^ rt_stat;
+
+ if (triggered || changed)
+ rt_stat ? handlers[i].irq_info[j].high++ :
+ handlers[i].irq_info[j].low++;
+
+ if ((triggered || changed)
+ && handlers[i].irq_info[j].smb_irq != NULL) {
+ handler_count++;
+ rc = handlers[i].irq_info[j].smb_irq(chip,
+ rt_stat);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't handle %d irq for reg 0x%02x rc = %d\n",
+ j, handlers[i].stat_reg, rc);
+ }
+ }
+ handlers[i].prev_val = handlers[i].val;
+ }
+
+ pr_debug("handler count = %d\n", handler_count);
+ if (handler_count)
+ power_supply_changed(chip->batt_psy);
+
+ mutex_unlock(&chip->irq_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int show_irq_count(struct seq_file *m, void *data)
+{
+ int i, j, total = 0;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++)
+ for (j = 0; j < 4; j++) {
+ if (!handlers[i].irq_info[j].name)
+ continue;
+ seq_printf(m, "%s=%d\t(high=%d low=%d)\n",
+ handlers[i].irq_info[j].name,
+ handlers[i].irq_info[j].high
+ + handlers[i].irq_info[j].low,
+ handlers[i].irq_info[j].high,
+ handlers[i].irq_info[j].low);
+ total += (handlers[i].irq_info[j].high
+ + handlers[i].irq_info[j].low);
+ }
+
+ seq_printf(m, "\n\tTotal = %d\n", total);
+
+ return 0;
+}
+
+static int irq_count_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_irq_count, chip);
+}
+
+static const struct file_operations irq_count_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = irq_count_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int get_reg(void *data, u64 *val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb1360_read(chip, chip->peek_poke_address, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read reg %x rc = %d\n",
+ chip->peek_poke_address, rc);
+ return -EAGAIN;
+ }
+ *val = temp;
+ return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ temp = (u8) val;
+ rc = smb1360_write(chip, chip->peek_poke_address, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't write 0x%02x to 0x%02x rc= %d\n",
+ chip->peek_poke_address, temp, rc);
+ return -EAGAIN;
+ }
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
+
+static int fg_get_reg(void *data, u64 *val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ return -EINVAL;
+ }
+
+ rc = smb1360_fg_read(chip, chip->fg_peek_poke_address, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read reg %x rc = %d\n",
+ chip->fg_peek_poke_address, rc);
+ return -EAGAIN;
+ }
+ *val = temp;
+ return 0;
+}
+
+static int fg_set_reg(void *data, u64 val)
+{
+ struct smb1360_chip *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb1360_select_fg_i2c_address(chip);
+ if (rc) {
+ pr_err("Unable to set FG access I2C address\n");
+ return -EINVAL;
+ }
+
+ temp = (u8) val;
+ rc = smb1360_fg_write(chip, chip->fg_peek_poke_address, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't write 0x%02x to 0x%02x rc= %d\n",
+ chip->fg_peek_poke_address, temp, rc);
+ return -EAGAIN;
+ }
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fg_poke_poke_debug_ops, fg_get_reg,
+ fg_set_reg, "0x%02llx\n");
+
+#define LAST_CNFG_REG 0x17
+static int show_cnfg_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int cnfg_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_cnfg_regs, chip);
+}
+
+static const struct file_operations cnfg_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = cnfg_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_CMD_REG 0x40
+#define LAST_CMD_REG 0x42
+static int show_cmd_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int cmd_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_cmd_regs, chip);
+}
+
+static const struct file_operations cmd_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = cmd_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_STATUS_REG 0x48
+#define LAST_STATUS_REG 0x4B
+static int show_status_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int status_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_status_regs, chip);
+}
+
+static const struct file_operations status_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = status_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_IRQ_REG 0x50
+#define LAST_IRQ_REG 0x58
+static int show_irq_stat_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_IRQ_REG; addr <= LAST_IRQ_REG; addr++) {
+ rc = smb1360_read(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int irq_stat_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_irq_stat_regs, chip);
+}
+
+static const struct file_operations irq_stat_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = irq_stat_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int data_8(u8 *reg)
+{
+ return reg[0];
+}
+static int data_16(u8 *reg)
+{
+ return (reg[1] << 8) | reg[0];
+}
+static int data_24(u8 *reg)
+{
+ return (reg[2] << 16) | (reg[1] << 8) | reg[0];
+}
+static int data_28(u8 *reg)
+{
+ return ((reg[3] & 0xF) << 24) | (reg[2] << 16) |
+ (reg[1] << 8) | reg[0];
+}
+static int data_32(u8 *reg)
+{
+ return (reg[3] << 24) | (reg[2] << 16) |
+ (reg[1] << 8) | reg[0];
+}
+
+struct fg_regs {
+ int index;
+ int length;
+ char *param_name;
+ int (*calc_func)(u8 *);
+};
+
+static struct fg_regs fg_scratch_pad[] = {
+ {0, 2, "v_current_predicted", data_16},
+ {2, 2, "v_cutoff_predicted", data_16},
+ {4, 2, "v_full_predicted", data_16},
+ {6, 2, "ocv_estimate", data_16},
+ {8, 2, "rslow_drop", data_16},
+ {10, 2, "voltage_old", data_16},
+ {12, 2, "current_old", data_16},
+ {14, 4, "current_average_full", data_32},
+ {18, 2, "temperature", data_16},
+ {20, 2, "temp_last_track", data_16},
+ {22, 2, "ESR_nominal", data_16},
+ {26, 2, "Rslow", data_16},
+ {28, 2, "counter_imptr", data_16},
+ {30, 2, "counter_pulse", data_16},
+ {32, 1, "IRQ_delta_prev", data_8},
+ {33, 1, "cap_learning_counter", data_8},
+ {34, 4, "Vact_int_error", data_32},
+ {38, 3, "SOC_cutoff", data_24},
+ {41, 3, "SOC_full", data_24},
+ {44, 3, "SOC_auto_rechrge_temp", data_24},
+ {47, 3, "Battery_SOC", data_24},
+ {50, 4, "CC_SOC", data_28},
+ {54, 2, "SOC_filtered", data_16},
+ {56, 2, "SOC_Monotonic", data_16},
+ {58, 2, "CC_SOC_coeff", data_16},
+ {60, 2, "nominal_capacity", data_16},
+ {62, 2, "actual_capacity", data_16},
+ {68, 1, "temperature_counter", data_8},
+ {69, 3, "Vbatt_filtered", data_24},
+ {72, 3, "Ibatt_filtered", data_24},
+ {75, 2, "Current_CC_shadow", data_16},
+ {79, 2, "Ibatt_standby", data_16},
+ {82, 1, "Auto_recharge_SOC_threshold", data_8},
+ {83, 2, "System_cutoff_voltage", data_16},
+ {85, 2, "System_CC_to_CV_voltage", data_16},
+ {87, 2, "System_term_current", data_16},
+ {89, 2, "System_fake_term_current", data_16},
+ {91, 2, "thermistor_c1_coeff", data_16},
+};
+
+static struct fg_regs fg_cfg[] = {
+ {0, 2, "ESR_actual", data_16},
+ {4, 1, "IRQ_SOC_max", data_8},
+ {5, 1, "IRQ_SOC_min", data_8},
+ {6, 1, "IRQ_volt_empty", data_8},
+ {7, 1, "Temp_external", data_8},
+ {8, 1, "IRQ_delta_threshold", data_8},
+ {9, 1, "JIETA_soft_cold", data_8},
+ {10, 1, "JIETA_soft_hot", data_8},
+ {11, 1, "IRQ_volt_min", data_8},
+ {14, 2, "ESR_sys_replace", data_16},
+};
+
+static struct fg_regs fg_shdw[] = {
+ {0, 1, "Latest_battery_info", data_8},
+ {1, 1, "Latest_Msys_SOC", data_8},
+ {2, 2, "Battery_capacity", data_16},
+ {4, 2, "Rslow_drop", data_16},
+ {6, 1, "Latest_SOC", data_8},
+ {7, 1, "Latest_Cutoff_SOC", data_8},
+ {8, 1, "Latest_full_SOC", data_8},
+ {9, 2, "Voltage_shadow", data_16},
+ {11, 2, "Current_shadow", data_16},
+ {13, 2, "Latest_temperature", data_16},
+ {15, 1, "Latest_system_sbits", data_8},
+};
+
+#define FIRST_FG_CFG_REG 0x20
+#define LAST_FG_CFG_REG 0x2F
+#define FIRST_FG_SHDW_REG 0x60
+#define LAST_FG_SHDW_REG 0x6F
+#define FG_SCRATCH_PAD_MAX 93
+#define FG_SCRATCH_PAD_BASE_REG 0x80
+#define SMB1360_I2C_READ_LENGTH 32
+
+static int smb1360_check_cycle_stretch(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ u8 reg;
+
+ rc = smb1360_read(chip, STATUS_4_REG, ®);
+ if (rc) {
+ pr_err("Unable to read status regiseter\n");
+ } else if (reg & CYCLE_STRETCH_ACTIVE_BIT) {
+ /* clear cycle stretch */
+ rc = smb1360_masked_write(chip, CMD_I2C_REG,
+ CYCLE_STRETCH_CLEAR_BIT, CYCLE_STRETCH_CLEAR_BIT);
+ if (rc)
+ pr_err("Unable to clear cycle stretch\n");
+ }
+
+ return rc;
+}
+
+static int show_fg_regs(struct seq_file *m, void *data)
+{
+ struct smb1360_chip *chip = m->private;
+ int rc, i, j, rem_length;
+ u8 reg[FG_SCRATCH_PAD_MAX];
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc)
+ pr_err("Unable to check cycle-stretch\n");
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't request FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < (FG_SCRATCH_PAD_MAX / SMB1360_I2C_READ_LENGTH); i++) {
+ j = i * SMB1360_I2C_READ_LENGTH;
+ rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j,
+ ®[j], SMB1360_I2C_READ_LENGTH);
+ if (rc) {
+ pr_err("Couldn't read scratch registers rc=%d\n", rc);
+ break;
+ }
+ }
+
+ j = i * SMB1360_I2C_READ_LENGTH;
+ rem_length = (FG_SCRATCH_PAD_MAX % SMB1360_I2C_READ_LENGTH);
+ if (rem_length) {
+ rc = smb1360_read_bytes(chip, FG_SCRATCH_PAD_BASE_REG + j,
+ ®[j], rem_length);
+ if (rc)
+ pr_err("Couldn't read scratch registers rc=%d\n", rc);
+ }
+
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't disable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc)
+ pr_err("Unable to check cycle-stretch\n");
+
+
+ seq_puts(m, "FG scratch-pad registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_scratch_pad); i++)
+ seq_printf(m, "\t%s = %x\n", fg_scratch_pad[i].param_name,
+ fg_scratch_pad[i].calc_func(®[fg_scratch_pad[i].index]));
+
+ rem_length = LAST_FG_CFG_REG - FIRST_FG_CFG_REG + 1;
+ rc = smb1360_read_bytes(chip, FIRST_FG_CFG_REG,
+ ®[0], rem_length);
+ if (rc)
+ pr_err("Couldn't read config registers rc=%d\n", rc);
+
+ seq_puts(m, "FG config registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_cfg); i++)
+ seq_printf(m, "\t%s = %x\n", fg_cfg[i].param_name,
+ fg_cfg[i].calc_func(®[fg_cfg[i].index]));
+
+ rem_length = LAST_FG_SHDW_REG - FIRST_FG_SHDW_REG + 1;
+ rc = smb1360_read_bytes(chip, FIRST_FG_SHDW_REG,
+ ®[0], rem_length);
+ if (rc)
+ pr_err("Couldn't read shadow registers rc=%d\n", rc);
+
+ seq_puts(m, "FG shadow registers\n");
+ for (i = 0; i < ARRAY_SIZE(fg_shdw); i++)
+ seq_printf(m, "\t%s = %x\n", fg_shdw[i].param_name,
+ fg_shdw[i].calc_func(®[fg_shdw[i].index]));
+
+ return rc;
+}
+
+static int fg_regs_open(struct inode *inode, struct file *file)
+{
+ struct smb1360_chip *chip = inode->i_private;
+
+ return single_open(file, show_fg_regs, chip);
+}
+
+static const struct file_operations fg_regs_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = fg_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smb1360_otg_regulator_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smb1360_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smb1360_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT,
+ CMD_OTG_EN_BIT);
+ if (rc) {
+ pr_err("Couldn't enable OTG mode rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("OTG mode enabled\n");
+ /* Enable current gain configuration */
+ mutex_lock(&chip->otp_gain_lock);
+ if (chip->otg_fet_present) {
+ /* Enable FET */
+ gpio_set_value(chip->otg_fet_enable_gpio, 0);
+ rc = smb1360_otp_gain_config(chip, 3);
+ if (rc < 0)
+ pr_err("Couldn't config OTP gain config rc=%d\n", rc);
+ else
+ chip->fet_gain_enabled = true;
+ }
+ mutex_unlock(&chip->otp_gain_lock);
+
+ return rc;
+}
+
+static int smb1360_otg_regulator_disable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smb1360_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG regulator rc=%d\n", rc);
+
+ pr_debug("OTG mode disabled\n");
+ return rc;
+}
+
+static int smb1360_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+ u8 reg = 0;
+ int rc = 0;
+ struct smb1360_chip *chip = rdev_get_drvdata(rdev);
+
+ rc = smb1360_read(chip, CMD_CHG_REG, ®);
+ if (rc) {
+ pr_err("Couldn't read OTG enable bit rc=%d\n", rc);
+ return rc;
+ }
+
+ return (reg & CMD_OTG_EN_BIT) ? 1 : 0;
+}
+
+static struct regulator_ops smb1360_otg_reg_ops = {
+ .enable = smb1360_otg_regulator_enable,
+ .disable = smb1360_otg_regulator_disable,
+ .is_enabled = smb1360_otg_regulator_is_enable,
+};
+
+static int smb1360_regulator_init(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ struct regulator_config cfg = {};
+
+ chip->otg_vreg.rdesc.owner = THIS_MODULE;
+ chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+ chip->otg_vreg.rdesc.ops = &smb1360_otg_reg_ops;
+ chip->otg_vreg.rdesc.of_match = chip->dev->of_node->name;
+ chip->otg_vreg.rdesc.name = chip->dev->of_node->name;
+
+ cfg.dev = chip->dev;
+ cfg.driver_data = chip;
+ cfg.of_node = chip->dev->of_node;
+
+ chip->otg_vreg.rdev = regulator_register(
+ &chip->otg_vreg.rdesc, &cfg);
+ if (IS_ERR(chip->otg_vreg.rdev)) {
+ rc = PTR_ERR(chip->otg_vreg.rdev);
+ chip->otg_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev,
+ "OTG reg failed, rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+static int smb1360_check_batt_profile(struct smb1360_chip *chip)
+{
+ int rc, i, timeout = 50;
+ u8 reg = 0, loaded_profile, new_profile = 0, bid_mask;
+
+ if (!chip->connected_rid) {
+ pr_debug("Skip batt-profile loading connected_rid=%d\n",
+ chip->connected_rid);
+ return 0;
+ }
+
+ rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, ®);
+ if (rc) {
+ pr_err("Couldn't read FG_BATT_STATUS rc=%d\n", rc);
+ return rc;
+ }
+
+ loaded_profile = !!(reg & BATTERY_PROFILE_BIT) ?
+ BATTERY_PROFILE_B : BATTERY_PROFILE_A;
+
+ pr_debug("fg_batt_status=%x loaded_profile=%d\n", reg, loaded_profile);
+
+ for (i = 0; i < BATTERY_PROFILE_MAX; i++) {
+ pr_debug("profile=%d profile_rid=%d connected_rid=%d\n", i,
+ chip->profile_rid[i],
+ chip->connected_rid);
+ if (abs(chip->profile_rid[i] - chip->connected_rid) <
+ (div_u64(chip->connected_rid, 10)))
+ break;
+ }
+
+ if (i == BATTERY_PROFILE_MAX) {
+ pr_err("None of the battery-profiles match the connected-RID\n");
+ return 0;
+ }
+
+ if (i == loaded_profile) {
+ pr_debug("Loaded Profile-RID == connected-RID\n");
+ return 0;
+ }
+
+ new_profile = (loaded_profile == BATTERY_PROFILE_A) ?
+ BATTERY_PROFILE_B : BATTERY_PROFILE_A;
+ bid_mask = (new_profile == BATTERY_PROFILE_A) ?
+ BATT_PROFILEA_MASK : BATT_PROFILEB_MASK;
+ pr_info("Loaded Profile-RID != connected-RID, switch-profile old_profile=%d new_profile=%d\n",
+ loaded_profile, new_profile);
+
+ /* set the BID mask */
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ BATT_PROFILE_SELECT_MASK, bid_mask);
+ if (rc) {
+ pr_err("Couldn't reset battery-profile rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("FG access timed-out, rc = %d\n", rc);
+ return rc;
+ }
+ /* delay after handshaking for profile-switch to continue */
+ msleep(1500);
+
+ rc = smb1360_force_fg_reset(chip);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ goto restore_fg;
+ }
+
+ rc = smb1360_disable_fg_access(chip);
+ if (rc) {
+ pr_err("disable FG access failed, rc = %d\n", rc);
+ return rc;
+ }
+
+ timeout = 10;
+ while (timeout) {
+ /* delay for profile to change */
+ msleep(500);
+ rc = smb1360_read(chip, SHDW_FG_BATT_STATUS, ®);
+ if (rc) {
+ pr_err("Could't read FG_BATT_STATUS rc=%d\n", rc);
+ return rc;
+ }
+
+ reg = !!(reg & BATTERY_PROFILE_BIT);
+ if (reg == new_profile) {
+ pr_info("New profile=%d loaded\n", new_profile);
+ break;
+ }
+ timeout--;
+ }
+
+ if (!timeout) {
+ pr_err("New profile could not be loaded\n");
+ return -EBUSY;
+ }
+
+ return 0;
+
+restore_fg:
+ smb1360_disable_fg_access(chip);
+ return rc;
+}
+
+#define UPDATE_IRQ_STAT(irq_reg, value) \
+ handlers[irq_reg - IRQ_A_REG].prev_val = value
+
+static int determine_initial_status(struct smb1360_chip *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ /*
+ * It is okay to read the IRQ status as the irq's are
+ * not registered yet.
+ */
+ chip->batt_present = true;
+ rc = smb1360_read(chip, IRQ_B_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read IRQ_B_REG rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_B_REG, reg);
+
+ if (reg & IRQ_B_BATT_TERMINAL_BIT || reg & IRQ_B_BATT_MISSING_BIT)
+ chip->batt_present = false;
+
+ rc = smb1360_read(chip, IRQ_C_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read IRQ_C_REG rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_C_REG, reg);
+
+ if (reg & IRQ_C_CHG_TERM)
+ chip->batt_full = true;
+
+ rc = smb1360_read(chip, IRQ_A_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_A_REG, reg);
+
+ if (chip->workaround_flags & WRKRND_HARD_JEITA) {
+ schedule_delayed_work(&chip->jeita_work, 0);
+ } else {
+ if (reg & IRQ_A_HOT_HARD_BIT)
+ chip->batt_hot = true;
+ if (reg & IRQ_A_COLD_HARD_BIT)
+ chip->batt_cold = true;
+ if (!chip->config_hard_thresholds) {
+ if (reg & IRQ_A_HOT_SOFT_BIT)
+ chip->batt_warm = true;
+ if (reg & IRQ_A_COLD_SOFT_BIT)
+ chip->batt_cool = true;
+ }
+ }
+
+ rc = smb1360_read(chip, IRQ_E_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read irq E rc = %d\n", rc);
+ return rc;
+ }
+ UPDATE_IRQ_STAT(IRQ_E_REG, reg);
+
+ /* Check usb charger presence and notify */
+ chip->usb_present = (reg & IRQ_E_USBIN_UV_BIT) ? false : true;
+ /* USB removed */
+ if (!chip->usb_present)
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+ /* USB inserted */
+ else
+ extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+
+ power_supply_changed(chip->usb_psy);
+ return 0;
+}
+
+static int smb1360_fg_config(struct smb1360_chip *chip)
+{
+ int rc = 0, temp, fcc_mah;
+ u8 reg = 0, reg2[2];
+
+ if (chip->fg_reset_at_pon) {
+ int v_predicted, v_now;
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_read_bytes(chip, VOLTAGE_PREDICTED_REG, reg2, 2);
+ if (rc) {
+ pr_err("Failed to read VOLTAGE_PREDICTED rc=%d\n", rc);
+ goto disable_fg_reset;
+ }
+ v_predicted = (reg2[1] << 8) | reg2[0];
+ v_predicted = div_u64(v_predicted * 5000, 0x7FFF);
+
+ rc = smb1360_read_bytes(chip, SHDW_FG_VTG_NOW, reg2, 2);
+ if (rc) {
+ pr_err("Failed to read SHDW_FG_VTG_NOW rc=%d\n", rc);
+ goto disable_fg_reset;
+ }
+ v_now = (reg2[1] << 8) | reg2[0];
+ v_now = div_u64(v_now * 5000, 0x7FFF);
+
+ pr_debug("v_predicted=%d v_now=%d reset_threshold=%d\n",
+ v_predicted, v_now, chip->fg_reset_threshold_mv);
+
+ /*
+ * Reset FG if the predicted voltage is off wrt
+ * the real-time voltage.
+ */
+ temp = abs(v_predicted - v_now);
+ if (temp >= chip->fg_reset_threshold_mv) {
+ pr_info("Resetting FG - v_delta=%d threshold=%d\n",
+ temp, chip->fg_reset_threshold_mv);
+ /* delay for the FG access to settle */
+ msleep(1500);
+ rc = smb1360_force_fg_reset(chip);
+ if (rc) {
+ pr_err("Couldn't reset FG rc=%d\n", rc);
+ goto disable_fg_reset;
+ }
+ }
+disable_fg_reset:
+ smb1360_disable_fg_access(chip);
+ }
+
+ /*
+ * The below IRQ thresholds are not accessible in REV_1
+ * of SMB1360.
+ */
+ if (!(chip->workaround_flags & WRKRND_FG_CONFIG_FAIL)) {
+ if (chip->delta_soc != -EINVAL) {
+ reg = abs(((chip->delta_soc * MAX_8_BITS) / 100) - 1);
+ pr_debug("delta_soc=%d reg=%x\n", chip->delta_soc, reg);
+ rc = smb1360_write(chip, SOC_DELTA_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to SOC_DELTA_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->soc_min != -EINVAL) {
+ if (is_between(chip->soc_min, 0, 100)) {
+ reg = DIV_ROUND_UP(chip->soc_min * MAX_8_BITS,
+ 100);
+ pr_debug("soc_min=%d reg=%x\n",
+ chip->soc_min, reg);
+ rc = smb1360_write(chip, SOC_MIN_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to SOC_MIN_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ if (chip->soc_max != -EINVAL) {
+ if (is_between(chip->soc_max, 0, 100)) {
+ reg = DIV_ROUND_UP(chip->soc_max * MAX_8_BITS,
+ 100);
+ pr_debug("soc_max=%d reg=%x\n",
+ chip->soc_max, reg);
+ rc = smb1360_write(chip, SOC_MAX_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to SOC_MAX_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ if (chip->voltage_min_mv != -EINVAL) {
+ temp = (chip->voltage_min_mv - 2500) * MAX_8_BITS;
+ reg = DIV_ROUND_UP(temp, 2500);
+ pr_debug("voltage_min=%d reg=%x\n",
+ chip->voltage_min_mv, reg);
+ rc = smb1360_write(chip, VTG_MIN_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to VTG_MIN_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ if (chip->voltage_empty_mv != -EINVAL) {
+ temp = (chip->voltage_empty_mv - 2500) * MAX_8_BITS;
+ reg = DIV_ROUND_UP(temp, 2500);
+ pr_debug("voltage_empty=%d reg=%x\n",
+ chip->voltage_empty_mv, reg);
+ rc = smb1360_write(chip, VTG_EMPTY_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to VTG_EMPTY_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ /* scratch-pad register config */
+ if (chip->batt_capacity_mah != -EINVAL
+ || chip->v_cutoff_mv != -EINVAL
+ || chip->fg_iterm_ma != -EINVAL
+ || chip->fg_ibatt_standby_ma != -EINVAL
+ || chip->fg_thermistor_c1_coeff != -EINVAL
+ || chip->fg_cc_to_cv_mv != -EINVAL
+ || chip->fg_auto_recharge_soc != -EINVAL) {
+
+ rc = smb1360_enable_fg_access(chip);
+ if (rc) {
+ pr_err("Couldn't enable FG access rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Update battery capacity */
+ if (chip->batt_capacity_mah != -EINVAL) {
+ rc = smb1360_read_bytes(chip, ACTUAL_CAPACITY_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Failed to read ACTUAL CAPACITY rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ fcc_mah = (reg2[1] << 8) | reg2[0];
+ if (fcc_mah == chip->batt_capacity_mah) {
+ pr_debug("battery capacity correct\n");
+ } else {
+ /* Update the battery capacity */
+ reg2[1] =
+ (chip->batt_capacity_mah & 0xFF00) >> 8;
+ reg2[0] = (chip->batt_capacity_mah & 0xFF);
+ rc = smb1360_write_bytes(chip,
+ ACTUAL_CAPACITY_REG, reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write batt-capacity rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ rc = smb1360_write_bytes(chip,
+ NOMINAL_CAPACITY_REG, reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write batt-capacity rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+
+ /* Update CC to SOC COEFF */
+ if (chip->cc_soc_coeff != -EINVAL) {
+ reg2[1] =
+ (chip->cc_soc_coeff & 0xFF00) >> 8;
+ reg2[0] = (chip->cc_soc_coeff & 0xFF);
+ rc = smb1360_write_bytes(chip,
+ CC_TO_SOC_COEFF, reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cc_soc_coeff rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+ }
+ }
+
+ /* Update cutoff voltage for SOC = 0 */
+ if (chip->v_cutoff_mv != -EINVAL) {
+ temp = (u16) div_u64(chip->v_cutoff_mv * 0x7FFF, 5000);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_SYS_CUTOFF_V_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cutoff_mv rc=%d\n", rc);
+ goto disable_fg;
+ }
+ }
+
+ /*
+ * Update FG iterm for SOC = 100, this value is always assumed
+ * to be -ve
+ */
+ if (chip->fg_iterm_ma != -EINVAL) {
+ int iterm = chip->fg_iterm_ma * -1;
+
+ temp = (s16) div_s64(iterm * 0x7FFF, 2500);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_ITERM_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write fg_iterm rc=%d\n", rc);
+ goto disable_fg;
+ }
+ }
+
+ /*
+ * Update FG iterm standby for SOC = 0, this value is always
+ * assumed to be +ve
+ */
+ if (chip->fg_ibatt_standby_ma != -EINVAL) {
+ int iterm = chip->fg_ibatt_standby_ma;
+
+ temp = (u16) div_u64(iterm * 0x7FFF, 2500);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_IBATT_STANDBY_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write fg_iterm rc=%d\n", rc);
+ goto disable_fg;
+ }
+ }
+
+ /* Update CC_to_CV voltage threshold */
+ if (chip->fg_cc_to_cv_mv != -EINVAL) {
+ temp = (u16) div_u64(chip->fg_cc_to_cv_mv * 0x7FFF,
+ 5000);
+ reg2[1] = (temp & 0xFF00) >> 8;
+ reg2[0] = temp & 0xFF;
+ rc = smb1360_write_bytes(chip, FG_CC_TO_CV_V_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write cc_to_cv_mv rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+
+ /* Update the thermistor c1 coefficient */
+ if (chip->fg_thermistor_c1_coeff != -EINVAL) {
+ reg2[1] = (chip->fg_thermistor_c1_coeff & 0xFF00) >> 8;
+ reg2[0] = (chip->fg_thermistor_c1_coeff & 0xFF);
+ rc = smb1360_write_bytes(chip, FG_THERM_C1_COEFF_REG,
+ reg2, 2);
+ if (rc) {
+ pr_err("Couldn't write thermistor_c1_coeff rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+
+ /* Update SoC based resume charging threshold */
+ if (chip->fg_auto_recharge_soc != -EINVAL) {
+ rc = smb1360_masked_write(chip, CFG_CHG_FUNC_CTRL_REG,
+ CHG_RECHG_THRESH_FG_SRC_BIT,
+ CHG_RECHG_THRESH_FG_SRC_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to CFG_CHG_FUNC_CTRL_REG rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+
+ reg = DIV_ROUND_UP(chip->fg_auto_recharge_soc *
+ MAX_8_BITS, 100);
+ pr_debug("fg_auto_recharge_soc=%d reg=%x\n",
+ chip->fg_auto_recharge_soc, reg);
+ rc = smb1360_write(chip, FG_AUTO_RECHARGE_SOC, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't write to FG_AUTO_RECHARGE_SOC rc=%d\n",
+ rc);
+ goto disable_fg;
+ }
+ }
+
+disable_fg:
+ /* disable FG access */
+ smb1360_disable_fg_access(chip);
+ }
+
+ return rc;
+}
+
+static void smb1360_check_feature_support(struct smb1360_chip *chip)
+{
+
+ if (is_usb100_broken(chip)) {
+ pr_debug("USB100 is not supported\n");
+ chip->workaround_flags |= WRKRND_USB100_FAIL;
+ }
+
+ /*
+ * FG Configuration
+ *
+ * The REV_1 of the chip does not allow access to
+ * FG config registers (20-2FH). Set the workaround flag.
+ * Also, the battery detection does not work when the DCIN is absent,
+ * add a workaround flag for it.
+ */
+ if (chip->revision == SMB1360_REV_1) {
+ pr_debug("FG config and Battery detection is not supported\n");
+ chip->workaround_flags |=
+ WRKRND_FG_CONFIG_FAIL | WRKRND_BATT_DET_FAIL;
+ }
+}
+
+static int smb1360_enable(struct smb1360_chip *chip, bool enable)
+{
+ int rc = 0;
+ u8 val = 0, shdn_cmd_polar;
+
+ rc = smb1360_read(chip, SHDN_CTRL_REG, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read 0x1A reg rc = %d\n", rc);
+ return rc;
+ }
+
+ /* Ignore if a CMD based shutdown is not enabled */
+ if (!(val & SHDN_CMD_USE_BIT)) {
+ pr_debug("SMB not configured for CMD based shutdown\n");
+ return 0;
+ }
+
+ shdn_cmd_polar = !!(val & SHDN_CMD_POLARITY_BIT);
+ val = (shdn_cmd_polar ^ enable) ? SHDN_CMD_BIT : 0;
+
+ pr_debug("enable=%d shdn_polarity=%d value=%d\n", enable,
+ shdn_cmd_polar, val);
+
+ rc = smb1360_masked_write(chip, CMD_IL_REG, SHDN_CMD_BIT, val);
+ if (rc < 0)
+ pr_err("Couldn't shutdown smb1360 rc = %d\n", rc);
+
+ return rc;
+}
+
+static inline int smb1360_poweroff(struct smb1360_chip *chip)
+{
+ pr_debug("power off smb1360\n");
+ return smb1360_enable(chip, false);
+}
+
+static inline int smb1360_poweron(struct smb1360_chip *chip)
+{
+ pr_debug("power on smb1360\n");
+ return smb1360_enable(chip, true);
+}
+
+static int smb1360_jeita_init(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ int temp;
+
+ if (chip->config_hard_thresholds) {
+ if (chip->soft_jeita_supported) {
+ chip->workaround_flags |= WRKRND_HARD_JEITA;
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cool_bat_decidegc, chip->warm_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set jeita threshold\n");
+ return rc;
+ }
+ } else {
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cold_bat_decidegc, chip->hot_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set jeita threshold\n");
+ return rc;
+ }
+ }
+ } else {
+ if (chip->soft_jeita_supported) {
+ temp = min(chip->warm_bat_ma, chip->cool_bat_ma);
+ rc = smb1360_set_jeita_comp_curr(chip, temp);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set comp current\n");
+ return rc;
+ }
+
+ temp = (chip->vfloat_mv - chip->warm_bat_mv) / 10;
+ rc = smb1360_masked_write(chip, CFG_FVC_REG,
+ FLT_VTG_COMP_MASK, temp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set VFLT compensation = %d",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_set_soft_jeita_threshold(chip,
+ chip->cool_bat_decidegc, chip->warm_bat_decidegc);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set jeita threshold\n");
+ return rc;
+ }
+
+ rc = smb1360_soft_jeita_comp_enable(chip, true);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't enable jeita\n");
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int smb1360_otp_gain_init(struct smb1360_chip *chip)
+{
+ int rc = 0, gain_factor;
+ bool otp_gain_config = false;
+
+ if (chip->rsense_10mohm) {
+ gain_factor = 2;
+ otp_gain_config = true;
+ }
+
+ mutex_lock(&chip->otp_gain_lock);
+ if (chip->otg_fet_present) {
+ /*
+ * Reset current gain to the default value if OTG
+ * is not enabled
+ */
+ if (!chip->fet_gain_enabled) {
+ otp_gain_config = true;
+ gain_factor = 0;
+ }
+ }
+
+ if (otp_gain_config) {
+ rc = smb1360_otp_gain_config(chip, gain_factor);
+ if (rc < 0)
+ pr_err("Couldn't config OTP gain rc=%d\n", rc);
+ }
+ mutex_unlock(&chip->otp_gain_lock);
+
+ return rc;
+}
+
+static int smb1360_hw_init(struct smb1360_chip *chip)
+{
+ int rc;
+ int i;
+ u8 reg, mask;
+
+ smb1360_check_feature_support(chip);
+
+ rc = smb1360_enable_volatile_writes(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't configure for volatile rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ /* Bring SMB1360 out of shutdown, if it was enabled by default */
+ rc = smb1360_poweron(chip);
+ if (rc < 0) {
+ pr_err("smb1360 power on failed\n");
+ return rc;
+ }
+
+ /*
+ * A 2 seconds delay is mandatory after bringing the chip out
+ * of shutdown. This guarantees that FG is in a proper state.
+ */
+ schedule_delayed_work(&chip->delayed_init_work,
+ msecs_to_jiffies(SMB1360_POWERON_DELAY_MS));
+
+ /*
+ * set chg en by cmd register, set chg en by writing bit 1,
+ * enable auto pre to fast
+ */
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CHG_EN_BY_PIN_BIT
+ | CHG_EN_ACTIVE_LOW_BIT
+ | PRE_TO_FAST_REQ_CMD_BIT,
+ 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_CHG_MISC_REG rc=%d\n", rc);
+ return rc;
+ }
+
+ /* USB/AC pin settings */
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_ICL_REG,
+ AC_INPUT_ICL_PIN_BIT
+ | AC_INPUT_PIN_HIGH_BIT
+ | RESET_STATE_USB_500,
+ AC_INPUT_PIN_HIGH_BIT
+ | RESET_STATE_USB_500);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_BATT_CHG_ICL_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* AICL enable and set input-uv glitch flt to 20ms*/
+ reg = AICL_ENABLED_BIT | INPUT_UV_GLITCH_FLT_20MS_BIT;
+ rc = smb1360_masked_write(chip, CFG_GLITCH_FLT_REG, reg, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set CFG_GLITCH_FLT_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* set the float voltage */
+ if (chip->vfloat_mv != -EINVAL) {
+ rc = smb1360_float_voltage_set(chip, chip->vfloat_mv);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set float voltage rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ /* set iterm */
+ if (chip->iterm_ma != -EINVAL) {
+ if (chip->iterm_disabled) {
+ dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
+ return -EINVAL;
+ }
+
+ if (chip->rsense_10mohm)
+ chip->iterm_ma /= 2;
+
+ if (chip->iterm_ma < 25)
+ reg = CHG_ITERM_25MA;
+ else if (chip->iterm_ma > 200)
+ reg = CHG_ITERM_200MA;
+ else
+ reg = DIV_ROUND_UP(chip->iterm_ma, 25) - 1;
+
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_REG,
+ CHG_ITERM_MASK, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set iterm rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CHG_CURR_TERM_DIS_BIT, 0);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't enable iterm rc = %d\n", rc);
+ return rc;
+ }
+ } else if (chip->iterm_disabled) {
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CHG_CURR_TERM_DIS_BIT,
+ CHG_CURR_TERM_DIS_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set iterm rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* set the safety time voltage */
+ if (chip->safety_time != -EINVAL) {
+ if (chip->safety_time == 0) {
+ /* safety timer disabled */
+ rc = smb1360_masked_write(chip, CFG_SFY_TIMER_CTRL_REG,
+ SAFETY_TIME_DISABLE_BIT, SAFETY_TIME_DISABLE_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't disable safety timer rc = %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(chg_time); i++) {
+ if (chip->safety_time <= chg_time[i]) {
+ reg = i << SAFETY_TIME_MINUTES_SHIFT;
+ break;
+ }
+ }
+ rc = smb1360_masked_write(chip, CFG_SFY_TIMER_CTRL_REG,
+ SAFETY_TIME_DISABLE_BIT | SAFETY_TIME_MINUTES_MASK,
+ reg);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set safety timer rc = %d\n", rc);
+ return rc;
+ }
+ }
+ }
+
+ /* configure resume threshold, auto recharge and charge inhibit */
+ if (chip->resume_delta_mv != -EINVAL) {
+ if (chip->recharge_disabled && chip->chg_inhibit_disabled) {
+ dev_err(chip->dev,
+ "Error: Both recharge_disabled and recharge_mv set\n");
+ return -EINVAL;
+ }
+ rc = smb1360_recharge_threshold_set(chip,
+ chip->resume_delta_mv);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set rechg thresh rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CFG_AUTO_RECHG_DIS_BIT,
+ chip->recharge_disabled ?
+ CFG_AUTO_RECHG_DIS_BIT : 0);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set rechg-cfg rc = %d\n", rc);
+ return rc;
+ }
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CFG_CHG_INHIBIT_EN_BIT,
+ chip->chg_inhibit_disabled ?
+ 0 : CFG_CHG_INHIBIT_EN_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set chg_inhibit rc = %d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_masked_write(chip, CFG_CHG_MISC_REG,
+ CFG_BAT_OV_ENDS_CHG_CYC,
+ chip->ov_ends_chg_cycle_disabled ?
+ 0 : CFG_BAT_OV_ENDS_CHG_CYC);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set bat_ov_ends_charge rc = %d\n"
+ , rc);
+ return rc;
+ }
+
+ /* battery missing detection */
+ rc = smb1360_masked_write(chip, CFG_BATT_MISSING_REG,
+ BATT_MISSING_SRC_THERM_BIT,
+ BATT_MISSING_SRC_THERM_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_jeita_init(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't init jeita, rc = %d\n", rc);
+ return rc;
+ }
+
+ /* interrupt enabling - active low */
+ if (chip->client->irq) {
+ mask = CHG_STAT_IRQ_ONLY_BIT
+ | CHG_STAT_ACTIVE_HIGH_BIT
+ | CHG_STAT_DISABLE_BIT
+ | CHG_TEMP_CHG_ERR_BLINK_BIT;
+
+ if (!chip->pulsed_irq)
+ reg = CHG_STAT_IRQ_ONLY_BIT;
+ else
+ reg = CHG_TEMP_CHG_ERR_BLINK_BIT;
+ rc = smb1360_masked_write(chip, CFG_STAT_CTRL_REG, mask, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set irq config rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ /* enabling only interesting interrupts */
+ rc = smb1360_write(chip, IRQ_CFG_REG,
+ IRQ_BAT_HOT_COLD_HARD_BIT
+ | IRQ_BAT_HOT_COLD_SOFT_BIT
+ | IRQ_INTERNAL_TEMPERATURE_BIT
+ | IRQ_DCIN_UV_BIT
+ | IRQ_AICL_DONE_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set irq1 config rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_write(chip, IRQ2_CFG_REG,
+ IRQ2_SAFETY_TIMER_BIT
+ | IRQ2_CHG_ERR_BIT
+ | IRQ2_CHG_PHASE_CHANGE_BIT
+ | IRQ2_POWER_OK_BIT
+ | IRQ2_BATT_MISSING_BIT
+ | IRQ2_VBAT_LOW_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set irq2 config rc = %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = smb1360_write(chip, IRQ3_CFG_REG,
+ IRQ3_FG_ACCESS_OK_BIT
+ | IRQ3_SOC_CHANGE_BIT
+ | IRQ3_SOC_MIN_BIT
+ | IRQ3_SOC_MAX_BIT
+ | IRQ3_SOC_EMPTY_BIT
+ | IRQ3_SOC_FULL_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set irq3 enable rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* batt-id configuration */
+ if (chip->batt_id_disabled) {
+ mask = BATT_ID_ENABLED_BIT | CHG_BATT_ID_FAIL;
+ reg = CHG_BATT_ID_FAIL;
+ rc = smb1360_masked_write(chip, CFG_FG_BATT_CTRL_REG,
+ mask, reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set batt_id_reg rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* USB OTG current limit configuration */
+ if (chip->otg_batt_curr_limit != -EINVAL) {
+ for (i = 0; i < ARRAY_SIZE(otg_curr_ma); i++) {
+ if (otg_curr_ma[i] >= chip->otg_batt_curr_limit)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(otg_curr_ma))
+ i = i - 1;
+
+ rc = smb1360_masked_write(chip, CFG_BATT_CHG_REG,
+ OTG_CURRENT_MASK,
+ i << OTG_CURRENT_SHIFT);
+ if (rc)
+ pr_err("Couldn't set OTG current limit, rc = %d\n", rc);
+ }
+
+ rc = smb1360_charging_disable(chip, USER, !!chip->charging_disabled);
+ if (rc)
+ dev_err(chip->dev, "Couldn't '%s' charging rc = %d\n",
+ chip->charging_disabled ? "disable" : "enable", rc);
+
+ if (chip->parallel_charging) {
+ rc = smb1360_parallel_charger_enable(chip, PARALLEL_USER,
+ !chip->charging_disabled);
+ if (rc)
+ dev_err(chip->dev, "Couldn't '%s' parallel-charging rc = %d\n",
+ chip->charging_disabled ? "disable" : "enable", rc);
+ }
+
+ return rc;
+}
+
+static int smb1360_delayed_hw_init(struct smb1360_chip *chip)
+{
+ int rc;
+
+ pr_debug("delayed hw init start!\n");
+
+ if (chip->otp_hard_jeita_config) {
+ rc = smb1360_hard_jeita_otp_init(chip);
+ if (rc) {
+ pr_err("Unable to change the OTP hard jeita, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ rc = smb1360_check_batt_profile(chip);
+ if (rc) {
+ pr_err("Unable to modify battery profile, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_otp_gain_init(chip);
+ if (rc) {
+ pr_err("Unable to config otp gain, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_fg_config(chip);
+ if (rc) {
+ pr_err("Couldn't configure FG rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smb1360_check_cycle_stretch(chip);
+ if (rc) {
+ pr_err("Unable to check cycle-stretch\n");
+ return rc;
+ }
+
+ pr_debug("delayed hw init complete!\n");
+ return rc;
+}
+
+static void smb1360_delayed_init_work_fn(struct work_struct *work)
+{
+ int rc = 0;
+ struct smb1360_chip *chip = container_of(work, struct smb1360_chip,
+ delayed_init_work.work);
+
+ rc = smb1360_delayed_hw_init(chip);
+
+ if (!rc) {
+ /*
+ * If the delayed hw init successfully, update battery
+ * power_supply to make sure the correct SoC reported
+ * timely.
+ */
+ power_supply_changed(chip->batt_psy);
+ } else if (rc == -ETIMEDOUT) {
+ /*
+ * If the delayed hw init failed causing by waiting for
+ * FG access timed-out, force a FG reset and queue the
+ * worker again to retry the initialization.
+ */
+ pr_debug("delayed hw init timed-out, retry!");
+ rc = smb1360_force_fg_reset(chip);
+ if (rc) {
+ pr_err("couldn't reset FG, rc = %d\n", rc);
+ return;
+ }
+ schedule_delayed_work(&chip->delayed_init_work, 0);
+ } else {
+ pr_err("delayed hw init failed, rc=%d\n", rc);
+ }
+}
+
+static int smb_parse_batt_id(struct smb1360_chip *chip)
+{
+ int rc = 0, rpull = 0, vref = 0;
+ int64_t denom, batt_id_uv;
+ struct device_node *node = chip->dev->of_node;
+ struct qpnp_vadc_result result;
+
+ chip->vadc_dev = qpnp_get_vadc(chip->dev, "smb1360");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc == -EPROBE_DEFER)
+ pr_err("vadc not found - defer rc=%d\n", rc);
+ else
+ pr_err("vadc property missing, rc=%d\n", rc);
+
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,profile-a-rid-kohm",
+ &chip->profile_rid[0]);
+ if (rc < 0) {
+ pr_err("Couldn't read profile-a-rid-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,profile-b-rid-kohm",
+ &chip->profile_rid[1]);
+ if (rc < 0) {
+ pr_err("Couldn't read profile-b-rid-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,batt-id-vref-uv", &vref);
+ if (rc < 0) {
+ pr_err("Couldn't read batt-id-vref-uv rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,batt-id-rpullup-kohm", &rpull);
+ if (rc < 0) {
+ pr_err("Couldn't read batt-id-rpullup-kohm rc=%d\n", rc);
+ return rc;
+ }
+
+ /* read battery ID */
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &result);
+ if (rc) {
+ pr_err("error reading batt id channel = %d, rc = %d\n",
+ LR_MUX2_BAT_ID, rc);
+ return rc;
+ }
+ batt_id_uv = result.physical;
+
+ if (batt_id_uv == 0) {
+ /* vadc not correct or batt id line grounded, report 0 kohms */
+ pr_err("batt_id_uv = 0, batt-id grounded using same profile\n");
+ return 0;
+ }
+
+ denom = div64_s64(vref * 1000000LL, batt_id_uv) - 1000000LL;
+ if (denom == 0) {
+ /* batt id connector might be open, return 0 kohms */
+ return 0;
+ }
+ chip->connected_rid = div64_s64(rpull * 1000000LL + denom/2, denom);
+
+ pr_debug("batt_id_voltage = %lld, connected_rid = %d\n",
+ batt_id_uv, chip->connected_rid);
+
+ return 0;
+}
+
+/*
+ * Note the below:
+ * 1. if both qcom,soft-jeita-supported and qcom,config-hard-thresholds
+ * are not defined, SMB continues with default OTP configuration.
+ * 2. if both are enabled, the hard thresholds are modified.
+ * 3. if only qcom,config-hard-thresholds is defined, the soft JEITA is disabled
+ * 4. if only qcom,soft-jeita-supported is defined, the soft JEITA thresholds
+ * are modified.
+ */
+static int smb1360_parse_jeita_params(struct smb1360_chip *chip)
+{
+ int rc = 0;
+ struct device_node *node = chip->dev->of_node;
+ int temp[2];
+
+ if (of_property_read_bool(node, "qcom,config-hard-thresholds")) {
+ rc = of_property_read_u32(node,
+ "qcom,cold-bat-decidegc", &chip->cold_bat_decidegc);
+ if (rc) {
+ pr_err("cold_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node,
+ "qcom,hot-bat-decidegc", &chip->hot_bat_decidegc);
+ if (rc) {
+ pr_err("hot_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ chip->config_hard_thresholds = true;
+ pr_debug("config_hard_thresholds = %d, cold_bat_decidegc = %d, hot_bat_decidegc = %d\n",
+ chip->config_hard_thresholds, chip->cold_bat_decidegc,
+ chip->hot_bat_decidegc);
+ } else if (of_property_read_bool(node, "qcom,otp-hard-jeita-config")) {
+ rc = of_property_read_u32(node, "qcom,otp-cold-bat-decidegc",
+ &chip->otp_cold_bat_decidegc);
+ if (rc) {
+ pr_err("otp-cold-bat-decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,otp-hot-bat-decidegc",
+ &chip->otp_hot_bat_decidegc);
+
+ if (rc) {
+ pr_err("otp-hot-bat-decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ chip->otp_hard_jeita_config = true;
+ rc = of_property_read_u32_array(node,
+ "qcom,otp-hard-jeita-hysteresis", temp, 2);
+ if (rc) {
+ if (rc != -EINVAL) {
+ pr_err("read otp-hard-jeita-hysteresis failed, rc = %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ chip->cold_hysteresis = temp[0];
+ chip->hot_hysteresis = temp[1];
+ }
+
+ pr_debug("otp_hard_jeita_config = %d, otp_cold_bat_decidegc = %d\n"
+ "otp_hot_bat_decidegc = %d, cold_hysteresis = %d\n"
+ "hot_hysteresis = %d\n",
+ chip->otp_hard_jeita_config,
+ chip->otp_cold_bat_decidegc,
+ chip->otp_hot_bat_decidegc, chip->cold_hysteresis,
+ chip->hot_hysteresis);
+ }
+
+ if (of_property_read_bool(node, "qcom,soft-jeita-supported")) {
+ rc = of_property_read_u32(node, "qcom,warm-bat-decidegc",
+ &chip->warm_bat_decidegc);
+ if (rc) {
+ pr_err("warm_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,cool-bat-decidegc",
+ &chip->cool_bat_decidegc);
+ if (rc) {
+ pr_err("cool_bat_decidegc property error, rc = %d\n",
+ rc);
+ return -EINVAL;
+ }
+ rc = of_property_read_u32(node, "qcom,cool-bat-mv",
+ &chip->cool_bat_mv);
+ if (rc) {
+ pr_err("cool_bat_mv property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,warm-bat-mv",
+ &chip->warm_bat_mv);
+ if (rc) {
+ pr_err("warm_bat_mv property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,cool-bat-ma",
+ &chip->cool_bat_ma);
+ if (rc) {
+ pr_err("cool_bat_ma property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,warm-bat-ma",
+ &chip->warm_bat_ma);
+
+ if (rc) {
+ pr_err("warm_bat_ma property error, rc = %d\n", rc);
+ return -EINVAL;
+ }
+
+ chip->soft_jeita_supported = true;
+ } else {
+ /*
+ * If no soft JEITA configuration required from devicetree,
+ * read the default soft JEITA setting for hard JEITA
+ * configuration sanity check.
+ */
+ rc = smb1360_get_soft_jeita_threshold(chip,
+ &chip->cool_bat_decidegc,
+ &chip->warm_bat_decidegc);
+ if (rc) {
+ pr_err("get default soft JEITA threshold failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ pr_debug("soft-jeita-enabled = %d, warm-bat-decidegc = %d, cool-bat-decidegc = %d, cool-bat-mv = %d, warm-bat-mv = %d, cool-bat-ma = %d, warm-bat-ma = %d\n",
+ chip->soft_jeita_supported, chip->warm_bat_decidegc,
+ chip->cool_bat_decidegc, chip->cool_bat_mv, chip->warm_bat_mv,
+ chip->cool_bat_ma, chip->warm_bat_ma);
+
+ return rc;
+}
+
+#define MAX_PARALLEL_CURRENT 540
+static int smb1360_parse_parallel_charging_params(struct smb1360_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+
+ if (of_property_read_bool(node, "qcom,parallel-charging-enabled")) {
+
+ if (!chip->rsense_10mohm) {
+ pr_err("10mohm-rsense configuration not enabled - parallel-charging disabled\n");
+ return 0;
+ }
+ chip->parallel_charging = true;
+ chip->max_parallel_chg_current = MAX_PARALLEL_CURRENT;
+ of_property_read_u32(node, "qcom,max-parallel-current-ma",
+ &chip->max_parallel_chg_current);
+
+ pr_debug("Max parallel charger current = %dma\n",
+ chip->max_parallel_chg_current);
+
+ /* mark the parallel-charger as disabled */
+ chip->parallel_chg_disable_status |= PARALLEL_CURRENT;
+ }
+
+ return 0;
+}
+
+static int smb_parse_dt(struct smb1360_chip *chip)
+{
+ int rc;
+ struct device_node *node = chip->dev->of_node;
+
+ if (!node) {
+ dev_err(chip->dev, "device tree info. missing\n");
+ return -EINVAL;
+ }
+
+ chip->rsense_10mohm = of_property_read_bool(node, "qcom,rsense-10mhom");
+
+ if (of_property_read_bool(node, "qcom,batt-profile-select")) {
+ rc = smb_parse_batt_id(chip);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ pr_err("Unable to parse batt-id rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ chip->otg_fet_present = of_property_read_bool(node,
+ "qcom,otg-fet-present");
+ if (chip->otg_fet_present) {
+ chip->otg_fet_enable_gpio = of_get_named_gpio(node,
+ "qcom,otg-fet-enable-gpio", 0);
+ if (!gpio_is_valid(chip->otg_fet_enable_gpio)) {
+ if (chip->otg_fet_enable_gpio != -EPROBE_DEFER)
+ pr_err("Unable to get OTG FET enable gpio=%d\n",
+ chip->otg_fet_enable_gpio);
+ return chip->otg_fet_enable_gpio;
+ }
+ /* Configure OTG FET control gpio */
+ rc = devm_gpio_request_one(chip->dev,
+ chip->otg_fet_enable_gpio,
+ GPIOF_OPEN_DRAIN | GPIOF_INIT_HIGH,
+ "smb1360_otg_fet_gpio");
+ if (rc) {
+ pr_err("Unable to request gpio rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ chip->pulsed_irq = of_property_read_bool(node, "qcom,stat-pulsed-irq");
+
+ rc = of_property_read_u32(node, "qcom,float-voltage-mv",
+ &chip->vfloat_mv);
+ if (rc < 0)
+ chip->vfloat_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,charging-timeout",
+ &chip->safety_time);
+ if (rc < 0)
+ chip->safety_time = -EINVAL;
+
+ if (!rc && (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) {
+ dev_err(chip->dev, "Bad charging-timeout %d\n",
+ chip->safety_time);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,recharge-thresh-mv",
+ &chip->resume_delta_mv);
+ if (rc < 0)
+ chip->resume_delta_mv = -EINVAL;
+
+ chip->recharge_disabled = of_property_read_bool(node,
+ "qcom,recharge-disabled");
+
+ rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma);
+ if (rc < 0)
+ chip->iterm_ma = -EINVAL;
+
+ chip->iterm_disabled = of_property_read_bool(node,
+ "qcom,iterm-disabled");
+
+ chip->chg_inhibit_disabled = of_property_read_bool(node,
+ "qcom,chg-inhibit-disabled");
+
+ chip->charging_disabled = of_property_read_bool(node,
+ "qcom,charging-disabled");
+
+ chip->batt_id_disabled = of_property_read_bool(node,
+ "qcom,batt-id-disabled");
+
+ chip->shdn_after_pwroff = of_property_read_bool(node,
+ "qcom,shdn-after-pwroff");
+
+ chip->min_icl_usb100 = of_property_read_bool(node,
+ "qcom,min-icl-100ma");
+
+ chip->ov_ends_chg_cycle_disabled = of_property_read_bool(node,
+ "qcom,disable-ov-ends-chg-cycle");
+
+ rc = smb1360_parse_parallel_charging_params(chip);
+ if (rc) {
+ pr_err("Couldn't parse parallel charginng params rc=%d\n", rc);
+ return rc;
+ }
+
+ if (of_find_property(node, "qcom,thermal-mitigation",
+ &chip->thermal_levels)) {
+ chip->thermal_mitigation = devm_kzalloc(chip->dev,
+ chip->thermal_levels,
+ GFP_KERNEL);
+
+ if (chip->thermal_mitigation == NULL) {
+ pr_err("thermal mitigation kzalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ chip->thermal_levels /= sizeof(int);
+ rc = of_property_read_u32_array(node,
+ "qcom,thermal-mitigation",
+ chip->thermal_mitigation, chip->thermal_levels);
+ if (rc) {
+ pr_err("Couldn't read threm limits rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = smb1360_parse_jeita_params(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse jeita params, rc = %d\n", rc);
+ return rc;
+ }
+
+ /* fg params */
+ chip->empty_soc_disabled = of_property_read_bool(node,
+ "qcom,empty-soc-disabled");
+
+ rc = of_property_read_u32(node, "qcom,fg-delta-soc", &chip->delta_soc);
+ if (rc < 0)
+ chip->delta_soc = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-soc-max", &chip->soc_max);
+ if (rc < 0)
+ chip->soc_max = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-soc-min", &chip->soc_min);
+ if (rc < 0)
+ chip->soc_min = -EINVAL;
+
+ chip->awake_min_soc = of_property_read_bool(node,
+ "qcom,awake-min-soc");
+
+ rc = of_property_read_u32(node, "qcom,fg-voltage-min-mv",
+ &chip->voltage_min_mv);
+ if (rc < 0)
+ chip->voltage_min_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-voltage-empty-mv",
+ &chip->voltage_empty_mv);
+ if (rc < 0)
+ chip->voltage_empty_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-batt-capacity-mah",
+ &chip->batt_capacity_mah);
+ if (rc < 0)
+ chip->batt_capacity_mah = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cc-soc-coeff",
+ &chip->cc_soc_coeff);
+ if (rc < 0)
+ chip->cc_soc_coeff = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage-mv",
+ &chip->v_cutoff_mv);
+ if (rc < 0)
+ chip->v_cutoff_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-iterm-ma",
+ &chip->fg_iterm_ma);
+ if (rc < 0)
+ chip->fg_iterm_ma = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-ibatt-standby-ma",
+ &chip->fg_ibatt_standby_ma);
+ if (rc < 0)
+ chip->fg_ibatt_standby_ma = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,thermistor-c1-coeff",
+ &chip->fg_thermistor_c1_coeff);
+ if (rc < 0)
+ chip->fg_thermistor_c1_coeff = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-cc-to-cv-mv",
+ &chip->fg_cc_to_cv_mv);
+ if (rc < 0)
+ chip->fg_cc_to_cv_mv = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,otg-batt-curr-limit",
+ &chip->otg_batt_curr_limit);
+ if (rc < 0)
+ chip->otg_batt_curr_limit = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,fg-auto-recharge-soc",
+ &chip->fg_auto_recharge_soc);
+ if (rc < 0)
+ chip->fg_auto_recharge_soc = -EINVAL;
+
+ if (of_property_read_bool(node, "qcom,fg-reset-at-pon")) {
+ chip->fg_reset_at_pon = true;
+ rc = of_property_read_u32(node, "qcom,fg-reset-thresold-mv",
+ &chip->fg_reset_threshold_mv);
+ if (rc) {
+ pr_debug("FG reset voltage threshold not specified using 50mV\n");
+ chip->fg_reset_threshold_mv = FG_RESET_THRESHOLD_MV;
+ }
+ }
+
+ return 0;
+}
+
+static int smb1360_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ u8 reg;
+ int rc;
+ struct smb1360_chip *chip;
+ struct power_supply_config batt_psy_cfg = {};
+ struct power_supply_config usb_psy_cfg = {};
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->resume_completed = true;
+ chip->client = client;
+ chip->dev = &client->dev;
+ chip->fake_battery_soc = -EINVAL;
+ chip->usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ chip->extcon = devm_extcon_dev_allocate(chip->dev,
+ smb1360_extcon_cable);
+ if (IS_ERR(chip->extcon)) {
+ pr_err("failed to allocate extcon device\n");
+ rc = PTR_ERR(chip->extcon);
+ return rc;
+ }
+
+ rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+ if (rc) {
+ pr_err("failed to register extcon device\n");
+ return rc;
+ }
+
+ mutex_init(&chip->read_write_lock);
+ mutex_init(&chip->parallel_chg_lock);
+ mutex_init(&chip->otp_gain_lock);
+ mutex_init(&chip->fg_access_request_lock);
+ mutex_init(&chip->irq_complete);
+ mutex_init(&chip->charging_disable_lock);
+ mutex_init(&chip->current_change_lock);
+
+ INIT_DELAYED_WORK(&chip->jeita_work, smb1360_jeita_work_fn);
+ INIT_DELAYED_WORK(&chip->delayed_init_work,
+ smb1360_delayed_init_work_fn);
+ init_completion(&chip->fg_mem_access_granted);
+ smb1360_wakeup_src_init(chip);
+
+ chip->usb_psy_d.name = "usb";
+ chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+ chip->usb_psy_d.get_property = smb1360_usb_get_property;
+ chip->usb_psy_d.set_property = smb1360_usb_set_property;
+ chip->usb_psy_d.properties = smb1360_usb_properties;
+ chip->usb_psy_d.num_properties = ARRAY_SIZE(smb1360_usb_properties);
+ chip->usb_psy_d.property_is_writeable = smb1360_usb_is_writeable;
+
+ usb_psy_cfg.drv_data = chip;
+ usb_psy_cfg.num_supplicants = 0;
+
+ chip->usb_psy = devm_power_supply_register(chip->dev,
+ &chip->usb_psy_d, &usb_psy_cfg);
+ if (IS_ERR(chip->usb_psy)) {
+ dev_err(chip->dev, "Unable to register usb_psy rc = %ld\n",
+ PTR_ERR(chip->usb_psy));
+ rc = PTR_ERR(chip->usb_psy);
+ return rc;
+ }
+
+ /* probe the device to check if its actually connected */
+ rc = smb1360_read(chip, CFG_BATT_CHG_REG, ®);
+ if (rc) {
+ pr_err("Failed to detect SMB1360, device may be absent\n");
+ goto destroy_mutex;
+ }
+
+ rc = read_revision(chip, &chip->revision);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read revision rc = %d\n", rc);
+
+ rc = smb_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(&client->dev, "Unable to parse DT nodes\n");
+ goto destroy_mutex;
+ }
+
+ device_init_wakeup(chip->dev, 1);
+ i2c_set_clientdata(client, chip);
+ chip->default_i2c_addr = client->addr;
+ INIT_WORK(&chip->parallel_work, smb1360_parallel_work);
+ if (chip->cold_hysteresis || chip->hot_hysteresis)
+ INIT_WORK(&chip->jeita_hysteresis_work,
+ smb1360_jeita_hysteresis_work);
+
+ pr_debug("default_i2c_addr=%x\n", chip->default_i2c_addr);
+ smb1360_otp_backup_pool_init(chip);
+ rc = smb1360_hw_init(chip);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "Unable to initialize hardware rc = %d\n", rc);
+ goto destroy_mutex;
+ }
+
+ rc = smb1360_regulator_init(chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Couldn't initialize smb1360 ragulator rc=%d\n", rc);
+ goto fail_hw_init;
+ }
+
+ rc = determine_initial_status(chip);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "Unable to determine init status rc = %d\n", rc);
+ goto fail_hw_init;
+ }
+
+ chip->batt_psy_d.name = "battery";
+ chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy_d.get_property = smb1360_battery_get_property;
+ chip->batt_psy_d.set_property = smb1360_battery_set_property;
+ chip->batt_psy_d.properties = smb1360_battery_properties;
+ chip->batt_psy_d.num_properties =
+ ARRAY_SIZE(smb1360_battery_properties);
+ chip->batt_psy_d.property_is_writeable = smb1360_battery_is_writeable;
+
+ batt_psy_cfg.drv_data = chip;
+ batt_psy_cfg.num_supplicants = 0;
+
+ chip->batt_psy = devm_power_supply_register(chip->dev,
+ &chip->batt_psy_d, &batt_psy_cfg);
+ if (IS_ERR(chip->batt_psy)) {
+ dev_err(&client->dev, "Unable to register batt_psy rc = %ld\n",
+ PTR_ERR(chip->batt_psy));
+ goto unregister_batt_psy;
+ }
+
+ /* STAT irq configuration */
+ if (client->irq) {
+ rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ smb1360_stat_handler, IRQF_ONESHOT,
+ "smb1360_stat_irq", chip);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "request_irq for irq=%d failed rc = %d\n",
+ client->irq, rc);
+ goto unregister_batt_psy;
+ }
+ enable_irq_wake(client->irq);
+ }
+
+ chip->debug_root = debugfs_create_dir("smb1360", NULL);
+ if (!chip->debug_root)
+ dev_err(chip->dev, "Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("config_registers", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &cnfg_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create cnfg debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("status_registers", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &status_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create status debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("irq_status", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &irq_stat_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create irq_stat debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("cmd_registers", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &cmd_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create cmd debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("fg_regs",
+ S_IFREG | 0444, chip->debug_root, chip,
+ &fg_regs_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create fg_scratch_pad debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("address", S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->peek_poke_address));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create address debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("data", S_IFREG | 0644,
+ chip->debug_root, chip,
+ &poke_poke_debug_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("fg_address",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->fg_peek_poke_address));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create address debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("fg_data",
+ S_IFREG | 0644,
+ chip->debug_root, chip,
+ &fg_poke_poke_debug_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("fg_access_type",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->fg_access_type));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("skip_writes",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->skip_writes));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_x32("skip_reads",
+ S_IFREG | 0644,
+ chip->debug_root,
+ &(chip->skip_reads));
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+
+ ent = debugfs_create_file("irq_count", S_IFREG | 0444,
+ chip->debug_root, chip,
+ &irq_count_debugfs_ops);
+ if (!ent)
+ dev_err(chip->dev,
+ "Couldn't create count debug file rc = %d\n",
+ rc);
+ }
+
+ dev_info(chip->dev, "SMB1360 revision=0x%x probe success! batt=%d usb=%d soc=%d\n",
+ chip->revision,
+ smb1360_get_prop_batt_present(chip),
+ chip->usb_present,
+ smb1360_get_prop_batt_capacity(chip));
+
+ return 0;
+
+unregister_batt_psy:
+ power_supply_unregister(chip->batt_psy);
+fail_hw_init:
+ regulator_unregister(chip->otg_vreg.rdev);
+destroy_mutex:
+ power_supply_unregister(chip->usb_psy);
+ wakeup_source_trash(&chip->smb1360_ws.source);
+ mutex_destroy(&chip->read_write_lock);
+ mutex_destroy(&chip->parallel_chg_lock);
+ mutex_destroy(&chip->otp_gain_lock);
+ mutex_destroy(&chip->fg_access_request_lock);
+ mutex_destroy(&chip->irq_complete);
+ mutex_destroy(&chip->charging_disable_lock);
+ mutex_destroy(&chip->current_change_lock);
+ return rc;
+}
+
+static int smb1360_remove(struct i2c_client *client)
+{
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ regulator_unregister(chip->otg_vreg.rdev);
+ power_supply_unregister(chip->usb_psy);
+ power_supply_unregister(chip->batt_psy);
+ wakeup_source_trash(&chip->smb1360_ws.source);
+ mutex_destroy(&chip->charging_disable_lock);
+ mutex_destroy(&chip->current_change_lock);
+ mutex_destroy(&chip->read_write_lock);
+ mutex_destroy(&chip->parallel_chg_lock);
+ mutex_destroy(&chip->irq_complete);
+ mutex_destroy(&chip->otp_gain_lock);
+ mutex_destroy(&chip->fg_access_request_lock);
+ debugfs_remove_recursive(chip->debug_root);
+
+ return 0;
+}
+
+static int smb1360_suspend(struct device *dev)
+{
+ int i, rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ /* Save the current IRQ config */
+ for (i = 0; i < 3; i++) {
+ rc = smb1360_read(chip, IRQ_CFG_REG + i,
+ &chip->irq_cfg_mask[i]);
+ if (rc)
+ pr_err("Couldn't save irq cfg regs rc=%d\n", rc);
+ }
+
+ /* enable only important IRQs */
+ rc = smb1360_write(chip, IRQ_CFG_REG, IRQ_DCIN_UV_BIT
+ | IRQ_AICL_DONE_BIT
+ | IRQ_BAT_HOT_COLD_SOFT_BIT
+ | IRQ_BAT_HOT_COLD_HARD_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set irq_cfg rc=%d\n", rc);
+
+ rc = smb1360_write(chip, IRQ2_CFG_REG, IRQ2_BATT_MISSING_BIT
+ | IRQ2_VBAT_LOW_BIT
+ | IRQ2_POWER_OK_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set irq2_cfg rc=%d\n", rc);
+
+ rc = smb1360_write(chip, IRQ3_CFG_REG, IRQ3_SOC_FULL_BIT
+ | IRQ3_SOC_MIN_BIT
+ | IRQ3_SOC_EMPTY_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set irq3_cfg rc=%d\n", rc);
+
+ mutex_lock(&chip->irq_complete);
+ chip->resume_completed = false;
+ mutex_unlock(&chip->irq_complete);
+
+ return 0;
+}
+
+static int smb1360_suspend_noirq(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ if (chip->irq_waiting) {
+ pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int smb1360_resume(struct device *dev)
+{
+ int i, rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ /* Restore the IRQ config */
+ for (i = 0; i < 3; i++) {
+ rc = smb1360_write(chip, IRQ_CFG_REG + i,
+ chip->irq_cfg_mask[i]);
+ if (rc)
+ pr_err("Couldn't restore irq cfg regs rc=%d\n", rc);
+ }
+
+ mutex_lock(&chip->irq_complete);
+ chip->resume_completed = true;
+ if (chip->irq_waiting) {
+ chip->irq_disabled = false;
+ enable_irq(client->irq);
+ mutex_unlock(&chip->irq_complete);
+ smb1360_stat_handler(client->irq, chip);
+ } else {
+ mutex_unlock(&chip->irq_complete);
+ }
+
+ power_supply_changed(chip->batt_psy);
+
+ return 0;
+}
+
+static void smb1360_shutdown(struct i2c_client *client)
+{
+ int rc;
+ struct smb1360_chip *chip = i2c_get_clientdata(client);
+
+ rc = smb1360_otg_disable(chip);
+ if (rc)
+ pr_err("Couldn't disable OTG mode rc=%d\n", rc);
+
+ if (chip->shdn_after_pwroff) {
+ rc = smb1360_poweroff(chip);
+ if (rc)
+ pr_err("Couldn't shutdown smb1360, rc = %d\n", rc);
+ pr_info("smb1360 power off\n");
+ }
+}
+
+static const struct dev_pm_ops smb1360_pm_ops = {
+ .resume = smb1360_resume,
+ .suspend_noirq = smb1360_suspend_noirq,
+ .suspend = smb1360_suspend,
+};
+
+static const struct of_device_id smb1360_match_table[] = {
+ { .compatible = "qcom,smb1360-chg-fg",},
+ { },
+};
+
+static const struct i2c_device_id smb1360_id[] = {
+ {"smb1360-chg-fg", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, smb1360_id);
+
+static struct i2c_driver smb1360_driver = {
+ .driver = {
+ .name = "smb1360-chg-fg",
+ .owner = THIS_MODULE,
+ .of_match_table = smb1360_match_table,
+ .pm = &smb1360_pm_ops,
+ },
+ .probe = smb1360_probe,
+ .remove = smb1360_remove,
+ .shutdown = smb1360_shutdown,
+ .id_table = smb1360_id,
+};
+
+module_i2c_driver(smb1360_driver);
+
+MODULE_DESCRIPTION("SMB1360 Charger and Fuel Gauge");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb1360-chg-fg");
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index f1df8f0..86ecda5 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -1371,10 +1371,12 @@
[USBIN_PLUGIN_IRQ] = {
.name = "usbin-plugin",
.handler = smblib_handle_usb_plugin,
+ .wake = true,
},
[USBIN_SRC_CHANGE_IRQ] = {
.name = "usbin-src-change",
.handler = smblib_handle_usb_source_change,
+ .wake = true,
},
[USBIN_ICL_CHANGE_IRQ] = {
.name = "usbin-icl-change",
@@ -1383,6 +1385,7 @@
[TYPE_C_CHANGE_IRQ] = {
.name = "type-c-change",
.handler = smblib_handle_usb_typec_change,
+ .wake = true,
},
/* DC INPUT IRQs */
[DCIN_COLLAPSE_IRQ] = {
@@ -1825,6 +1828,8 @@
goto cleanup;
}
+ device_init_wakeup(chip->chg.dev, true);
+
pr_info("SMB138X probed successfully mode=%d\n", chip->chg.mode);
return rc;
diff --git a/drivers/power/supply/qcom/smb1390-charger.c b/drivers/power/supply/qcom/smb1390-charger.c
index 636265b..a92c975 100644
--- a/drivers/power/supply/qcom/smb1390-charger.c
+++ b/drivers/power/supply/qcom/smb1390-charger.c
@@ -67,12 +67,22 @@
#define CORE_FTRIM_ILIM_REG 0x1030
#define CFG_ILIM_MASK GENMASK(4, 0)
+#define CORE_FTRIM_LVL_REG 0x1033
+#define CFG_WIN_HI_MASK GENMASK(3, 2)
+#define WIN_OV_LVL_1000MV 0x08
+
+#define CORE_FTRIM_MISC_REG 0x1034
+#define TR_WIN_1P5X_BIT BIT(0)
+#define WINDOW_DETECTION_DELTA_X1P0 0
+#define WINDOW_DETECTION_DELTA_X1P5 1
+
#define CP_VOTER "CP_VOTER"
#define USER_VOTER "USER_VOTER"
#define ILIM_VOTER "ILIM_VOTER"
#define FCC_VOTER "FCC_VOTER"
#define ICL_VOTER "ICL_VOTER"
#define USB_VOTER "USB_VOTER"
+#define SWITCHER_TOGGLE_VOTER "SWITCHER_TOGGLE_VOTER"
enum {
SWITCHER_OFF_WINDOW_IRQ = 0,
@@ -91,6 +101,7 @@
struct regmap *regmap;
struct notifier_block nb;
struct class cp_class;
+ struct wakeup_source *cp_ws;
/* work structs */
struct work_struct status_change_work;
@@ -105,6 +116,7 @@
struct votable *pl_disable_votable;
struct votable *fcc_votable;
struct votable *hvdcp_hw_inov_dis_votable;
+ struct votable *cp_awake_votable;
/* power supplies */
struct power_supply *usb_psy;
@@ -115,6 +127,7 @@
bool status_change_running;
bool taper_work_running;
int adc_channel;
+ int irq_status;
};
struct smb_irq {
@@ -195,6 +208,18 @@
return true;
}
+static void cp_toggle_switcher(struct smb1390 *chip)
+{
+ vote(chip->disable_votable, SWITCHER_TOGGLE_VOTER, true, 0);
+
+ /* Delay for toggling switcher */
+ usleep_range(20, 30);
+
+ vote(chip->disable_votable, SWITCHER_TOGGLE_VOTER, false, 0);
+
+ return;
+}
+
static irqreturn_t default_irq_handler(int irq, void *data)
{
struct smb1390 *chip = data;
@@ -203,40 +228,13 @@
for (i = 0; i < NUM_IRQS; ++i) {
if (irq == chip->irqs[i])
pr_debug("%s IRQ triggered\n", smb_irqs[i].name);
+ chip->irq_status |= 1 << i;
}
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
return IRQ_HANDLED;
}
-static irqreturn_t irev_irq_handler(int irq, void *data)
-{
- struct smb1390 *chip = data;
- int rc;
-
- pr_debug("IREV IRQ triggered\n");
-
- rc = smb1390_masked_write(chip, CORE_CONTROL1_REG,
- CMD_EN_SWITCHER_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable switcher by command mode, rc=%d\n",
- rc);
- goto out;
- }
-
- rc = smb1390_masked_write(chip, CORE_CONTROL1_REG,
- CMD_EN_SWITCHER_BIT, CMD_EN_SWITCHER_BIT);
- if (rc < 0) {
- pr_err("Couldn't enable switcher by command mode, rc=%d\n",
- rc);
- goto out;
- }
-
-out:
- kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
- return IRQ_HANDLED;
-}
-
static const struct smb_irq smb_irqs[] = {
[SWITCHER_OFF_WINDOW_IRQ] = {
.name = "switcher-off-window",
@@ -255,7 +253,7 @@
},
[IREV_IRQ] = {
.name = "irev-fault",
- .handler = irev_irq_handler,
+ .handler = default_irq_handler,
.wake = true,
},
[VPH_OV_HARD_IRQ] = {
@@ -329,6 +327,38 @@
return count;
}
+static ssize_t cp_irq_show(struct class *c, struct class_attribute *attr,
+ char *buf)
+{
+ struct smb1390 *chip = container_of(c, struct smb1390, cp_class);
+ int rc, val;
+
+ rc = smb1390_read(chip, CORE_INT_RT_STS_REG, &val);
+ if (rc < 0)
+ return -EINVAL;
+
+ val |= chip->irq_status;
+ chip->irq_status = 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", val);
+}
+
+static ssize_t toggle_switcher_store(struct class *c,
+ struct class_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct smb1390 *chip = container_of(c, struct smb1390, cp_class);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val)
+ cp_toggle_switcher(chip);
+
+ return count;
+}
+
static ssize_t die_temp_show(struct class *c, struct class_attribute *attr,
char *buf)
{
@@ -349,6 +379,8 @@
__ATTR_RO(stat1),
__ATTR_RO(stat2),
__ATTR_RW(enable),
+ __ATTR_RO(cp_irq),
+ __ATTR_WO(toggle_switcher),
__ATTR_RO(die_temp),
__ATTR_NULL,
};
@@ -369,11 +401,12 @@
if (rc < 0)
return rc;
- vote(chip->hvdcp_hw_inov_dis_votable, CP_VOTER, false, 0);
vote(chip->pl_disable_votable, CP_VOTER, false, 0);
+ vote(chip->cp_awake_votable, CP_VOTER, false, 0);
} else {
vote(chip->hvdcp_hw_inov_dis_votable, CP_VOTER, true, 0);
vote(chip->pl_disable_votable, CP_VOTER, true, 0);
+ vote(chip->cp_awake_votable, CP_VOTER, true, 0);
rc = smb1390_masked_write(chip, CORE_CONTROL1_REG,
CMD_EN_SWITCHER_BIT, CMD_EN_SWITCHER_BIT);
if (rc < 0)
@@ -419,6 +452,20 @@
return rc;
}
+static int smb1390_awake_vote_cb(struct votable *votable, void *data,
+ int awake, const char *client)
+{
+ struct smb1390 *chip = data;
+
+ if (awake)
+ __pm_stay_awake(chip->cp_ws);
+ else
+ __pm_relax(chip->cp_ws);
+
+ pr_debug("client: %s awake: %d\n", client, awake);
+ return 0;
+}
+
static int smb1390_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -580,6 +627,11 @@
if (IS_ERR(chip->ilim_votable))
return PTR_ERR(chip->ilim_votable);
+ chip->cp_awake_votable = create_votable("CP_AWAKE", VOTE_SET_ANY,
+ smb1390_awake_vote_cb, chip);
+ if (IS_ERR(chip->cp_awake_votable))
+ return PTR_ERR(chip->cp_awake_votable);
+
return 0;
}
@@ -591,11 +643,30 @@
static int smb1390_init_hw(struct smb1390 *chip)
{
+ int rc;
+
/*
* charge pump is initially disabled; this indirectly votes to allow
* traditional parallel charging if present
*/
vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ /*
+ * Improve ILIM accuracy:
+ * - Configure window (Vin - 2Vout) OV level to 1000mV
+ * - Configure VOUT tracking value to 1.0
+ */
+ rc = smb1390_masked_write(chip, CORE_FTRIM_LVL_REG,
+ CFG_WIN_HI_MASK, WIN_OV_LVL_1000MV);
+ if (rc < 0)
+ return rc;
+
+ rc = smb1390_masked_write(chip, CORE_FTRIM_MISC_REG,
+ TR_WIN_1P5X_BIT, WINDOW_DETECTION_DELTA_X1P0);
+ if (rc < 0)
+ return rc;
+
+
return 0;
}
@@ -694,16 +765,21 @@
rc = smb1390_parse_dt(chip);
if (rc < 0) {
pr_err("Couldn't parse device tree rc=%d\n", rc);
- goto out_work;
+ return rc;
}
chip->vadc_dev = qpnp_get_vadc(chip->dev, "smb");
if (IS_ERR(chip->vadc_dev)) {
rc = PTR_ERR(chip->vadc_dev);
- pr_err("Couldn't get vadc dev rc=%d\n", rc);
- goto out_work;
+ if (rc != -EPROBE_DEFER)
+ pr_err("Couldn't get vadc dev rc=%d\n", rc);
+ return rc;
}
+ chip->cp_ws = wakeup_source_register("qcom-chargepump");
+ if (!chip->cp_ws)
+ return rc;
+
rc = smb1390_create_votables(chip);
if (rc < 0) {
pr_err("Couldn't create votables rc=%d\n", rc);
@@ -750,6 +826,7 @@
out_work:
cancel_work(&chip->taper_work);
cancel_work(&chip->status_change_work);
+ wakeup_source_unregister(chip->cp_ws);
return rc;
}
@@ -762,8 +839,10 @@
/* explicitly disable charging */
vote(chip->disable_votable, USER_VOTER, true, 0);
+ vote(chip->hvdcp_hw_inov_dis_votable, CP_VOTER, false, 0);
cancel_work(&chip->taper_work);
cancel_work(&chip->status_change_work);
+ wakeup_source_unregister(chip->cp_ws);
smb1390_destroy_votables(chip);
return 0;
}
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 3152669..d79e12b 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -39,6 +39,10 @@
__func__, ##__VA_ARGS__); \
} while (0)
+#define typec_rp_med_high(chg, typec_mode) \
+ ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM \
+ || typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) \
+ && !chg->typec_legacy)
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
{
@@ -112,6 +116,19 @@
return 0;
}
+int smblib_icl_override(struct smb_charger *chg, bool override)
+{
+ int rc;
+
+ rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG,
+ ICL_OVERRIDE_AFTER_APSD_BIT,
+ override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
+
+ return rc;
+}
+
int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override)
{
int rc = 0;
@@ -455,6 +472,23 @@
{
int rc = 0;
+ /* PMI632 only support max. 9V */
+ if (chg->smb_version == PMI632_SUBTYPE) {
+ switch (allowed_voltage) {
+ case USBIN_ADAPTER_ALLOW_12V:
+ case USBIN_ADAPTER_ALLOW_9V_TO_12V:
+ allowed_voltage = USBIN_ADAPTER_ALLOW_9V;
+ break;
+ case USBIN_ADAPTER_ALLOW_5V_OR_12V:
+ case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V:
+ allowed_voltage = USBIN_ADAPTER_ALLOW_5V_OR_9V;
+ break;
+ case USBIN_ADAPTER_ALLOW_5V_TO_12V:
+ allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V;
+ break;
+ }
+ }
+
rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage);
if (rc < 0) {
smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n",
@@ -508,6 +542,23 @@
/********************
* HELPER FUNCTIONS *
********************/
+int smblib_configure_hvdcp_apsd(struct smb_charger *chg, bool enable)
+{
+ int rc;
+ u8 mask = HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT;
+
+ if (chg->pd_not_supported)
+ return 0;
+
+ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, mask,
+ enable ? mask : 0);
+ if (rc < 0)
+ smblib_err(chg, "failed to write USBIN_OPTIONS_1_CFG rc=%d\n",
+ rc);
+
+ return rc;
+}
+
static int smblib_request_dpdm(struct smb_charger *chg, bool enable)
{
int rc = 0;
@@ -654,7 +705,7 @@
return 0;
}
-#define USBIN_100MA 100000
+#define SDP_100_MA 100000
static void smblib_uusb_removal(struct smb_charger *chg)
{
int rc;
@@ -663,10 +714,6 @@
cancel_delayed_work_sync(&chg->pl_enable_work);
- rc = smblib_request_dpdm(chg, false);
- if (rc < 0)
- smblib_err(chg, "Couldn't to disable DPDM rc=%d\n", rc);
-
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
if (data) {
@@ -683,6 +730,7 @@
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
/* reconfigure allowed voltage for HVDCP */
@@ -705,8 +753,6 @@
smblib_err(chg, "Couldn't write float charger options rc=%d\n",
rc);
- /* leave the ICL configured to 100mA for next insertion */
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, true, USBIN_100MA);
/* clear USB ICL vote for USB_PSY_VOTER */
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
if (rc < 0)
@@ -772,6 +818,7 @@
}
#define USBIN_25MA 25000
+#define USBIN_100MA 100000
#define USBIN_150MA 150000
#define USBIN_500MA 500000
#define USBIN_900MA 900000
@@ -854,7 +901,7 @@
int smblib_set_icl_current(struct smb_charger *chg, int icl_ua)
{
int rc = 0;
- bool hc_mode = false;
+ bool hc_mode = false, override = false;
/* suspend and return if 25mA or less is requested */
if (icl_ua <= USBIN_25MA)
@@ -864,9 +911,10 @@
goto set_mode;
/* configure current */
- if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT)
- || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB))
- && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) {
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB
+ && (chg->typec_legacy
+ || chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
+ || chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) {
rc = set_sdp_current(chg, icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc);
@@ -880,11 +928,28 @@
goto out;
}
hc_mode = true;
+
+ /*
+ * Micro USB mode follows ICL register independent of override
+ * bit, configure override only for typeC mode.
+ */
+ if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC)
+ override = true;
}
set_mode:
rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
USBIN_MODE_CHG_BIT, hc_mode ? USBIN_MODE_CHG_BIT : 0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = smblib_icl_override(chg, override);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc);
+ goto out;
+ }
/* unsuspend after configuring current and override */
rc = smblib_set_usb_suspend(chg, false);
@@ -925,7 +990,8 @@
return INT_MAX;
/* override is set */
- rc = smblib_get_charge_param(chg, &chg->param.usb_icl, icl_ua);
+ rc = smblib_get_charge_param(chg, &chg->param.icl_max_stat,
+ icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc);
return rc;
@@ -954,18 +1020,6 @@
return smblib_set_dc_suspend(chg, (bool)suspend);
}
-static int smblib_pd_disallowed_votable_indirect_callback(
- struct votable *votable, void *data, int disallowed, const char *client)
-{
- struct smb_charger *chg = data;
- int rc;
-
- rc = vote(chg->pd_allowed_votable, PD_DISALLOWED_INDIRECT_VOTER,
- !disallowed, 0);
-
- return rc;
-}
-
static int smblib_awake_vote_callback(struct votable *votable, void *data,
int awake, const char *client)
{
@@ -1026,13 +1080,25 @@
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
int rc = 0;
+ u8 stat, orientation;
smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
- VCONN_EN_VALUE_BIT, VCONN_EN_VALUE_BIT);
+ rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc);
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ return rc;
+ }
+
+ /* VCONN orientation is opposite to that of CC */
+ orientation =
+ stat & TYPEC_CCOUT_VALUE_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
+ rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
+ VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
+ VCONN_EN_VALUE_BIT | orientation);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+ rc);
return rc;
}
@@ -1447,6 +1513,19 @@
return rc;
}
+int smblib_get_prop_batt_cycle_count(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->bms_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chg->bms_psy,
+ POWER_SUPPLY_PROP_CYCLE_COUNT, val);
+ return rc;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1590,7 +1669,7 @@
return rc;
}
-static int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
+int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
{
int rc;
@@ -1977,13 +2056,6 @@
return rc;
}
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
- union power_supply_propval *val)
-{
- val->intval = get_effective_result(chg->pd_allowed_votable);
- return 0;
-}
-
int smblib_get_prop_input_current_settled(struct smb_charger *chg,
union power_supply_propval *val)
{
@@ -2027,13 +2099,7 @@
int smblib_get_pe_start(struct smb_charger *chg,
union power_supply_propval *val)
{
- /*
- * hvdcp timeout voter is the last one to allow pd. Use its vote
- * to indicate start of pe engine
- */
- val->intval
- = !get_client_vote_locked(chg->pd_disallowed_votable_indirect,
- APSD_VOTER);
+ val->intval = chg->ok_to_pd;
return 0;
}
@@ -2120,24 +2186,40 @@
if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
if (usb_current == -ETIMEDOUT) {
- /*
- * Valid FLOAT charger, report the current based
- * of Rp
- */
+ if ((chg->float_cfg & FLOAT_OPTIONS_MASK)
+ == FORCE_FLOAT_SDP_CFG_BIT) {
+ /*
+ * Confiugure USB500 mode if Float charger is
+ * configured for SDP.
+ */
+ rc = set_sdp_current(chg, USBIN_500MA);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't set SDP ICL rc=%d\n",
+ rc);
+
+ return rc;
+ }
+
if (chg->connector_type ==
POWER_SUPPLY_CONNECTOR_TYPEC) {
+ /*
+ * Valid FLOAT charger, report the current
+ * based of Rp.
+ */
typec_mode = smblib_get_prop_typec_mode(chg);
rp_ua = get_rp_based_dcp_current(chg,
typec_mode);
rc = vote(chg->usb_icl_votable,
- DYNAMIC_RP_VOTER, true, rp_ua);
- if (rc < 0) {
- pr_err("Couldn't vote ICL DYNAMIC_RP_VOTER rc=%d\n",
- rc);
+ SW_ICL_MAX_VOTER, true, rp_ua);
+ if (rc < 0)
return rc;
- }
+ } else {
+ rc = vote(chg->usb_icl_votable,
+ SW_ICL_MAX_VOTER, true, DCP_CURRENT_UA);
+ if (rc < 0)
+ return rc;
}
- /* No specific handling required for micro-USB */
} else {
/*
* FLOAT charger detected as SDP by USB driver,
@@ -2146,12 +2228,13 @@
*/
chg->real_charger_type = POWER_SUPPLY_TYPE_USB;
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
- true, usb_current);
- if (rc < 0) {
- pr_err("Couldn't vote ICL USB_PSY_VOTER rc=%d\n",
- rc);
+ true, usb_current);
+ if (rc < 0)
return rc;
- }
+ rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER,
+ false, 0);
+ if (rc < 0)
+ return rc;
}
} else {
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
@@ -2161,12 +2244,12 @@
return rc;
}
- }
+ rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
+ if (rc < 0) {
+ pr_err("Couldn't remove SW_ICL_MAX vote rc=%d\n", rc);
+ return rc;
+ }
- rc = vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
- if (rc < 0) {
- pr_err("Couldn't unvote ICL DEFAULT_100MA_VOTER rc=%d\n", rc);
- return rc;
}
return 0;
@@ -2286,14 +2369,14 @@
return rc;
}
-static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active)
+int smblib_set_prop_pd_active(struct smb_charger *chg,
+ const union power_supply_propval *val)
{
int rc = 0;
- chg->pd_active = pd_active;
+ chg->pd_active = val->intval;
if (chg->pd_active) {
- vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
/*
@@ -2301,24 +2384,24 @@
* It is guaranteed that pd_active is set prior to
* pd_current_max
*/
- rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
- if (rc < 0)
- smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
- rc);
-
- /* clear USB ICL vote for DCP_VOTER */
- rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
- rc);
-
- /* remove USB_PSY_VOTER */
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
+ vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
} else {
- vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
+ vote(chg->usb_icl_votable, PD_VOTER, false, 0);
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+
+ /* PD hard resets failed, rerun apsd */
+ if (chg->ok_to_pd) {
+ chg->ok_to_pd = false;
+ rc = smblib_configure_hvdcp_apsd(chg, true);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't enable APSD rc=%d\n", rc);
+ return rc;
+ }
+ smblib_rerun_apsd_if_required(chg);
+ }
}
smblib_update_usb_type(chg);
@@ -2326,15 +2409,6 @@
return rc;
}
-int smblib_set_prop_pd_active(struct smb_charger *chg,
- const union power_supply_propval *val)
-{
- if (!get_effective_result(chg->pd_allowed_votable))
- return -EINVAL;
-
- return __smblib_set_prop_pd_active(chg, val->intval);
-}
-
int smblib_set_prop_ship_mode(struct smb_charger *chg,
const union power_supply_propval *val)
{
@@ -2645,11 +2719,7 @@
static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
{
- if (vbus_rising) {
- /* use the typec flag even though its not typec */
- chg->typec_present = 1;
- } else {
- chg->typec_present = 0;
+ if (!vbus_rising) {
smblib_update_usb_type(chg);
smblib_notify_device_mode(chg, false);
smblib_uusb_removal(chg);
@@ -2737,6 +2807,8 @@
rc = smblib_request_dpdm(chg, false);
if (rc < 0)
smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
+
+ smblib_update_usb_type(chg);
}
if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
@@ -2752,12 +2824,10 @@
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- mutex_lock(&chg->lock);
if (chg->pd_hard_reset)
smblib_usb_plugin_hard_reset_locked(chg);
else
smblib_usb_plugin_locked(chg);
- mutex_unlock(&chg->lock);
return IRQ_HANDLED;
}
@@ -2846,8 +2916,6 @@
if (!rising)
return;
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
-
if (chg->mode == PARALLEL_MASTER)
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
@@ -2860,33 +2928,18 @@
static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
bool rising, bool qc_charger)
{
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
-
- /* Hold off PD only until hvdcp 2.0 detection timeout */
if (rising) {
- /* enable HDC and ICL irq for QC2/3 charger */
- if (qc_charger)
+ if (qc_charger) {
+ /* enable HDC and ICL irq for QC2/3 charger */
vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
- else
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- false, 0);
-
- /*
- * HVDCP detection timeout done
- * If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
- */
- if (!qc_charger && (apsd_result->bit & DCP_CHARGER_BIT))
- /* enforce DCP ICL if specified */
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ HVDCP_CURRENT_UA);
+ } else {
+ /* A plain DCP, enforce DCP ICL if specified */
vote(chg->usb_icl_votable, DCP_VOTER,
chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
-
- /*
- * if pd is not allowed, then set pd_active = false right here,
- * so that it starts the hvdcp engine
- */
- if (!get_effective_result(chg->pd_allowed_votable))
- __smblib_set_prop_pd_active(chg, 0);
+ }
}
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__,
@@ -2901,6 +2954,69 @@
rising ? "rising" : "falling");
}
+static void update_sw_icl_max(struct smb_charger *chg, int pst)
+{
+ int typec_mode;
+ int rp_ua;
+
+ /* while PD is active it should have complete ICL control */
+ if (chg->pd_active)
+ return;
+
+ /*
+ * HVDCP 2/3, handled separately
+ * For UNKNOWN(input not present) return without updating ICL
+ */
+ if (pst == POWER_SUPPLY_TYPE_USB_HVDCP
+ || pst == POWER_SUPPLY_TYPE_USB_HVDCP_3
+ || pst == POWER_SUPPLY_TYPE_UNKNOWN)
+ return;
+
+ /* TypeC rp med or high, use rp value */
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (typec_rp_med_high(chg, typec_mode)) {
+ rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua);
+ return;
+ }
+
+ /* rp-std or legacy, USB BC 1.2 */
+ switch (pst) {
+ case POWER_SUPPLY_TYPE_USB:
+ /*
+ * USB_PSY will vote to increase the current to 500/900mA once
+ * enumeration is done.
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, true,
+ SDP_100_MA);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ CDP_CURRENT_UA);
+ break;
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ DCP_CURRENT_UA);
+ break;
+ case POWER_SUPPLY_TYPE_USB_FLOAT:
+ /*
+ * limit ICL to 100mA, the USB driver will enumerate to check
+ * if this is a SDP and appropriately set the current
+ */
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ SDP_100_MA);
+ break;
+ default:
+ smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+ SDP_CURRENT_UA);
+ break;
+ }
+}
+
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
const struct apsd_result *apsd_result;
@@ -2910,25 +3026,18 @@
apsd_result = smblib_update_usb_type(chg);
+ update_sw_icl_max(chg, apsd_result->pst);
+
switch (apsd_result->bit) {
case SDP_CHARGER_BIT:
case CDP_CHARGER_BIT:
case FLOAT_CHARGER_BIT:
- /* if not DCP then no hvdcp timeout happens. Enable pd here */
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- false, 0);
if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
|| chg->use_extcon)
smblib_notify_device_mode(chg, true);
break;
case OCP_CHARGER_BIT:
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
- /* if not DCP then no hvdcp timeout happens, Enable pd here. */
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
- false, 0);
- break;
case DCP_CHARGER_BIT:
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
break;
default:
break;
@@ -2945,6 +3054,14 @@
int rc = 0;
u8 stat;
+ /*
+ * Prepared to run PD or PD is active. At this moment, APSD is disabled,
+ * but there still can be irq on apsd_done from previously unfinished
+ * APSD run, skip it.
+ */
+ if (chg->ok_to_pd)
+ return IRQ_HANDLED;
+
rc = smblib_read(chg, APSD_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
@@ -2960,7 +3077,7 @@
* charger-mis-detection.
*/
chg->uusb_apsd_rerun_done = true;
- smblib_rerun_apsd(chg);
+ smblib_rerun_apsd_if_required(chg);
return IRQ_HANDLED;
}
@@ -2999,32 +3116,70 @@
static void typec_sink_insertion(struct smb_charger *chg)
{
- /* when a sink is inserted we should not wait on hvdcp timeout to
- * enable pd
- */
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
+ vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
+
if (chg->use_extcon) {
smblib_notify_usb_host(chg, true);
chg->otg_present = true;
}
+
+ if (!chg->pr_swap_in_progress)
+ chg->ok_to_pd = (!(*chg->pd_disabled) || chg->early_usb_attach)
+ && !chg->pd_not_supported;
+}
+
+static void typec_src_insertion(struct smb_charger *chg)
+{
+ int rc = 0;
+ u8 stat;
+
+ if (chg->pr_swap_in_progress)
+ return;
+
+ rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+ rc);
+ return;
+ }
+
+ chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
+ chg->ok_to_pd = (!(chg->typec_legacy || *chg->pd_disabled)
+ || chg->early_usb_attach) && !chg->pd_not_supported;
+ if (!chg->ok_to_pd) {
+ rc = smblib_configure_hvdcp_apsd(chg, true);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't enable APSD rc=%d\n", rc);
+ return;
+ }
+ smblib_rerun_apsd_if_required(chg);
+ }
}
static void typec_sink_removal(struct smb_charger *chg)
{
- smblib_set_charge_param(chg, &chg->param.freq_switcher,
- chg->chg_freq.freq_above_otg_threshold);
- chg->boost_current_ua = 0;
+ vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
+
+ if (chg->use_extcon) {
+ if (chg->otg_present)
+ smblib_notify_usb_host(chg, false);
+ chg->otg_present = false;
+ }
}
-static void smblib_handle_typec_removal(struct smb_charger *chg)
+static void typec_src_removal(struct smb_charger *chg)
{
int rc;
struct smb_irq_data *data;
struct storm_watch *wdata;
- rc = smblib_request_dpdm(chg, false);
+ /* disable apsd */
+ rc = smblib_configure_hvdcp_apsd(chg, false);
if (rc < 0)
- smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
+ smblib_err(chg, "Couldn't disable APSD rc=%d\n", rc);
+
+ smblib_update_usb_type(chg);
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
@@ -3040,7 +3195,7 @@
cancel_delayed_work_sync(&chg->pl_enable_work);
/* reset input current limit voters */
- vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, true, USBIN_100MA);
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
vote(chg->usb_icl_votable, PD_VOTER, false, 0);
vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
@@ -3048,12 +3203,6 @@
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
- vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, false, 0);
-
- /* reset power delivery voters */
- vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
- vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, true, 0);
/* reset usb irq voters */
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
@@ -3066,14 +3215,10 @@
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
- chg->vconn_attempts = 0;
- chg->otg_attempts = 0;
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
chg->voltage_min_uv = MICRO_5V;
chg->voltage_max_uv = MICRO_5V;
- chg->pd_active = 0;
- chg->pd_hard_reset = 0;
/* write back the default FLOAT charger configuration */
rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
@@ -3082,12 +3227,6 @@
smblib_err(chg, "Couldn't write float charger options rc=%d\n",
rc);
- /* reset back to 103mS tCC debounce */
- rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
- REDUCE_TCCDEBOUNCE_TO_2MS_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc);
-
/* reconfigure allowed voltage for HVDCP */
rc = smblib_set_adapter_allowance(chg,
USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
@@ -3095,164 +3234,35 @@
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
rc);
- /* enable DRP */
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+ if (chg->use_extcon)
+ smblib_notify_device_mode(chg, false);
- /* HW controlled CC_OUT */
- rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
- TYPEC_CCOUT_SRC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
-
-
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
- VCONN_EN_SRC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set TYPE_C_VCONN_CONTROL_REG rc=%d\n",
- rc);
-
- /* clear exit sink based on cc */
- rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
- EXIT_SNK_BASED_ON_CC_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't clear exit_sink_based_on_cc rc=%d\n",
- rc);
-
- typec_sink_removal(chg);
- smblib_update_usb_type(chg);
-
- if (chg->use_extcon) {
- if (chg->otg_present)
- smblib_notify_usb_host(chg, false);
- else
- smblib_notify_device_mode(chg, false);
- }
- chg->otg_present = false;
-}
-
-static void smblib_handle_typec_insertion(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
-
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return;
- }
-
- if (stat & SNK_SRC_MODE_BIT) {
- typec_sink_insertion(chg);
- } else {
- rc = smblib_request_dpdm(chg, true);
- if (rc < 0)
- smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
- typec_sink_removal(chg);
- }
+ chg->typec_legacy = false;
}
static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
{
- int rp_ua;
const struct apsd_result *apsd = smblib_get_apsd_result(chg);
- if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP)
- && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT))
- return;
-
- /*
- * if APSD indicates FLOAT and the USB stack had detected SDP,
- * do not respond to Rp changes as we do not confirm that its
- * a legacy cable
- */
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
- return;
/*
* We want the ICL vote @ 100mA for a FLOAT charger
* until the detection by the USB stack is complete.
* Ignore the Rp changes unless there is a
- * pre-existing valid vote.
+ * pre-existing valid vote or FLOAT is configured for
+ * SDP current.
*/
- if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT &&
- (get_client_vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER)
- <= USBIN_100MA))
+ if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER)
+ <= USBIN_100MA
+ || (chg->float_cfg & FLOAT_OPTIONS_MASK)
+ == FORCE_FLOAT_SDP_CFG_BIT)
return;
+ }
- /*
- * handle Rp change for DCP/FLOAT/OCP.
- * Update the current only if the Rp is different from
- * the last Rp value.
- */
+ update_sw_icl_max(chg, apsd->pst);
+
smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n",
chg->typec_mode, typec_mode);
-
- rp_ua = get_rp_based_dcp_current(chg, typec_mode);
- vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, true, rp_ua);
-}
-
-static void smblib_handle_typec_cc_state_change(struct smb_charger *chg)
-{
- u8 stat;
- int typec_mode, rc;
-
- if (chg->pr_swap_in_progress)
- return;
-
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (chg->typec_present && (typec_mode != chg->typec_mode))
- smblib_handle_rp_change(chg, typec_mode);
-
- chg->typec_mode = typec_mode;
-
- if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
- chg->typec_present = true;
- smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
- smblib_typec_mode_name[chg->typec_mode]);
- smblib_handle_typec_insertion(chg);
- } else if (chg->typec_present &&
- chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
- chg->typec_present = false;
- smblib_dbg(chg, PR_MISC, "TypeC removal\n");
- smblib_handle_typec_removal(chg);
- }
-
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return;
- }
- /* suspend usb if sink */
- if ((stat & SNK_SRC_MODE_BIT) && chg->typec_present)
- vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
- else
- vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
-
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
- smblib_typec_mode_name[chg->typec_mode]);
-}
-
-static void smblib_usb_typec_change(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- smblib_handle_typec_cc_state_change(chg);
-
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return;
- }
-
- if (stat & TYPEC_VBUS_ERROR_STATUS_BIT)
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
-
- power_supply_changed(chg->usb_psy);
}
irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data)
@@ -3276,17 +3286,81 @@
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
+ int typec_mode;
- if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
- || chg->pr_swap_in_progress) {
+ if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
smblib_dbg(chg, PR_INTERRUPT,
- "Ignoring since pr_swap_in_progress\n");
+ "Ignoring for micro USB\n");
return IRQ_HANDLED;
}
- mutex_lock(&chg->lock);
- smblib_usb_typec_change(chg);
- mutex_unlock(&chg->lock);
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (chg->sink_src_mode != UNATTACHED_MODE
+ && (typec_mode != chg->typec_mode))
+ smblib_handle_rp_change(chg, typec_mode);
+ chg->typec_mode = typec_mode;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
+ smblib_typec_mode_name[chg->typec_mode]);
+
+ power_supply_changed(chg->usb_psy);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ u8 stat;
+ int rc;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+ rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+ rc);
+ return IRQ_HANDLED;
+ }
+
+ if (stat & TYPEC_ATTACH_DETACH_STATE_BIT) {
+ rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
+ rc);
+ return IRQ_HANDLED;
+ }
+
+ if (stat & SNK_SRC_MODE_BIT) {
+ chg->sink_src_mode = SRC_MODE;
+ typec_sink_insertion(chg);
+ } else {
+ chg->sink_src_mode = SINK_MODE;
+ typec_src_insertion(chg);
+ }
+
+ } else {
+ switch (chg->sink_src_mode) {
+ case SRC_MODE:
+ typec_sink_removal(chg);
+ break;
+ case SINK_MODE:
+ typec_src_removal(chg);
+ break;
+ default:
+ break;
+ }
+
+ if (!chg->pr_swap_in_progress) {
+ chg->ok_to_pd = false;
+ chg->sink_src_mode = UNATTACHED_MODE;
+ chg->early_usb_attach = false;
+ }
+ }
+
+ power_supply_changed(chg->usb_psy);
+
return IRQ_HANDLED;
}
@@ -3421,19 +3495,55 @@
const union power_supply_propval *val)
{
int rc;
+ u8 stat = 0, orientation;
chg->pr_swap_in_progress = val->intval;
- /*
- * call the cc changed irq to handle real removals while
- * PR_SWAP was in progress
- */
- smblib_usb_typec_change(chg);
rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
REDUCE_TCCDEBOUNCE_TO_2MS_BIT,
val->intval ? REDUCE_TCCDEBOUNCE_TO_2MS_BIT : 0);
if (rc < 0)
smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc);
+
+ rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
+ BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT,
+ val->intval ? BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't set exit state cfg rc=%d\n", rc);
+
+ if (chg->pr_swap_in_progress) {
+ rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+ rc);
+ }
+
+ orientation =
+ stat & CC_ORIENTATION_BIT ? TYPEC_CCOUT_VALUE_BIT : 0;
+ rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
+ TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
+ | TYPEC_CCOUT_VALUE_BIT,
+ TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
+ | orientation);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+ rc);
+ }
+ } else {
+ rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
+ TYPEC_CCOUT_SRC_BIT, 0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+ rc);
+ }
+
+ /* enable DRP */
+ rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+ TYPEC_POWER_ROLE_CMD_MASK, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+ }
+
return 0;
}
@@ -3456,6 +3566,9 @@
otg = !!(stat & U_USB_GROUND_NOVBUS_BIT);
if (chg->otg_present != otg)
smblib_notify_usb_host(chg, otg);
+ else
+ goto out;
+
chg->otg_present = otg;
if (!otg)
chg->boost_current_ua = 0;
@@ -3676,23 +3789,6 @@
return rc;
}
- chg->pd_disallowed_votable_indirect
- = create_votable("PD_DISALLOWED_INDIRECT", VOTE_SET_ANY,
- smblib_pd_disallowed_votable_indirect_callback, chg);
- if (IS_ERR(chg->pd_disallowed_votable_indirect)) {
- rc = PTR_ERR(chg->pd_disallowed_votable_indirect);
- chg->pd_disallowed_votable_indirect = NULL;
- return rc;
- }
-
- chg->pd_allowed_votable = create_votable("PD_ALLOWED",
- VOTE_SET_ANY, NULL, NULL);
- if (IS_ERR(chg->pd_allowed_votable)) {
- rc = PTR_ERR(chg->pd_allowed_votable);
- chg->pd_allowed_votable = NULL;
- return rc;
- }
-
chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY,
smblib_awake_vote_callback,
chg);
@@ -3730,10 +3826,6 @@
destroy_votable(chg->dc_suspend_votable);
if (chg->usb_icl_votable)
destroy_votable(chg->usb_icl_votable);
- if (chg->pd_disallowed_votable_indirect)
- destroy_votable(chg->pd_disallowed_votable_indirect);
- if (chg->pd_allowed_votable)
- destroy_votable(chg->pd_allowed_votable);
if (chg->awake_votable)
destroy_votable(chg->awake_votable);
if (chg->chg_disable_votable)
@@ -3745,7 +3837,6 @@
int rc = 0;
mutex_init(&chg->lock);
- mutex_init(&chg->otg_oc_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
INIT_WORK(&chg->pl_update_work, pl_update_work);
INIT_WORK(&chg->jeita_update_work, jeita_update_work);
@@ -3758,6 +3849,7 @@
chg->fake_input_current_limited = -EINVAL;
chg->fake_batt_status = -EINVAL;
chg->jeita_configured = false;
+ chg->sink_src_mode = UNATTACHED_MODE;
switch (chg->mode) {
case PARALLEL_MASTER:
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 668ce5f..7c02468 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -56,6 +56,7 @@
#define CTM_VOTER "CTM_VOTER"
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
+#define SW_ICL_MAX_VOTER "SW_ICL_MAX_VOTER"
#define QNOVO_VOTER "QNOVO_VOTER"
#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER"
#define OTG_DELAY_VOTER "OTG_DELAY_VOTER"
@@ -65,8 +66,6 @@
#define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER"
#define WBC_VOTER "WBC_VOTER"
#define HW_LIMIT_VOTER "HW_LIMIT_VOTER"
-#define DYNAMIC_RP_VOTER "DYNAMIC_RP_VOTER"
-#define DEFAULT_100MA_VOTER "DEFAULT_100MA_VOTER"
#define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER"
#define BOOST_BACK_STORM_COUNT 3
@@ -80,6 +79,12 @@
NUM_MODES,
};
+enum sink_src_mode {
+ SINK_MODE,
+ SRC_MODE,
+ UNATTACHED_MODE,
+};
+
enum {
BOOST_BACK_WA = BIT(0),
};
@@ -228,6 +233,7 @@
struct smb_chg_param fcc;
struct smb_chg_param fv;
struct smb_chg_param usb_icl;
+ struct smb_chg_param icl_max_stat;
struct smb_chg_param icl_stat;
struct smb_chg_param otg_cl;
struct smb_chg_param jeita_cc_comp_hot;
@@ -259,16 +265,18 @@
struct smb_params param;
struct smb_iio iio;
int *debug_mask;
+ int *pd_disabled;
enum smb_mode mode;
struct smb_chg_freq chg_freq;
int smb_version;
int otg_delay_ms;
int *weak_chg_icl_ua;
+ struct qpnp_vadc_chip *vadc_dev;
+ bool pd_not_supported;
/* locks */
struct mutex lock;
struct mutex ps_change_lock;
- struct mutex otg_oc_lock;
/* power supplies */
struct power_supply *batt_psy;
@@ -295,8 +303,6 @@
struct votable *fcc_votable;
struct votable *fv_votable;
struct votable *usb_icl_votable;
- struct votable *pd_disallowed_votable_indirect;
- struct votable *pd_allowed_votable;
struct votable *awake_votable;
struct votable *pl_disable_votable;
struct votable *chg_disable_votable;
@@ -314,10 +320,17 @@
struct delayed_work uusb_otg_work;
struct delayed_work bb_removal_work;
- /* cached status */
+ /* pd */
int voltage_min_uv;
int voltage_max_uv;
int pd_active;
+ bool pd_hard_reset;
+ bool pr_swap_in_progress;
+ bool early_usb_attach;
+ bool ok_to_pd;
+ bool typec_legacy;
+
+ /* cached status */
bool system_suspend_supported;
int boost_threshold_ua;
int system_temp_level;
@@ -333,15 +346,10 @@
int connector_type;
bool otg_en;
bool suspend_input_on_debug_batt;
- int otg_attempts;
- int vconn_attempts;
int default_icl_ua;
int otg_cl_ua;
bool uusb_apsd_rerun_done;
- bool pd_hard_reset;
- bool typec_present;
int fake_input_current_limited;
- bool pr_swap_in_progress;
int typec_mode;
int usb_icl_change_irq_enabled;
u32 jeita_status;
@@ -351,6 +359,7 @@
int hw_max_icl_ua;
int auto_recharge_soc;
bool jeita_configured;
+ enum sink_src_mode sink_src_mode;
/* workaround flag */
u32 wa_flags;
@@ -419,6 +428,7 @@
irqreturn_t usb_source_change_irq_handler(int irq, void *data);
irqreturn_t icl_change_irq_handler(int irq, void *data);
irqreturn_t typec_state_change_irq_handler(int irq, void *data);
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data);
irqreturn_t dc_plugin_irq_handler(int irq, void *data);
irqreturn_t high_duty_cycle_irq_handler(int irq, void *data);
irqreturn_t switcher_power_ok_irq_handler(int irq, void *data);
@@ -453,6 +463,8 @@
union power_supply_propval *val);
int smblib_get_prop_batt_charge_counter(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_batt_cycle_count(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_set_prop_input_suspend(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_batt_capacity(struct smb_charger *chg,
@@ -484,8 +496,6 @@
union power_supply_propval *val);
int smblib_get_prop_typec_power_role(struct smb_charger *chg,
union power_supply_propval *val);
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
- union power_supply_propval *val);
int smblib_get_prop_input_current_settled(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
@@ -528,6 +538,9 @@
const union power_supply_propval *val);
int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override);
int smblib_configure_wdog(struct smb_charger *chg, bool enable);
+int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val);
+int smblib_configure_hvdcp_apsd(struct smb_charger *chg, bool enable);
+int smblib_icl_override(struct smb_charger *chg, bool override);
int smblib_init(struct smb_charger *chg);
int smblib_deinit(struct smb_charger *chg);
diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h
index bb28423..79a8bd0 100644
--- a/drivers/power/supply/qcom/smb5-reg.h
+++ b/drivers/power/supply/qcom/smb5-reg.h
@@ -105,6 +105,8 @@
/********************************
* DCDC Peripheral Registers *
********************************/
+#define ICL_MAX_STATUS_REG (DCDC_BASE + 0x06)
+
#define AICL_ICL_STATUS_REG (DCDC_BASE + 0x08)
#define AICL_STATUS_REG (DCDC_BASE + 0x0A)
@@ -194,6 +196,7 @@
#define FORCE_12V_BIT BIT(5)
#define FORCE_9V_BIT BIT(4)
#define FORCE_5V_BIT BIT(3)
+#define IDLE_BIT BIT(2)
#define SINGLE_DECREMENT_BIT BIT(1)
#define SINGLE_INCREMENT_BIT BIT(0)
@@ -214,6 +217,7 @@
#define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6)
#define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5)
#define BC1P2_SRC_DETECT_BIT BIT(3)
+#define HVDCP_EN_BIT BIT(2)
#define USBIN_OPTIONS_2_CFG_REG (USBIN_BASE + 0x63)
#define FLOAT_OPTIONS_MASK GENMASK(2, 0)
@@ -261,6 +265,9 @@
#define SRC_RA_OPEN_BIT BIT(1)
#define AUDIO_ACCESS_RA_RA_BIT BIT(0)
+#define TYPE_C_STATE_MACHINE_STATUS_REG (TYPEC_BASE + 0x09)
+#define TYPEC_ATTACH_DETACH_STATE_BIT BIT(5)
+
#define TYPE_C_MISC_STATUS_REG (TYPEC_BASE + 0x0B)
#define SNK_SRC_MODE_BIT BIT(6)
#define TYPEC_VBUS_ERROR_STATUS_BIT BIT(4)
@@ -268,6 +275,7 @@
#define CC_ATTACHED_BIT BIT(0)
#define LEGACY_CABLE_STATUS_REG (TYPEC_BASE + 0x0D)
+#define TYPEC_LEGACY_CABLE_STATUS_BIT BIT(1)
#define TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT BIT(0)
#define TYPEC_U_USB_STATUS_REG (TYPEC_BASE + 0x0F)
@@ -275,19 +283,23 @@
#define U_USB_GROUND_BIT BIT(4)
#define TYPE_C_MODE_CFG_REG (TYPEC_BASE + 0x44)
-#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0)
+#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 1)
#define EN_SRC_ONLY_BIT BIT(2)
#define EN_SNK_ONLY_BIT BIT(1)
#define TYPEC_DISABLE_CMD_BIT BIT(0)
#define TYPE_C_VCONN_CONTROL_REG (TYPEC_BASE + 0x46)
+#define VCONN_EN_ORIENTATION_BIT BIT(2)
#define VCONN_EN_VALUE_BIT BIT(1)
#define VCONN_EN_SRC_BIT BIT(0)
#define TYPE_C_CCOUT_CONTROL_REG (TYPEC_BASE + 0x48)
+#define TYPEC_CCOUT_BUFFER_EN_BIT BIT(2)
+#define TYPEC_CCOUT_VALUE_BIT BIT(1)
#define TYPEC_CCOUT_SRC_BIT BIT(0)
#define TYPE_C_EXIT_STATE_CFG_REG (TYPEC_BASE + 0x50)
+#define BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3)
#define EXIT_SNK_BASED_ON_CC_BIT BIT(0)
#define TYPE_C_INTERRUPT_EN_CFG_1_REG (TYPEC_BASE + 0x5E)
@@ -315,7 +327,7 @@
#define TYPEC_U_USB_CFG_REG (TYPEC_BASE + 0x70)
#define EN_MICRO_USB_MODE_BIT BIT(0)
-#define TYPEC_MICRO_USB_MODE_REG (TYPEC_BASE + 0x70)
+#define TYPEC_MICRO_USB_MODE_REG (TYPEC_BASE + 0x73)
#define MICRO_USB_MODE_ONLY_BIT BIT(0)
/********************************
* MISC Peripheral Registers *
diff --git a/drivers/powercap/powercap_sys.c b/drivers/powercap/powercap_sys.c
index 14bde0d..5b10b50 100644
--- a/drivers/powercap/powercap_sys.c
+++ b/drivers/powercap/powercap_sys.c
@@ -538,6 +538,7 @@
power_zone->id = result;
idr_init(&power_zone->idr);
+ result = -ENOMEM;
power_zone->name = kstrdup(name, GFP_KERNEL);
if (!power_zone->name)
goto err_name_alloc;
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 86280b7..2aa5b37 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -97,30 +97,26 @@
/* posix clock implementation */
-static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp)
+static int ptp_clock_getres(struct posix_clock *pc, struct timespec64 *tp)
{
tp->tv_sec = 0;
tp->tv_nsec = 1;
return 0;
}
-static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp)
+static int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp)
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
- struct timespec64 ts = timespec_to_timespec64(*tp);
- return ptp->info->settime64(ptp->info, &ts);
+ return ptp->info->settime64(ptp->info, tp);
}
-static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp)
+static int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp)
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
- struct timespec64 ts;
int err;
- err = ptp->info->gettime64(ptp->info, &ts);
- if (!err)
- *tp = timespec64_to_timespec(ts);
+ err = ptp->info->gettime64(ptp->info, tp);
return err;
}
@@ -133,7 +129,7 @@
ops = ptp->info;
if (tx->modes & ADJ_SETOFFSET) {
- struct timespec ts;
+ struct timespec64 ts;
ktime_t kt;
s64 delta;
@@ -146,7 +142,7 @@
if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
- kt = timespec_to_ktime(ts);
+ kt = timespec64_to_ktime(ts);
delta = ktime_to_ns(kt);
err = ops->adjtime(ops, delta);
} else if (tx->modes & ADJ_FREQUENCY) {
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
index 31f5204..d24bef1 100644
--- a/drivers/pwm/pwm-qti-lpg.c
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -19,10 +19,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
+#include <linux/qpnp/qpnp-pbs.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -105,6 +107,34 @@
#define LPG_LUT_VALUE_MSB_MASK BIT(0)
#define LPG_LUT_COUNT_MAX 47
+/* LPG config settings in SDAM */
+#define SDAM_REG_PBS_SEQ_EN 0x42
+#define PBS_SW_TRG_BIT BIT(0)
+
+#define SDAM_REG_RAMP_STEP_DURATION 0x47
+
+#define SDAM_LUT_EN_OFFSET 0x0
+#define SDAM_PATTERN_CONFIG_OFFSET 0x1
+#define SDAM_END_INDEX_OFFSET 0x3
+#define SDAM_START_INDEX_OFFSET 0x4
+#define SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET 0x6
+
+/* SDAM_REG_LUT_EN */
+#define SDAM_LUT_EN_BIT BIT(0)
+
+/* SDAM_REG_PATTERN_CONFIG */
+#define SDAM_PATTERN_LOOP_ENABLE BIT(3)
+#define SDAM_PATTERN_RAMP_TOGGLE BIT(2)
+#define SDAM_PATTERN_EN_PAUSE_END BIT(1)
+#define SDAM_PATTERN_EN_PAUSE_START BIT(0)
+
+/* SDAM_REG_PAUSE_MULTIPLIER */
+#define SDAM_PAUSE_START_SHIFT 4
+#define SDAM_PAUSE_START_MASK GENMASK(7, 4)
+#define SDAM_PAUSE_END_MASK GENMASK(3, 0)
+
+#define SDAM_LUT_COUNT_MAX 64
+
enum lpg_src {
LUT_PATTERN = 0,
PWM_VALUE,
@@ -151,6 +181,7 @@
u32 lpg_idx;
u32 reg_base;
u32 max_pattern_length;
+ u32 lpg_sdam_base;
u8 src_sel;
u8 subtype;
bool lut_written;
@@ -165,7 +196,11 @@
struct qpnp_lpg_channel *lpgs;
struct qpnp_lpg_lut *lut;
struct mutex bus_lock;
+ struct nvmem_device *sdam_nvmem;
+ struct device_node *pbs_dev_node;
u32 num_lpgs;
+ unsigned long pbs_en_bitmap;
+ bool use_sdam;
};
static int qpnp_lpg_read(struct qpnp_lpg_channel *lpg, u16 addr, u8 *val)
@@ -192,7 +227,7 @@
mutex_lock(&lpg->chip->bus_lock);
rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
if (rc < 0)
- dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+ dev_err(lpg->chip->dev, "Write addr 0x%x with value 0x%x failed, rc=%d\n",
lpg->reg_base + addr, val, rc);
mutex_unlock(&lpg->chip->bus_lock);
@@ -245,6 +280,90 @@
return rc;
}
+static int qpnp_sdam_write(struct qpnp_lpg_chip *chip, u16 addr, u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+ rc = nvmem_device_write(chip->sdam_nvmem, addr, 1, &val);
+ if (rc < 0)
+ dev_err(chip->dev, "write SDAM add 0x%x failed, rc=%d\n",
+ addr, rc);
+
+ mutex_unlock(&chip->bus_lock);
+
+ return rc > 0 ? 0 : rc;
+}
+
+static int qpnp_lpg_sdam_write(struct qpnp_lpg_channel *lpg, u16 addr, u8 val)
+{
+ struct qpnp_lpg_chip *chip = lpg->chip;
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+ rc = nvmem_device_write(chip->sdam_nvmem,
+ lpg->lpg_sdam_base + addr, 1, &val);
+ if (rc < 0)
+ dev_err(chip->dev, "write SDAM add 0x%x failed, rc=%d\n",
+ lpg->lpg_sdam_base + addr, rc);
+
+ mutex_unlock(&chip->bus_lock);
+
+ return rc > 0 ? 0 : rc;
+}
+
+static int qpnp_lpg_sdam_masked_write(struct qpnp_lpg_channel *lpg,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+ u8 tmp;
+ struct qpnp_lpg_chip *chip = lpg->chip;
+
+ mutex_lock(&chip->bus_lock);
+
+ rc = nvmem_device_read(chip->sdam_nvmem,
+ lpg->lpg_sdam_base + addr, 1, &tmp);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read SDAM addr %d failed, rc=%d\n",
+ lpg->lpg_sdam_base + addr, rc);
+ goto unlock;
+ }
+
+ tmp = tmp & ~mask;
+ tmp |= val & mask;
+ rc = nvmem_device_write(chip->sdam_nvmem,
+ lpg->lpg_sdam_base + addr, 1, &tmp);
+ if (rc < 0)
+ dev_err(chip->dev, "write SDAM addr %d failed, rc=%d\n",
+ lpg->lpg_sdam_base + addr, rc);
+
+unlock:
+ mutex_unlock(&chip->bus_lock);
+
+ return rc > 0 ? 0 : rc;
+}
+
+static int qpnp_lut_sdam_write(struct qpnp_lpg_lut *lut,
+ u16 addr, u8 *val, size_t length)
+{
+ struct qpnp_lpg_chip *chip = lut->chip;
+ int rc;
+
+ if (addr >= SDAM_LUT_COUNT_MAX)
+ return -EINVAL;
+
+ mutex_lock(&chip->bus_lock);
+ rc = nvmem_device_write(chip->sdam_nvmem,
+ lut->reg_base + addr, length, val);
+ if (rc < 0)
+ dev_err(chip->dev, "write SDAM addr %d failed, rc=%d\n",
+ lut->reg_base + addr, rc);
+
+ mutex_unlock(&chip->bus_lock);
+
+ return rc > 0 ? 0 : rc;
+}
+
static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
struct pwm_device *pwm) {
@@ -365,14 +484,111 @@
return rc;
}
-static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
+static int qpnp_lpg_set_sdam_lut_pattern(struct qpnp_lpg_channel *lpg,
unsigned int *pattern, unsigned int length)
{
struct qpnp_lpg_lut *lut = lpg->chip->lut;
int i, rc = 0;
- u16 full_duty_value, pwm_values[LPG_LUT_COUNT_MAX + 1] = {0};
+ u8 val[SDAM_LUT_COUNT_MAX + 1], addr;
+
+ if (length > lpg->max_pattern_length) {
+ dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
+ length, lpg->max_pattern_length);
+ return -EINVAL;
+ }
+
+ /* Program LUT pattern */
+ mutex_lock(&lut->lock);
+ addr = lpg->ramp_config.lo_idx;
+ for (i = 0; i < length; i++)
+ val[i] = pattern[i] * 255 / 100;
+
+ rc = qpnp_lut_sdam_write(lut, addr, val, length);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write pattern in SDAM failed, rc=%d",
+ rc);
+ goto unlock;
+ }
+
+ lpg->ramp_config.pattern_length = length;
+unlock:
+ mutex_unlock(&lut->lock);
+
+ return rc;
+}
+
+static int qpnp_lpg_set_sdam_ramp_config(struct qpnp_lpg_channel *lpg)
+{
+ struct lpg_ramp_config *ramp = &lpg->ramp_config;
+ u8 addr, mask, val;
+ int rc = 0;
+
+ /* clear PBS scatchpad register */
+ val = 0;
+ rc = qpnp_lpg_sdam_write(lpg,
+ SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Set ramp step duration, one WAIT_TICK is 7.8ms */
+ val = (ramp->step_ms * 1000 / 7800) & 0xff;
+ if (val > 0)
+ val--;
+ addr = SDAM_REG_RAMP_STEP_DURATION;
+ rc = qpnp_sdam_write(lpg->chip, addr, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write SDAM_REG_RAMP_STEP_DURATION failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Set hi_idx and lo_idx */
+ rc = qpnp_lpg_sdam_write(lpg, SDAM_END_INDEX_OFFSET, ramp->hi_idx);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write SDAM_REG_END_INDEX failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = qpnp_lpg_sdam_write(lpg, SDAM_START_INDEX_OFFSET,
+ ramp->lo_idx);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write SDAM_REG_START_INDEX failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Set LPG_PATTERN_CONFIG */
+ addr = SDAM_PATTERN_CONFIG_OFFSET;
+ mask = SDAM_PATTERN_LOOP_ENABLE;
+ val = 0;
+ if (ramp->pattern_repeat)
+ val |= SDAM_PATTERN_LOOP_ENABLE;
+
+ rc = qpnp_lpg_sdam_masked_write(lpg, addr, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write SDAM_REG_PATTERN_CONFIG failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
+ unsigned int *pattern, unsigned int length)
+{
+ struct qpnp_lpg_lut *lut = lpg->chip->lut;
+ u16 full_duty_value, pwm_values[SDAM_LUT_COUNT_MAX + 1] = {0};
+ int i, rc = 0;
u8 lsb, msb, addr;
+ if (lpg->chip->use_sdam)
+ return qpnp_lpg_set_sdam_lut_pattern(lpg, pattern, length);
+
if (length > lpg->max_pattern_length) {
dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
length, lpg->max_pattern_length);
@@ -426,6 +642,9 @@
u8 lsb, msb, addr, mask, val;
int rc = 0;
+ if (lpg->chip->use_sdam)
+ return qpnp_lpg_set_sdam_ramp_config(lpg);
+
/* Set ramp step duration */
lsb = ramp->step_ms & 0xff;
msb = ramp->step_ms >> 8;
@@ -507,6 +726,8 @@
static void __qpnp_lpg_calc_pwm_period(int period_ns,
struct lpg_pwm_config *pwm_config)
{
+ struct qpnp_lpg_channel *lpg = container_of(pwm_config,
+ struct qpnp_lpg_channel, pwm_config);
struct lpg_pwm_config configs[NUM_PWM_SIZE];
int i, j, m, n;
int tmp1, tmp2;
@@ -522,7 +743,12 @@
*
* Searching the closest settings for the requested PWM period.
*/
- for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
+ if (lpg->chip->use_sdam)
+ /* SDAM pattern control can only use 9 bit resolution */
+ n = 1;
+ else
+ n = 0;
+ for (; n < ARRAY_SIZE(pwm_size); n++) {
pwm_clk_period_ns = period_ns >> pwm_size[n];
for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
@@ -654,6 +880,45 @@
return rc;
}
+static int qpnp_lpg_pbs_trigger_enable(struct qpnp_lpg_channel *lpg, bool en)
+{
+ struct qpnp_lpg_chip *chip = lpg->chip;
+ int rc = 0;
+
+ if (en) {
+ if (chip->pbs_en_bitmap == 0) {
+ rc = qpnp_sdam_write(chip, SDAM_REG_PBS_SEQ_EN,
+ PBS_SW_TRG_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Write SDAM_REG_PBS_SEQ_EN failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = qpnp_pbs_trigger_event(chip->pbs_dev_node,
+ PBS_SW_TRG_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Failed to trigger PBS, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ set_bit(lpg->lpg_idx, &chip->pbs_en_bitmap);
+ } else {
+ clear_bit(lpg->lpg_idx, &chip->pbs_en_bitmap);
+ if (chip->pbs_en_bitmap == 0) {
+ rc = qpnp_sdam_write(chip, SDAM_REG_PBS_SEQ_EN, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Write SDAM_REG_PBS_SEQ_EN failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
{
struct qpnp_lpg_chip *chip = lpg->chip;
@@ -665,7 +930,7 @@
LPG_EN_RAMP_GEN_MASK;
val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
- if (lpg->src_sel == LUT_PATTERN)
+ if (lpg->src_sel == LUT_PATTERN && !chip->use_sdam)
val |= 1 << LPG_EN_RAMP_GEN_SHIFT;
if (en)
@@ -678,6 +943,27 @@
return rc;
}
+ if (chip->use_sdam) {
+ if (lpg->src_sel == LUT_PATTERN && en) {
+ val = SDAM_LUT_EN_BIT;
+ en = true;
+ } else {
+ val = 0;
+ en = false;
+ }
+
+ rc = qpnp_lpg_sdam_write(lpg, SDAM_LUT_EN_OFFSET, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Write SDAM_REG_LUT_EN failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ qpnp_lpg_pbs_trigger_enable(lpg, en);
+
+ return rc;
+ }
+
if (lpg->src_sel == LUT_PATTERN && en) {
mutex_lock(&lut->lock);
val = 1 << lpg->lpg_idx;
@@ -697,6 +983,7 @@
struct qpnp_lpg_channel *lpg;
enum lpg_src src_sel;
int rc;
+ bool is_enabled;
lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
if (lpg == NULL) {
@@ -714,6 +1001,23 @@
if (src_sel == lpg->src_sel)
return 0;
+ is_enabled = pwm_is_enabled(pwm);
+ if (is_enabled) {
+ /*
+ * Disable the channel first then enable it later to make
+ * sure the output type is changed successfully. This is
+ * especially useful in SDAM use case to stop the PBS
+ * sequence when changing the PWM output type from
+ * MODULATED to FIXED.
+ */
+ rc = qpnp_lpg_pwm_src_enable(lpg, false);
+ if (rc < 0) {
+ dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+ return rc;
+ }
+ }
+
if (src_sel == LUT_PATTERN) {
/* program LUT if it's never been programmed */
if (!lpg->lut_written) {
@@ -738,7 +1042,14 @@
lpg->src_sel = src_sel;
- if (pwm_is_enabled(pwm)) {
+ if (is_enabled) {
+ rc = qpnp_lpg_set_pwm_config(lpg);
+ if (rc < 0) {
+ dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+ return rc;
+ }
+
rc = qpnp_lpg_pwm_src_enable(lpg, true);
if (rc < 0) {
dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
@@ -979,7 +1290,7 @@
struct qpnp_lpg_channel *lpg;
struct lpg_ramp_config *ramp;
int rc = 0, i;
- u32 base, length, lpg_chan_id, tmp;
+ u32 base, length, lpg_chan_id, tmp, max_count;
const __be32 *addr;
addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
@@ -1010,18 +1321,47 @@
}
}
- addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
- if (!addr) {
- pr_debug("NO LUT address assigned\n");
- return 0;
- }
-
chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL);
if (!chip->lut)
return -ENOMEM;
+ chip->sdam_nvmem = devm_nvmem_device_get(chip->dev, "ppg_sdam");
+ if (IS_ERR_OR_NULL(chip->sdam_nvmem)) {
+ if (PTR_ERR(chip->sdam_nvmem) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
+ if (!addr) {
+ pr_debug("NO LUT address assigned\n");
+ devm_kfree(chip->dev, chip->lut);
+ chip->lut = NULL;
+ return 0;
+ }
+
+ chip->lut->reg_base = be32_to_cpu(*addr);
+ max_count = LPG_LUT_COUNT_MAX;
+ } else {
+ chip->use_sdam = true;
+ chip->pbs_dev_node = of_parse_phandle(chip->dev->of_node,
+ "qcom,pbs-client", 0);
+ if (!chip->pbs_dev_node) {
+ dev_err(chip->dev, "Missing qcom,pbs-client property\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(chip->dev->of_node,
+ "qcom,lut-sdam-base",
+ &chip->lut->reg_base);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read qcom,lut-sdam-base failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ max_count = SDAM_LUT_COUNT_MAX;
+ }
+
chip->lut->chip = chip;
- chip->lut->reg_base = be32_to_cpu(*addr);
mutex_init(&chip->lut->lock);
rc = of_property_count_elems_of_size(chip->dev->of_node,
@@ -1033,13 +1373,13 @@
}
length = rc;
- if (length > LPG_LUT_COUNT_MAX) {
+ if (length > max_count) {
dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n",
- length, LPG_LUT_COUNT_MAX);
+ length, max_count);
return -EINVAL;
}
- chip->lut->pattern = devm_kcalloc(chip->dev, LPG_LUT_COUNT_MAX,
+ chip->lut->pattern = devm_kcalloc(chip->dev, max_count,
sizeof(*chip->lut->pattern), GFP_KERNEL);
if (!chip->lut->pattern)
return -ENOMEM;
@@ -1066,12 +1406,24 @@
return rc;
}
- if (lpg_chan_id > chip->num_lpgs) {
+ if (lpg_chan_id < 1 || lpg_chan_id > chip->num_lpgs) {
dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n",
lpg_chan_id, chip->num_lpgs);
return -EINVAL;
}
+ if (chip->use_sdam) {
+ rc = of_property_read_u32(child,
+ "qcom,lpg-sdam-base",
+ &tmp);
+ if (rc < 0) {
+ dev_err(chip->dev, "get qcom,lpg-sdam-base failed for lpg%d, rc=%d\n",
+ lpg_chan_id, rc);
+ return rc;
+ }
+ chip->lpgs[lpg_chan_id - 1].lpg_sdam_base = tmp;
+ }
+
/* lpg channel id is indexed from 1 in hardware */
lpg = &chip->lpgs[lpg_chan_id - 1];
ramp = &lpg->ramp_config;
@@ -1091,9 +1443,9 @@
return rc;
}
ramp->lo_idx = (u8)tmp;
- if (ramp->lo_idx >= LPG_LUT_COUNT_MAX) {
+ if (ramp->lo_idx >= max_count) {
dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n",
- LPG_LUT_COUNT_MAX);
+ max_count);
return -EINVAL;
}
@@ -1105,14 +1457,14 @@
}
ramp->hi_idx = (u8)tmp;
- if (ramp->hi_idx > LPG_LUT_COUNT_MAX) {
+ if (ramp->hi_idx > max_count) {
dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n",
- LPG_LUT_COUNT_MAX);
+ max_count);
return -EINVAL;
}
- if (ramp->hi_idx <= ramp->lo_idx) {
- dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d)\n",
+ if (chip->use_sdam && ramp->hi_idx <= ramp->lo_idx) {
+ dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d) when SDAM used\n",
ramp->hi_idx, ramp->lo_idx);
return -EINVAL;
}
@@ -1121,6 +1473,12 @@
ramp->pattern = &chip->lut->pattern[ramp->lo_idx];
lpg->max_pattern_length = ramp->pattern_length;
+ ramp->pattern_repeat = of_property_read_bool(child,
+ "qcom,ramp-pattern-repeat");
+
+ if (chip->use_sdam)
+ continue;
+
rc = of_property_read_u32(child,
"qcom,ramp-pause-hi-count", &tmp);
if (rc < 0)
@@ -1138,9 +1496,6 @@
ramp->ramp_dir_low_to_hi = of_property_read_bool(child,
"qcom,ramp-from-low-to-high");
- ramp->pattern_repeat = of_property_read_bool(child,
- "qcom,ramp-pattern-repeat");
-
ramp->toggle = of_property_read_bool(child,
"qcom,ramp-toggle");
}
@@ -1148,6 +1503,36 @@
return 0;
}
+static int qpnp_lpg_sdam_hw_init(struct qpnp_lpg_chip *chip)
+{
+ struct qpnp_lpg_channel *lpg;
+ int i, rc = 0;
+
+ if (!chip->use_sdam)
+ return 0;
+
+ for (i = 0; i < chip->num_lpgs; i++) {
+ lpg = &chip->lpgs[i];
+ if (lpg->lpg_sdam_base != 0) {
+ rc = qpnp_lpg_sdam_write(lpg, SDAM_LUT_EN_OFFSET, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Write SDAM_REG_LUT_EN failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ rc = qpnp_lpg_sdam_write(lpg,
+ SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET, 0);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write SDAM_REG_PBS_SCRATCH_LUT_COUNTER failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
static int qpnp_lpg_probe(struct platform_device *pdev)
{
int rc;
@@ -1172,6 +1557,13 @@
goto err_out;
}
+ rc = qpnp_lpg_sdam_hw_init(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "SDAM HW init failed, rc=%d\n",
+ rc);
+ goto err_out;
+ }
+
dev_set_drvdata(chip->dev, chip);
chip->pwm_chip.dev = chip->dev;
chip->pwm_chip.base = -1;
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c
index 1c85ecc..0fcf94f 100644
--- a/drivers/pwm/pwm-rcar.c
+++ b/drivers/pwm/pwm-rcar.c
@@ -156,8 +156,12 @@
if (div < 0)
return div;
- /* Let the core driver set pwm->period if disabled and duty_ns == 0 */
- if (!pwm_is_enabled(pwm) && !duty_ns)
+ /*
+ * Let the core driver set pwm->period if disabled and duty_ns == 0.
+ * But, this driver should prevent to set the new duty_ns if current
+ * duty_cycle is not set
+ */
+ if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
return 0;
rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c
index e464582..3439f1e 100644
--- a/drivers/pwm/pwm-stmpe.c
+++ b/drivers/pwm/pwm-stmpe.c
@@ -145,7 +145,7 @@
break;
case 2:
- offset = STMPE24XX_PWMIC1;
+ offset = STMPE24XX_PWMIC2;
break;
default:
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index e464784..7e8906d6 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -76,6 +76,7 @@
struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
unsigned long long c = duty_ns;
unsigned long rate, hz;
+ unsigned long long ns100 = NSEC_PER_SEC;
u32 val = 0;
int err;
@@ -95,9 +96,11 @@
* cycles at the PWM clock rate will take period_ns nanoseconds.
*/
rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH;
- hz = NSEC_PER_SEC / period_ns;
- rate = (rate + (hz / 2)) / hz;
+ /* Consider precision in PWM_SCALE_WIDTH rate calculation */
+ ns100 *= 100;
+ hz = DIV_ROUND_CLOSEST_ULL(ns100, period_ns);
+ rate = DIV_ROUND_CLOSEST(rate * 100, hz);
/*
* Since the actual PWM divider is the register's frequency divider
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index 3a6d029..c5e272e 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -296,6 +296,11 @@
if (!sreg->sel && !strcmp(sreg->name, "vddpu"))
sreg->sel = 22;
+ /* set the default voltage of the pcie phy to be 1.100v */
+ if (!sreg->sel && rdesc->name &&
+ !strcmp(rdesc->name, "vddpcie"))
+ sreg->sel = 0x10;
+
if (!sreg->bypass && !sreg->sel) {
dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
return -EINVAL;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 1f60635..1a25499 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2498,7 +2498,7 @@
ret = ops->list_voltage(rdev, selector);
if (lock)
mutex_unlock(&rdev->mutex);
- } else if (rdev->supply) {
+ } else if (rdev->is_switch && rdev->supply) {
ret = _regulator_list_voltage(rdev->supply, selector, lock);
} else {
return -EINVAL;
@@ -2556,7 +2556,7 @@
if (rdev->desc->n_voltages)
return rdev->desc->n_voltages;
- if (!rdev->supply)
+ if (!rdev->is_switch || !rdev->supply)
return -EINVAL;
return regulator_count_voltages(rdev->supply);
@@ -4453,6 +4453,11 @@
mutex_unlock(®ulator_list_mutex);
}
+ if (!rdev->desc->ops->get_voltage &&
+ !rdev->desc->ops->list_voltage &&
+ !rdev->desc->fixed_uV)
+ rdev->is_switch = true;
+
ret = device_register(&rdev->dev);
if (ret != 0) {
put_device(&rdev->dev);
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 9510016..2eff0cf 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -178,6 +178,7 @@
#define CPR4_REG_MISC 0x700
#define CPR4_MISC_RESET_STEP_QUOT_LOOP_EN BIT(2)
+#define CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN BIT(3)
#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK GENMASK(23, 20)
#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT 20
#define CPR4_MISC_TEMP_SENSOR_ID_START_MASK GENMASK(27, 24)
@@ -733,6 +734,11 @@
CPR4_MISC_RESET_STEP_QUOT_LOOP_EN,
CPR4_MISC_RESET_STEP_QUOT_LOOP_EN);
+ if (ctrl->thread_has_always_vote_en)
+ cpr3_masked_write(ctrl, CPR4_REG_MISC,
+ CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN,
+ CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN);
+
if (ctrl->supports_hw_closed_loop) {
if (ctrl->saw_use_unit_mV)
pmic_step_size = ctrl->step_volt / 1000;
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 778f482..39d3f82 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -766,6 +766,12 @@
* the CPR controller to first use the default step_quot
* and then later switch to the run-time calibrated
* step_quot.
+ * @thread_has_always_vote_en: Boolean value which indicates that this CPR
+ * controller should be configured to keep thread vote
+ * always enabled. This configuration allows the CPR
+ * controller to not consider MID/DN recommendations from
+ * other thread when all sensors mapped to a thread
+ * collapsed.
*
* This structure contains both configuration and runtime state data. The
* elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled,
@@ -879,6 +885,7 @@
struct notifier_block panic_notifier;
bool support_ldo300_vreg;
bool reset_step_quot_loop_en;
+ bool thread_has_always_vote_en;
};
/* Used for rounding voltages to the closest physically available set point. */
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
index 39ee3c5..9e138ce 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1241,6 +1241,16 @@
"qcom,cpr-reset-step-quot-loop-en");
/*
+ * Configure CPR controller to not consider MID/DN recommendations
+ * from other thread when all sensors mapped to a thread collapsed
+ * in a multi-thread configuration.
+ */
+ if (ctrl->thread_count > 1)
+ ctrl->thread_has_always_vote_en
+ = of_property_read_bool(ctrl->dev->of_node,
+ "qcom,cpr-thread-has-always-vote-en");
+
+ /*
* Regulator device handles are not necessary for CPRh controllers
* since communication with the regulators is completely managed
* in hardware.
diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c
index 1f8f8be..725fc58 100644
--- a/drivers/regulator/cpr4-apss-regulator.c
+++ b/drivers/regulator/cpr4-apss-regulator.c
@@ -36,8 +36,8 @@
#include "cpr3-regulator.h"
#define MSM8953_APSS_FUSE_CORNERS 4
-#define SDM632_POWER_APSS_FUSE_CORNERS 5
-#define SDM632_PERF_APSS_FUSE_CORNERS 3
+#define SDM632_POWER_APSS_FUSE_CORNERS 4
+#define SDM632_PERF_APSS_FUSE_CORNERS 4
/**
* struct cpr4_apss_fuses - APSS specific fuse data
@@ -103,27 +103,27 @@
enum cpr4_sdm632_power_apss_fuse_corner {
CPR4_SDM632_POWER_APSS_FUSE_CORNER_LOWSVS = 0,
- CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS = 1,
- CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS_L1 = 2,
- CPR4_SDM632_POWER_APSS_FUSE_CORNER_NOM = 3,
- CPR4_SDM632_POWER_APSS_FUSE_CORNER_TURBO_L1 = 4,
+ CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS_L1 = 1,
+ CPR4_SDM632_POWER_APSS_FUSE_CORNER_NOM = 2,
+ CPR4_SDM632_POWER_APSS_FUSE_CORNER_TURBO_L1 = 3,
};
static const char * const cpr4_sdm632_power_apss_fuse_corner_name[] = {
[CPR4_SDM632_POWER_APSS_FUSE_CORNER_LOWSVS] = "LowSVS",
- [CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS] = "SVS",
[CPR4_SDM632_POWER_APSS_FUSE_CORNER_SVS_L1] = "SVS_L1",
[CPR4_SDM632_POWER_APSS_FUSE_CORNER_NOM] = "NOM",
[CPR4_SDM632_POWER_APSS_FUSE_CORNER_TURBO_L1] = "TURBO_L1",
};
enum cpr4_sdm632_perf_apss_fuse_corner {
- CPR4_SDM632_PERF_APSS_FUSE_CORNER_SVS_L1 = 0,
- CPR4_SDM632_PERF_APSS_FUSE_CORNER_NOM = 1,
- CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1 = 2,
+ CPR4_SDM632_PERF_APSS_FUSE_CORNER_LOWSVS = 0,
+ CPR4_SDM632_PERF_APSS_FUSE_CORNER_SVS_L1 = 1,
+ CPR4_SDM632_PERF_APSS_FUSE_CORNER_NOM = 2,
+ CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1 = 3,
};
static const char * const cpr4_sdm632_perf_apss_fuse_corner_name[] = {
+ [CPR4_SDM632_PERF_APSS_FUSE_CORNER_LOWSVS] = "LowSVS",
[CPR4_SDM632_PERF_APSS_FUSE_CORNER_SVS_L1] = "SVS_L1",
[CPR4_SDM632_PERF_APSS_FUSE_CORNER_NOM] = "NOM",
[CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1] = "TURBO_L1",
@@ -229,12 +229,12 @@
sdm632_apss_ro_sel_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
[CPR4_APSS_POWER_CLUSTER_ID] = {
{{73, 28, 31}, {} },
- {{73, 24, 27}, {} },
{{73, 20, 23}, {} },
{{73, 16, 19}, {} },
{{73, 12, 15}, {} },
},
[CPR4_APSS_PERF_CLUSTER_ID] = {
+ {{73, 28, 31}, {} },
{{73, 8, 11}, {} },
{{73, 4, 7}, {} },
{{73, 0, 3}, {} },
@@ -245,12 +245,12 @@
sdm632_apss_init_voltage_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
[CPR4_APSS_POWER_CLUSTER_ID] = {
{{74, 18, 23}, {} },
- {{74, 12, 17}, {} },
{{71, 24, 29}, {} },
{{74, 6, 11}, {} },
{{74, 0, 5}, {} },
},
[CPR4_APSS_PERF_CLUSTER_ID] = {
+ {{74, 18, 23}, {} },
{{71, 18, 23}, {} },
{{71, 12, 17}, {} },
{{71, 6, 11}, {} },
@@ -261,12 +261,12 @@
sdm632_apss_target_quot_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
[CPR4_APSS_POWER_CLUSTER_ID] = {
{{75, 44, 55}, {} },
- {{75, 32, 43}, {} },
{{72, 44, 55}, {} },
{{75, 20, 31}, {} },
{{75, 8, 19}, {} },
},
[CPR4_APSS_PERF_CLUSTER_ID] = {
+ {{75, 44, 55}, {} },
{{72, 32, 43}, {} },
{{72, 20, 31}, {} },
{{72, 8, 19}, {} },
@@ -277,13 +277,13 @@
sdm632_apss_quot_offset_param[2][SDM632_POWER_APSS_FUSE_CORNERS][2] = {
[CPR4_APSS_POWER_CLUSTER_ID] = {
{{} },
- {{74, 39, 45}, {} },
{{71, 46, 52}, {} },
{{74, 32, 38}, {} },
{{74, 24, 30}, {} },
},
[CPR4_APSS_PERF_CLUSTER_ID] = {
{{} },
+ {{74, 39, 45}, {} },
{{71, 39, 45}, {} },
{{71, 32, 38}, {} },
},
@@ -322,12 +322,12 @@
sdm632_apss_fuse_ref_volt[2][SDM632_POWER_APSS_FUSE_CORNERS] = {
[CPR4_APSS_POWER_CLUSTER_ID] = {
645000,
- 720000,
790000,
865000,
1065000,
},
[CPR4_APSS_PERF_CLUSTER_ID] = {
+ 645000,
790000,
865000,
1065000,
@@ -1025,7 +1025,7 @@
} else {
corner_name = cpr4_sdm632_perf_apss_fuse_corner_name;
lowest_fuse_corner =
- CPR4_SDM632_PERF_APSS_FUSE_CORNER_SVS_L1;
+ CPR4_SDM632_PERF_APSS_FUSE_CORNER_LOWSVS;
highest_fuse_corner =
CPR4_SDM632_PERF_APSS_FUSE_CORNER_TURBO_L1;
}
@@ -1840,20 +1840,29 @@
return 0;
}
-static int cpr4_apss_regulator_suspend(struct platform_device *pdev,
- pm_message_t state)
+#if CONFIG_PM
+static int cpr4_apss_regulator_suspend(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_suspend(ctrl);
}
-static int cpr4_apss_regulator_resume(struct platform_device *pdev)
+static int cpr4_apss_regulator_resume(struct device *dev)
{
- struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+ struct cpr3_controller *ctrl = dev_get_drvdata(dev);
return cpr3_regulator_resume(ctrl);
}
+#else
+#define cpr4_apss_regulator_suspend NULL
+#define cpr4_apss_regulator_resume NULL
+#endif
+
+static const struct dev_pm_ops cpr4_apss_regulator_pm_ops = {
+ .suspend = cpr4_apss_regulator_suspend,
+ .resume = cpr4_apss_regulator_resume,
+};
/* Data corresponds to the SoC revision */
static const struct of_device_id cpr4_regulator_match_table[] = {
@@ -1977,11 +1986,10 @@
.name = "qcom,cpr4-apss-regulator",
.of_match_table = cpr4_regulator_match_table,
.owner = THIS_MODULE,
+ .pm = &cpr4_apss_regulator_pm_ops,
},
.probe = cpr4_apss_regulator_probe,
.remove = cpr4_apss_regulator_remove,
- .suspend = cpr4_apss_regulator_suspend,
- .resume = cpr4_apss_regulator_resume,
};
static int cpr4_regulator_init(void)
diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c
index 07a6198..cc01399 100644
--- a/drivers/regulator/qpnp-lcdb-regulator.c
+++ b/drivers/regulator/qpnp-lcdb-regulator.c
@@ -167,6 +167,8 @@
#define PM660_BST_HEADROOM_DEFAULT_MV 200
#define BST_HEADROOM_DEFAULT_MV 150
+#define PMIC5_LCDB_OFF_ON_DELAY_US 20000
+
struct ldo_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
@@ -1340,22 +1342,27 @@
static int qpnp_lcdb_regulator_register(struct qpnp_lcdb *lcdb, u8 type)
{
- int rc = 0;
+ int rc = 0, off_on_delay = 0;
struct regulator_init_data *init_data;
struct regulator_config cfg = {};
struct regulator_desc *rdesc;
struct regulator_dev *rdev;
struct device_node *node;
+ if (lcdb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
+ off_on_delay = PMIC5_LCDB_OFF_ON_DELAY_US;
+
if (type == LDO) {
node = lcdb->ldo.node;
rdesc = &lcdb->ldo.rdesc;
rdesc->ops = &qpnp_lcdb_ldo_ops;
+ rdesc->off_on_delay = off_on_delay;
rdev = lcdb->ldo.rdev;
} else if (type == NCP) {
node = lcdb->ncp.node;
rdesc = &lcdb->ncp.rdesc;
rdesc->ops = &qpnp_lcdb_ncp_ops;
+ rdesc->off_on_delay = off_on_delay;
rdev = lcdb->ncp.rdev;
} else {
pr_err("Invalid regulator type %d\n", type);
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index e1cfa06..e79f2a1 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -49,6 +49,11 @@
tv64.tv_sec = rtc_tm_to_time64(&tm);
+#if BITS_PER_LONG == 32
+ if (tv64.tv_sec > INT_MAX)
+ goto err_read;
+#endif
+
err = do_settimeofday64(&tv64);
dev_info(rtc->dev.parent,
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 99b5e35..162afcc 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -227,6 +227,13 @@
missing = year;
}
+ /* Can't proceed if alarm is still invalid after replacing
+ * missing fields.
+ */
+ err = rtc_valid_tm(&alarm->time);
+ if (err)
+ goto done;
+
/* with luck, no rollover is needed */
t_now = rtc_tm_to_time64(&now);
t_alm = rtc_tm_to_time64(&alarm->time);
@@ -278,9 +285,9 @@
dev_warn(&rtc->dev, "alarm rollover not handled\n");
}
-done:
err = rtc_valid_tm(&alarm->time);
+done:
if (err) {
dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 7030d7c..c554e52 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -41,6 +41,9 @@
#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#ifdef CONFIG_X86
+#include <asm/i8259.h>
+#endif
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <linux/mc146818rtc.h>
@@ -1117,17 +1120,23 @@
{
cmos_wake_setup(&pnp->dev);
- if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0))
+ if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) {
+ unsigned int irq = 0;
+#ifdef CONFIG_X86
/* Some machines contain a PNP entry for the RTC, but
* don't define the IRQ. It should always be safe to
- * hardcode it in these cases
+ * hardcode it on systems with a legacy PIC.
*/
+ if (nr_legacy_irqs())
+ irq = 8;
+#endif
return cmos_do_probe(&pnp->dev,
- pnp_get_resource(pnp, IORESOURCE_IO, 0), 8);
- else
+ pnp_get_resource(pnp, IORESOURCE_IO, 0), irq);
+ } else {
return cmos_do_probe(&pnp->dev,
pnp_get_resource(pnp, IORESOURCE_IO, 0),
pnp_irq(pnp, 0));
+ }
}
static void cmos_pnp_remove(struct pnp_dev *pnp)
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 3b3049c..c0eb113 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -527,6 +527,10 @@
if (get_user(new_margin, (int __user *)arg))
return -EFAULT;
+ /* the hardware's tick rate is 4096 Hz, so
+ * the counter value needs to be scaled accordingly
+ */
+ new_margin <<= 12;
if (new_margin < 1 || new_margin > 16777216)
return -EINVAL;
@@ -535,7 +539,8 @@
ds1374_wdt_ping();
/* fallthrough */
case WDIOC_GETTIMEOUT:
- return put_user(wdt_margin, (int __user *)arg);
+ /* when returning ... inverse is true */
+ return put_user((wdt_margin >> 12), (int __user *)arg);
case WDIOC_SETOPTIONS:
if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
return -EFAULT;
@@ -543,14 +548,15 @@
if (options & WDIOS_DISABLECARD) {
pr_info("disable watchdog\n");
ds1374_wdt_disable();
+ return 0;
}
if (options & WDIOS_ENABLECARD) {
pr_info("enable watchdog\n");
ds1374_wdt_settimeout(wdt_margin);
ds1374_wdt_ping();
+ return 0;
}
-
return -EINVAL;
}
return -ENOTTY;
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 58698d2..c4ca6a3 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -168,6 +168,7 @@
/* Sets the given date and time to the real time clock. */
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
+ struct m41t80_data *clientdata = i2c_get_clientdata(client);
unsigned char buf[8];
int err, flags;
@@ -183,6 +184,17 @@
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
buf[M41T80_REG_WDAY] = tm->tm_wday;
+ /* If the square wave output is controlled in the weekday register */
+ if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY);
+ if (val < 0)
+ return val;
+
+ buf[M41T80_REG_WDAY] |= (val & 0xf0);
+ }
+
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
sizeof(buf), buf);
if (err < 0) {
diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c
index e4324dc..180ec1f 100644
--- a/drivers/rtc/rtc-opal.c
+++ b/drivers/rtc/rtc-opal.c
@@ -57,7 +57,7 @@
static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
{
- long rc = OPAL_BUSY;
+ s64 rc = OPAL_BUSY;
int retries = 10;
u32 y_m_d;
u64 h_m_s_ms;
@@ -66,13 +66,17 @@
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
- if (rc == OPAL_BUSY_EVENT)
+ if (rc == OPAL_BUSY_EVENT) {
+ msleep(OPAL_BUSY_DELAY_MS);
opal_poll_events(NULL);
- else if (retries-- && (rc == OPAL_HARDWARE
- || rc == OPAL_INTERNAL_ERROR))
- msleep(10);
- else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT)
- break;
+ } else if (rc == OPAL_BUSY) {
+ msleep(OPAL_BUSY_DELAY_MS);
+ } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
+ if (retries--) {
+ msleep(10); /* Wait 10ms before retry */
+ rc = OPAL_BUSY; /* go around again */
+ }
+ }
}
if (rc != OPAL_SUCCESS)
@@ -87,21 +91,26 @@
static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
{
- long rc = OPAL_BUSY;
+ s64 rc = OPAL_BUSY;
int retries = 10;
u32 y_m_d = 0;
u64 h_m_s_ms = 0;
tm_to_opal(tm, &y_m_d, &h_m_s_ms);
+
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
rc = opal_rtc_write(y_m_d, h_m_s_ms);
- if (rc == OPAL_BUSY_EVENT)
+ if (rc == OPAL_BUSY_EVENT) {
+ msleep(OPAL_BUSY_DELAY_MS);
opal_poll_events(NULL);
- else if (retries-- && (rc == OPAL_HARDWARE
- || rc == OPAL_INTERNAL_ERROR))
- msleep(10);
- else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT)
- break;
+ } else if (rc == OPAL_BUSY) {
+ msleep(OPAL_BUSY_DELAY_MS);
+ } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
+ if (retries--) {
+ msleep(10); /* Wait 10ms before retry */
+ rc = OPAL_BUSY; /* go around again */
+ }
+ }
}
return rc == OPAL_SUCCESS ? 0 : -EIO;
@@ -150,6 +159,16 @@
y_m_d = be32_to_cpu(__y_m_d);
h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
+
+ /* check if no alarm is set */
+ if (y_m_d == 0 && h_m_s_ms == 0) {
+ pr_debug("No alarm is set\n");
+ rc = -ENOENT;
+ goto exit;
+ } else {
+ pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
+ }
+
opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
exit:
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
index 0f11c2a..3e8fd33 100644
--- a/drivers/rtc/rtc-snvs.c
+++ b/drivers/rtc/rtc-snvs.c
@@ -132,20 +132,23 @@
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
unsigned long time;
+ int ret;
rtc_tm_to_time(tm, &time);
/* Disable RTC first */
- snvs_rtc_enable(data, false);
+ ret = snvs_rtc_enable(data, false);
+ if (ret)
+ return ret;
/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
/* Enable RTC again */
- snvs_rtc_enable(data, true);
+ ret = snvs_rtc_enable(data, true);
- return 0;
+ return ret;
}
static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -257,7 +260,7 @@
of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
}
- if (!data->regmap) {
+ if (IS_ERR(data->regmap)) {
dev_err(&pdev->dev, "Can't find snvs syscon\n");
return -ENODEV;
}
@@ -287,7 +290,11 @@
regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
/* Enable RTC */
- snvs_rtc_enable(data, true);
+ ret = snvs_rtc_enable(data, true);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable rtc %d\n", ret);
+ goto error_rtc_device_register;
+ }
device_init_wakeup(&pdev->dev, true);
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 560d9a5..a952808 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -86,7 +86,8 @@
for (i = 2; i < 6; i++)
buf[i] = __raw_readl(&rtcreg->dat);
spin_unlock_irq(&pdata->lock);
- sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
+ (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, tm);
return rtc_valid_tm(tm);
}
@@ -147,7 +148,8 @@
alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0;
alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0;
spin_unlock_irq(&pdata->lock);
- sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
+ (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, &alrm->time);
return rtc_valid_tm(&alrm->time);
}
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 5ecd408..0da2465 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1950,8 +1950,12 @@
{
int mask = ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM);
- if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
- /* dasd is being set offline. */
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
+ !test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
+ /*
+ * dasd is being set offline
+ * but it is no safe offline where we have to allow I/O
+ */
return 1;
}
if (device->stopped) {
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 8305ab6..a20dc29 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -2755,6 +2755,16 @@
erp = dasd_3990_erp_handle_match_erp(cqr, erp);
}
+
+ /*
+ * For path verification work we need to stick with the path that was
+ * originally chosen so that the per path configuration data is
+ * assigned correctly.
+ */
+ if (test_bit(DASD_CQR_VERIFY_PATH, &erp->flags) && cqr->lpm) {
+ erp->lpm = cqr->lpm;
+ }
+
if (device->features & DASD_FEATURE_ERPLOG) {
/* print current erp_chain */
dev_err(&device->cdev->dev,
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 1e56018..e453d2a 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -591,13 +591,22 @@
int dasd_alias_add_device(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
- struct alias_lcu *lcu;
+ __u8 uaddr = private->uid.real_unit_addr;
+ struct alias_lcu *lcu = private->lcu;
unsigned long flags;
int rc;
- lcu = private->lcu;
rc = 0;
spin_lock_irqsave(&lcu->lock, flags);
+ /*
+ * Check if device and lcu type differ. If so, the uac data may be
+ * outdated and needs to be updated.
+ */
+ if (private->uid.type != lcu->uac->unit[uaddr].ua_type) {
+ lcu->flags |= UPDATE_PENDING;
+ DBF_DEV_EVENT(DBF_WARNING, device, "%s",
+ "uid type mismatch - trigger rescan");
+ }
if (!(lcu->flags & UPDATE_PENDING)) {
rc = _add_device_to_lcu(lcu, device, device);
if (rc)
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 41e28b2..8ac27ef 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -2,6 +2,8 @@
# S/390 character devices
#
+CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_EXPOLINE)
+
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
sclp_early.o
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 1167469..67903c9 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -451,6 +451,7 @@
static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area)
{
+ struct channel_path *chp;
struct chp_link link;
struct chp_id chpid;
int status;
@@ -463,10 +464,17 @@
chpid.id = sei_area->rsid;
/* allocate a new channel path structure, if needed */
status = chp_get_status(chpid);
- if (status < 0)
- chp_new(chpid);
- else if (!status)
+ if (!status)
return;
+
+ if (status < 0) {
+ chp_new(chpid);
+ } else {
+ chp = chpid_to_chp(chpid);
+ mutex_lock(&chp->lock);
+ chp_update_desc(chp);
+ mutex_unlock(&chp->lock);
+ }
memset(&link, 0, sizeof(struct chp_link));
link.chpid = chpid;
if ((sei_area->vf & 0xc0) != 0) {
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 71bf9bd..66e9bb0 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -126,7 +126,7 @@
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
int start, int count, int auto_ack)
{
- int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0;
+ int rc, tmp_count = count, tmp_start = start, nr = q->nr;
unsigned int ccq = 0;
qperf_inc(q, eqbs);
@@ -149,14 +149,7 @@
qperf_inc(q, eqbs_partial);
DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x",
tmp_count);
- /*
- * Retry once, if that fails bail out and process the
- * extracted buffers before trying again.
- */
- if (!retried++)
- goto again;
- else
- return count - tmp_count;
+ return count - tmp_count;
}
DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
@@ -212,7 +205,10 @@
return 0;
}
-/* returns number of examined buffers and their common state in *state */
+/*
+ * Returns number of examined buffers and their common state in *state.
+ * Requested number of buffers-to-examine must be > 0.
+ */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
unsigned char *state, unsigned int count,
int auto_ack, int merge_pending)
@@ -223,17 +219,23 @@
if (is_qebsm(q))
return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
- for (i = 0; i < count; i++) {
- if (!__state) {
- __state = q->slsb.val[bufnr];
- if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
- __state = SLSB_P_OUTPUT_EMPTY;
- } else if (merge_pending) {
- if ((q->slsb.val[bufnr] & __state) != __state)
- break;
- } else if (q->slsb.val[bufnr] != __state)
- break;
+ /* get initial state: */
+ __state = q->slsb.val[bufnr];
+ if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
+ __state = SLSB_P_OUTPUT_EMPTY;
+
+ for (i = 1; i < count; i++) {
bufnr = next_buf(bufnr);
+
+ /* merge PENDING into EMPTY: */
+ if (merge_pending &&
+ q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING &&
+ __state == SLSB_P_OUTPUT_EMPTY)
+ continue;
+
+ /* stop if next state differs from initial state: */
+ if (q->slsb.val[bufnr] != __state)
+ break;
}
*state = __state;
return i;
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 48b3866..3528690 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -140,7 +140,7 @@
int i;
for (i = 0; i < nr_queues; i++) {
- q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL);
+ q = kmem_cache_zalloc(qdio_q_cache, GFP_KERNEL);
if (!q)
return -ENOMEM;
@@ -456,7 +456,6 @@
{
struct ciw *ciw;
struct qdio_irq *irq_ptr = init_data->cdev->private->qdio_data;
- int rc;
memset(&irq_ptr->qib, 0, sizeof(irq_ptr->qib));
memset(&irq_ptr->siga_flag, 0, sizeof(irq_ptr->siga_flag));
@@ -493,16 +492,14 @@
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
if (!ciw) {
DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
- rc = -EINVAL;
- goto out_err;
+ return -EINVAL;
}
irq_ptr->equeue = *ciw;
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
if (!ciw) {
DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
- rc = -EINVAL;
- goto out_err;
+ return -EINVAL;
}
irq_ptr->aqueue = *ciw;
@@ -510,9 +507,6 @@
irq_ptr->orig_handler = init_data->cdev->handler;
init_data->cdev->handler = qdio_int_handler;
return 0;
-out_err:
- qdio_release_memory(irq_ptr);
- return rc;
}
void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 9b5fc50..403712b 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -592,6 +592,11 @@
void (*callback) (struct qeth_channel *, struct qeth_cmd_buffer *);
};
+static inline struct qeth_ipa_cmd *__ipa_cmd(struct qeth_cmd_buffer *iob)
+{
+ return (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
+}
+
/**
* definition of a qeth channel, used for read and write
*/
@@ -849,7 +854,7 @@
*/
static inline int qeth_get_elements_for_range(addr_t start, addr_t end)
{
- return PFN_UP(end - 1) - PFN_DOWN(start);
+ return PFN_UP(end) - PFN_DOWN(start);
}
static inline int qeth_get_micros(void)
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index df8f74c..283416a 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -522,8 +522,7 @@
queue == card->qdio.no_in_queues - 1;
}
-
-static int qeth_issue_next_read(struct qeth_card *card)
+static int __qeth_issue_next_read(struct qeth_card *card)
{
int rc;
struct qeth_cmd_buffer *iob;
@@ -554,6 +553,17 @@
return rc;
}
+static int qeth_issue_next_read(struct qeth_card *card)
+{
+ int ret;
+
+ spin_lock_irq(get_ccwdev_lock(CARD_RDEV(card)));
+ ret = __qeth_issue_next_read(card);
+ spin_unlock_irq(get_ccwdev_lock(CARD_RDEV(card)));
+
+ return ret;
+}
+
static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)
{
struct qeth_reply *reply;
@@ -957,7 +967,7 @@
spin_lock_irqsave(&card->thread_mask_lock, flags);
card->thread_running_mask &= ~thread;
spin_unlock_irqrestore(&card->thread_mask_lock, flags);
- wake_up(&card->wait_q);
+ wake_up_all(&card->wait_q);
}
EXPORT_SYMBOL_GPL(qeth_clear_thread_running_bit);
@@ -1161,6 +1171,7 @@
}
rc = qeth_get_problem(cdev, irb);
if (rc) {
+ card->read_or_write_problem = 1;
qeth_clear_ipacmd_list(card);
qeth_schedule_recovery(card);
goto out;
@@ -1179,7 +1190,7 @@
return;
if (channel == &card->read &&
channel->state == CH_STATE_UP)
- qeth_issue_next_read(card);
+ __qeth_issue_next_read(card);
iob = channel->iob;
index = channel->buf_no;
@@ -2050,7 +2061,7 @@
unsigned long flags;
struct qeth_reply *reply = NULL;
unsigned long timeout, event_timeout;
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = NULL;
QETH_CARD_TEXT(card, 2, "sendctl");
@@ -2064,23 +2075,27 @@
}
reply->callback = reply_cb;
reply->param = reply_param;
- if (card->state == CARD_STATE_DOWN)
- reply->seqno = QETH_IDX_COMMAND_SEQNO;
- else
- reply->seqno = card->seqno.ipa++;
+
init_waitqueue_head(&reply->wait_q);
- spin_lock_irqsave(&card->lock, flags);
- list_add_tail(&reply->list, &card->cmd_waiter_list);
- spin_unlock_irqrestore(&card->lock, flags);
QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN);
while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ;
+
+ if (IS_IPA(iob->data)) {
+ cmd = __ipa_cmd(iob);
+ cmd->hdr.seqno = card->seqno.ipa++;
+ reply->seqno = cmd->hdr.seqno;
+ event_timeout = QETH_IPA_TIMEOUT;
+ } else {
+ reply->seqno = QETH_IDX_COMMAND_SEQNO;
+ event_timeout = QETH_TIMEOUT;
+ }
qeth_prepare_control_data(card, len, iob);
- if (IS_IPA(iob->data))
- event_timeout = QETH_IPA_TIMEOUT;
- else
- event_timeout = QETH_TIMEOUT;
+ spin_lock_irqsave(&card->lock, flags);
+ list_add_tail(&reply->list, &card->cmd_waiter_list);
+ spin_unlock_irqrestore(&card->lock, flags);
+
timeout = jiffies + event_timeout;
QETH_CARD_TEXT(card, 6, "noirqpnd");
@@ -2105,9 +2120,8 @@
/* we have only one long running ipassist, since we can ensure
process context of this command we can sleep */
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- if ((cmd->hdr.command == IPA_CMD_SETIP) &&
- (cmd->hdr.prot_version == QETH_PROT_IPV4)) {
+ if (cmd && cmd->hdr.command == IPA_CMD_SETIP &&
+ cmd->hdr.prot_version == QETH_PROT_IPV4) {
if (!wait_event_timeout(reply->wait_q,
atomic_read(&reply->received), event_timeout))
goto time_err;
@@ -2871,7 +2885,7 @@
memset(cmd, 0, sizeof(struct qeth_ipa_cmd));
cmd->hdr.command = command;
cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST;
- cmd->hdr.seqno = card->seqno.ipa;
+ /* cmd->hdr.seqno is set by qeth_send_control_data() */
cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
if (card->options.layer2)
@@ -3852,10 +3866,12 @@
int qeth_get_elements_no(struct qeth_card *card,
struct sk_buff *skb, int extra_elems, int data_offset)
{
- int elements = qeth_get_elements_for_range(
- (addr_t)skb->data + data_offset,
- (addr_t)skb->data + skb_headlen(skb)) +
- qeth_get_elements_for_frags(skb);
+ addr_t end = (addr_t)skb->data + skb_headlen(skb);
+ int elements = qeth_get_elements_for_frags(skb);
+ addr_t start = (addr_t)skb->data + data_offset;
+
+ if (start != end)
+ elements += qeth_get_elements_for_range(start, end);
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
@@ -4984,8 +5000,6 @@
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
qeth_clean_channel(&card->read);
qeth_clean_channel(&card->write);
- if (card->dev)
- free_netdev(card->dev);
qeth_free_qdio_buffers(card);
unregister_service_level(&card->qeth_service_level);
kfree(card);
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 5082dfe..e94e957 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1057,8 +1057,8 @@
qeth_l2_set_offline(cgdev);
if (card->dev) {
- netif_napi_del(&card->napi);
unregister_netdev(card->dev);
+ free_netdev(card->dev);
card->dev = NULL;
}
return;
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index eedf9b0..5735694 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -39,8 +39,40 @@
unsigned int pfxlen;
} a6;
} u;
-
};
+
+static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1,
+ struct qeth_ipaddr *a2)
+{
+ if (a1->proto != a2->proto)
+ return false;
+ if (a1->proto == QETH_PROT_IPV6)
+ return ipv6_addr_equal(&a1->u.a6.addr, &a2->u.a6.addr);
+ return a1->u.a4.addr == a2->u.a4.addr;
+}
+
+static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1,
+ struct qeth_ipaddr *a2)
+{
+ /* Assumes that the pair was obtained via qeth_l3_addr_find_by_ip(),
+ * so 'proto' and 'addr' match for sure.
+ *
+ * For ucast:
+ * - 'mac' is always 0.
+ * - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching
+ * values are required to avoid mixups in takeover eligibility.
+ *
+ * For mcast,
+ * - 'mac' is mapped from the IP, and thus always matches.
+ * - 'mask'/'pfxlen' is always 0.
+ */
+ if (a1->type != a2->type)
+ return false;
+ if (a1->proto == QETH_PROT_IPV6)
+ return a1->u.a6.pfxlen == a2->u.a6.pfxlen;
+ return a1->u.a4.mask == a2->u.a4.mask;
+}
+
static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr)
{
u64 ret = 0;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 1487f8a..4ca161b 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -154,6 +154,24 @@
return -EINVAL;
}
+static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
+ struct qeth_ipaddr *query)
+{
+ u64 key = qeth_l3_ipaddr_hash(query);
+ struct qeth_ipaddr *addr;
+
+ if (query->is_multicast) {
+ hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
+ if (qeth_l3_addr_match_ip(addr, query))
+ return addr;
+ } else {
+ hash_for_each_possible(card->ip_htable, addr, hnode, key)
+ if (qeth_l3_addr_match_ip(addr, query))
+ return addr;
+ }
+ return NULL;
+}
+
static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
{
int i, j;
@@ -207,34 +225,6 @@
return rc;
}
-inline int
-qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
-{
- return addr1->proto == addr2->proto &&
- !memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
- !memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac));
-}
-
-static struct qeth_ipaddr *
-qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
-{
- struct qeth_ipaddr *addr;
-
- if (tmp_addr->is_multicast) {
- hash_for_each_possible(card->ip_mc_htable, addr,
- hnode, qeth_l3_ipaddr_hash(tmp_addr))
- if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
- return addr;
- } else {
- hash_for_each_possible(card->ip_htable, addr,
- hnode, qeth_l3_ipaddr_hash(tmp_addr))
- if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
- return addr;
- }
-
- return NULL;
-}
-
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
{
int rc = 0;
@@ -249,8 +239,8 @@
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
}
- addr = qeth_l3_ip_from_hash(card, tmp_addr);
- if (!addr)
+ addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
+ if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
return -ENOENT;
addr->ref_counter--;
@@ -259,12 +249,8 @@
if (addr->in_progress)
return -EINPROGRESS;
- if (!qeth_card_hw_is_reachable(card)) {
- addr->disp_flag = QETH_DISP_ADDR_DELETE;
- return 0;
- }
-
- rc = qeth_l3_deregister_addr_entry(card, addr);
+ if (qeth_card_hw_is_reachable(card))
+ rc = qeth_l3_deregister_addr_entry(card, addr);
hash_del(&addr->hnode);
kfree(addr);
@@ -276,6 +262,7 @@
{
int rc = 0;
struct qeth_ipaddr *addr;
+ char buf[40];
QETH_CARD_TEXT(card, 4, "addip");
@@ -286,8 +273,20 @@
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
}
- addr = qeth_l3_ip_from_hash(card, tmp_addr);
- if (!addr) {
+ addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
+ if (addr) {
+ if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
+ return -EADDRINUSE;
+ if (qeth_l3_addr_match_all(addr, tmp_addr)) {
+ addr->ref_counter++;
+ return 0;
+ }
+ qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
+ buf);
+ dev_warn(&card->gdev->dev,
+ "Registering IP address %s failed\n", buf);
+ return -EADDRINUSE;
+ } else {
addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
if (!addr)
return -ENOMEM;
@@ -327,18 +326,15 @@
(rc == IPA_RC_LAN_OFFLINE)) {
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
if (addr->ref_counter < 1) {
- qeth_l3_delete_ip(card, addr);
+ qeth_l3_deregister_addr_entry(card, addr);
+ hash_del(&addr->hnode);
kfree(addr);
}
} else {
hash_del(&addr->hnode);
kfree(addr);
}
- } else {
- if (addr->type == QETH_IP_TYPE_NORMAL)
- addr->ref_counter++;
}
-
return rc;
}
@@ -406,11 +402,7 @@
spin_lock_bh(&card->ip_lock);
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
- if (addr->disp_flag == QETH_DISP_ADDR_DELETE) {
- qeth_l3_deregister_addr_entry(card, addr);
- hash_del(&addr->hnode);
- kfree(addr);
- } else if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
+ if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
if (addr->proto == QETH_PROT_IPV4) {
addr->in_progress = 1;
spin_unlock_bh(&card->ip_lock);
@@ -726,12 +718,7 @@
return -ENOMEM;
spin_lock_bh(&card->ip_lock);
-
- if (qeth_l3_ip_from_hash(card, ipaddr))
- rc = -EEXIST;
- else
- qeth_l3_add_ip(card, ipaddr);
-
+ rc = qeth_l3_add_ip(card, ipaddr);
spin_unlock_bh(&card->ip_lock);
kfree(ipaddr);
@@ -794,12 +781,7 @@
return -ENOMEM;
spin_lock_bh(&card->ip_lock);
-
- if (qeth_l3_ip_from_hash(card, ipaddr))
- rc = -EEXIST;
- else
- qeth_l3_add_ip(card, ipaddr);
-
+ rc = qeth_l3_add_ip(card, ipaddr);
spin_unlock_bh(&card->ip_lock);
kfree(ipaddr);
@@ -1444,8 +1426,9 @@
memcpy(tmp->mac, buf, sizeof(tmp->mac));
tmp->is_multicast = 1;
- ipm = qeth_l3_ip_from_hash(card, tmp);
+ ipm = qeth_l3_find_addr_by_ip(card, tmp);
if (ipm) {
+ /* for mcast, by-IP match means full match */
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
} else {
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
@@ -1528,8 +1511,9 @@
sizeof(struct in6_addr));
tmp->is_multicast = 1;
- ipm = qeth_l3_ip_from_hash(card, tmp);
+ ipm = qeth_l3_find_addr_by_ip(card, tmp);
if (ipm) {
+ /* for mcast, by-IP match means full match */
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
continue;
}
@@ -2784,11 +2768,12 @@
static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
struct sk_buff *skb, int extra_elems)
{
- addr_t tcpdptr = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
- int elements = qeth_get_elements_for_range(
- tcpdptr,
- (addr_t)skb->data + skb_headlen(skb)) +
- qeth_get_elements_for_frags(skb);
+ addr_t start = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
+ addr_t end = (addr_t)skb->data + skb_headlen(skb);
+ int elements = qeth_get_elements_for_frags(skb);
+
+ if (start != end)
+ elements += qeth_get_elements_for_range(start, end);
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2,
@@ -3207,8 +3192,8 @@
qeth_l3_set_offline(cgdev);
if (card->dev) {
- netif_napi_del(&card->napi);
unregister_netdev(card->dev);
+ free_netdev(card->dev);
card->dev = NULL;
}
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 34367d1..4534a7c 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
*
* Debug traces for zfcp.
*
- * Copyright IBM Corp. 2002, 2017
+ * Copyright IBM Corp. 2002, 2018
*/
#define KMSG_COMPONENT "zfcp"
@@ -287,6 +287,27 @@
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
+/**
+ * zfcp_dbf_rec_trig_lock - trace event related to triggered recovery with lock
+ * @tag: identifier for event
+ * @adapter: adapter on which the erp_action should run
+ * @port: remote port involved in the erp_action
+ * @sdev: scsi device involved in the erp_action
+ * @want: wanted erp_action
+ * @need: required erp_action
+ *
+ * The adapter->erp_lock must not be held.
+ */
+void zfcp_dbf_rec_trig_lock(char *tag, struct zfcp_adapter *adapter,
+ struct zfcp_port *port, struct scsi_device *sdev,
+ u8 want, u8 need)
+{
+ unsigned long flags;
+
+ read_lock_irqsave(&adapter->erp_lock, flags);
+ zfcp_dbf_rec_trig(tag, adapter, port, sdev, want, need);
+ read_unlock_irqrestore(&adapter->erp_lock, flags);
+}
/**
* zfcp_dbf_rec_run_lvl - trace event related to running recovery
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 21c8c68..7a7984a 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corp. 2002, 2016
+ * Copyright IBM Corp. 2002, 2018
*/
#ifndef ZFCP_EXT_H
@@ -34,6 +34,9 @@
extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *);
extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *,
struct zfcp_port *, struct scsi_device *, u8, u8);
+extern void zfcp_dbf_rec_trig_lock(char *tag, struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct scsi_device *sdev, u8 want, u8 need);
extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *);
extern void zfcp_dbf_rec_run_lvl(int level, char *tag,
struct zfcp_erp_action *erp);
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index a9b8104..bb99db2 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2017
+ * Copyright IBM Corp. 2002, 2018
*/
#define KMSG_COMPONENT "zfcp"
@@ -616,9 +616,9 @@
ids.port_id = port->d_id;
ids.roles = FC_RPORT_ROLE_FCP_TARGET;
- zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL,
- ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
- ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
+ zfcp_dbf_rec_trig_lock("scpaddy", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
if (!rport) {
dev_err(&port->adapter->ccw_device->dev,
@@ -640,9 +640,9 @@
struct fc_rport *rport = port->rport;
if (rport) {
- zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL,
- ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
- ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
+ zfcp_dbf_rec_trig_lock("scpdely", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
fc_remote_port_delete(rport);
port->rport = NULL;
}
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index e2962f1..fe670b6 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1374,9 +1374,10 @@
host = aac->scsi_host_ptr;
scsi_block_requests(host);
aac_adapter_disable_int(aac);
- if (aac->thread->pid != current->pid) {
+ if (aac->thread && aac->thread->pid != current->pid) {
spin_unlock_irq(host->host_lock);
kthread_stop(aac->thread);
+ aac->thread = NULL;
jafo = 1;
}
@@ -1445,6 +1446,7 @@
aac->name);
if (IS_ERR(aac->thread)) {
retval = PTR_ERR(aac->thread);
+ aac->thread = NULL;
goto out;
}
}
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index d5b26fa..ad902a6 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1083,6 +1083,7 @@
up(&fib->event_wait);
}
kthread_stop(aac->thread);
+ aac->thread = NULL;
}
aac_adapter_disable_int(aac);
cpu = cpumask_first(cpu_online_mask);
@@ -1203,8 +1204,10 @@
* Map in the registers from the adapter.
*/
aac->base_size = AAC_MIN_FOOTPRINT_SIZE;
- if ((*aac_drivers[index].init)(aac))
+ if ((*aac_drivers[index].init)(aac)) {
+ error = -ENODEV;
goto out_unmap;
+ }
if (aac->sync_mode) {
if (aac_sync_mode)
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
index 2438879..936e8c7 100644
--- a/drivers/scsi/arm/fas216.c
+++ b/drivers/scsi/arm/fas216.c
@@ -2011,7 +2011,7 @@
* have valid data in the sense buffer that could
* confuse the higher levels.
*/
- memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
+ memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id);
//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); }
/*
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index be65da2..838edbb 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -246,6 +246,12 @@
{
int rc = 0;
+ if (!tag || tag > MAX_MCC_CMD) {
+ __beiscsi_log(phba, KERN_ERR,
+ "BC_%d : invalid tag %u\n", tag);
+ return -EINVAL;
+ }
+
if (beiscsi_hba_in_error(phba)) {
clear_bit(MCC_TAG_STATE_RUNNING,
&phba->ctrl.ptag_state[tag].tag_state);
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index fdd4eb4..e58786f 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -191,6 +191,7 @@
struct bnx2fc_cmd_mgr *cmd_mgr;
spinlock_t hba_lock;
struct mutex hba_mutex;
+ struct mutex hba_stats_mutex;
unsigned long adapter_state;
#define ADAPTER_STATE_UP 0
#define ADAPTER_STATE_GOING_DOWN 1
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index f9ddb61..bee7d37 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -670,15 +670,17 @@
if (!fw_stats)
return NULL;
+ mutex_lock(&hba->hba_stats_mutex);
+
bnx2fc_stats = fc_get_host_stats(shost);
init_completion(&hba->stat_req_done);
if (bnx2fc_send_stat_req(hba))
- return bnx2fc_stats;
+ goto unlock_stats_mutex;
rc = wait_for_completion_timeout(&hba->stat_req_done, (2 * HZ));
if (!rc) {
BNX2FC_HBA_DBG(lport, "FW stat req timed out\n");
- return bnx2fc_stats;
+ goto unlock_stats_mutex;
}
BNX2FC_STATS(hba, rx_stat2, fc_crc_cnt);
bnx2fc_stats->invalid_crc_count += hba->bfw_stats.fc_crc_cnt;
@@ -700,6 +702,9 @@
memcpy(&hba->prev_stats, hba->stats_buffer,
sizeof(struct fcoe_statistics_params));
+
+unlock_stats_mutex:
+ mutex_unlock(&hba->hba_stats_mutex);
return bnx2fc_stats;
}
@@ -1348,6 +1353,7 @@
}
spin_lock_init(&hba->hba_lock);
mutex_init(&hba->hba_mutex);
+ mutex_init(&hba->hba_stats_mutex);
hba->cnic = cnic;
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index f501095..bd39590 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1869,6 +1869,7 @@
/* we will not receive ABTS response for this IO */
BNX2FC_IO_DBG(io_req, "Timer context finished processing "
"this scsi cmd\n");
+ return;
}
/* Cancel the timeout_work, as we received IO completion */
diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c
index 622bdab..dab195f 100644
--- a/drivers/scsi/csiostor/csio_hw.c
+++ b/drivers/scsi/csiostor/csio_hw.c
@@ -1769,7 +1769,6 @@
goto bye;
}
- mempool_free(mbp, hw->mb_mempool);
if (finicsum != cfcsum) {
csio_warn(hw,
"Config File checksum mismatch: csum=%#x, computed=%#x\n",
@@ -1780,6 +1779,10 @@
rv = csio_hw_validate_caps(hw, mbp);
if (rv != 0)
goto bye;
+
+ mempool_free(mbp, hw->mb_mempool);
+ mbp = NULL;
+
/*
* Note that we're operating with parameters
* not supplied by the driver, rather than from hard-wired
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 375c536..c5eb0c4 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -32,13 +32,13 @@
MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs");
MODULE_LICENSE("GPL v2");
-static int fcoe_transport_create(const char *, struct kernel_param *);
-static int fcoe_transport_destroy(const char *, struct kernel_param *);
+static int fcoe_transport_create(const char *, const struct kernel_param *);
+static int fcoe_transport_destroy(const char *, const struct kernel_param *);
static int fcoe_transport_show(char *buffer, const struct kernel_param *kp);
static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device);
static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device);
-static int fcoe_transport_enable(const char *, struct kernel_param *);
-static int fcoe_transport_disable(const char *, struct kernel_param *);
+static int fcoe_transport_enable(const char *, const struct kernel_param *);
+static int fcoe_transport_disable(const char *, const struct kernel_param *);
static int libfcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr);
@@ -865,7 +865,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_create(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -930,7 +931,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_destroy(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -974,7 +976,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_disable(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
@@ -1008,7 +1011,8 @@
*
* Returns: 0 for success
*/
-static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
+static int fcoe_transport_enable(const char *buffer,
+ const struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 44dd372..c056b81 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -1127,12 +1127,6 @@
else
CMD_ABTS_STATUS(sc) = hdr_status;
- atomic64_dec(&fnic_stats->io_stats.active_ios);
- if (atomic64_read(&fnic->io_cmpl_skip))
- atomic64_dec(&fnic->io_cmpl_skip);
- else
- atomic64_inc(&fnic_stats->io_stats.io_completions);
-
if (!(CMD_FLAGS(sc) & (FNIC_IO_ABORTED | FNIC_IO_DONE)))
atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls);
@@ -1173,6 +1167,11 @@
(((u64)CMD_FLAGS(sc) << 32) |
CMD_STATE(sc)));
sc->scsi_done(sc);
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
}
}
@@ -1962,6 +1961,11 @@
/* Call SCSI completion function to complete the IO */
sc->result = (DID_ABORT << 16);
sc->scsi_done(sc);
+ atomic64_dec(&fnic_stats->io_stats.active_ios);
+ if (atomic64_read(&fnic->io_cmpl_skip))
+ atomic64_dec(&fnic->io_cmpl_skip);
+ else
+ atomic64_inc(&fnic_stats->io_stats.io_completions);
}
fnic_abort_cmd_end:
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 9a0696f..b81a53c 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -367,7 +367,7 @@
};
struct ibmvfc_fcp_rsp_info {
- __be16 reserved;
+ u8 reserved[3];
u8 rsp_code;
u8 reserved2[4];
}__attribute__((packed, aligned (2)));
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 5324741..c5bc41d 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -836,8 +836,10 @@
qc->err_mask |= AC_ERR_OTHER;
sata_port->ioasa.status |= ATA_BUSY;
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
ata_qc_complete(qc);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -5947,8 +5949,10 @@
res->in_erp = 0;
}
scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
scsi_cmd->scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -6338,8 +6342,10 @@
}
scsi_dma_unmap(ipr_cmd->scsi_cmd);
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
scsi_cmd->scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
}
/**
@@ -6365,8 +6371,10 @@
scsi_dma_unmap(scsi_cmd);
spin_lock_irqsave(ipr_cmd->hrrq->lock, lock_flags);
- list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
scsi_cmd->scsi_done(scsi_cmd);
+ if (ipr_cmd->eh_comp)
+ complete(ipr_cmd->eh_comp);
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q);
spin_unlock_irqrestore(ipr_cmd->hrrq->lock, lock_flags);
} else {
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 4abd3fc..c2b6829 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1695,6 +1695,15 @@
*/
switch (session->state) {
case ISCSI_STATE_FAILED:
+ /*
+ * cmds should fail during shutdown, if the session
+ * state is bad, allowing completion to happen
+ */
+ if (unlikely(system_state != SYSTEM_RUNNING)) {
+ reason = FAILURE_SESSION_FAILED;
+ sc->result = DID_NO_CONNECT << 16;
+ break;
+ }
case ISCSI_STATE_IN_RECOVERY:
reason = FAILURE_SESSION_IN_RECOVERY;
sc->result = DID_IMM_RETRY << 16;
@@ -1980,6 +1989,19 @@
if (session->state != ISCSI_STATE_LOGGED_IN) {
/*
+ * During shutdown, if session is prematurely disconnected,
+ * recovery won't happen and there will be hung cmds. Not
+ * handling cmds would trigger EH, also bad in this case.
+ * Instead, handle cmd, allow completion to happen and let
+ * upper layer to deal with the result.
+ */
+ if (unlikely(system_state != SYSTEM_RUNNING)) {
+ sc->result = DID_NO_CONNECT << 16;
+ ISCSI_DBG_EH(session, "sc on shutdown, handled\n");
+ rc = BLK_EH_HANDLED;
+ goto done;
+ }
+ /*
* We are probably in the middle of iscsi recovery so let
* that complete and handle the error.
*/
@@ -2083,7 +2105,7 @@
task->last_timeout = jiffies;
spin_unlock(&session->frwd_lock);
ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ?
- "timer reset" : "nh");
+ "timer reset" : "shutdown or nh");
return rc;
}
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 022bb6e..12886f9 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -282,6 +282,7 @@
phy->phy->minimum_linkrate = dr->pmin_linkrate;
phy->phy->maximum_linkrate = dr->pmax_linkrate;
phy->phy->negotiated_linkrate = phy->linkrate;
+ phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED);
skip:
if (new_phy)
@@ -675,7 +676,7 @@
res = smp_execute_task(dev, req, RPEL_REQ_SIZE,
resp, RPEL_RESP_SIZE);
- if (!res)
+ if (res)
goto out;
phy->invalid_dword_count = scsi_to_u32(&resp[12]);
@@ -684,6 +685,7 @@
phy->phy_reset_problem_count = scsi_to_u32(&resp[24]);
out:
+ kfree(req);
kfree(resp);
return res;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 519dac4..9a8c2f9 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -222,6 +222,7 @@
static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)
{
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host);
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
struct sas_task *task = TO_SAS_TASK(cmd);
/* At this point, we only get called following an actual abort
@@ -230,6 +231,14 @@
*/
sas_end_task(cmd, task);
+ if (dev_is_sata(dev)) {
+ /* defer commands to libata so that libata EH can
+ * handle ata qcs correctly
+ */
+ list_move_tail(&cmd->eh_entry, &sas_ha->eh_ata_q);
+ return;
+ }
+
/* now finish the command and move it on to the error
* handler done list, this also takes it off the
* error handler pending list.
@@ -237,22 +246,6 @@
scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q);
}
-static void sas_eh_defer_cmd(struct scsi_cmnd *cmd)
-{
- struct domain_device *dev = cmd_to_domain_dev(cmd);
- struct sas_ha_struct *ha = dev->port->ha;
- struct sas_task *task = TO_SAS_TASK(cmd);
-
- if (!dev_is_sata(dev)) {
- sas_eh_finish_cmd(cmd);
- return;
- }
-
- /* report the timeout to libata */
- sas_end_task(cmd, task);
- list_move_tail(&cmd->eh_entry, &ha->eh_ata_q);
-}
-
static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd)
{
struct scsi_cmnd *cmd, *n;
@@ -260,7 +253,7 @@
list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
if (cmd->device->sdev_target == my_cmd->device->sdev_target &&
cmd->device->lun == my_cmd->device->lun)
- sas_eh_defer_cmd(cmd);
+ sas_eh_finish_cmd(cmd);
}
}
@@ -622,12 +615,12 @@
case TASK_IS_DONE:
SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
task);
- sas_eh_defer_cmd(cmd);
+ sas_eh_finish_cmd(cmd);
continue;
case TASK_IS_ABORTED:
SAS_DPRINTK("%s: task 0x%p is aborted\n",
__func__, task);
- sas_eh_defer_cmd(cmd);
+ sas_eh_finish_cmd(cmd);
continue;
case TASK_IS_AT_LU:
SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task);
@@ -638,7 +631,7 @@
"recovered\n",
SAS_ADDR(task->dev),
cmd->device->lun);
- sas_eh_defer_cmd(cmd);
+ sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_lu(work_q, cmd);
goto Again;
}
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 4532990..cf15b97 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -635,7 +635,12 @@
LPFC_MBOXQ_t *pmboxq;
int mbxstatus = MBXERR_ERROR;
+ /*
+ * If the link is offline, disabled or BLOCK_MGMT_IO
+ * it doesn't make any sense to allow issue_lip
+ */
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+ (phba->hba_flag & LINK_DISABLED) ||
(phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO))
return -EPERM;
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 7d2ad63..8173645 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -690,8 +690,9 @@
(phba->hba_flag & HBA_SP_QUEUE_EVT)) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
- /* Set the lpfc data pending flag */
- set_bit(LPFC_DATA_READY, &phba->data_flags);
+ /* Preserve legacy behavior. */
+ if (!(phba->hba_flag & HBA_SP_QUEUE_EVT))
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
} else {
if (phba->link_state >= LPFC_LINK_UP) {
pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index f7e3f27..e9ea8f4 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -11312,6 +11312,7 @@
lpfc_fof_queue_create(struct lpfc_hba *phba)
{
struct lpfc_queue *qdesc;
+ uint32_t wqesize;
/* Create FOF EQ */
qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
@@ -11332,8 +11333,11 @@
phba->sli4_hba.oas_cq = qdesc;
/* Create OAS WQ */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
+ wqesize = (phba->fcp_embed_io) ?
+ LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
+ qdesc = lpfc_sli4_queue_alloc(phba, wqesize,
phba->sli4_hba.wq_ecount);
+
if (!qdesc)
goto out_error;
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 8f1df76..6df06e7 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -116,6 +116,8 @@
/* set consumption flag every once in a while */
if (!((q->host_index + 1) % q->entry_repost))
bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
+ else
+ bf_set(wqe_wqec, &wqe->generic.wqe_com, 0);
if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED)
bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id);
lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
@@ -13696,6 +13698,9 @@
case LPFC_Q_CREATE_VERSION_1:
bf_set(lpfc_mbx_wq_create_wqe_count, &wq_create->u.request_1,
wq->entry_count);
+ bf_set(lpfc_mbox_hdr_version, &shdr->request,
+ LPFC_Q_CREATE_VERSION_1);
+
switch (wq->entry_size) {
default:
case 64:
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
index 14c0334..26c67c4 100644
--- a/drivers/scsi/mac_esp.c
+++ b/drivers/scsi/mac_esp.c
@@ -55,6 +55,7 @@
int error;
};
static struct esp *esp_chips[2];
+static DEFINE_SPINLOCK(esp_chips_lock);
#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
platform_get_drvdata((struct platform_device *) \
@@ -562,15 +563,18 @@
}
host->irq = IRQ_MAC_SCSI;
- esp_chips[dev->id] = esp;
- mb();
- if (esp_chips[!dev->id] == NULL) {
- err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
- if (err < 0) {
- esp_chips[dev->id] = NULL;
- goto fail_free_priv;
- }
+
+ /* The request_irq() call is intended to succeed for the first device
+ * and fail for the second device.
+ */
+ err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
+ spin_lock(&esp_chips_lock);
+ if (err < 0 && esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
+ goto fail_free_priv;
}
+ esp_chips[dev->id] = esp;
+ spin_unlock(&esp_chips_lock);
err = scsi_esp_register(esp, &dev->dev);
if (err)
@@ -579,8 +583,13 @@
return 0;
fail_free_irq:
- if (esp_chips[!dev->id] == NULL)
+ spin_lock(&esp_chips_lock);
+ esp_chips[dev->id] = NULL;
+ if (esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
free_irq(host->irq, esp);
+ } else
+ spin_unlock(&esp_chips_lock);
fail_free_priv:
kfree(mep);
fail_free_command_block:
@@ -599,9 +608,13 @@
scsi_esp_unregister(esp);
+ spin_lock(&esp_chips_lock);
esp_chips[dev->id] = NULL;
- if (!(esp_chips[0] || esp_chips[1]))
+ if (esp_chips[!dev->id] == NULL) {
+ spin_unlock(&esp_chips_lock);
free_irq(irq, NULL);
+ } else
+ spin_unlock(&esp_chips_lock);
kfree(mep);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index a1a5ceb..8e83e34 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -105,7 +105,7 @@
*
*/
static int
-_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
+_scsih_set_fwfault_debug(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
struct MPT3SAS_ADAPTER *ioc;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 468acab..caa0045 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -281,7 +281,7 @@
* Note: The logging levels are defined in mpt3sas_debug.h.
*/
static int
-_scsih_set_debug_level(const char *val, struct kernel_param *kp)
+_scsih_set_debug_level(const char *val, const struct kernel_param *kp)
{
int ret = param_set_int(val, kp);
struct MPT3SAS_ADAPTER *ioc;
@@ -4065,19 +4065,6 @@
return 0;
}
- /*
- * Bug work around for firmware SATL handling. The loop
- * is based on atomic operations and ensures consistency
- * since we're lockless at this point
- */
- do {
- if (test_bit(0, &sas_device_priv_data->ata_command_pending)) {
- scmd->result = SAM_STAT_BUSY;
- scmd->scsi_done(scmd);
- return 0;
- }
- } while (_scsih_set_satl_pending(scmd, true));
-
sas_target_priv_data = sas_device_priv_data->sas_target;
/* invalid device handle */
@@ -4103,6 +4090,19 @@
sas_device_priv_data->block)
return SCSI_MLQUEUE_DEVICE_BUSY;
+ /*
+ * Bug work around for firmware SATL handling. The loop
+ * is based on atomic operations and ensures consistency
+ * since we're lockless at this point
+ */
+ do {
+ if (test_bit(0, &sas_device_priv_data->ata_command_pending)) {
+ scmd->result = SAM_STAT_BUSY;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+ } while (_scsih_set_satl_pending(scmd, true));
+
if (scmd->sc_data_direction == DMA_FROM_DEVICE)
mpi_control = MPI2_SCSIIO_CONTROL_READ;
else if (scmd->sc_data_direction == DMA_TO_DEVICE)
@@ -4124,6 +4124,7 @@
if (!smid) {
pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
ioc->name, __func__);
+ _scsih_set_satl_pending(scmd, false);
goto out;
}
mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
@@ -4154,6 +4155,7 @@
if (mpi_request->DataLength) {
if (ioc->build_sg_scmd(ioc, scmd, smid)) {
mpt3sas_base_free_smid(ioc, smid);
+ _scsih_set_satl_pending(scmd, false);
goto out;
}
} else
@@ -8851,7 +8853,7 @@
snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
"fw_event_%s%d", ioc->driver_name, ioc->id);
ioc->firmware_event_thread = alloc_ordered_workqueue(
- ioc->firmware_event_name, WQ_MEM_RECLAIM);
+ ioc->firmware_event_name, 0);
if (!ioc->firmware_event_thread) {
pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 7de5d8d..eb5471b 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -1080,16 +1080,16 @@
void __iomem *regs = mvi->regs_ex - 0x10200;
int drive = (i/3) & (4-1); /* drive number on host */
- u32 block = mr32(MVS_SGPIO_DCTRL +
+ int driveshift = drive * 8; /* bit offset of drive */
+ u32 block = ioread32be(regs + MVS_SGPIO_DCTRL +
MVS_SGPIO_HOST_OFFSET * mvi->id);
-
/*
* if bit is set then create a mask with the first
* bit of the drive set in the mask ...
*/
- u32 bit = (write_data[i/8] & (1 << (i&(8-1)))) ?
- 1<<(24-drive*8) : 0;
+ u32 bit = get_unaligned_be32(write_data) & (1 << i) ?
+ 1 << driveshift : 0;
/*
* ... and then shift it to the right position based
@@ -1098,26 +1098,27 @@
switch (i%3) {
case 0: /* activity */
block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
- << (24-drive*8));
+ << driveshift);
/* hardwire activity bit to SOF */
block |= LED_BLINKA_SOF << (
MVS_SGPIO_DCTRL_ACT_SHIFT +
- (24-drive*8));
+ driveshift);
break;
case 1: /* id */
block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
- << (24-drive*8));
+ << driveshift);
block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
break;
case 2: /* fail */
block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
- << (24-drive*8));
+ << driveshift);
block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
break;
}
- mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
- block);
+ iowrite32be(block,
+ regs + MVS_SGPIO_DCTRL +
+ MVS_SGPIO_HOST_OFFSET * mvi->id);
}
@@ -1132,7 +1133,7 @@
void __iomem *regs = mvi->regs_ex - 0x10200;
mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
- be32_to_cpu(((u32 *) write_data)[i]));
+ ((u32 *) write_data)[i]);
}
return reg_count;
}
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 8f12f6b..4441a55 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -369,6 +369,7 @@
srb_t *sp = (srb_t *)ptr;
struct srb_iocb *abt = &sp->u.iocb_cmd;
+ del_timer(&sp->u.iocb_cmd.timer);
complete(&abt->u.abt.comp);
}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index bddaabb..73c99f2 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -272,7 +272,8 @@
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Read all mbox registers? */
- mboxes = (1 << ha->mbx_count) - 1;
+ WARN_ON_ONCE(ha->mbx_count > 32);
+ mboxes = (1ULL << ha->mbx_count) - 1;
if (!ha->mcp)
ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERROR.\n");
else
@@ -2516,7 +2517,8 @@
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
/* Read all mbox registers? */
- mboxes = (1 << ha->mbx_count) - 1;
+ WARN_ON_ONCE(ha->mbx_count > 32);
+ mboxes = (1ULL << ha->mbx_count) - 1;
if (!ha->mcp)
ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERROR.\n");
else
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 94630d4..baccd11 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1443,7 +1443,7 @@
void
qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
{
- int que, cnt;
+ int que, cnt, status;
unsigned long flags;
srb_t *sp;
struct qla_hw_data *ha = vha->hw;
@@ -1473,8 +1473,12 @@
*/
sp_get(sp);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- qla2xxx_eh_abort(GET_CMD_SP(sp));
+ status = qla2xxx_eh_abort(GET_CMD_SP(sp));
spin_lock_irqsave(&ha->hardware_lock, flags);
+ /* Get rid of extra reference if immediate exit
+ * from ql2xxx_eh_abort */
+ if (status == FAILED && (qla2x00_isp_reg_stat(ha)))
+ atomic_dec(&sp->ref_count);
}
req->outstanding_cmds[cnt] = NULL;
sp->done(vha, sp, res);
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index 59059ff..11f45cb 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -5789,7 +5789,7 @@
fc_port_t *fcport;
int rc;
- fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (!fcport) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f,
"qla_target(%d): Allocation of tmp FC port failed",
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index a7cfc27..ce1d063f 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -168,6 +168,8 @@
#define DEV_DB_NON_PERSISTENT 0
#define DEV_DB_PERSISTENT 1
+#define QL4_ISP_REG_DISCONNECT 0xffffffffU
+
#define COPY_ISID(dst_isid, src_isid) { \
int i, j; \
for (i = 0, j = ISID_SIZE - 1; i < ISID_SIZE;) \
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 01c3610..d8c0343 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -262,6 +262,24 @@
static struct scsi_transport_template *qla4xxx_scsi_transport;
+static int qla4xxx_isp_check_reg(struct scsi_qla_host *ha)
+{
+ u32 reg_val = 0;
+ int rval = QLA_SUCCESS;
+
+ if (is_qla8022(ha))
+ reg_val = readl(&ha->qla4_82xx_reg->host_status);
+ else if (is_qla8032(ha) || is_qla8042(ha))
+ reg_val = qla4_8xxx_rd_direct(ha, QLA8XXX_PEG_ALIVE_COUNTER);
+ else
+ reg_val = readw(&ha->reg->ctrl_status);
+
+ if (reg_val == QL4_ISP_REG_DISCONNECT)
+ rval = QLA_ERROR;
+
+ return rval;
+}
+
static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num,
uint32_t iface_type, uint32_t payload_size,
uint32_t pid, struct sockaddr *dst_addr)
@@ -9196,10 +9214,17 @@
struct srb *srb = NULL;
int ret = SUCCESS;
int wait = 0;
+ int rval;
ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%llu: Abort command issued cmd=%p, cdb=0x%x\n",
ha->host_no, id, lun, cmd, cmd->cmnd[0]);
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
spin_lock_irqsave(&ha->hardware_lock, flags);
srb = (struct srb *) CMD_SP(cmd);
if (!srb) {
@@ -9251,6 +9276,7 @@
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
struct ddb_entry *ddb_entry = cmd->device->hostdata;
int ret = FAILED, stat;
+ int rval;
if (!ddb_entry)
return ret;
@@ -9270,6 +9296,12 @@
cmd, jiffies, cmd->request->timeout / HZ,
ha->dpc_flags, cmd->result, cmd->allowed));
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
/* FIXME: wait for hba to go online */
stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun);
if (stat != QLA_SUCCESS) {
@@ -9313,6 +9345,7 @@
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
struct ddb_entry *ddb_entry = cmd->device->hostdata;
int stat, ret;
+ int rval;
if (!ddb_entry)
return FAILED;
@@ -9330,6 +9363,12 @@
ha->host_no, cmd, jiffies, cmd->request->timeout / HZ,
ha->dpc_flags, cmd->result, cmd->allowed));
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
stat = qla4xxx_reset_target(ha, ddb_entry);
if (stat != QLA_SUCCESS) {
starget_printk(KERN_INFO, scsi_target(cmd->device),
@@ -9384,9 +9423,16 @@
{
int return_status = FAILED;
struct scsi_qla_host *ha;
+ int rval;
ha = to_qla_host(cmd->device->host);
+ rval = qla4xxx_isp_check_reg(ha);
+ if (rval != QLA_SUCCESS) {
+ ql4_printk(KERN_INFO, ha, "PCI/Register disconnect, exiting.\n");
+ return FAILED;
+ }
+
if ((is_qla8032(ha) || is_qla8042(ha)) && ql4xdontresethba)
qla4_83xx_set_idc_dontreset(ha);
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 26e6b05..43d4b30 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -180,7 +180,7 @@
{"HITACHI", "6586-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HITACHI", "6588-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */
- {"HP", "OPEN-", "*", BLIST_REPORTLUN2}, /* HP XP Arrays */
+ {"HP", "OPEN-", "*", BLIST_REPORTLUN2 | BLIST_TRY_VPD_PAGES}, /* HP XP Arrays */
{"HP", "NetRAID-4M", NULL, BLIST_FORCELUN},
{"HP", "HSV100", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD},
{"HP", "C1557A", NULL, BLIST_FORCELUN},
@@ -596,17 +596,12 @@
int key)
{
struct scsi_dev_info_list *devinfo;
- int err;
devinfo = scsi_dev_info_list_find(vendor, model, key);
if (!IS_ERR(devinfo))
return devinfo->flags;
- err = PTR_ERR(devinfo);
- if (err != -ENOENT)
- return err;
-
- /* nothing found, return nothing */
+ /* key or device not found: return nothing */
if (key != SCSI_DEVINFO_GLOBAL)
return 0;
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index 84addee..a5e30e9 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -56,10 +56,13 @@
{"IBM", "1815", "rdac", },
{"IBM", "1818", "rdac", },
{"IBM", "3526", "rdac", },
+ {"IBM", "3542", "rdac", },
+ {"IBM", "3552", "rdac", },
{"SGI", "TP9", "rdac", },
{"SGI", "IS", "rdac", },
- {"STK", "OPENstorage D280", "rdac", },
+ {"STK", "OPENstorage", "rdac", },
{"STK", "FLEXLINE 380", "rdac", },
+ {"STK", "BladeCtlr", "rdac", },
{"SUN", "CSM", "rdac", },
{"SUN", "LCSM100", "rdac", },
{"SUN", "STK6580_6780", "rdac", },
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 57a3ee0..3ccc858 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2044,6 +2044,8 @@
if (!shost->use_clustering)
q->limits.cluster = 0;
+ if (shost->inlinecrypt_support)
+ queue_flag_set_unlocked(QUEUE_FLAG_INLINECRYPT, q);
/*
* Set a reasonable default alignment: The larger of 32-byte (dword),
* which is a common minimum for HBAs, and the minimum DMA alignment,
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 4fb494a..1cb0403 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1860,6 +1860,8 @@
break; /* standby */
if (sshdr.asc == 4 && sshdr.ascq == 0xc)
break; /* unavailable */
+ if (sshdr.asc == 4 && sshdr.ascq == 0x1b)
+ break; /* sanitize in progress */
/*
* Issue command to spin up drive when not ready
*/
@@ -2319,6 +2321,7 @@
int res;
struct scsi_device *sdp = sdkp->device;
struct scsi_mode_data data;
+ int disk_ro = get_disk_ro(sdkp->disk);
set_disk_ro(sdkp->disk, 0);
if (sdp->skip_ms_page_3f) {
@@ -2358,7 +2361,7 @@
"Test WP failed, assume Write Enabled\n");
} else {
sdkp->write_prot = ((data.device_specific & 0x80) != 0);
- set_disk_ro(sdkp->disk, sdkp->write_prot);
+ set_disk_ro(sdkp->disk, sdkp->write_prot || disk_ro);
}
}
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 50adabb..69046d3 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -548,7 +548,6 @@
ecomp = &edev->component[components++];
if (!IS_ERR(ecomp)) {
- ses_get_power_status(edev, ecomp);
if (addl_desc_ptr)
ses_process_descriptor(
ecomp,
@@ -579,13 +578,16 @@
}
static void ses_match_to_enclosure(struct enclosure_device *edev,
- struct scsi_device *sdev)
+ struct scsi_device *sdev,
+ int refresh)
{
+ struct scsi_device *edev_sdev = to_scsi_device(edev->edev.parent);
struct efd efd = {
.addr = 0,
};
- ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
+ if (refresh)
+ ses_enclosure_data_process(edev, edev_sdev, 0);
if (scsi_is_sas_rphy(sdev->sdev_target->dev.parent))
efd.addr = sas_get_address(sdev);
@@ -616,7 +618,7 @@
struct enclosure_device *prev = NULL;
while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
- ses_match_to_enclosure(edev, sdev);
+ ses_match_to_enclosure(edev, sdev, 1);
prev = edev;
}
return -ENODEV;
@@ -728,7 +730,7 @@
shost_for_each_device(tmp_sdev, sdev->host) {
if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev))
continue;
- ses_match_to_enclosure(edev, tmp_sdev);
+ ses_match_to_enclosure(edev, tmp_sdev, 0);
}
return 0;
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 3a7a86d..15d5af0 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -524,6 +524,7 @@
} else
count = (old_hdr->result == 0) ? 0 : -EIO;
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
retval = count;
free_old_hdr:
kfree(old_hdr);
@@ -564,6 +565,7 @@
}
err_out:
err2 = sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
return err ? : err2 ? : count;
}
@@ -663,18 +665,14 @@
* is a non-zero input_size, so emit a warning.
*/
if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) {
- static char cmd[TASK_COMM_LEN];
- if (strcmp(current->comm, cmd)) {
- printk_ratelimited(KERN_WARNING
- "sg_write: data in/out %d/%d bytes "
- "for SCSI command 0x%x-- guessing "
- "data in;\n program %s not setting "
- "count and/or reply_len properly\n",
- old_hdr.reply_len - (int)SZ_SG_HEADER,
- input_size, (unsigned int) cmnd[0],
- current->comm);
- strcpy(cmd, current->comm);
- }
+ printk_ratelimited(KERN_WARNING
+ "sg_write: data in/out %d/%d bytes "
+ "for SCSI command 0x%x-- guessing "
+ "data in;\n program %s not setting "
+ "count and/or reply_len properly\n",
+ old_hdr.reply_len - (int)SZ_SG_HEADER,
+ input_size, (unsigned int) cmnd[0],
+ current->comm);
}
k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);
return (k < 0) ? k : count;
@@ -773,11 +771,15 @@
"sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n",
(int) cmnd[0], (int) hp->cmd_len));
+ if (hp->dxfer_len >= SZ_256M)
+ return -EINVAL;
+
k = sg_start_req(srp, cmnd);
if (k) {
SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp,
"sg_common_write: start_req err=%d\n", k));
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
return k; /* probably out of space --> ENOMEM */
}
if (atomic_read(&sdp->detaching)) {
@@ -790,6 +792,7 @@
}
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
return -ENODEV;
}
@@ -1287,6 +1290,7 @@
struct sg_fd *sfp = srp->parentfp;
sg_finish_rem_req(srp);
+ sg_remove_request(sfp, srp);
kref_put(&sfp->f_ref, sg_remove_sfp);
}
@@ -1828,8 +1832,6 @@
else
sg_remove_scat(sfp, req_schp);
- sg_remove_request(sfp, srp);
-
return ret;
}
@@ -1895,7 +1897,7 @@
num = (rem_sz > scatter_elem_sz_prev) ?
scatter_elem_sz_prev : rem_sz;
- schp->pages[k] = alloc_pages(gfp_mask, order);
+ schp->pages[k] = alloc_pages(gfp_mask | __GFP_ZERO, order);
if (!schp->pages[k])
goto out;
@@ -2066,11 +2068,12 @@
if ((1 == resp->done) && (!resp->sg_io_owned) &&
((-1 == pack_id) || (resp->header.pack_id == pack_id))) {
resp->done = 2; /* guard against other readers */
- break;
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return resp;
}
}
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
- return resp;
+ return NULL;
}
/* always adds to end of list */
@@ -2176,12 +2179,17 @@
struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work);
struct sg_device *sdp = sfp->parentdp;
Sg_request *srp;
+ unsigned long iflags;
/* Cleanup any responses which were never read(). */
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
while (!list_empty(&sfp->rq_list)) {
srp = list_first_entry(&sfp->rq_list, Sg_request, entry);
sg_finish_rem_req(srp);
+ list_del(&srp->entry);
+ srp->parentfp = NULL;
}
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
if (sfp->reserve.bufflen > 0) {
SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp,
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 2bf96d3..d92b280 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -915,10 +915,11 @@
case TEST_UNIT_READY:
break;
default:
- set_host_byte(scmnd, DID_TARGET_FAILURE);
+ set_host_byte(scmnd, DID_ERROR);
}
break;
case SRB_STATUS_INVALID_LUN:
+ set_host_byte(scmnd, DID_NO_CONNECT);
do_work = true;
process_err_fn = storvsc_remove_lun;
break;
@@ -1579,7 +1580,7 @@
.eh_timed_out = storvsc_eh_timed_out,
.slave_alloc = storvsc_device_alloc,
.slave_configure = storvsc_device_configure,
- .cmd_per_lun = 255,
+ .cmd_per_lun = 2048,
.this_id = -1,
.use_clustering = ENABLE_CLUSTERING,
/* Make sure we dont get a sg segment crosses a page boundary */
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
index 6b349e3..c6425e3 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.c
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -536,7 +536,7 @@
* Look for the greatest clock divisor that allows an
* input speed faster than the period.
*/
- while (div-- > 0)
+ while (--div > 0)
if (kpc >= (div_10M[div] << 2)) break;
/*
diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c
index b1c86d4..d4fe6ee 100644
--- a/drivers/scsi/ufs/ufs-qcom-ice.c
+++ b/drivers/scsi/ufs/ufs-qcom-ice.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -172,17 +172,15 @@
static void ufs_qcom_ice_cfg_work(struct work_struct *work)
{
unsigned long flags;
- struct ice_data_setting ice_set;
struct ufs_qcom_host *qcom_host =
container_of(work, struct ufs_qcom_host, ice_cfg_work);
- struct request *req_pending = NULL;
if (!qcom_host->ice.vops->config_start)
return;
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
- req_pending = qcom_host->req_pending;
- if (!req_pending) {
+ if (!qcom_host->req_pending) {
+ qcom_host->work_pending = false;
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
return;
}
@@ -191,24 +189,15 @@
/*
* config_start is called again as previous attempt returned -EAGAIN,
* this call shall now take care of the necessary key setup.
- * 'ice_set' will not actually be used, instead the next call to
- * config_start() for this request, in the normal call flow, will
- * succeed as the key has now been setup.
*/
qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
- qcom_host->req_pending, &ice_set, false);
+ qcom_host->req_pending, NULL, false);
spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
qcom_host->req_pending = NULL;
+ qcom_host->work_pending = false;
spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
- /*
- * Resume with requests processing. We assume config_start has been
- * successful, but even if it wasn't we still must resume in order to
- * allow for the request to be retried.
- */
- ufshcd_scsi_unblock_requests(qcom_host->hba);
-
}
/**
@@ -294,18 +283,14 @@
* requires a non-atomic context, this means we should
* call the function again from the worker thread to do
* the configuration. For this request the error will
- * propagate so it will be re-queued and until the
- * configuration is is completed we block further
- * request processing.
+ * propagate so it will be re-queued.
*/
if (err == -EAGAIN) {
dev_dbg(qcom_host->hba->dev,
"%s: scheduling task for ice setup\n",
__func__);
- if (!qcom_host->req_pending) {
- ufshcd_scsi_block_requests(
- qcom_host->hba);
+ if (!qcom_host->work_pending) {
qcom_host->req_pending = cmd->request;
if (!queue_work(ice_workqueue,
@@ -316,10 +301,9 @@
&qcom_host->ice_work_lock,
flags);
- ufshcd_scsi_unblock_requests(
- qcom_host->hba);
return err;
}
+ qcom_host->work_pending = true;
}
} else {
@@ -418,9 +402,7 @@
* requires a non-atomic context, this means we should
* call the function again from the worker thread to do
* the configuration. For this request the error will
- * propagate so it will be re-queued and until the
- * configuration is is completed we block further
- * request processing.
+ * propagate so it will be re-queued.
*/
if (err == -EAGAIN) {
@@ -428,9 +410,8 @@
"%s: scheduling task for ice setup\n",
__func__);
- if (!qcom_host->req_pending) {
- ufshcd_scsi_block_requests(
- qcom_host->hba);
+ if (!qcom_host->work_pending) {
+
qcom_host->req_pending = cmd->request;
if (!queue_work(ice_workqueue,
&qcom_host->ice_cfg_work)) {
@@ -440,10 +421,9 @@
&qcom_host->ice_work_lock,
flags);
- ufshcd_scsi_unblock_requests(
- qcom_host->hba);
return err;
}
+ qcom_host->work_pending = true;
}
} else {
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 39ab28a..93c3af0 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -897,11 +897,18 @@
req = lrbp->cmd->request;
else
return 0;
-
- /* Use request LBA as the DUN value */
- if (req->bio)
- *dun = (req->bio->bi_iter.bi_sector) >>
- UFS_QCOM_ICE_TR_DATA_UNIT_4_KB;
+ /*
+ * Right now ICE do not support variable dun but can be
+ * taken as future enhancement
+ * if (bio_dun(req->bio)) {
+ * dun @bio can be split, so we have to adjust offset
+ * *dun = bio_dun(req->bio);
+ * } else
+ */
+ if (req->bio) {
+ *dun = req->bio->bi_iter.bi_sector;
+ *dun >>= UFS_QCOM_ICE_TR_DATA_UNIT_4_KB;
+ }
ret = ufs_qcom_ice_req_setup(host, lrbp->cmd, cc_index, enable);
@@ -2133,6 +2140,8 @@
dev_err(dev, "%s: ufs_qcom_ice_get_dev failed %d\n",
__func__, err);
goto out_host_free;
+ } else {
+ hba->host->inlinecrypt_support = 1;
}
host->generic_phy = devm_phy_get(dev, "ufsphy");
@@ -2740,7 +2749,7 @@
* the regulators.
*/
if (of_property_read_bool(np, "non-removable") &&
- strlen(android_boot_dev) &&
+ !of_property_read_bool(np, "force-ufshc-probe") &&
strcmp(android_boot_dev, dev_name(dev)))
return -ENODEV;
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index a03ecb0..27f6a05 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -375,6 +375,7 @@
struct work_struct ice_cfg_work;
struct request *req_pending;
struct ufs_vreg *vddp_ref_clk;
+ bool work_pending;
};
static inline u32
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d427fb3..c6cfd18 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5433,7 +5433,7 @@
/* REPORT SUPPORTED OPERATION CODES is not supported */
sdev->no_report_opcodes = 1;
- /* WRITE_SAME command is not supported*/
+ /* WRITE_SAME command is not supported */
sdev->no_write_same = 1;
ufshcd_set_queue_depth(sdev);
@@ -6559,8 +6559,8 @@
u32 mode;
hba = container_of(work, struct ufs_hba, rls_work);
- ufshcd_scsi_block_requests(hba);
pm_runtime_get_sync(hba->dev);
+ ufshcd_scsi_block_requests(hba);
down_write(&hba->lock);
ret = ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
if (ret) {
@@ -7090,7 +7090,10 @@
* To avoid these unnecessary/illegal step we skip to the last error
* handling stage: reset and restore.
*/
- if (lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN)
+ if ((lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN) ||
+ (lrbp->lun == UFS_UPIU_REPORT_LUNS_WLUN) ||
+ (lrbp->lun == UFS_UPIU_BOOT_WLUN) ||
+ (lrbp->lun == UFS_UPIU_RPMB_WLUN))
return ufshcd_eh_host_reset_handler(cmd);
ufshcd_hold_all(hba);
@@ -8093,9 +8096,16 @@
/*
* If we failed to initialize the device or the device is not
* present, turn off the power/clocks etc.
+ * In cases when there's both ufs and emmc present and regualtors
+ * are shared b/w the two, this shouldn't turn-off the regulators
+ * w/o giving emmc a chance to send PON.
+ * Hence schedule a delayed suspend, thus giving enough time to
+ * emmc to vote for the shared regulator.
*/
- if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress)
- pm_runtime_put_sync(hba->dev);
+ if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) {
+ pm_runtime_put_noidle(hba->dev);
+ pm_schedule_suspend(hba->dev, MSEC_PER_SEC * 10);
+ }
trace_ufshcd_init(dev_name(hba->dev), ret,
ktime_to_us(ktime_sub(ktime_get(), start)),
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index c680d76..cbc8e93 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -28,6 +28,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_devinfo.h>
#include <linux/seqlock.h>
#define VIRTIO_SCSI_MEMPOOL_SZ 64
@@ -705,6 +706,28 @@
return virtscsi_tmf(vscsi, cmd);
}
+static int virtscsi_device_alloc(struct scsi_device *sdevice)
+{
+ /*
+ * Passed through SCSI targets (e.g. with qemu's 'scsi-block')
+ * may have transfer limits which come from the host SCSI
+ * controller or something on the host side other than the
+ * target itself.
+ *
+ * To make this work properly, the hypervisor can adjust the
+ * target's VPD information to advertise these limits. But
+ * for that to work, the guest has to look at the VPD pages,
+ * which we won't do by default if it is an SPC-2 device, even
+ * if it does actually support it.
+ *
+ * So, set the blist to always try to read the VPD pages.
+ */
+ sdevice->sdev_bflags = BLIST_TRY_VPD_PAGES;
+
+ return 0;
+}
+
+
/**
* virtscsi_change_queue_depth() - Change a virtscsi target's queue depth
* @sdev: Virtscsi target whose queue depth to change
@@ -776,6 +799,7 @@
.change_queue_depth = virtscsi_change_queue_depth,
.eh_abort_handler = virtscsi_abort,
.eh_device_reset_handler = virtscsi_device_reset,
+ .slave_alloc = virtscsi_device_alloc,
.can_queue = 1024,
.dma_boundary = UINT_MAX,
@@ -795,6 +819,7 @@
.change_queue_depth = virtscsi_change_queue_depth,
.eh_abort_handler = virtscsi_abort,
.eh_device_reset_handler = virtscsi_device_reset,
+ .slave_alloc = virtscsi_device_alloc,
.can_queue = 1024,
.dma_boundary = UINT_MAX,
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 5fb0c29..26dfd3f 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -425,6 +425,7 @@
const u8 *old_wbuf = NULL;
bool report_sat = false;
bool sync_wr = true;
+ bool txn_async = txn->async;
memset(wbuf, 0, sizeof(wbuf));
if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
@@ -769,9 +770,9 @@
msm_slim_put_ctrl(dev);
}
ngd_xfer_ret:
- if (txn->wbuf == wbuf)
+ if (!txn_async && txn->wbuf == wbuf)
txn->wbuf = old_wbuf;
- if (txn->comp == &done)
+ if (!txn_async && txn->comp == &done)
txn->comp = NULL;
return ret ? ret : dev->err;
}
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
index 119054b..2caacd9 100644
--- a/drivers/soc/fsl/qbman/qman.c
+++ b/drivers/soc/fsl/qbman/qman.c
@@ -2429,39 +2429,21 @@
struct completion completion;
};
-static int qman_delete_cgr_thread(void *p)
+static void qman_delete_cgr_smp_call(void *p)
{
- struct cgr_comp *cgr_comp = (struct cgr_comp *)p;
- int ret;
-
- ret = qman_delete_cgr(cgr_comp->cgr);
- complete(&cgr_comp->completion);
-
- return ret;
+ qman_delete_cgr((struct qman_cgr *)p);
}
void qman_delete_cgr_safe(struct qman_cgr *cgr)
{
- struct task_struct *thread;
- struct cgr_comp cgr_comp;
-
preempt_disable();
if (qman_cgr_cpus[cgr->cgrid] != smp_processor_id()) {
- init_completion(&cgr_comp.completion);
- cgr_comp.cgr = cgr;
- thread = kthread_create(qman_delete_cgr_thread, &cgr_comp,
- "cgr_del");
-
- if (IS_ERR(thread))
- goto out;
-
- kthread_bind(thread, qman_cgr_cpus[cgr->cgrid]);
- wake_up_process(thread);
- wait_for_completion(&cgr_comp.completion);
+ smp_call_function_single(qman_cgr_cpus[cgr->cgrid],
+ qman_delete_cgr_smp_call, cgr, true);
preempt_enable();
return;
}
-out:
+
qman_delete_cgr(cgr);
preempt_enable();
}
diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c
index 2707a82..5482302 100644
--- a/drivers/soc/fsl/qe/qe.c
+++ b/drivers/soc/fsl/qe/qe.c
@@ -163,11 +163,15 @@
*/
static unsigned int brg_clk = 0;
+#define CLK_GRAN (1000)
+#define CLK_GRAN_LIMIT (5)
+
unsigned int qe_get_brg_clk(void)
{
struct device_node *qe;
int size;
const u32 *prop;
+ unsigned int mod;
if (brg_clk)
return brg_clk;
@@ -185,6 +189,15 @@
of_node_put(qe);
+ /* round this if near to a multiple of CLK_GRAN */
+ mod = brg_clk % CLK_GRAN;
+ if (mod) {
+ if (mod < CLK_GRAN_LIMIT)
+ brg_clk -= mod;
+ else if (mod > (CLK_GRAN - CLK_GRAN_LIMIT))
+ brg_clk += CLK_GRAN - mod;
+ }
+
return brg_clk;
}
EXPORT_SYMBOL(qe_get_brg_clk);
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 5fafaca..3ecca59 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -684,6 +684,17 @@
determines last CPU to call into PSCI for cluster Low power
modes.
+config MSM_PM_LEGACY
+ depends on PM
+ select MSM_IDLE_STATS if DEBUG_FS
+ select CPU_IDLE_MULTIPLE_DRIVERS
+ bool "Qualcomm platform specific Legacy PM driver"
+ help
+ Platform specific legacy power driver to manage
+ cores and l2 low power modes. It interface with
+ various system driver and put the cores into
+ low power modes.
+
config MSM_NOPM
default y if !PM
bool
@@ -700,7 +711,7 @@
trusted apps, unloading them and marshalling buffers to the
trusted fingerprint app.
-if MSM_PM
+if (MSM_PM || MSM_PM_LEGACY)
menuconfig MSM_IDLE_STATS
bool "Collect idle statistics"
help
@@ -733,7 +744,7 @@
histogram. This is for collecting statistics on suspend.
endif # MSM_IDLE_STATS
-endif # MSM_PM
+endif # MSM_PM || MSM_PM_LEGACY
config QCOM_DCC_V2
bool "Qualcomm Technologies Data Capture and Compare engine support for V2"
@@ -868,3 +879,11 @@
interrupt event and event data.
source "drivers/soc/qcom/wcnss/Kconfig"
+
+config BIG_CLUSTER_MIN_FREQ_ADJUST
+ bool "Adjust BIG cluster min frequency based on power collapse state"
+ default n
+ help
+ This driver is used to set the floor of the min frequency of big cluster
+ to the user specified value when the cluster is not power collapsed. When
+ the cluster is power collpsed it resets the value to physical limits.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index b83f554..0b71121 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -14,6 +14,7 @@
obj-$(CONFIG_QCOM_SMD) += smd.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
obj-$(CONFIG_QCOM_SMEM) += smem.o
+obj-$(CONFIG_MSM_PM_LEGACY) += pm-boot.o msm-pm.o
obj-$(CONFIG_MSM_SPM) += msm-spm.o spm_devices.o
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
@@ -105,3 +106,4 @@
obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o
obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o
obj-$(CONFIG_WCNSS_CORE) += wcnss/
+obj-$(CONFIG_BIG_CLUSTER_MIN_FREQ_ADJUST) += big_cluster_min_freq_adjust.o
diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c
index fdfd7b7..ffba372 100644
--- a/drivers/soc/qcom/bg_rsb.c
+++ b/drivers/soc/qcom/bg_rsb.c
@@ -33,7 +33,7 @@
#define BGRSB_GLINK_INTENT_SIZE 0x04
#define BGRSB_MSG_SIZE 0x08
-#define TIMEOUT_MS 500
+#define TIMEOUT_MS 2000
#define BGRSB_LDO15_VTG_MIN_UV 3300000
#define BGRSB_LDO15_VTG_MAX_UV 3300000
@@ -122,6 +122,8 @@
struct device *ldev;
+ struct wakeup_source bgrsb_ws;
+
wait_queue_head_t link_state_wait;
uint32_t calbrtion_intrvl;
@@ -462,6 +464,7 @@
if (!dev->chnl_state)
return -ENODEV;
+ __pm_stay_awake(&dev->bgrsb_ws);
mutex_lock(&dev->glink_mutex);
init_completion(&dev->tx_done);
init_completion(&dev->bg_resp_cmplt);
@@ -507,6 +510,7 @@
err_ret:
mutex_unlock(&dev->glink_mutex);
+ __pm_relax(&dev->bgrsb_ws);
return rc;
}
@@ -544,7 +548,7 @@
rc = wait_event_timeout(dev->link_state_wait,
(dev->chnl_state == true),
- msecs_to_jiffies(TIMEOUT_MS*2));
+ msecs_to_jiffies(TIMEOUT_MS));
if (rc == 0) {
pr_err("Glink channel connection time out\n");
return;
@@ -574,7 +578,7 @@
rc = wait_event_timeout(dev->link_state_wait,
(dev->chnl_state == true),
- msecs_to_jiffies(TIMEOUT_MS*2));
+ msecs_to_jiffies(TIMEOUT_MS));
if (rc == 0) {
pr_err("Glink channel connection time out\n");
return;
@@ -904,6 +908,9 @@
if (!dev)
return -ENOMEM;
+ /* Add wake lock for PM suspend */
+ wakeup_source_init(&dev->bgrsb_ws, "BGRSB_wake_lock");
+
dev->bgrsb_current_state = BGRSB_STATE_UNKNOWN;
rc = bgrsb_init(dev);
if (rc)
@@ -964,13 +971,14 @@
destroy_workqueue(dev->bgrsb_event_wq);
destroy_workqueue(dev->bgrsb_wq);
input_free_device(dev->input);
+ wakeup_source_trash(&dev->bgrsb_ws);
return 0;
}
-static int bg_rsb_resume(struct platform_device *pdev)
+static int bg_rsb_resume(struct device *pldev)
{
- int rc;
+ struct platform_device *pdev = to_platform_device(pldev);
struct bgrsb_priv *dev = platform_get_drvdata(pdev);
if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED)
@@ -978,12 +986,6 @@
if (dev->bgrsb_current_state == BGRSB_STATE_INIT) {
if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) {
- rc = bgrsb_configr_rsb(dev, true);
- if (rc != 0) {
- pr_err("BG failed to configure RSB %d\n", rc);
- bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11);
- return rc;
- }
dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
pr_debug("RSB Cofigured\n");
return 0;
@@ -993,8 +995,9 @@
return -EINVAL;
}
-static int bg_rsb_suspend(struct platform_device *pdev, pm_message_t state)
+static int bg_rsb_suspend(struct device *pldev)
{
+ struct platform_device *pdev = to_platform_device(pldev);
struct bgrsb_priv *dev = platform_get_drvdata(pdev);
if (dev->bgrsb_current_state == BGRSB_STATE_INIT)
@@ -1021,15 +1024,19 @@
{ }
};
+static const struct dev_pm_ops pm_rsb = {
+ .resume = bg_rsb_resume,
+ .suspend = bg_rsb_suspend,
+};
+
static struct platform_driver bg_rsb_driver = {
.driver = {
.name = "bg-rsb",
.of_match_table = bg_rsb_of_match,
+ .pm = &pm_rsb,
},
.probe = bg_rsb_probe,
.remove = bg_rsb_remove,
- .resume = bg_rsb_resume,
- .suspend = bg_rsb_suspend,
};
module_platform_driver(bg_rsb_driver);
diff --git a/drivers/soc/qcom/bgcom_interface.c b/drivers/soc/qcom/bgcom_interface.c
index efef26d..1cde8c6 100644
--- a/drivers/soc/qcom/bgcom_interface.c
+++ b/drivers/soc/qcom/bgcom_interface.c
@@ -43,7 +43,8 @@
#define BGDAEMON_LDO03_LPM_VTG 0
#define BGDAEMON_LDO03_NPM_VTG 10000
-#define MPPS_DOWN_EVENT_TO_BG_TIMEOUT 100
+#define MPPS_DOWN_EVENT_TO_BG_TIMEOUT 3000
+#define SLEEP_FOR_SPI_BUS 2000
enum {
SSR_DOMAIN_BG,
@@ -93,6 +94,7 @@
static dev_t bg_dev;
static int device_open;
static void *handle;
+static bool twm_exit;
static struct bgcom_open_config_type config_type;
static DECLARE_COMPLETION(bg_modem_down_wait);
@@ -353,6 +355,8 @@
break;
case SET_SPI_BUSY:
ret = bgcom_set_spi_state(BGCOM_SPI_BUSY);
+ /* Add sleep for SPI Bus to release*/
+ msleep(SLEEP_FOR_SPI_BUS);
break;
case BG_SOFT_RESET:
ret = bg_soft_reset();
@@ -360,6 +364,10 @@
case BG_MODEM_DOWN2_BG_DONE:
ret = modem_down2_bg();
break;
+ case BG_TWM_EXIT:
+ twm_exit = true;
+ ret = 0;
+ break;
default:
ret = -ENOIOCTLCMD;
}
@@ -515,6 +523,10 @@
bgcom_set_spi_state(BGCOM_SPI_BUSY);
send_uevent(&bge);
break;
+ case SUBSYS_AFTER_SHUTDOWN:
+ /* Add sleep for SPI Bus to release*/
+ msleep(SLEEP_FOR_SPI_BUS);
+ break;
case SUBSYS_AFTER_POWERUP:
bge.e_type = BG_AFTER_POWER_UP;
bgdaemon_ldowork(DISABLE_LDO03);
@@ -555,6 +567,16 @@
return NOTIFY_DONE;
}
+bool is_twm_exit(void)
+{
+ if (twm_exit) {
+ twm_exit = false;
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(is_twm_exit);
+
static struct notifier_block ssr_modem_nb = {
.notifier_call = ssr_modem_cb,
.priority = 0,
diff --git a/drivers/soc/qcom/bgcom_interface.h b/drivers/soc/qcom/bgcom_interface.h
index 500ca6d..235995e 100644
--- a/drivers/soc/qcom/bgcom_interface.h
+++ b/drivers/soc/qcom/bgcom_interface.h
@@ -13,10 +13,17 @@
#ifndef BGCOM_INTERFACE_H
#define BGCOM_INTERFACE_H
-/**
+/*
* bg_soft_reset() - soft reset Blackghost
* Return 0 on success or -Ve on error
*/
int bg_soft_reset(void);
+/*
+ * is_twm_exit()
+ * Return true if device is booting up on TWM exit.
+ * value is auto cleared once read.
+ */
+bool is_twm_exit(void);
+
#endif /* BGCOM_INTERFACE_H */
diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c
index b8e3b84..d2dc05f 100644
--- a/drivers/soc/qcom/bgcom_spi.c
+++ b/drivers/soc/qcom/bgcom_spi.c
@@ -25,6 +25,7 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
#include "bgcom.h"
#include "bgrsb.h"
#include "bgcom_interface.h"
@@ -43,12 +44,11 @@
#define BG_SPI_MAX_WORDS (0x3FFFFFFD)
#define BG_SPI_MAX_REGS (0x0A)
-#define SLEEP_IN_STATE_CHNG 2000
#define HED_EVENT_ID_LEN (0x02)
#define HED_EVENT_SIZE_LEN (0x02)
#define HED_EVENT_DATA_STRT_LEN (0x05)
-#define MAX_RETRY 200
+#define MAX_RETRY 500
enum bgcom_state {
/*BGCOM Staus ready*/
@@ -63,6 +63,7 @@
BGCOM_READ_REG = 0,
BGCOM_READ_FIFO = 1,
BGCOM_READ_AHB = 2,
+ BGCOM_WRITE_REG = 3,
};
struct bg_spi_priv {
@@ -112,6 +113,17 @@
static struct mutex bg_resume_mutex;
+static atomic_t bg_is_spi_active;
+static int bg_irq;
+
+static struct spi_device *get_spi_device(void)
+{
+ struct bg_spi_priv *bg_spi = container_of(bg_com_drv,
+ struct bg_spi_priv, lhandle);
+ struct spi_device *spi = bg_spi->spi;
+ return spi;
+}
+
static void augmnt_fifo(uint8_t *data, int pos)
{
data[pos] = '\0';
@@ -149,8 +161,6 @@
mutex_lock(&bg_spi->xfer_mutex);
spi_state = state;
- if (spi_state == BGCOM_SPI_BUSY)
- msleep(SLEEP_IN_STATE_CHNG);
mutex_unlock(&bg_spi->xfer_mutex);
return 0;
}
@@ -197,6 +207,10 @@
case BGCOM_READ_FIFO:
ret = bgcom_fifo_read(&clnt_handle, no_of_words, buf);
break;
+ case BGCOM_WRITE_REG:
+ ret = bgcom_reg_write(&clnt_handle, BG_CMND_REG,
+ no_of_words, buf);
+ break;
case BGCOM_READ_AHB:
break;
}
@@ -232,6 +246,9 @@
tx_xfer = &bg_spi->xfer1;
spi = bg_spi->spi;
+ if (!atomic_read(&bg_is_spi_active))
+ return -ECANCELED;
+
mutex_lock(&bg_spi->xfer_mutex);
bg_spi_reinit_xfer(tx_xfer);
tx_xfer->tx_buf = tx_buf;
@@ -443,6 +460,7 @@
int bgcom_ahb_read(void *handle, uint32_t ahb_start_addr,
uint32_t num_words, void *read_buf)
{
+ dma_addr_t dma_hndl_tx, dma_hndl_rx;
uint32_t txn_len;
uint8_t *tx_buf;
uint8_t *rx_buf;
@@ -450,6 +468,7 @@
int ret;
uint8_t cmnd = 0;
uint32_t ahb_addr = 0;
+ struct spi_device *spi = get_spi_device();
if (!handle || !read_buf || num_words == 0
|| num_words > BG_SPI_MAX_WORDS) {
@@ -472,15 +491,16 @@
size = num_words*BG_SPI_WORD_SIZE;
txn_len = BG_SPI_AHB_READ_CMD_LEN + size;
- tx_buf = kzalloc(txn_len, GFP_KERNEL);
+ tx_buf = dma_zalloc_coherent(&spi->dev, txn_len,
+ &dma_hndl_tx, GFP_KERNEL);
if (!tx_buf)
return -ENOMEM;
- rx_buf = kzalloc(txn_len, GFP_KERNEL);
-
+ rx_buf = dma_zalloc_coherent(&spi->dev, txn_len,
+ &dma_hndl_rx, GFP_KERNEL);
if (!rx_buf) {
- kfree(tx_buf);
+ dma_free_coherent(&spi->dev, txn_len, tx_buf, dma_hndl_tx);
return -ENOMEM;
}
@@ -495,8 +515,8 @@
if (!ret)
memcpy(read_buf, rx_buf+BG_SPI_AHB_READ_CMD_LEN, size);
- kfree(tx_buf);
- kfree(rx_buf);
+ dma_free_coherent(&spi->dev, txn_len, tx_buf, dma_hndl_tx);
+ dma_free_coherent(&spi->dev, txn_len, rx_buf, dma_hndl_rx);
return ret;
}
EXPORT_SYMBOL(bgcom_ahb_read);
@@ -504,12 +524,14 @@
int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr,
uint32_t num_words, void *write_buf)
{
+ dma_addr_t dma_hndl;
uint32_t txn_len;
uint8_t *tx_buf;
uint32_t size;
int ret;
uint8_t cmnd = 0;
uint32_t ahb_addr = 0;
+ struct spi_device *spi = get_spi_device();
if (!handle || !write_buf || num_words == 0
|| num_words > BG_SPI_MAX_WORDS) {
@@ -532,9 +554,7 @@
size = num_words*BG_SPI_WORD_SIZE;
txn_len = BG_SPI_AHB_CMD_LEN + size;
-
- tx_buf = kzalloc(txn_len, GFP_KERNEL);
-
+ tx_buf = dma_zalloc_coherent(&spi->dev, txn_len, &dma_hndl, GFP_KERNEL);
if (!tx_buf)
return -ENOMEM;
@@ -546,7 +566,7 @@
memcpy(tx_buf+BG_SPI_AHB_CMD_LEN, write_buf, size);
ret = bgcom_transfer(handle, tx_buf, NULL, txn_len);
- kfree(tx_buf);
+ dma_free_coherent(&spi->dev, txn_len, tx_buf, dma_hndl);
return ret;
}
EXPORT_SYMBOL(bgcom_ahb_write);
@@ -621,6 +641,11 @@
return -EBUSY;
}
+ if (bgcom_resume(handle)) {
+ pr_err("Failed to resume\n");
+ return -EBUSY;
+ }
+
size = num_words*BG_SPI_WORD_SIZE;
txn_len = BG_SPI_READ_LEN + size;
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
@@ -671,6 +696,11 @@
return -EBUSY;
}
+ if (bgcom_resume(handle)) {
+ pr_err("Failed to resume\n");
+ return -EBUSY;
+ }
+
size = num_regs*BG_SPI_WORD_SIZE;
txn_len = BG_SPI_WRITE_CMND_LEN + size;
@@ -749,11 +779,18 @@
uint8_t rx_buf[8] = {0};
uint32_t cmnd_reg = 0;
+ if (spi_state == BGCOM_SPI_BUSY) {
+ printk_ratelimited("SPI is held by TZ\n");
+ goto ret_err;
+ }
+
txn_len = 0x08;
tx_buf[0] = 0x05;
ret = bgcom_transfer(handle, tx_buf, rx_buf, txn_len);
if (!ret)
memcpy(&cmnd_reg, rx_buf+BG_SPI_READ_LEN, 0x04);
+
+ret_err:
return cmnd_reg & BIT(31);
}
@@ -766,6 +803,9 @@
if (handle == NULL)
return -EINVAL;
+ if (!atomic_read(&bg_is_spi_active))
+ return -ECANCELED;
+
cntx = (struct bg_context *)handle;
bg_spi = cntx->bg_spi;
@@ -789,36 +829,15 @@
bg_soft_reset();
return -ETIMEDOUT;
}
- pr_info("BG retries for wake up : %d\n", retry);
return 0;
}
EXPORT_SYMBOL(bgcom_resume);
int bgcom_suspend(void *handle)
{
- struct bg_spi_priv *bg_spi;
- struct bg_context *cntx;
- uint32_t cmnd_reg = 0;
- int ret = 0;
-
- if (handle == NULL)
+ if (!handle)
return -EINVAL;
-
- cntx = (struct bg_context *)handle;
- bg_spi = cntx->bg_spi;
- mutex_lock(&bg_resume_mutex);
- if (bg_spi->bg_state == BGCOM_STATE_SUSPEND)
- goto unlock;
-
- cmnd_reg |= BIT(31);
- ret = bgcom_reg_write(handle, BG_CMND_REG, 1, &cmnd_reg);
- if (ret == 0)
- bg_spi->bg_state = BGCOM_STATE_SUSPEND;
-
-unlock:
- mutex_unlock(&bg_resume_mutex);
- pr_info("suspended with : %d\n", ret);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(bgcom_suspend);
@@ -931,7 +950,6 @@
struct bg_spi_priv *bg_spi;
struct device_node *node;
int irq_gpio = 0;
- int bg_irq = 0;
int ret;
bg_spi = devm_kzalloc(&spi->dev, sizeof(*bg_spi),
@@ -969,6 +987,8 @@
if (ret)
goto err_ret;
+ atomic_set(&bg_is_spi_active, 1);
+ dma_set_coherent_mask(&spi->dev, DMA_BIT_MASK(64));
pr_info("Bgcom Probed successfully\n");
return ret;
@@ -990,6 +1010,46 @@
return 0;
}
+static int bgcom_pm_suspend(struct device *dev)
+{
+ uint32_t cmnd_reg = 0;
+ struct spi_device *s_dev = to_spi_device(dev);
+ struct bg_spi_priv *bg_spi = spi_get_drvdata(s_dev);
+ int ret = 0;
+
+ if (bg_spi->bg_state == BGCOM_STATE_SUSPEND)
+ return 0;
+
+ cmnd_reg |= BIT(31);
+ ret = read_bg_locl(BGCOM_WRITE_REG, 1, &cmnd_reg);
+ if (ret == 0) {
+ bg_spi->bg_state = BGCOM_STATE_SUSPEND;
+ atomic_set(&bg_is_spi_active, 0);
+ }
+ pr_info("suspended with : %d\n", ret);
+ return ret;
+}
+
+static int bgcom_pm_resume(struct device *dev)
+{
+ struct bg_context clnt_handle;
+ int ret;
+ struct bg_spi_priv *spi =
+ container_of(bg_com_drv, struct bg_spi_priv, lhandle);
+
+ clnt_handle.bg_spi = spi;
+ atomic_set(&bg_is_spi_active, 1);
+ ret = bgcom_resume(&clnt_handle);
+ if (ret == 0)
+ pr_info("Bgcom resumed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops bgcom_pm = {
+ .suspend = bgcom_pm_suspend,
+ .resume = bgcom_pm_resume,
+};
+
static const struct of_device_id bg_spi_of_match[] = {
{ .compatible = "qcom,bg-spi", },
{ }
@@ -1000,6 +1060,7 @@
.driver = {
.name = "bg-spi",
.of_match_table = bg_spi_of_match,
+ .pm = &bgcom_pm,
},
.probe = bg_spi_probe,
.remove = bg_spi_remove,
diff --git a/drivers/soc/qcom/big_cluster_min_freq_adjust.c b/drivers/soc/qcom/big_cluster_min_freq_adjust.c
new file mode 100644
index 0000000..dbc89e1
--- /dev/null
+++ b/drivers/soc/qcom/big_cluster_min_freq_adjust.c
@@ -0,0 +1,278 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "big_min_freq_adjust: " fmt
+
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/types.h>
+#include <linux/smp.h>
+#include <linux/moduleparam.h>
+
+enum min_freq_adjust {
+ ADJUST_MIN_FLOOR, /* Set min floor to user supplied value */
+ RESET_MIN_FLOOR, /* Reset min floor cpuinfo value */
+};
+
+struct big_min_freq_adjust_data {
+ struct cpumask cluster_cpumask;
+ unsigned int min_freq_floor;
+ struct delayed_work min_freq_work;
+ unsigned long min_down_delay_jiffies;
+ enum min_freq_adjust min_freq_state;
+ enum min_freq_adjust min_freq_request;
+ spinlock_t lock;
+ bool big_min_freq_on;
+ bool is_init;
+};
+static struct big_min_freq_adjust_data big_min_freq_adjust_data;
+
+static void cpufreq_min_freq_work(struct work_struct *work)
+{
+ struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+
+ spin_lock(&p->lock);
+ if (p->min_freq_state == p->min_freq_request) {
+ spin_unlock(&p->lock);
+ return;
+ }
+ spin_unlock(&p->lock);
+ cpufreq_update_policy(cpumask_first(&p->cluster_cpumask));
+}
+
+static int cpufreq_callback(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+ struct cpufreq_policy *policy = data;
+ unsigned int min_freq_floor;
+
+ if (p->big_min_freq_on == false)
+ return NOTIFY_DONE;
+
+ if (val != CPUFREQ_ADJUST)
+ return NOTIFY_DONE;
+
+ if (!cpumask_test_cpu(cpumask_first(&p->cluster_cpumask),
+ policy->related_cpus))
+ return NOTIFY_DONE;
+
+ spin_lock(&p->lock);
+ if (p->min_freq_request == ADJUST_MIN_FLOOR)
+ min_freq_floor = p->min_freq_floor;
+ else
+ min_freq_floor = policy->cpuinfo.min_freq;
+ cpufreq_verify_within_limits(policy, min_freq_floor,
+ policy->cpuinfo.max_freq);
+ p->min_freq_state = p->min_freq_request;
+ spin_unlock(&p->lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_nb = {
+ .notifier_call = cpufreq_callback
+};
+
+#define AFFINITY_LEVEL_L2 1
+static int cpu_pm_callback(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+ unsigned long aff_level = (unsigned long) v;
+ unsigned long delay;
+ int cpu;
+
+ if (p->big_min_freq_on == false)
+ return NOTIFY_DONE;
+
+ if (aff_level != AFFINITY_LEVEL_L2)
+ return NOTIFY_DONE;
+
+ cpu = smp_processor_id();
+
+ if (!cpumask_test_cpu(cpu, &p->cluster_cpumask))
+ return NOTIFY_DONE;
+
+ spin_lock(&p->lock);
+ switch (cmd) {
+ case CPU_CLUSTER_PM_ENTER:
+ p->min_freq_request = RESET_MIN_FLOOR;
+ delay = p->min_down_delay_jiffies;
+ break;
+ case CPU_CLUSTER_PM_ENTER_FAILED:
+ case CPU_CLUSTER_PM_EXIT:
+ p->min_freq_request = ADJUST_MIN_FLOOR;
+ /* To avoid unnecessary oscillations between exit and idle */
+ delay = 1;
+ break;
+ default:
+ spin_unlock(&p->lock);
+ return NOTIFY_DONE;
+ }
+
+ cancel_delayed_work(&p->min_freq_work);
+
+ if (p->min_freq_state != p->min_freq_request)
+ schedule_delayed_work(&p->min_freq_work, delay);
+ spin_unlock(&p->lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpu_pm_nb = {
+ .notifier_call = cpu_pm_callback
+};
+
+static unsigned long __read_mostly big_min_down_delay_ms;
+#define MIN_DOWN_DELAY_MSEC 80 /* Default big_min_down_delay in msec */
+#define POLICY_MIN 1094400 /* Default min_freq_floor in KHz */
+
+static void trigger_state_machine(void *d)
+{
+ struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+ bool *update_policy = d;
+
+ if (p->min_freq_request != ADJUST_MIN_FLOOR) {
+ p->min_freq_request = ADJUST_MIN_FLOOR;
+ *update_policy = true;
+ }
+}
+
+static int enable_big_min_freq_adjust(void)
+{
+ struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+ int ret;
+ bool update_policy = false;
+
+ if (p->big_min_freq_on == true)
+ return 0;
+
+ INIT_DEFERRABLE_WORK(&p->min_freq_work, cpufreq_min_freq_work);
+
+ cpumask_clear(&p->cluster_cpumask);
+ cpumask_set_cpu(4, &p->cluster_cpumask);
+ cpumask_set_cpu(5, &p->cluster_cpumask);
+ cpumask_set_cpu(6, &p->cluster_cpumask);
+ cpumask_set_cpu(7, &p->cluster_cpumask);
+
+ if (!big_min_down_delay_ms) {
+ big_min_down_delay_ms = MIN_DOWN_DELAY_MSEC;
+ p->min_down_delay_jiffies = msecs_to_jiffies(
+ big_min_down_delay_ms);
+ }
+ if (!p->min_freq_floor)
+ p->min_freq_floor = POLICY_MIN;
+
+ ret = cpu_pm_register_notifier(&cpu_pm_nb);
+ if (ret) {
+ pr_err("Failed to register for PM notification\n");
+ return ret;
+ }
+
+ ret = cpufreq_register_notifier(&cpufreq_nb, CPUFREQ_POLICY_NOTIFIER);
+ if (ret) {
+ pr_err("Failed to register for CPUFREQ POLICY notification\n");
+ cpu_pm_unregister_notifier(&cpu_pm_nb);
+ return ret;
+ }
+
+ p->min_freq_state = RESET_MIN_FLOOR;
+ p->min_freq_request = RESET_MIN_FLOOR;
+ spin_lock_init(&p->lock);
+ p->big_min_freq_on = true;
+
+ /* If BIG cluster is active at this time and continue to be active
+ * forever, in that case min frequency of the cluster will never be
+ * set to floor value. This is to trigger the state machine and set
+ * the min freq and min_freq_state to appropriate values.
+ *
+ * Two possibilities here.
+ * 1) If cluster is idle before this, the wakeup is unnecessary but
+ * the state machine is set to proper state.
+ * 2) If cluster is active before this, the wakeup is necessary and
+ * the state machine is set to proper state.
+ */
+ smp_call_function_any(&p->cluster_cpumask,
+ trigger_state_machine, &update_policy, true);
+ if (update_policy)
+ cpufreq_update_policy(cpumask_first(&p->cluster_cpumask));
+
+ pr_info("big min freq ajustment enabled\n");
+
+ return 0;
+}
+
+static bool __read_mostly big_min_freq_adjust_enabled;
+
+static int set_big_min_freq_adjust(const char *buf,
+ const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_bool_enable_only(buf, kp);
+ if (ret) {
+ pr_err("Unable to set big_min_freq_adjust_enabled: %d\n", ret);
+ return ret;
+ }
+
+ if (!big_min_freq_adjust_data.is_init)
+ return ret;
+
+ return enable_big_min_freq_adjust();
+}
+
+static const struct kernel_param_ops param_ops_big_min_freq_adjust = {
+ .set = set_big_min_freq_adjust,
+ .get = param_get_bool,
+};
+module_param_cb(min_freq_adjust, ¶m_ops_big_min_freq_adjust,
+ &big_min_freq_adjust_enabled, 0644);
+
+module_param_named(min_freq_floor, big_min_freq_adjust_data.min_freq_floor,
+ uint, 0644);
+
+static int set_min_down_delay_ms(const char *buf, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_ulong(buf, kp);
+ if (ret) {
+ pr_err("Unable to set big_min_down_delay_ms: %d\n", ret);
+ return ret;
+ }
+
+ big_min_freq_adjust_data.min_down_delay_jiffies = msecs_to_jiffies(
+ big_min_down_delay_ms);
+
+ return 0;
+}
+
+static const struct kernel_param_ops param_ops_big_min_down_delay_ms = {
+ .set = set_min_down_delay_ms,
+ .get = param_get_ulong,
+};
+module_param_cb(min_down_delay_ms, ¶m_ops_big_min_down_delay_ms,
+ &big_min_down_delay_ms, 0644);
+
+static int __init big_min_freq_adjust_init(void)
+{
+ big_min_freq_adjust_data.is_init = true;
+ if (!big_min_freq_adjust_enabled)
+ return 0;
+
+ return enable_big_min_freq_adjust();
+}
+late_initcall(big_min_freq_adjust_init);
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index c9dc547..23fde2e 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -365,6 +365,7 @@
res.start = readl_relaxed(dict);
res.end = res.start + readl_relaxed(dict + 0x4);
res.flags = IORESOURCE_MEM;
+ res.name = NULL;
iounmap(dict);
start_addr = devm_ioremap_resource(&pdev->dev, &res);
diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
index a687286..bef0757 100644
--- a/drivers/soc/qcom/dcc.c
+++ b/drivers/soc/qcom/dcc.c
@@ -731,7 +731,8 @@
mutex_lock(&drvdata->mutex);
- if (!len) {
+ /* Check the len to avoid allocate huge memory */
+ if (!len || len > (drvdata->ram_size / 8)) {
dev_err(drvdata->dev, "DCC: Invalid length!\n");
ret = -EINVAL;
goto err;
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index b8deec1..c976f17 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -1775,6 +1775,18 @@
return tx_pkt;
}
}
+ list_for_each_entry(tx_pkt, &ctx->tx_active, list_node) {
+ if (tx_pkt->riid == riid) {
+ if (tx_pkt->size_remaining) {
+ GLINK_ERR_CH(ctx, "%s: R[%u] TX not complete",
+ __func__, riid);
+ tx_pkt = NULL;
+ }
+ spin_unlock_irqrestore(
+ &ctx->tx_pending_rmt_done_lock_lhc4, flags);
+ return tx_pkt;
+ }
+ }
spin_unlock_irqrestore(&ctx->tx_pending_rmt_done_lock_lhc4, flags);
GLINK_ERR_CH(ctx, "%s: R[%u] Tx packet for intent not found.\n",
@@ -1817,6 +1829,20 @@
return;
}
}
+ list_for_each_entry_safe(local_tx_pkt, tmp_tx_pkt,
+ &ctx->tx_active, list_node) {
+ if (tx_pkt == local_tx_pkt) {
+ list_del_init(&tx_pkt->list_done);
+ GLINK_DBG_CH(ctx,
+ "%s: R[%u] Removed Tx packet for intent\n",
+ __func__,
+ tx_pkt->riid);
+ rwref_put(&tx_pkt->pkt_ref);
+ spin_unlock_irqrestore(
+ &ctx->tx_pending_rmt_done_lock_lhc4, flags);
+ return;
+ }
+ }
spin_unlock_irqrestore(&ctx->tx_pending_rmt_done_lock_lhc4, flags);
GLINK_ERR_CH(ctx, "%s: R[%u] Tx packet for intent not found", __func__,
@@ -5578,12 +5604,6 @@
tx_info = list_first_entry(&ctx->tx_active,
struct glink_core_tx_pkt, list_node);
rwref_get(&tx_info->pkt_ref);
-
- spin_lock(&ctx->tx_pending_rmt_done_lock_lhc4);
- if (list_empty(&tx_info->list_done))
- list_add(&tx_info->list_done,
- &ctx->tx_pending_remote_done);
- spin_unlock(&ctx->tx_pending_rmt_done_lock_lhc4);
spin_unlock_irqrestore(&ctx->tx_lists_lock_lhc3, flags);
if (unlikely(tx_info->tracer_pkt)) {
@@ -5648,9 +5668,16 @@
txd_len += tx_len;
if (!tx_info->size_remaining) {
num_pkts++;
+ spin_lock(&ctx->tx_pending_rmt_done_lock_lhc4);
list_del_init(&tx_info->list_node);
+ if (list_empty(&tx_info->list_done))
+ list_add(&tx_info->list_done,
+ &ctx->tx_pending_remote_done);
+ rwref_put(&tx_info->pkt_ref);
+ spin_unlock(&ctx->tx_pending_rmt_done_lock_lhc4);
+ } else {
+ rwref_put(&tx_info->pkt_ref);
}
- rwref_put(&tx_info->pkt_ref);
}
ctx->txd_len += txd_len;
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index dda2178..5640666 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -175,8 +175,6 @@
* @kwork: Work to be executed when an irq is received.
* @kworker: Handle to the entity processing of
deferred commands.
- * @tasklet Handle to tasklet to process incoming data
- packets in atomic manner.
* @task: Handle to the task context used to run @kworker.
* @use_ref: Active uses of this transport use this to grab
* a reference. Used for ssr synchronization.
@@ -222,7 +220,6 @@
struct kthread_work kwork;
struct kthread_worker kworker;
struct task_struct *task;
- struct tasklet_struct tasklet;
struct srcu_struct use_ref;
bool in_ssr;
spinlock_t rx_lock;
@@ -952,6 +949,12 @@
return;
}
+ if (!einfo->rx_fifo) {
+ if (!get_rx_fifo(einfo))
+ return;
+ einfo->xprt_if.glink_core_if_ptr->link_up(&einfo->xprt_if);
+ }
+
if ((atomic_ctx) && ((einfo->tx_resume_needed) ||
(waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/
tx_wakeup_worker(einfo);
@@ -1254,18 +1257,6 @@
}
/**
- * rx_worker_atomic() - worker function to process received command in atomic
- * context.
- * @param: The param parameter passed during initialization of the tasklet.
- */
-static void rx_worker_atomic(unsigned long param)
-{
- struct edge_info *einfo = (struct edge_info *)param;
-
- __rx_worker(einfo, true);
-}
-
-/**
* rx_worker() - worker function to process received commands
* @work: kwork associated with the edge to process commands on.
*/
@@ -1284,7 +1275,7 @@
if (einfo->rx_reset_reg)
writel_relaxed(einfo->out_irq_mask, einfo->rx_reset_reg);
- tasklet_hi_schedule(&einfo->tasklet);
+ __rx_worker(einfo, true);
einfo->rx_irq_count++;
return IRQ_HANDLED;
@@ -1569,10 +1560,10 @@
struct edge_info *einfo;
einfo = container_of(if_ptr, struct edge_info, xprt_if);
+ einfo->in_ssr = false;
if (!einfo->rx_fifo) {
if (!get_rx_fifo(einfo))
return;
- einfo->in_ssr = false;
einfo->xprt_if.glink_core_if_ptr->link_up(&einfo->xprt_if);
}
}
@@ -2479,7 +2470,6 @@
init_waitqueue_head(&einfo->tx_blocked_queue);
kthread_init_work(&einfo->kwork, rx_worker);
kthread_init_worker(&einfo->kworker);
- tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
init_srcu_struct(&einfo->use_ref);
@@ -2607,7 +2597,6 @@
kthread_flush_worker(&einfo->kworker);
kthread_stop(einfo->task);
einfo->task = NULL;
- tasklet_kill(&einfo->tasklet);
kthread_fail:
iounmap(einfo->out_irq_reg);
ioremap_fail:
@@ -2693,7 +2682,6 @@
init_waitqueue_head(&einfo->tx_blocked_queue);
kthread_init_work(&einfo->kwork, rx_worker);
kthread_init_worker(&einfo->kworker);
- tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->intentless = true;
einfo->read_from_fifo = memcpy32_fromio;
einfo->write_to_fifo = memcpy32_toio;
@@ -2864,7 +2852,6 @@
kthread_flush_worker(&einfo->kworker);
kthread_stop(einfo->task);
einfo->task = NULL;
- tasklet_kill(&einfo->tasklet);
kthread_fail:
iounmap(msgram);
msgram_ioremap_fail:
@@ -2994,7 +2981,6 @@
init_waitqueue_head(&einfo->tx_blocked_queue);
kthread_init_work(&einfo->kwork, rx_worker);
kthread_init_worker(&einfo->kworker);
- tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
init_srcu_struct(&einfo->use_ref);
@@ -3126,7 +3112,6 @@
kthread_flush_worker(&einfo->kworker);
kthread_stop(einfo->task);
einfo->task = NULL;
- tasklet_kill(&einfo->tasklet);
kthread_fail:
iounmap(einfo->rx_reset_reg);
rx_reset_ioremap_fail:
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 683b074..b8e9268 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2113,6 +2113,7 @@
set_bit(ICNSS_WLFW_EXISTS, &penv->state);
clear_bit(ICNSS_FW_DOWN, &penv->state);
+ icnss_ignore_qmi_timeout(false);
penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
if (!penv->wlfw_clnt) {
@@ -2450,8 +2451,13 @@
int ret = 0;
struct icnss_event_pd_service_down_data *event_data = data;
- if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
+ if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+ icnss_ignore_qmi_timeout(false);
goto out;
+ }
+
+ if (priv->force_err_fatal)
+ ICNSS_ASSERT(0);
if (priv->early_crash_ind) {
icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
@@ -2467,16 +2473,11 @@
goto out;
}
- if (priv->force_err_fatal)
- ICNSS_ASSERT(0);
-
icnss_fw_crashed(priv, event_data);
out:
kfree(data);
- icnss_ignore_qmi_timeout(false);
-
return ret;
}
@@ -2485,15 +2486,16 @@
{
int ret = 0;
- if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
+ if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+ icnss_ignore_qmi_timeout(false);
goto out;
+ }
priv->early_crash_ind = true;
icnss_fw_crashed(priv, NULL);
out:
kfree(data);
- icnss_ignore_qmi_timeout(false);
return ret;
}
@@ -3178,7 +3180,8 @@
if (!dev)
return -ENODEV;
- if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
+ if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
+ !test_bit(ICNSS_FW_READY, &penv->state)) {
icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
penv->state);
return -EINVAL;
@@ -3277,7 +3280,8 @@
if (!dev)
return -ENODEV;
- if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
+ if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
+ !test_bit(ICNSS_FW_READY, &penv->state)) {
icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
penv->state);
return -EINVAL;
diff --git a/drivers/soc/qcom/idle.h b/drivers/soc/qcom/idle.h
new file mode 100644
index 0000000..2f852c3
--- /dev/null
+++ b/drivers/soc/qcom/idle.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2007-2009,2012-2014, 2018, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_IDLE_H_
+#define _ARCH_ARM_MACH_MSM_IDLE_H_
+
+#define MAX_CPUS_PER_CLUSTER 4
+#define MAX_NUM_CLUSTER 4
+
+#ifndef __ASSEMBLY__
+#if defined(CONFIG_CPU_V7) || defined(CONFIG_ARM64)
+extern unsigned long msm_pm_boot_vector[MAX_NUM_CLUSTER * MAX_CPUS_PER_CLUSTER];
+void msm_pm_boot_entry(void);
+#else
+static inline void msm_pm_boot_entry(void) {}
+#endif
+#endif
+#endif
diff --git a/drivers/soc/qcom/jtagv8-etm.c b/drivers/soc/qcom/jtagv8-etm.c
index 3f4b8bc..23cbb7b 100644
--- a/drivers/soc/qcom/jtagv8-etm.c
+++ b/drivers/soc/qcom/jtagv8-etm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -345,7 +345,7 @@
TRCCNTVRn(j));
}
/* resource selection registers */
- for (j = 0; j < etmdata->nr_resource; j++)
+ for (j = 0; j < etmdata->nr_resource * 2; j++)
etmdata->state[i++] = etm_readl(etmdata, TRCRSCTLRn(j));
/* comparator registers */
for (j = 0; j < etmdata->nr_addr_cmp * 2; j++) {
@@ -448,7 +448,7 @@
etm_writel(etmdata, etmdata->state[i++], TRCCNTVRn(j));
}
/* resource selection registers */
- for (j = 0; j < etmdata->nr_resource; j++)
+ for (j = 0; j < etmdata->nr_resource * 2; j++)
etm_writel(etmdata, etmdata->state[i++], TRCRSCTLRn(j));
/* comparator registers */
for (j = 0; j < etmdata->nr_addr_cmp * 2; j++) {
@@ -932,7 +932,7 @@
for (j = 0; j < etmdata->nr_cntr; j++)
i = etm_read_crxr(etmdata->state, i, j);
/* resource selection registers */
- for (j = 0; j < etmdata->nr_resource; j++)
+ for (j = 0; j < etmdata->nr_resource * 2; j++)
i = etm_read_rsxr(etmdata->state, i, j + 2);
/* comparator registers */
for (j = 0; j < etmdata->nr_addr_cmp * 2; j++)
@@ -1387,7 +1387,7 @@
for (j = 0; j < etmdata->nr_cntr; j++)
i = etm_write_crxr(etmdata->state, i, j);
/* resource selection registers */
- for (j = 0; j < etmdata->nr_resource; j++)
+ for (j = 0; j < etmdata->nr_resource * 2; j++)
i = etm_write_rsxr(etmdata->state, i, j + 2);
/* comparator registers */
for (j = 0; j < etmdata->nr_addr_cmp * 2; j++)
@@ -1496,7 +1496,7 @@
val = etm_readl(etmdata, TRCIDR4);
etmdata->nr_addr_cmp = BMVAL(val, 0, 3);
etmdata->nr_data_cmp = BMVAL(val, 4, 7);
- etmdata->nr_resource = BMVAL(val, 16, 19);
+ etmdata->nr_resource = BMVAL(val, 16, 19) + 1;
etmdata->nr_ss_cmp = BMVAL(val, 20, 23);
etmdata->nr_ctxid_cmp = BMVAL(val, 24, 27);
etmdata->nr_vmid_cmp = BMVAL(val, 28, 31);
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index 696c043..6542861 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -406,6 +406,7 @@
memblock[i].peripheral ==
DHMS_MEM_PROC_MPSS_V01 &&
!memblock[i].guarantee &&
+ !memblock[i].client_request &&
memblock[i].allotted &&
!memblock[i].alloc_request) {
pr_debug("memshare: hypervisor unmapping for client id: %d\n",
@@ -665,9 +666,10 @@
__func__);
flag = 1;
} else if (!memblock[client_id].guarantee &&
- memblock[client_id].allotted) {
- pr_debug("memshare: %s: size: %d",
- __func__, memblock[client_id].size);
+ !memblock[client_id].client_request &&
+ memblock[client_id].allotted) {
+ pr_debug("memshare: %s:client_id:%d - size: %d",
+ __func__, client_id, memblock[client_id].size);
ret = hyp_assign_phys(memblock[client_id].phy_addr,
memblock[client_id].size, source_vmlist, 1,
dest_vmids, dest_perms, 1);
@@ -676,8 +678,8 @@
* This is an error case as hyp mapping was successful
* earlier but during unmap it lead to failure.
*/
- pr_err("memshare: %s, failed to unmap the region\n",
- __func__);
+ pr_err("memshare: %s, failed to unmap the region for client id:%d\n",
+ __func__, client_id);
}
size = memblock[client_id].size;
if (memblock[client_id].client_id == 1) {
@@ -696,8 +698,8 @@
attrs);
free_client(client_id);
} else {
- pr_err("memshare: %s, Request came for a guaranteed client cannot free up the memory\n",
- __func__);
+ pr_err("memshare: %s, Request came for a guaranteed client (client_id: %d) cannot free up the memory\n",
+ __func__, client_id);
}
if (flag) {
@@ -992,6 +994,10 @@
pdev->dev.of_node,
"qcom,allocate-boot-time");
+ memblock[num_clients].client_request = of_property_read_bool(
+ pdev->dev.of_node,
+ "qcom,allocate-on-request");
+
rc = of_property_read_string(pdev->dev.of_node, "label",
&name);
if (rc) {
diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h
index 6b54652..908f091 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.h
+++ b/drivers/soc/qcom/memshare/msm_memshare.h
@@ -41,6 +41,8 @@
uint32_t allotted;
/* Memory allocation request received or not */
uint32_t alloc_request;
+ /* Allocation on request from a client*/
+ uint32_t client_request;
/* Size required for client */
uint32_t size;
/*
diff --git a/drivers/soc/qcom/microdump_collector.c b/drivers/soc/qcom/microdump_collector.c
index 4a22b4d..6e9da60 100644
--- a/drivers/soc/qcom/microdump_collector.c
+++ b/drivers/soc/qcom/microdump_collector.c
@@ -48,9 +48,9 @@
crash_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size_reason
, 0, SMEM_ANY_HOST_FLAG);
if (IS_ERR_OR_NULL(crash_reason)) {
- pr_err("%s: Error in getting SMEM_reason pointer\n",
- __func__);
- return -ENODEV;
+ pr_info("%s: smem %d not available\n",
+ __func__, SMEM_SSR_REASON_MSS0);
+ goto out;
}
segment[0].v_address = crash_reason;
@@ -58,9 +58,9 @@
crash_data = smem_get_entry(smem_id, &size_data, SMEM_MODEM, 0);
if (IS_ERR_OR_NULL(crash_data)) {
- pr_err("%s: Error in getting SMEM_data pointer\n",
- __func__);
- return -ENODEV;
+ pr_info("%s: smem %d not available\n ",
+ __func__, smem_id);
+ goto out;
}
segment[1].v_address = crash_data;
@@ -68,10 +68,11 @@
ret = do_ramdump(drv->microdump_dev, segment, 2);
if (ret)
- pr_err("%s: do_ramdump() failed\n", __func__);
+ pr_info("%s: do_ramdump() failed\n", __func__);
}
- return ret;
+out:
+ return NOTIFY_OK;
}
static int microdump_modem_ssr_register_notifier(struct microdump_data *drv)
diff --git a/drivers/soc/qcom/msm-pm.c b/drivers/soc/qcom/msm-pm.c
new file mode 100644
index 0000000..129ebce
--- /dev/null
+++ b/drivers/soc/qcom/msm-pm.c
@@ -0,0 +1,921 @@
+/* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/smp.h>
+#include <linux/tick.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/msm-bus.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm-legacy.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+#include <asm/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+#ifdef CONFIG_VFP
+#include <asm/vfp.h>
+#endif
+#include <soc/qcom/jtag.h>
+#include "pm-boot.h"
+#include "idle.h"
+
+#define SCM_CMD_TERMINATE_PC (0x2)
+#define SCM_CMD_CORE_HOTPLUGGED (0x10)
+#define SCM_FLUSH_FLAG_MASK (0x3)
+
+#define SCLK_HZ (32768)
+
+#define MAX_BUF_SIZE 1024
+
+static int msm_pm_debug_mask = 1;
+module_param_named(
+ debug_mask, msm_pm_debug_mask, int, 0664
+);
+
+enum {
+ MSM_PM_DEBUG_SUSPEND = BIT(0),
+ MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
+ MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2),
+ MSM_PM_DEBUG_CLOCK = BIT(3),
+ MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
+ MSM_PM_DEBUG_IDLE = BIT(5),
+ MSM_PM_DEBUG_IDLE_LIMITS = BIT(6),
+ MSM_PM_DEBUG_HOTPLUG = BIT(7),
+};
+
+enum msm_pc_count_offsets {
+ MSM_PC_ENTRY_COUNTER,
+ MSM_PC_EXIT_COUNTER,
+ MSM_PC_FALLTHRU_COUNTER,
+ MSM_PC_UNUSED,
+ MSM_PC_NUM_COUNTERS,
+};
+
+static bool msm_pm_ldo_retention_enabled = true;
+static bool msm_pm_tz_flushes_cache;
+static bool msm_pm_ret_no_pll_switch;
+static bool msm_no_ramp_down_pc;
+static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
+static DEFINE_PER_CPU(struct clk *, cpu_clks);
+static struct clk *l2_clk;
+
+static long *msm_pc_debug_counters;
+
+static cpumask_t retention_cpus;
+static DEFINE_SPINLOCK(retention_lock);
+static DEFINE_MUTEX(msm_pc_debug_mutex);
+
+static bool msm_pm_is_L1_writeback(void)
+{
+ u32 cache_id = 0;
+
+#if defined(CONFIG_CPU_V7)
+ u32 sel = 0;
+
+ asm volatile ("mcr p15, 2, %[ccselr], c0, c0, 0\n\t"
+ "isb\n\t"
+ "mrc p15, 1, %[ccsidr], c0, c0, 0\n\t"
+ :[ccsidr]"=r" (cache_id)
+ :[ccselr]"r" (sel)
+ );
+ return cache_id & BIT(30);
+#elif defined(CONFIG_ARM64)
+ u32 sel = 0;
+
+ asm volatile("msr csselr_el1, %[ccselr]\n\t"
+ "isb\n\t"
+ "mrs %[ccsidr],ccsidr_el1\n\t"
+ :[ccsidr]"=r" (cache_id)
+ :[ccselr]"r" (sel)
+ );
+ return cache_id & BIT(30);
+#else
+#error No valid CPU arch selected
+#endif
+}
+
+static bool msm_pm_swfi(bool from_idle)
+{
+ msm_arch_idle();
+ return true;
+}
+
+static bool msm_pm_retention(bool from_idle)
+{
+ int ret = 0;
+ unsigned int cpu = smp_processor_id();
+ struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
+
+ spin_lock(&retention_lock);
+
+ if (!msm_pm_ldo_retention_enabled)
+ goto bailout;
+
+ cpumask_set_cpu(cpu, &retention_cpus);
+ spin_unlock(&retention_lock);
+
+ if (!msm_pm_ret_no_pll_switch)
+ clk_disable(cpu_clk);
+
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_RETENTION, false);
+ WARN_ON(ret);
+
+ msm_arch_idle();
+
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+ WARN_ON(ret);
+
+ if (!msm_pm_ret_no_pll_switch)
+ if (clk_enable(cpu_clk))
+ pr_err("%s(): Error restore cpu clk\n", __func__);
+
+ spin_lock(&retention_lock);
+ cpumask_clear_cpu(cpu, &retention_cpus);
+bailout:
+ spin_unlock(&retention_lock);
+ return true;
+}
+
+static inline void msm_pc_inc_debug_count(uint32_t cpu,
+ enum msm_pc_count_offsets offset)
+{
+ int cntr_offset;
+ uint32_t cluster_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+ uint32_t cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+
+ if (cluster_id >= MAX_NUM_CLUSTER || cpu_id >= MAX_CPUS_PER_CLUSTER)
+ WARN_ON(cpu);
+
+ cntr_offset = (cluster_id * MAX_CPUS_PER_CLUSTER * MSM_PC_NUM_COUNTERS)
+ + (cpu_id * MSM_PC_NUM_COUNTERS) + offset;
+
+ if (!msm_pc_debug_counters)
+ return;
+
+ msm_pc_debug_counters[cntr_offset]++;
+}
+
+static bool msm_pm_pc_hotplug(void)
+{
+ uint32_t cpu = smp_processor_id();
+ enum msm_pm_l2_scm_flag flag;
+ struct scm_desc desc;
+
+ flag = lpm_cpu_pre_pc_cb(cpu);
+
+ if (!msm_pm_tz_flushes_cache) {
+ if (flag == MSM_SCM_L2_OFF)
+ flush_cache_all();
+ else if (msm_pm_is_L1_writeback())
+ flush_cache_louis();
+ }
+
+ msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
+
+ if (is_scm_armv8()) {
+ desc.args[0] = SCM_CMD_CORE_HOTPLUGGED |
+ (flag & SCM_FLUSH_FLAG_MASK);
+ desc.arginfo = SCM_ARGS(1);
+ scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
+ SCM_CMD_TERMINATE_PC), &desc);
+ } else {
+ scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
+ SCM_CMD_CORE_HOTPLUGGED | (flag & SCM_FLUSH_FLAG_MASK));
+ }
+
+ /* Should not return here */
+ msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
+ return 0;
+}
+
+static bool msm_pm_fastpc(bool from_idle)
+{
+ int ret = 0;
+ unsigned int cpu = smp_processor_id();
+
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_FASTPC, false);
+ WARN_ON(ret);
+
+ if (from_idle || cpu_online(cpu))
+ msm_arch_idle();
+ else
+ msm_pm_pc_hotplug();
+
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+ WARN_ON(ret);
+
+ return true;
+}
+
+int msm_pm_collapse(unsigned long unused)
+{
+ uint32_t cpu = smp_processor_id();
+ enum msm_pm_l2_scm_flag flag;
+ struct scm_desc desc;
+
+ flag = lpm_cpu_pre_pc_cb(cpu);
+
+ if (!msm_pm_tz_flushes_cache) {
+ if (flag == MSM_SCM_L2_OFF)
+ flush_cache_all();
+ else if (msm_pm_is_L1_writeback())
+ flush_cache_louis();
+ }
+ msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
+
+ if (is_scm_armv8()) {
+ desc.args[0] = flag;
+ desc.arginfo = SCM_ARGS(1);
+ scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
+ SCM_CMD_TERMINATE_PC), &desc);
+ } else {
+ scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
+ }
+
+ msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_pm_collapse);
+
+static bool __ref msm_pm_spm_power_collapse(
+ unsigned int cpu, int mode, bool from_idle, bool notify_rpm)
+{
+ void *entry;
+ bool collapsed = 0;
+ int ret;
+ bool save_cpu_regs = (cpu_online(cpu) || from_idle);
+
+ if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: notify_rpm %d\n",
+ cpu, __func__, (int) notify_rpm);
+
+ ret = msm_spm_set_low_power_mode(mode, notify_rpm);
+ WARN_ON(ret);
+
+ entry = save_cpu_regs ? cpu_resume : msm_secondary_startup;
+
+ msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry));
+
+ if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: program vector to %pk\n",
+ cpu, __func__, entry);
+
+ msm_jtag_save_state();
+
+ collapsed = save_cpu_regs ?
+ !cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
+
+ msm_jtag_restore_state();
+
+ if (collapsed)
+ local_fiq_enable();
+
+ msm_pm_boot_config_after_pc(cpu);
+
+ if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
+ cpu, __func__, collapsed);
+
+ ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+ WARN_ON(ret);
+ return collapsed;
+}
+
+static bool msm_pm_power_collapse_standalone(
+ bool from_idle)
+{
+ unsigned int cpu = smp_processor_id();
+ bool collapsed;
+
+ collapsed = msm_pm_spm_power_collapse(cpu,
+ MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE,
+ from_idle, false);
+
+ return collapsed;
+}
+
+static int ramp_down_last_cpu(int cpu)
+{
+ struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
+ int ret = 0;
+
+ clk_disable(cpu_clk);
+ clk_disable(l2_clk);
+
+ return ret;
+}
+
+static int ramp_up_first_cpu(int cpu, int saved_rate)
+{
+ struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
+ int rc = 0;
+
+ if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: restore clock rate\n",
+ cpu, __func__);
+
+ clk_enable(l2_clk);
+
+ if (cpu_clk) {
+ int ret = clk_enable(cpu_clk);
+
+ if (ret) {
+ pr_err("%s(): Error restoring cpu clk\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return rc;
+}
+
+static bool msm_pm_power_collapse(bool from_idle)
+{
+ unsigned int cpu = smp_processor_id();
+ unsigned long saved_acpuclk_rate = 0;
+ bool collapsed;
+
+ if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: idle %d\n",
+ cpu, __func__, (int)from_idle);
+
+ if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
+
+ if (cpu_online(cpu) && !msm_no_ramp_down_pc)
+ saved_acpuclk_rate = ramp_down_last_cpu(cpu);
+
+ collapsed = msm_pm_spm_power_collapse(cpu, MSM_SPM_MODE_POWER_COLLAPSE,
+ from_idle, true);
+
+ if (cpu_online(cpu) && !msm_no_ramp_down_pc)
+ ramp_up_first_cpu(cpu, saved_acpuclk_rate);
+
+ if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: post power up\n", cpu, __func__);
+
+ if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+ pr_info("CPU%u: %s: return\n", cpu, __func__);
+ return collapsed;
+}
+/******************************************************************************
+ * External Idle/Suspend Functions
+ *****************************************************************************/
+
+static void arch_idle(void) {}
+
+static bool (*execute[MSM_PM_SLEEP_MODE_NR])(bool idle) = {
+ [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = msm_pm_swfi,
+ [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
+ msm_pm_power_collapse_standalone,
+ [MSM_PM_SLEEP_MODE_RETENTION] = msm_pm_retention,
+ [MSM_PM_SLEEP_MODE_FASTPC] = msm_pm_fastpc,
+ [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = msm_pm_power_collapse,
+};
+
+/**
+ * msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu
+ *
+ * @mode - sleep mode to enter
+ * @from_idle - bool to indicate that the mode is exercised during idle/suspend
+ *
+ * returns none
+ *
+ * The code should be with interrupts disabled and on the core on which the
+ * low power is to be executed.
+ *
+ */
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
+{
+ bool exit_stat = false;
+ unsigned int cpu = smp_processor_id();
+
+ if ((!from_idle && cpu_online(cpu))
+ || (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask))
+ pr_info("CPU%u:%s mode:%d during %s\n", cpu, __func__,
+ mode, from_idle ? "idle" : "suspend");
+
+ if (execute[mode])
+ exit_stat = execute[mode](from_idle);
+
+ return exit_stat;
+}
+
+/**
+ * msm_pm_wait_cpu_shutdown() - Wait for a core to be power collapsed during
+ * hotplug
+ *
+ * @ cpu - cpu to wait on.
+ *
+ * Blocking function call that waits on the core to be power collapsed. This
+ * function is called from platform_cpu_die to ensure that a core is power
+ * collapsed before sending the CPU_DEAD notification so the drivers could
+ * remove the resource votes for this CPU(regulator and clock)
+ */
+int msm_pm_wait_cpu_shutdown(unsigned int cpu)
+{
+ int timeout = 0;
+
+ if (!msm_pm_slp_sts)
+ return 0;
+ if (!msm_pm_slp_sts[cpu].base_addr)
+ return 0;
+ while (1) {
+ /*
+ * Check for the SPM of the core being hotplugged to set
+ * its sleep state.The SPM sleep state indicates that the
+ * core has been power collapsed.
+ */
+ int acc_sts = __raw_readl(msm_pm_slp_sts[cpu].base_addr);
+
+ if (acc_sts & msm_pm_slp_sts[cpu].mask)
+ return 0;
+
+ udelay(100);
+ /*
+ * Dump spm registers for debugging
+ */
+ if (++timeout == 20) {
+ msm_spm_dump_regs(cpu);
+ __WARN_printf(
+ "CPU%u didn't collapse in 2ms, sleep status: 0x%x\n",
+ cpu, acc_sts);
+ }
+ }
+
+ return -EBUSY;
+}
+
+static void msm_pm_ack_retention_disable(void *data)
+{
+ /*
+ * This is a NULL function to ensure that the core has woken up
+ * and is safe to disable retention.
+ */
+}
+/**
+ * msm_pm_enable_retention() - Disable/Enable retention on all cores
+ * @enable: Enable/Disable retention
+ *
+ */
+void msm_pm_enable_retention(bool enable)
+{
+ if (enable == msm_pm_ldo_retention_enabled)
+ return;
+
+ msm_pm_ldo_retention_enabled = enable;
+
+ /*
+ * If retention is being disabled, wakeup all online core to ensure
+ * that it isn't executing retention. Offlined cores need not be woken
+ * up as they enter the deepest sleep mode, namely RPM assited power
+ * collapse
+ */
+ if (!enable) {
+ preempt_disable();
+ smp_call_function_many(&retention_cpus,
+ msm_pm_ack_retention_disable,
+ NULL, true);
+ preempt_enable();
+ }
+}
+EXPORT_SYMBOL(msm_pm_enable_retention);
+
+/**
+ * msm_pm_retention_enabled() - Check if retention is enabled
+ *
+ * returns true if retention is enabled
+ */
+bool msm_pm_retention_enabled(void)
+{
+ return msm_pm_ldo_retention_enabled;
+}
+EXPORT_SYMBOL(msm_pm_retention_enabled);
+
+static int msm_pm_snoc_client_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ static struct msm_bus_scale_pdata *msm_pm_bus_pdata;
+ static uint32_t msm_pm_bus_client;
+
+ msm_pm_bus_pdata = msm_bus_cl_get_pdata(pdev);
+
+ if (msm_pm_bus_pdata) {
+ msm_pm_bus_client =
+ msm_bus_scale_register_client(msm_pm_bus_pdata);
+
+ if (!msm_pm_bus_client) {
+ pr_err("%s: Failed to register SNOC client", __func__);
+ rc = -ENXIO;
+ goto snoc_cl_probe_done;
+ }
+
+ rc = msm_bus_scale_client_update_request(msm_pm_bus_client, 1);
+
+ if (rc)
+ pr_err("%s: Error setting bus rate", __func__);
+ }
+
+snoc_cl_probe_done:
+ return rc;
+}
+
+static int msm_cpu_status_probe(struct platform_device *pdev)
+{
+ u32 cpu;
+ int rc;
+
+ if (!pdev || !pdev->dev.of_node)
+ return -EFAULT;
+
+ msm_pm_slp_sts = devm_kzalloc(&pdev->dev,
+ sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
+ GFP_KERNEL);
+
+ if (!msm_pm_slp_sts)
+ return -ENOMEM;
+
+
+ for_each_possible_cpu(cpu) {
+ struct device_node *cpun, *node;
+ char *key;
+
+ cpun = of_get_cpu_node(cpu, NULL);
+
+ if (!cpun) {
+ __WARN();
+ continue;
+ }
+
+ node = of_parse_phandle(cpun, "qcom,sleep-status", 0);
+ if (!node)
+ return -ENODEV;
+
+ msm_pm_slp_sts[cpu].base_addr = of_iomap(node, 0);
+ if (!msm_pm_slp_sts[cpu].base_addr) {
+ pr_err("%s: Can't find base addr\n", __func__);
+ return -ENODEV;
+ }
+
+ key = "qcom,sleep-status-mask";
+ rc = of_property_read_u32(node, key, &msm_pm_slp_sts[cpu].mask);
+ if (rc) {
+ pr_err("%s: Can't find %s property\n", __func__, key);
+ iounmap(msm_pm_slp_sts[cpu].base_addr);
+ return rc;
+ }
+ }
+
+ return 0;
+};
+
+static const struct of_device_id msm_slp_sts_match_tbl[] = {
+ {.compatible = "qcom,cpu-sleep-status"},
+ {},
+};
+
+static struct platform_driver msm_cpu_status_driver = {
+ .probe = msm_cpu_status_probe,
+ .driver = {
+ .name = "cpu_slp_status",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_slp_sts_match_tbl,
+ },
+};
+
+static const struct of_device_id msm_snoc_clnt_match_tbl[] = {
+ {.compatible = "qcom,pm-snoc-client"},
+ {},
+};
+
+static struct platform_driver msm_cpu_pm_snoc_client_driver = {
+ .probe = msm_pm_snoc_client_probe,
+ .driver = {
+ .name = "pm_snoc_client",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_snoc_clnt_match_tbl,
+ },
+};
+
+struct msm_pc_debug_counters_buffer {
+ long *reg;
+ u32 len;
+ char buf[MAX_BUF_SIZE];
+};
+
+static char *counter_name[MSM_PC_NUM_COUNTERS] = {
+ "PC Entry Counter",
+ "Warmboot Entry Counter",
+ "PC Bailout Counter"
+};
+
+static int msm_pc_debug_counters_copy(
+ struct msm_pc_debug_counters_buffer *data)
+{
+ int j;
+ u32 stat;
+ unsigned int cpu;
+ unsigned int len;
+ uint32_t cluster_id;
+ uint32_t cpu_id;
+ uint32_t offset;
+
+ for_each_possible_cpu(cpu) {
+ len = scnprintf(data->buf + data->len,
+ sizeof(data->buf)-data->len,
+ "CPU%d\n", cpu);
+ cluster_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+ cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+ offset = (cluster_id * MAX_CPUS_PER_CLUSTER
+ * MSM_PC_NUM_COUNTERS)
+ + (cpu_id * MSM_PC_NUM_COUNTERS);
+
+ data->len += len;
+
+ for (j = 0; j < MSM_PC_NUM_COUNTERS - 1; j++) {
+ stat = data->reg[offset + j];
+ len = scnprintf(data->buf + data->len,
+ sizeof(data->buf) - data->len,
+ "\t%s: %d", counter_name[j], stat);
+
+ data->len += len;
+ }
+ len = scnprintf(data->buf + data->len,
+ sizeof(data->buf) - data->len,
+ "\n");
+
+ data->len += len;
+ }
+
+ return data->len;
+}
+
+static ssize_t msm_pc_debug_counters_file_read(struct file *file,
+ char __user *bufu, size_t count, loff_t *ppos)
+{
+ struct msm_pc_debug_counters_buffer *data;
+ ssize_t ret;
+
+ mutex_lock(&msm_pc_debug_mutex);
+ data = file->private_data;
+
+ if (!data) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (!bufu) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (!access_ok(VERIFY_WRITE, bufu, count)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ if (*ppos >= data->len && data->len == 0)
+ data->len = msm_pc_debug_counters_copy(data);
+
+ ret = simple_read_from_buffer(bufu, count, ppos,
+ data->buf, data->len);
+exit:
+ mutex_unlock(&msm_pc_debug_mutex);
+ return ret;
+}
+
+static int msm_pc_debug_counters_file_open(struct inode *inode,
+ struct file *file)
+{
+ struct msm_pc_debug_counters_buffer *buf;
+ int ret = 0;
+
+ mutex_lock(&msm_pc_debug_mutex);
+
+ if (!inode->i_private) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ file->private_data = kzalloc(
+ sizeof(struct msm_pc_debug_counters_buffer), GFP_KERNEL);
+
+ if (!file->private_data) {
+ pr_err("%s: ERROR kmalloc failed to allocate %zu bytes\n",
+ __func__, sizeof(struct msm_pc_debug_counters_buffer));
+
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ buf = file->private_data;
+ buf->reg = (long *)inode->i_private;
+
+exit:
+ mutex_unlock(&msm_pc_debug_mutex);
+ return ret;
+}
+
+static int msm_pc_debug_counters_file_close(struct inode *inode,
+ struct file *file)
+{
+ mutex_lock(&msm_pc_debug_mutex);
+ kfree(file->private_data);
+ mutex_unlock(&msm_pc_debug_mutex);
+ return 0;
+}
+
+static const struct file_operations msm_pc_debug_counters_fops = {
+ .open = msm_pc_debug_counters_file_open,
+ .read = msm_pc_debug_counters_file_read,
+ .release = msm_pc_debug_counters_file_close,
+ .llseek = no_llseek,
+};
+
+static int msm_pm_clk_init(struct platform_device *pdev)
+{
+ bool synced_clocks;
+ u32 cpu;
+ char clk_name[] = "cpu??_clk";
+ char *key;
+
+ key = "qcom,saw-turns-off-pll";
+ if (of_property_read_bool(pdev->dev.of_node, key))
+ return 0;
+
+ key = "qcom,synced-clocks";
+ synced_clocks = of_property_read_bool(pdev->dev.of_node, key);
+
+ for_each_possible_cpu(cpu) {
+ struct clk *clk;
+
+ snprintf(clk_name, sizeof(clk_name), "cpu%d_clk", cpu);
+ clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(clk)) {
+ if (cpu && synced_clocks)
+ return 0;
+ clk = NULL;
+ }
+ per_cpu(cpu_clks, cpu) = clk;
+ }
+
+ if (synced_clocks)
+ return 0;
+
+ l2_clk = clk_get(&pdev->dev, "l2_clk");
+ if (IS_ERR(l2_clk))
+ pr_warn("%s: Could not get l2_clk (-%ld)\n", __func__,
+ PTR_ERR(l2_clk));
+
+ return 0;
+}
+
+static int msm_cpu_pm_probe(struct platform_device *pdev)
+{
+ struct dentry *dent = NULL;
+ struct resource *res = NULL;
+ int ret = 0;
+ void __iomem *msm_pc_debug_counters_imem;
+ char *key;
+ int alloc_size = (MAX_NUM_CLUSTER * MAX_CPUS_PER_CLUSTER
+ * MSM_PC_NUM_COUNTERS
+ * sizeof(*msm_pc_debug_counters));
+
+ msm_pc_debug_counters = dma_alloc_coherent(&pdev->dev, alloc_size,
+ &msm_pc_debug_counters_phys, GFP_KERNEL);
+
+ if (msm_pc_debug_counters) {
+ memset(msm_pc_debug_counters, 0, alloc_size);
+ dent = debugfs_create_file("pc_debug_counter", 0444, NULL,
+ msm_pc_debug_counters,
+ &msm_pc_debug_counters_fops);
+ if (!dent)
+ pr_err("%s: ERROR debugfs_create_file failed\n",
+ __func__);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto skip_save_imem;
+ msm_pc_debug_counters_imem = devm_ioremap(&pdev->dev,
+ res->start, resource_size(res));
+ if (msm_pc_debug_counters_imem) {
+ writel_relaxed(msm_pc_debug_counters_phys,
+ msm_pc_debug_counters_imem);
+ /* memory barrier */
+ mb();
+ devm_iounmap(&pdev->dev,
+ msm_pc_debug_counters_imem);
+ }
+ } else {
+ msm_pc_debug_counters = NULL;
+ msm_pc_debug_counters_phys = 0;
+ }
+skip_save_imem:
+ if (pdev->dev.of_node) {
+ key = "qcom,tz-flushes-cache";
+ msm_pm_tz_flushes_cache =
+ of_property_read_bool(pdev->dev.of_node, key);
+
+ key = "qcom,no-pll-switch-for-retention";
+ msm_pm_ret_no_pll_switch =
+ of_property_read_bool(pdev->dev.of_node, key);
+
+ ret = msm_pm_clk_init(pdev);
+ if (ret) {
+ pr_info("msm_pm_clk_init returned error\n");
+ return ret;
+ }
+ }
+
+ if (pdev->dev.of_node)
+ of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+ return ret;
+}
+
+static const struct of_device_id msm_cpu_pm_table[] = {
+ {.compatible = "qcom,pm"},
+ {},
+};
+
+static struct platform_driver msm_cpu_pm_driver = {
+ .probe = msm_cpu_pm_probe,
+ .driver = {
+ .name = "msm-pm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cpu_pm_table,
+ },
+};
+
+static int __init msm_pm_drv_init(void)
+{
+ int rc;
+
+ cpumask_clear(&retention_cpus);
+
+ rc = platform_driver_register(&msm_cpu_pm_snoc_client_driver);
+
+ if (rc)
+ pr_err("%s(): failed to register driver %s\n", __func__,
+ msm_cpu_pm_snoc_client_driver.driver.name);
+ return rc;
+}
+late_initcall(msm_pm_drv_init);
+
+static int __init msm_pm_debug_counters_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&msm_cpu_pm_driver);
+
+ if (rc)
+ pr_err("%s(): failed to register driver %s\n", __func__,
+ msm_cpu_pm_driver.driver.name);
+ return rc;
+}
+fs_initcall(msm_pm_debug_counters_init);
+
+int __init msm_pm_sleep_status_init(void)
+{
+ static bool registered;
+
+ if (registered)
+ return 0;
+ registered = true;
+
+ return platform_driver_register(&msm_cpu_status_driver);
+}
+arch_initcall(msm_pm_sleep_status_init);
+
+#ifdef CONFIG_ARM
+static int idle_initialize(void)
+{
+ arm_pm_idle = arch_idle;
+ return 0;
+}
+early_initcall(idle_initialize);
+#endif
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 360dbcf..8776379 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -263,6 +263,7 @@
struct pil_priv *priv = desc->priv;
struct pil_seg *seg;
int count = 0, ret;
+ u32 encryption_status = 0;
if (desc->minidump_ss) {
pr_info("Minidump : md_ss_toc->md_ss_toc_init is 0x%x\n",
@@ -280,12 +281,14 @@
* Collect minidump if SS ToC is valid and segment table
* is initialized in memory and encryption status is set.
*/
+ encryption_status = desc->minidump_ss->encryption_status;
+
if ((desc->minidump_ss->md_ss_smem_regions_baseptr != 0) &&
(desc->minidump_ss->md_ss_toc_init == true) &&
(desc->minidump_ss->md_ss_enable_status ==
MD_SS_ENABLED)) {
- if (desc->minidump_ss->encryption_status ==
- MD_SS_ENCR_DONE) {
+ if (encryption_status == MD_SS_ENCR_DONE ||
+ encryption_status == MD_SS_ENCR_NOTREQ) {
pr_info("Minidump : Dumping for %s\n",
desc->name);
return pil_do_minidump(desc, minidump_dev);
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 6283477..2a46d0b 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -797,14 +797,18 @@
int pil_mss_debug_reset(struct pil_desc *pil)
{
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+ u32 encryption_status;
int ret;
+
if (!pil->minidump_ss)
return 0;
- if (pil->minidump_ss) {
- if (pil->minidump_ss->md_ss_enable_status != MD_SS_ENABLED)
- return 0;
- }
+
+ encryption_status = pil->minidump_ss->encryption_status;
+
+ if ((pil->minidump_ss->md_ss_enable_status != MD_SS_ENABLED) ||
+ encryption_status == MD_SS_ENCR_NOTREQ)
+ return 0;
/*
* Bring subsystem out of reset and enable required
@@ -836,7 +840,7 @@
* complete before returning
*/
pr_info("Minidump: waiting encryption to complete\n");
- msleep(10000);
+ msleep(13000);
if (pil->minidump_ss) {
writel_relaxed(0x2, drv->reg_base + QDSP6SS_NMI_CFG);
/* Let write complete before proceeding */
diff --git a/drivers/soc/qcom/pil_bg_intf.h b/drivers/soc/qcom/pil_bg_intf.h
index 722024b..46aed25 100644
--- a/drivers/soc/qcom/pil_bg_intf.h
+++ b/drivers/soc/qcom/pil_bg_intf.h
@@ -36,7 +36,7 @@
__packed struct tzapp_bg_rsp {
uint32_t tzapp_bg_cmd;
uint32_t bg_info_len;
- uint32_t status;
+ int32_t status;
uint32_t bg_info[100];
};
diff --git a/drivers/soc/qcom/pm-boot.c b/drivers/soc/qcom/pm-boot.c
new file mode 100644
index 0000000..f0daeba
--- /dev/null
+++ b/drivers/soc/qcom/pm-boot.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 2011-2014, 2016, 2018, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <soc/qcom/scm-boot.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+#include "pm-boot.h"
+#include "idle.h"
+
+#define CPU_INDEX(cluster, cpu) (cluster * MAX_CPUS_PER_CLUSTER + cpu)
+
+static void (*msm_pm_boot_before_pc)(unsigned int cpu, unsigned long entry);
+static void (*msm_pm_boot_after_pc)(unsigned int cpu);
+
+static int msm_pm_tz_boot_init(void)
+{
+ int ret;
+ phys_addr_t warmboot_addr = virt_to_phys(msm_pm_boot_entry);
+
+ if (scm_is_mc_boot_available())
+ ret = scm_set_warm_boot_addr_mc_for_all(warmboot_addr);
+ else {
+ unsigned int flag = 0;
+
+ if (num_possible_cpus() == 1)
+ flag = SCM_FLAG_WARMBOOT_CPU0;
+ else if (num_possible_cpus() == 2)
+ flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1;
+ else if (num_possible_cpus() == 4)
+ flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1 |
+ SCM_FLAG_WARMBOOT_CPU2 | SCM_FLAG_WARMBOOT_CPU3;
+ else
+ pr_warn("%s: set warmboot address failed\n",
+ __func__);
+
+ ret = scm_set_boot_addr(virt_to_phys(msm_pm_boot_entry), flag);
+ }
+ return ret;
+}
+static void msm_pm_write_boot_vector(unsigned int cpu, unsigned long address)
+{
+ uint32_t clust_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+ uint32_t cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+ unsigned long *start_address;
+ unsigned long *end_address;
+
+ if (clust_id >= MAX_NUM_CLUSTER || cpu_id >= MAX_CPUS_PER_CLUSTER)
+ WARN_ON(cpu);
+
+ msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id)] = address;
+ start_address = &msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id)];
+ end_address = &msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id + 1)];
+ dmac_clean_range((void *)start_address, (void *)end_address);
+}
+
+static void msm_pm_config_tz_before_pc(unsigned int cpu,
+ unsigned long entry)
+{
+ msm_pm_write_boot_vector(cpu, entry);
+}
+
+void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry)
+{
+ if (msm_pm_boot_before_pc)
+ msm_pm_boot_before_pc(cpu, entry);
+}
+
+void msm_pm_boot_config_after_pc(unsigned int cpu)
+{
+ if (msm_pm_boot_after_pc)
+ msm_pm_boot_after_pc(cpu);
+}
+
+static int __init msm_pm_boot_init(void)
+{
+ int ret = 0;
+
+ ret = msm_pm_tz_boot_init();
+ msm_pm_boot_before_pc = msm_pm_config_tz_before_pc;
+ msm_pm_boot_after_pc = NULL;
+
+ return ret;
+}
+postcore_initcall(msm_pm_boot_init);
diff --git a/drivers/soc/qcom/pm-boot.h b/drivers/soc/qcom/pm-boot.h
new file mode 100644
index 0000000..7ec053a
--- /dev/null
+++ b/drivers/soc/qcom/pm-boot.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011-2014, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _ARCH_ARM_MACH_MSM_PM_BOOT_H
+#define _ARCH_ARM_MACH_MSM_PM_BOOT_H
+
+void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry);
+void msm_pm_boot_config_after_pc(unsigned int cpu);
+
+#endif
diff --git a/drivers/soc/qcom/qdss_bridge.c b/drivers/soc/qcom/qdss_bridge.c
index 8668155..fb70a07 100644
--- a/drivers/soc/qcom/qdss_bridge.c
+++ b/drivers/soc/qcom/qdss_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -245,7 +245,7 @@
switch (event) {
case USB_QDSS_CONNECT:
- usb_qdss_alloc_req(drvdata->usb_ch, poolsize, 0);
+ usb_qdss_alloc_req(ch, poolsize, 0);
mhi_queue_read(drvdata);
break;
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index 7758c64..789eee7 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -455,19 +455,19 @@
}
static inline unsigned int set_section_name(const char *name,
- struct elfhdr *ehdr)
+ struct elfhdr *ehdr,
+ int *strtable_idx)
{
char *strtab = elf_str_table(ehdr);
- static int strtable_idx = 1;
int idx, ret = 0;
- idx = strtable_idx;
+ idx = *strtable_idx;
if ((strtab == NULL) || (name == NULL))
return 0;
ret = idx;
idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
- strtable_idx = idx + 1;
+ *strtable_idx = idx + 1;
return ret;
}
@@ -480,6 +480,7 @@
struct elfhdr *ehdr;
struct elf_shdr *shdr;
unsigned long offset, strtbl_off;
+ int strtable_idx = 1;
if (!rd_dev->consumer_present) {
pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name);
@@ -519,13 +520,14 @@
shdr->sh_size = MAX_STRTBL_SIZE;
shdr->sh_entsize = 0;
shdr->sh_flags = 0;
- shdr->sh_name = set_section_name("STR_TBL", ehdr);
+ shdr->sh_name = set_section_name("STR_TBL", ehdr, &strtable_idx);
shdr++;
for (i = 0; i < nsegments; i++, shdr++) {
/* Update elf header */
shdr->sh_type = SHT_PROGBITS;
- shdr->sh_name = set_section_name(segments[i].name, ehdr);
+ shdr->sh_name = set_section_name(segments[i].name, ehdr,
+ &strtable_idx);
shdr->sh_addr = (elf_addr_t)segments[i].address;
shdr->sh_size = segments[i].size;
shdr->sh_flags = SHF_WRITE;
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 9dfe281..5abc093 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -34,6 +34,7 @@
#define QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT 2000
#define QMI_SERVREG_LOC_SERVER_TIMEOUT 2000
#define INITIAL_TIMEOUT 100000
+#define LOCATOR_SERVICE_TIMEOUT 300000
#define LOCATOR_NOT_PRESENT 0
#define LOCATOR_PRESENT 1
@@ -302,13 +303,20 @@
static int init_service_locator(void)
{
int rc = 0;
+ static bool service_timedout;
- mutex_lock(&service_init_mutex);
+ rc = mutex_lock_interruptible(&service_init_mutex);
+ if (rc)
+ return rc;
if (locator_status == LOCATOR_NOT_PRESENT) {
pr_err("Service Locator not enabled\n");
rc = -ENODEV;
goto inited;
}
+ if (service_timedout) {
+ rc = -ETIME;
+ goto inited;
+ }
if (service_inited)
goto inited;
@@ -336,7 +344,20 @@
goto inited;
}
- wait_for_completion(&service_locator.service_available);
+ rc = wait_for_completion_interruptible_timeout(
+ &service_locator.service_available,
+ msecs_to_jiffies(LOCATOR_SERVICE_TIMEOUT));
+ if (rc < 0) {
+ pr_err("Wait for locator service interrupted by signal\n");
+ goto inited;
+ }
+ if (!rc) {
+ pr_err("%s: wait for locator service timed out\n", __func__);
+ service_timedout = true;
+ rc = -ETIME;
+ goto inited;
+ }
+
service_inited = true;
mutex_unlock(&service_init_mutex);
pr_info("Service locator initialized\n");
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index e02bf84..fffef10 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -593,12 +593,18 @@
/* QCS605 ID */
[347] = {MSM_CPU_QCS605, "QCS605"},
+ /* SXR1130 ID */
+ [371] = {MSM_CPU_SXR1130, "SXR1130"},
+
/* SDA670 ID */
[337] = {MSM_CPU_SDA670, "SDA670"},
/* SDM710 ID */
[360] = {MSM_CPU_SDM710, "SDM710"},
+ /* SXR1120 ID */
+ [370] = {MSM_CPU_SXR1120, "SXR1120"},
+
/* 8953 ID */
[293] = {MSM_CPU_8953, "MSM8953"},
[304] = {MSM_CPU_8953, "APQ8053"},
@@ -615,6 +621,12 @@
[294] = {MSM_CPU_8937, "MSM8937"},
[295] = {MSM_CPU_8937, "APQ8937"},
+ /* MSM8917 IDs */
+ [303] = {MSM_CPU_8917, "MSM8917"},
+ [307] = {MSM_CPU_8917, "APQ8017"},
+ [308] = {MSM_CPU_8917, "MSM8217"},
+ [309] = {MSM_CPU_8917, "MSM8617"},
+
/* SDM429 and SDM439 ID*/
[353] = {MSM_CPU_SDM439, "SDM439"},
[354] = {MSM_CPU_SDM429, "SDM429"},
@@ -1532,6 +1544,10 @@
dummy_socinfo.id = 336;
strlcpy(dummy_socinfo.build_id, "sdm670 - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_sxr1130()) {
+ dummy_socinfo.id = 371;
+ strlcpy(dummy_socinfo.build_id, "sxr1130 - ",
+ sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_sdm710()) {
dummy_socinfo.id = 360;
strlcpy(dummy_socinfo.build_id, "sdm710 - ",
@@ -1544,6 +1560,10 @@
dummy_socinfo.id = 347;
strlcpy(dummy_socinfo.build_id, "qcs605 - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_sxr1120()) {
+ dummy_socinfo.id = 370;
+ strlcpy(dummy_socinfo.build_id, "sxr1120 - ",
+ sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_mdm9650()) {
dummy_socinfo.id = 286;
strlcpy(dummy_socinfo.build_id, "mdm9650 - ",
@@ -1560,6 +1580,10 @@
dummy_socinfo.id = 294;
strlcpy(dummy_socinfo.build_id, "msm8937 - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msm8917()) {
+ dummy_socinfo.id = 303;
+ strlcpy(dummy_socinfo.build_id, "msm8917 - ",
+ sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_sdm450()) {
dummy_socinfo.id = 338;
strlcpy(dummy_socinfo.build_id, "sdm450 - ",
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 0b037d4..d568014 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -374,7 +374,7 @@
switch (cb_info->link_state) {
case GLINK_LINK_STATE_UP:
- pr_info("GLINK_LINK_STATE_UP.\n");
+ pr_debug("GLINK_LINK_STATE_UP.\n");
spcom_create_predefined_channels_chardev();
break;
case GLINK_LINK_STATE_DOWN:
@@ -1170,7 +1170,7 @@
kfree(client);
client = NULL;
} else {
- pr_info("remote side connect to channel [%s].\n", name);
+ pr_debug("remote side connect to channel [%s].\n", name);
}
return client;
@@ -2010,6 +2010,11 @@
pr_debug("cmd_id [0x%x] cmd_name [%s].\n", cmd_id, cmd_name);
+ if (!ch && cmd_id != SPCOM_CMD_CREATE_CHANNEL) {
+ pr_err("channel context is null\n");
+ return -EINVAL;
+ }
+
switch (cmd_id) {
case SPCOM_CMD_SEND:
ret = spcom_handle_send_command(ch, buf, buf_size);
@@ -2342,8 +2347,12 @@
ch = filp->private_data;
if (!ch) {
- pr_err("invalid ch pointer, command not allowed.\n");
- return -EINVAL;
+ if (strcmp(name, DEVICE_NAME) == 0) {
+ pr_debug("control device - no channel context.\n");
+ } else {
+ pr_err("NULL ch pointer, command not allowed.\n");
+ return -EINVAL;
+ }
} else {
/* Check if remote side connect */
if (!spcom_is_channel_connected(ch)) {
@@ -2784,7 +2793,7 @@
goto fail_ion_client;
}
- pr_info("Driver Initialization ok.\n");
+ pr_debug("Driver Initialization ok.\n");
return 0;
@@ -2821,7 +2830,7 @@
{
int ret;
- pr_info("spcom driver version 1.3 28-Dec-2017.\n");
+ pr_debug("spcom driver version 1.4 30-Apr-2018.\n");
ret = platform_driver_register(&spcom_driver);
if (ret)
diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c
index 5f1ac4e..268a8fe 100644
--- a/drivers/soc/qcom/spm_devices.c
+++ b/drivers/soc/qcom/spm_devices.c
@@ -234,7 +234,7 @@
}
static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
- unsigned int mode, bool notify_rpm)
+ unsigned int mode, bool notify_rpm, bool set_spm_enable)
{
uint32_t i;
int ret = -EINVAL;
@@ -251,9 +251,11 @@
if (!dev->num_modes)
return 0;
- if (mode == MSM_SPM_MODE_DISABLED) {
+ if (mode == MSM_SPM_MODE_DISABLED && set_spm_enable) {
ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
- } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
+ } else {
+ if (set_spm_enable)
+ ret = msm_spm_drv_set_spm_enable(&dev->reg_data, true);
for (i = 0; i < dev->num_modes; i++) {
if (dev->modes[i].mode != mode)
continue;
@@ -539,10 +541,24 @@
{
struct msm_spm_device *dev = this_cpu_ptr(&msm_cpu_spm_device);
- return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+ return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, true);
}
EXPORT_SYMBOL(msm_spm_set_low_power_mode);
+void msm_spm_set_rpm_hs(bool allow_rpm_hs)
+{
+ struct msm_spm_device *dev = this_cpu_ptr(&msm_cpu_spm_device);
+
+ dev->allow_rpm_hs = allow_rpm_hs;
+}
+EXPORT_SYMBOL(msm_spm_set_rpm_hs);
+
+int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev,
+ unsigned int mode, bool notify_rpm)
+{
+ return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, false);
+}
+
/**
* msm_spm_init(): Board initalization function
* @data: platform specific SPM register configuration data
@@ -586,7 +602,7 @@
int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
unsigned int mode, bool notify_rpm)
{
- return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+ return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, true);
}
#ifdef CONFIG_MSM_L2_SPM
diff --git a/drivers/soc/qcom/subsys-pil-bg.c b/drivers/soc/qcom/subsys-pil-bg.c
index 75c3666..070733e 100644
--- a/drivers/soc/qcom/subsys-pil-bg.c
+++ b/drivers/soc/qcom/subsys-pil-bg.c
@@ -30,6 +30,7 @@
#include "peripheral-loader.h"
#include "../../misc/qseecom_kernel.h"
#include "pil_bg_intf.h"
+#include "bgcom_interface.h"
#define INVALID_GPIO -1
#define NUM_GPIOS 4
@@ -37,7 +38,7 @@
#define desc_to_data(d) container_of(d, struct pil_bg_data, desc)
#define subsys_to_data(d) container_of(d, struct pil_bg_data, subsys_desc)
#define BG_RAMDUMP_SZ 0x00102000
-#define BG_CRASH_IN_TWM 2
+#define BG_CRASH_IN_TWM -2
/**
* struct pil_bg_data
* @qseecom_handle: handle of TZ app
@@ -90,9 +91,18 @@
static void bg_app_shutdown_notify(const struct subsys_desc *subsys)
{
struct pil_bg_data *bg_data = subsys_to_data(subsys);
+
+ /* Disable irq if already BG is up */
+ if (bg_data->is_ready) {
+ disable_irq(bg_data->status_irq);
+ disable_irq(bg_data->errfatal_irq);
+ bg_data->is_ready = false;
+ }
/* Toggle AP2BG err fatal gpio here to inform apps err fatal event */
- if (gpio_is_valid(bg_data->gpios[2]))
+ if (gpio_is_valid(bg_data->gpios[2])) {
+ pr_debug("Sending Apps shutdown signal\n");
gpio_set_value(bg_data->gpios[2], 1);
+ }
}
/**
@@ -106,9 +116,18 @@
{
struct pil_bg_data *bg_data = container_of(nb,
struct pil_bg_data, reboot_blk);
+
+ /* Disable irq if already BG is up */
+ if (bg_data->is_ready) {
+ disable_irq(bg_data->status_irq);
+ disable_irq(bg_data->errfatal_irq);
+ bg_data->is_ready = false;
+ }
/* Toggle AP2BG err fatal gpio here to inform apps err fatal event */
- if (gpio_is_valid(bg_data->gpios[2]))
+ if (gpio_is_valid(bg_data->gpios[2])) {
+ pr_debug("Sending reboot signal\n");
gpio_set_value(bg_data->gpios[2], 1);
+ }
return NOTIFY_DONE;
}
@@ -266,7 +285,6 @@
return ret;
}
enable_irq(bg_data->status_irq);
- enable_irq(bg_data->errfatal_irq);
ret = wait_for_err_ready(bg_data);
if (ret) {
dev_err(bg_data->desc.dev,
@@ -289,10 +307,12 @@
{
struct pil_bg_data *bg_data = subsys_to_data(subsys);
- disable_irq(bg_data->status_irq);
- devm_free_irq(bg_data->desc.dev, bg_data->status_irq, bg_data);
- disable_irq(bg_data->errfatal_irq);
- bg_data->is_ready = false;
+ if (bg_data->is_ready) {
+ disable_irq(bg_data->status_irq);
+ devm_free_irq(bg_data->desc.dev, bg_data->status_irq, bg_data);
+ disable_irq(bg_data->errfatal_irq);
+ bg_data->is_ready = false;
+ }
return 0;
}
@@ -393,7 +413,9 @@
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req);
if (bg_data->cmd_status == BG_CRASH_IN_TWM) {
/* Do ramdump and resend boot cmd */
- bg_data->subsys_desc.ramdump(true, &bg_data->subsys_desc);
+ if (is_twm_exit())
+ bg_data->subsys_desc.ramdump(true,
+ &bg_data->subsys_desc);
bg_tz_req.tzapp_bg_cmd = BGPIL_DLOAD_CONT;
ret = bgpil_tzapp_comm(bg_data, &bg_tz_req);
}
@@ -524,7 +546,6 @@
} else if (value == false && drvdata->is_ready) {
dev_err(drvdata->desc.dev,
"BG got unexpected reset: irq state changed 1->0\n");
- drvdata->is_ready = false;
queue_work(drvdata->bg_queue, &drvdata->restart_work);
} else {
dev_err(drvdata->desc.dev,
@@ -586,7 +607,6 @@
goto err;
}
drvdata->errfatal_irq = irq;
- enable_irq(drvdata->errfatal_irq);
/* Configure outgoing GPIO's */
if (gpio_request(drvdata->gpios[2], "AP2BG_ERRFATAL")) {
dev_err(&pdev->dev,
diff --git a/drivers/soc/qcom/system_pm.c b/drivers/soc/qcom/system_pm.c
index 9c6edbf..1befdcf 100644
--- a/drivers/soc/qcom/system_pm.c
+++ b/drivers/soc/qcom/system_pm.c
@@ -22,6 +22,13 @@
#define PDC_TIME_VALID_SHIFT 31
#define PDC_TIME_UPPER_MASK 0xFFFFFF
+#ifdef CONFIG_ARM_GIC_V3
+#include <linux/irqchip/arm-gic-v3.h>
+#else
+static inline void gic_v3_dist_restore(void) {}
+static inline void gic_v3_dist_save(void) {}
+#endif
+
static struct rpmh_client *rpmh_client;
static int setup_wakeup(uint32_t lo, uint32_t hi)
@@ -61,6 +68,7 @@
*/
static int system_sleep_enter(struct cpumask *mask)
{
+ gic_v3_dist_save();
return rpmh_flush(rpmh_client);
}
@@ -70,6 +78,7 @@
static void system_sleep_exit(void)
{
msm_rpmh_master_stats_update();
+ gic_v3_dist_restore();
}
static struct system_pm_ops pm_ops = {
diff --git a/drivers/soc/qcom/wcnss/wcnss_vreg.c b/drivers/soc/qcom/wcnss/wcnss_vreg.c
index 9d24ce1..1e61250 100644
--- a/drivers/soc/qcom/wcnss/wcnss_vreg.c
+++ b/drivers/soc/qcom/wcnss/wcnss_vreg.c
@@ -206,7 +206,7 @@
voltage_levels,
ARRAY_SIZE(voltage_levels));
if (ret) {
- dev_err(dev, "error reading %s property\n", vreg_name);
+ wcnss_log(ERR, "error reading %s property\n", vreg_name);
return ret;
}
@@ -217,7 +217,8 @@
ret = of_property_read_u32(dev->of_node, current_vreg_name,
¤t_vreg);
if (ret) {
- dev_err(dev, "error reading %s property\n", current_vreg_name);
+ wcnss_log(ERR, "error reading %s property\n",
+ current_vreg_name);
return ret;
}
@@ -240,12 +241,14 @@
if (IS_ERR(pronto_vregs[vreg_i].regulator)) {
if (pronto_vregs[vreg_i].required) {
rc = PTR_ERR(pronto_vregs[vreg_i].regulator);
- dev_err(dev, "regulator get of %s failed (%d)\n",
+ wcnss_log(ERR,
+ "regulator get of %s failed (%d)\n",
pronto_vregs[vreg_i].name, rc);
return rc;
} else {
- dev_dbg(dev, "Skip optional regulator configuration: %s\n",
- pronto_vregs[vreg_i].name);
+ wcnss_log(DBG,
+ "Skip optional regulator configuration: %s\n",
+ pronto_vregs[vreg_i].name);
continue;
}
}
@@ -255,7 +258,8 @@
pronto_vregs[vreg_i].volt,
wlan_config->pronto_vlevel);
if (rc) {
- dev_err(dev, "error reading voltage-level property\n");
+ wcnss_log(ERR,
+ "error reading voltage-level property\n");
return rc;
}
pronto_vregs[vreg_i].state |= VREG_GET_REGULATOR_MASK;
@@ -269,12 +273,14 @@
if (IS_ERR(iris_vregs[vreg_i].regulator)) {
if (iris_vregs[vreg_i].required) {
rc = PTR_ERR(iris_vregs[vreg_i].regulator);
- dev_err(dev, "regulator get of %s failed (%d)\n",
+ wcnss_log(ERR,
+ "regulator get of %s failed (%d)\n",
iris_vregs[vreg_i].name, rc);
return rc;
} else {
- dev_dbg(dev, "Skip optional regulator configuration: %s\n",
- iris_vregs[vreg_i].name);
+ wcnss_log(DBG,
+ "Skip optional regulator configuration: %s\n",
+ iris_vregs[vreg_i].name);
continue;
}
}
@@ -284,7 +290,8 @@
iris_vregs[vreg_i].volt,
wlan_config->iris_vlevel);
if (rc) {
- dev_err(dev, "error reading voltage-level property\n");
+ wcnss_log(ERR,
+ "error reading voltage-level property\n");
return rc;
}
iris_vregs[vreg_i].state |= VREG_GET_REGULATOR_MASK;
@@ -334,7 +341,7 @@
clk = clk_get(dev, "xo");
if (IS_ERR(clk)) {
- pr_err("Couldn't get xo clock\n");
+ wcnss_log(ERR, "Couldn't get xo clock\n");
return PTR_ERR(clk);
}
@@ -344,7 +351,7 @@
clk = clk_get(dev, "cxo");
if (IS_ERR(clk)) {
- pr_err("Couldn't get cxo clock\n");
+ wcnss_log(ERR, "Couldn't get cxo clock\n");
return PTR_ERR(clk);
}
}
@@ -352,21 +359,21 @@
if (on) {
msm_wcnss_base = cfg->msm_wcnss_base;
if (!msm_wcnss_base) {
- pr_err("ioremap wcnss physical failed\n");
+ wcnss_log(ERR, "ioremap wcnss physical failed\n");
goto fail;
}
/* Enable IRIS XO */
rc = clk_prepare_enable(clk);
if (rc) {
- pr_err("clk enable failed\n");
+ wcnss_log(ERR, "clk enable failed\n");
goto fail;
}
/* NV bit is set to indicate that platform driver is capable
* of doing NV download.
*/
- pr_debug("wcnss: Indicate NV bin download\n");
+ wcnss_log(DBG, "Indicate NV bin download\n");
spare_reg = msm_wcnss_base + spare_offset;
reg = readl_relaxed(spare_reg);
reg |= NVBIN_DLND_BIT;
@@ -403,10 +410,11 @@
cpu_relax();
iris_reg = readl_relaxed(iris_read_reg);
- pr_info("wcnss: IRIS Reg: %08x\n", iris_reg);
+ wcnss_log(INFO, "IRIS Reg: %08x\n", iris_reg);
if (validate_iris_chip_id(iris_reg) && i >= 4) {
- pr_info("wcnss: IRIS Card absent/invalid\n");
+ wcnss_log(INFO,
+ "IRIS Card absent/invalid\n");
auto_detect = WCNSS_XO_INVALID;
/* Reset iris read bit */
reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
@@ -416,7 +424,8 @@
reg &= ~(WCNSS_PMU_CFG_IRIS_XO_MODE);
goto xo_configure;
} else if (!validate_iris_chip_id(iris_reg)) {
- pr_debug("wcnss: IRIS Card is present\n");
+ wcnss_log(DBG,
+ "IRIS Card is present\n");
break;
}
reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
@@ -472,13 +481,13 @@
auto_detect == WCNSS_XO_19MHZ) {
clk_rf = clk_get(dev, "rf_clk");
if (IS_ERR(clk_rf)) {
- pr_err("Couldn't get rf_clk\n");
+ wcnss_log(ERR, "Couldn't get rf_clk\n");
goto fail;
}
rc = clk_prepare_enable(clk_rf);
if (rc) {
- pr_err("clk_rf enable failed\n");
+ wcnss_log(ERR, "clk_rf enable failed\n");
goto fail;
}
if (iris_xo_set)
@@ -489,7 +498,7 @@
auto_detect == WCNSS_XO_19MHZ) {
clk_rf = clk_get(dev, "rf_clk");
if (IS_ERR(clk_rf)) {
- pr_err("Couldn't get rf_clk\n");
+ wcnss_log(ERR, "Couldn't get rf_clk\n");
goto fail;
}
clk_disable_unprepare(clk_rf);
@@ -517,7 +526,7 @@
cfg = wcnss_get_wlan_config();
if (!cfg) {
- pr_err("Failed to get WLAN configuration\n");
+ wcnss_log(ERR, "Failed to get WLAN configuration\n");
return;
}
@@ -530,7 +539,8 @@
if (regulators[i].state & VREG_OPTIMUM_MODE_MASK) {
rc = regulator_set_load(regulators[i].regulator, 0);
if (rc < 0) {
- pr_err("regulator set load(%s) failed (%d)\n",
+ wcnss_log(ERR,
+ "regulator set load(%s) failed (%d)\n",
regulators[i].name, rc);
}
}
@@ -553,15 +563,16 @@
max_voltage);
if (rc)
- pr_err("regulator_set_voltage(%s) failed (%d)\n",
- regulators[i].name, rc);
+ wcnss_log(ERR,
+ "regulator_set_voltage(%s) failed (%d)\n",
+ regulators[i].name, rc);
}
/* Disable regulator */
if (regulators[i].state & VREG_ENABLE_MASK) {
rc = regulator_disable(regulators[i].regulator);
if (rc < 0)
- pr_err("vreg %s disable failed (%d)\n",
+ wcnss_log(ERR, "vreg %s disable failed (%d)\n",
regulators[i].name, rc);
}
}
@@ -579,7 +590,7 @@
cfg = wcnss_get_wlan_config();
if (!cfg) {
- pr_err("Failed to get WLAN configuration\n");
+ wcnss_log(ERR, "Failed to get WLAN configuration\n");
return -EINVAL;
}
@@ -608,7 +619,8 @@
max_voltage);
if (rc) {
- pr_err("regulator_set_voltage(%s) failed (%d)\n",
+ wcnss_log(ERR,
+ "regulator_set_voltage(%s) failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
@@ -620,7 +632,8 @@
rc = regulator_set_load(regulators[i].regulator,
voltage_level[i].uA_load);
if (rc < 0) {
- pr_err("regulator set load(%s) failed (%d)\n",
+ wcnss_log(ERR,
+ "regulator set load(%s) failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
@@ -630,7 +643,7 @@
/* Enable the regulator */
rc = regulator_enable(regulators[i].regulator);
if (rc) {
- pr_err("vreg %s enable failed (%d)\n",
+ wcnss_log(ERR, "vreg %s enable failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
@@ -653,7 +666,7 @@
cfg->iris_vlevel);
break;
default:
- pr_err("%s invalid hardware %d\n", __func__, hw_type);
+ wcnss_log(ERR, "%s invalid hardware %d\n", __func__, hw_type);
}
}
@@ -669,7 +682,7 @@
cfg->iris_vlevel);
break;
default:
- pr_err("%s invalid hardware %d\n", __func__, hw_type);
+ wcnss_log(ERR, "%s invalid hardware %d\n", __func__, hw_type);
}
return ret;
}
@@ -683,7 +696,7 @@
ARRAY_SIZE(pronto_vregs), cfg->pronto_vlevel);
break;
default:
- pr_err("%s invalid hardware %d\n", __func__, hw_type);
+ wcnss_log(ERR, "%s invalid hardware %d\n", __func__, hw_type);
}
}
@@ -700,7 +713,7 @@
cfg->pronto_vlevel);
break;
default:
- pr_err("%s invalid hardware %d\n", __func__, hw_type);
+ wcnss_log(ERR, "%s invalid hardware %d\n", __func__, hw_type);
}
return ret;
diff --git a/drivers/soc/qcom/wcnss/wcnss_wlan.c b/drivers/soc/qcom/wcnss/wcnss_wlan.c
index 481dbd0..08bd78b 100644
--- a/drivers/soc/qcom/wcnss/wcnss_wlan.c
+++ b/drivers/soc/qcom/wcnss/wcnss_wlan.c
@@ -38,6 +38,7 @@
#include <linux/pm_qos.h>
#include <linux/bitops.h>
#include <linux/cdev.h>
+#include <linux/ipc_logging.h>
#include <soc/qcom/socinfo.h>
#include <soc/qcom/subsystem_restart.h>
@@ -452,6 +453,53 @@
struct cdev ctrl_dev, node_dev;
} *penv = NULL;
+static void *wcnss_ipc_log;
+
+#define IPC_NUM_LOG_PAGES 12
+#define wcnss_ipc_log_string(_x...) do { \
+ if (wcnss_ipc_log) \
+ ipc_log_string(wcnss_ipc_log, _x); \
+ } while (0)
+
+void wcnss_log(enum wcnss_log_type type, const char *_fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = _fmt,
+ };
+ va_list args;
+
+ va_start(args, _fmt);
+ vaf.va = &args;
+ switch (type) {
+ case ERR:
+ pr_err("wcnss: %pV", &vaf);
+ wcnss_ipc_log_string("wcnss: %pV", &vaf);
+ break;
+ case WARN:
+ pr_warn("wcnss: %pV", &vaf);
+ wcnss_ipc_log_string("wcnss: %pV", &vaf);
+ break;
+ case INFO:
+ pr_info("wcnss: %pV", &vaf);
+ wcnss_ipc_log_string("wcnss: %pV", &vaf);
+ break;
+ case DBG:
+#if defined(CONFIG_DYNAMIC_DEBUG)
+ pr_debug("wcnss: %pV", &vaf);
+ wcnss_ipc_log_string("wcnss: %pV", &vaf);
+#elif defined(DEBUG)
+ pr_debug("wcnss: %pV", &vaf);
+ wcnss_ipc_log_string("wcnss: %pV", &vaf);
+#else
+ pr_devel("wcnss: %pV", &vaf);
+ wcnss_ipc_log_string("wcnss: %pV", &vaf);
+#endif
+ break;
+ }
+ va_end(args);
+}
+EXPORT_SYMBOL(wcnss_log);
+
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -463,14 +511,14 @@
return -ENODEV;
if (strlen(buf) != WCNSS_USER_MAC_ADDR_LENGTH) {
- dev_err(dev, "%s: Invalid MAC addr length\n", __func__);
+ wcnss_log(ERR, "%s: Invalid MAC addr length\n", __func__);
return -EINVAL;
}
if (sscanf(buf, MAC_ADDRESS_STR, &mac_addr[0], &mac_addr[1],
&mac_addr[2], &mac_addr[3], &mac_addr[4],
&mac_addr[5]) != WLAN_MAC_ADDR_SIZE) {
- pr_err("%s: Failed to Copy MAC\n", __func__);
+ wcnss_log(ERR, "%s: Failed to Copy MAC\n", __func__);
return -EINVAL;
}
@@ -479,7 +527,7 @@
(char *)&mac_addr[index], sizeof(char));
}
- pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
+ wcnss_log(INFO, "%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
penv->wlan_nv_mac_addr[0], penv->wlan_nv_mac_addr[1],
penv->wlan_nv_mac_addr[2], penv->wlan_nv_mac_addr[3],
penv->wlan_nv_mac_addr[4], penv->wlan_nv_mac_addr[5]);
@@ -555,19 +603,19 @@
ccu_reg = penv->riva_ccu_base + CCU_RIVA_INVALID_ADDR_OFFSET;
reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
+ wcnss_log(INFO, "%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR0_OFFSET;
reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
+ wcnss_log(INFO, "%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR1_OFFSET;
reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
+ wcnss_log(INFO, "%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+ wcnss_log(INFO, "%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
}
EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
@@ -586,13 +634,12 @@
break;
}
- if (iter == A2XB_FIFO_COUNTER) {
- pr_err("%s data FIFO testbus possibly stalled reg%08x\n",
- type, reg);
- } else {
- pr_err("%s data FIFO tstbus not stalled reg%08x\n",
- type, reg);
- }
+ if (iter == A2XB_FIFO_COUNTER)
+ wcnss_log(ERR,
+ "%s data FIFO testbus possibly stalled reg%08x\n", type, reg);
+ else
+ wcnss_log(ERR,
+ "%s data FIFO tstbus not stalled reg%08x\n", type, reg);
}
int wcnss_get_dual_band_capability_info(struct platform_device *pdev)
@@ -627,71 +674,72 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_SPARE %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_SPARE %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_CFG %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_CFG %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_CSR %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_COM_CSR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_SOFT_RESET %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WDOG_CTL;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_WDOG_CTL %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_WDOG_CTL %08x\n", reg);
reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_SAW2_SPM_STS %08x\n", reg);
reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_CTL;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_SAW2_SPM_CTL %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_SAW2_SPM_CTL %08x\n", reg);
if (penv->is_a2xb_split_reg) {
reg_addr = penv->msm_wcnss_base +
PMU_A2XB_CFG_HSPLIT_RESP_LIMIT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PMU_A2XB_CFG_HSPLIT_RESP_LIMIT %08x\n", reg);
+ wcnss_log(ERR, "PMU_A2XB_CFG_HSPLIT_RESP_LIMIT %08x\n", reg);
}
reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SAW2_VERSION;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_SAW2_SAW2_VERSION %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_SAW2_SAW2_VERSION %08x\n", reg);
reg >>= PRONTO_SAW2_MAJOR_VER_OFFSET;
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CCPU_BOOT_REMAP_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_CCPU_BOOT_REMAP %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_CCPU_BOOT_REMAP %08x\n", reg);
reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PLL_STATUS %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PLL_STATUS %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
reg4 = readl_relaxed(reg_addr);
- pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4);
+ wcnss_log(ERR, "PMU_CPU_CMD_RCGR %08x\n", reg4);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg);
+ wcnss_log(ERR, "PRONTO_PMU_COM_GDSCR %08x\n", reg);
reg >>= 31;
if (!reg) {
- pr_err("Cannot log, Pronto common SS is power collapsed\n");
+ wcnss_log(ERR,
+ "Cannot log, Pronto common SS is power collapsed\n");
return;
}
reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
@@ -705,47 +753,47 @@
reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("A2XB_CFG_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "A2XB_CFG_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "A2XB_INT_SRC_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "A2XB_ERR_INFO_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg);
+ wcnss_log(ERR, "CCU_CCPU_INVALID_ADDR %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg);
+ wcnss_log(ERR, "CCU_CCPU_LAST_ADDR0 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg);
+ wcnss_log(ERR, "CCU_CCPU_LAST_ADDR1 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg);
+ wcnss_log(ERR, "CCU_CCPU_LAST_ADDR2 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_AOWBR_ERR_ADDR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_PRONTO_AOWBR_ERR_ADDR_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "CCU_PRONTO_AOWBR_ERR_ADDR_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_AOWBR_TIMEOUT_REG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_PRONTO_AOWBR_TIMEOUT_REG_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "CCU_PRONTO_AOWBR_TIMEOUT_REG_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_AOWBR_ERR_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_PRONTO_AOWBR_ERR_TIMEOUT_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "CCU_PRONTO_AOWBR_ERR_TIMEOUT_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_A2AB_ERR_ADDR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("CCU_PRONTO_A2AB_ERR_ADDR_OFFSET %08x\n", reg);
+ wcnss_log(ERR, "CCU_PRONTO_A2AB_ERR_ADDR_OFFSET %08x\n", reg);
tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
@@ -760,7 +808,7 @@
A2XB_READ_FIFO_FILL_MASK,
"Read");
} else {
- pr_err("Read data FIFO testbus %08x\n", reg);
+ wcnss_log(ERR, "Read data FIFO testbus %08x\n", reg);
}
/* command FIFO */
reg = 0;
@@ -771,7 +819,7 @@
wcnss_pronto_is_a2xb_bus_stall(tst_addr,
A2XB_CMD_FIFO_FILL_MASK, "Cmd");
} else {
- pr_err("Command FIFO testbus %08x\n", reg);
+ wcnss_log(ERR, "Command FIFO testbus %08x\n", reg);
}
/* write data FIFO */
@@ -784,7 +832,7 @@
A2XB_WRITE_FIFO_FILL_MASK,
"Write");
} else {
- pr_err("Write data FIFO testbus %08x\n", reg);
+ wcnss_log(ERR, "Write data FIFO testbus %08x\n", reg);
}
/* AXIM SEL CFG0 */
@@ -793,7 +841,7 @@
WCNSS_TSTBUS_CTRL_AXIM_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_err("AXIM SEL CFG0 testbus %08x\n", reg);
+ wcnss_log(ERR, "AXIM SEL CFG0 testbus %08x\n", reg);
/* AXIM SEL CFG1 */
reg = 0;
@@ -801,7 +849,7 @@
WCNSS_TSTBUS_CTRL_AXIM_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_err("AXIM SEL CFG1 testbus %08x\n", reg);
+ wcnss_log(ERR, "AXIM SEL CFG1 testbus %08x\n", reg);
/* CTRL SEL CFG0 */
reg = 0;
@@ -809,7 +857,7 @@
WCNSS_TSTBUS_CTRL_CTRL_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_err("CTRL SEL CFG0 testbus %08x\n", reg);
+ wcnss_log(ERR, "CTRL SEL CFG0 testbus %08x\n", reg);
/* CTRL SEL CFG1 */
reg = 0;
@@ -817,7 +865,7 @@
WCNSS_TSTBUS_CTRL_CTRL_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_err("CTRL SEL CFG1 testbus %08x\n", reg);
+ wcnss_log(ERR, "CTRL SEL CFG1 testbus %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
reg = readl_relaxed(reg_addr);
@@ -827,7 +875,7 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
reg3 = readl_relaxed(reg_addr);
- pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3);
+ wcnss_log(ERR, "PMU_WLAN_AHB_CBCR %08x\n", reg3);
msleep(50);
@@ -836,76 +884,76 @@
(!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
(!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
- pr_err("Cannot log, wlan domain is power collapsed\n");
+ wcnss_log(ERR, "Cannot log, wlan domain is power collapsed\n");
return;
}
reg = readl_relaxed(penv->wlan_tx_phy_aborts);
- pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
+ wcnss_log(ERR, "WLAN_TX_PHY_ABORTS %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_APB2PHY_STATUS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_APB2PHY_STATUS %08x\n", reg);
+ wcnss_log(ERR, "MCU_APB2PHY_STATUS %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CCAHB_ERR %08x\n", reg);
+ wcnss_log(ERR, "MCU_CBR_CCAHB_ERR %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CAHB_ERR %08x\n", reg);
+ wcnss_log(ERR, "MCU_CBR_CAHB_ERR %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CCAHB_TIMEOUT %08x\n", reg);
+ wcnss_log(ERR, "MCU_CBR_CCAHB_TIMEOUT %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CAHB_TIMEOUT %08x\n", reg);
+ wcnss_log(ERR, "MCU_CBR_CAHB_TIMEOUT %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_CDAHB_ERR %08x\n", reg);
+ wcnss_log(ERR, "MCU_DBR_CDAHB_ERR %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_DAHB_ERR %08x\n", reg);
+ wcnss_log(ERR, "MCU_DBR_DAHB_ERR %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_CDAHB_TIMEOUT %08x\n", reg);
+ wcnss_log(ERR, "MCU_DBR_CDAHB_TIMEOUT %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_DAHB_TIMEOUT %08x\n", reg);
+ wcnss_log(ERR, "MCU_DBR_DAHB_TIMEOUT %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_CDAHB_ERR %08x\n", reg);
+ wcnss_log(ERR, "MCU_FDBR_CDAHB_ERR %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_FDAHB_ERR %08x\n", reg);
+ wcnss_log(ERR, "MCU_FDBR_FDAHB_ERR %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_CDAHB_TIMEOUT %08x\n", reg);
+ wcnss_log(ERR, "MCU_FDBR_CDAHB_TIMEOUT %08x\n", reg);
reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_TIMEOUT_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_FDAHB_TIMEOUT %08x\n", reg);
+ wcnss_log(ERR, "MCU_FDBR_FDAHB_TIMEOUT %08x\n", reg);
reg = readl_relaxed(penv->wlan_brdg_err_source);
- pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg);
+ wcnss_log(ERR, "WLAN_BRDG_ERR_SOURCE %08x\n", reg);
reg = readl_relaxed(penv->wlan_tx_status);
- pr_err("WLAN_TXP_STATUS %08x\n", reg);
+ wcnss_log(ERR, "WLAN_TXP_STATUS %08x\n", reg);
reg = readl_relaxed(penv->alarms_txctl);
- pr_err("ALARMS_TXCTL %08x\n", reg);
+ wcnss_log(ERR, "ALARMS_TXCTL %08x\n", reg);
reg = readl_relaxed(penv->alarms_tactl);
- pr_err("ALARMS_TACTL %08x\n", reg);
+ wcnss_log(ERR, "ALARMS_TACTL %08x\n", reg);
}
EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
@@ -930,13 +978,13 @@
if (!IS_ERR_OR_NULL(pin_state)) {
ret = pinctrl_select_state(penv->pinctrl, pin_state);
if (ret < 0) {
- pr_err("%s: can not set gpio pins err: %d\n",
+ wcnss_log(ERR, "%s: can not set gpio pins err: %d\n",
__func__, ret);
goto pinctrl_set_err;
}
} else {
- pr_err("%s: invalid gpio pinstate err: %lu\n",
+ wcnss_log(ERR, "%s: invalid gpio pinstate err: %lu\n",
__func__, PTR_ERR(pin_state));
goto pinctrl_set_err;
}
@@ -945,7 +993,7 @@
ret = gpio_request_one(penv->gpios[i],
GPIOF_DIR_IN, NULL);
if (ret) {
- pr_err("%s: request failed for gpio:%d\n",
+ wcnss_log(ERR, "%s: request failed for gpio:%d\n",
__func__, penv->gpios[i]);
i--;
goto gpio_req_err;
@@ -956,7 +1004,7 @@
ret = gpio_request_one(penv->gpios[i],
GPIOF_OUT_INIT_LOW, NULL);
if (ret) {
- pr_err("%s: request failed for gpio:%d\n",
+ wcnss_log(ERR, "%s: request failed for gpio:%d\n",
__func__, penv->gpios[i]);
i--;
goto gpio_req_err;
@@ -1072,12 +1120,13 @@
0x04, 0x05, 0x11, 0x1e, 0x40, 0x48,
0x49, 0x4b, 0x00, 0x01, 0x4d};
- pr_info("%s: IRIS Registers [address] : value\n", __func__);
+ wcnss_log(INFO, "%s: IRIS Registers [address] : value\n", __func__);
for (i = 0; i < ARRAY_SIZE(regs_array); i++) {
reg_val = wcnss_rf_read_reg(regs_array[i]);
- pr_info("[0x%08x] : 0x%08x\n", regs_array[i], reg_val);
+ wcnss_log(INFO, "IRIS Reg Addr: [0x%08x] : Reg val: 0x%08x\n",
+ regs_array[i], reg_val);
}
}
@@ -1111,24 +1160,25 @@
if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) {
if (clk_set_parent(measure, wcnss_debug_mux)) {
- pr_err("Setting measure clk parent failed\n");
+ wcnss_log(ERR, "Setting measure clk parent failed\n");
return;
}
if (clk_prepare_enable(measure)) {
- pr_err("measure clk enable failed\n");
+ wcnss_log(ERR, "measure clk enable failed\n");
return;
}
clk_rate = clk_get_rate(measure);
- pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate);
+ wcnss_log(DBG, "clock frequency is: %luHz\n", clk_rate);
if (clk_rate) {
wcnss_pronto_log_debug_regs();
if (wcnss_get_mux_control())
wcnss_log_iris_regs();
} else {
- pr_err("clock frequency is zero, cannot access PMU or other registers\n");
+ wcnss_log(ERR, "clock frequency is zero, cannot");
+ wcnss_log(ERR, " access PMU or other registers\n");
wcnss_log_iris_regs();
}
@@ -1153,8 +1203,8 @@
wmb();
__raw_writel(1 << 16, penv->fiq_reg);
} else {
- pr_info("%s: Block FIQ during power up sequence\n",
- __func__);
+ wcnss_log(INFO,
+ "%s: Block FIQ during power up sequence\n", __func__);
}
} else {
wcnss_riva_log_debug_regs();
@@ -1202,20 +1252,20 @@
static void wcnss_pm_qos_add_request(void)
{
- pr_info("%s: add request\n", __func__);
+ wcnss_log(INFO, "%s: add request\n", __func__);
pm_qos_add_request(&penv->wcnss_pm_qos_request, PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
}
static void wcnss_pm_qos_remove_request(void)
{
- pr_info("%s: remove request\n", __func__);
+ wcnss_log(INFO, "%s: remove request\n", __func__);
pm_qos_remove_request(&penv->wcnss_pm_qos_request);
}
void wcnss_pm_qos_update_request(int val)
{
- pr_info("%s: update request %d\n", __func__, val);
+ wcnss_log(INFO, "%s: update request %d\n", __func__, val);
pm_qos_update_request(&penv->wcnss_pm_qos_request, val);
}
@@ -1248,21 +1298,21 @@
int len = 0;
if (penv != data) {
- pr_err("wcnss: invalid env pointer in smd callback\n");
+ wcnss_log(ERR, "invalid env pointer in smd callback\n");
return;
}
switch (event) {
case SMD_EVENT_DATA:
len = smd_read_avail(penv->smd_ch);
if (len < 0) {
- pr_err("wcnss: failed to read from smd %d\n", len);
+ wcnss_log(ERR, "failed to read from smd %d\n", len);
return;
}
schedule_work(&penv->wcnssctrl_rx_work);
break;
case SMD_EVENT_OPEN:
- pr_debug("wcnss: opening WCNSS SMD channel :%s",
+ wcnss_log(DBG, "opening WCNSS SMD channel :%s",
WCNSS_CTRL_CHANNEL);
schedule_work(&penv->wcnssctrl_version_work);
schedule_work(&penv->wcnss_pm_config_work);
@@ -1273,7 +1323,7 @@
break;
case SMD_EVENT_CLOSE:
- pr_debug("wcnss: closing WCNSS SMD channel :%s",
+ wcnss_log(DBG, "closing WCNSS SMD channel :%s",
WCNSS_CTRL_CHANNEL);
penv->nv_downloaded = 0;
penv->is_cbc_done = 0;
@@ -1290,7 +1340,7 @@
struct pinctrl_state *pin_state;
int ret;
- pr_debug("%s: Set GPIO state : %d\n", __func__, active);
+ wcnss_log(DBG, "%s: Set GPIO state : %d\n", __func__, active);
pin_state = active ? penv->wcnss_5wire_active
: penv->wcnss_5wire_suspend;
@@ -1298,13 +1348,13 @@
if (!IS_ERR_OR_NULL(pin_state)) {
ret = pinctrl_select_state(penv->pinctrl, pin_state);
if (ret < 0) {
- pr_err("%s: can not set %s pins\n", __func__,
+ wcnss_log(ERR, "%s: can not set %s pins\n", __func__,
active ? WCNSS_PINCTRL_STATE_DEFAULT
: WCNSS_PINCTRL_STATE_SLEEP);
return ret;
}
} else {
- pr_err("%s: invalid '%s' pinstate\n", __func__,
+ wcnss_log(ERR, "%s: invalid '%s' pinstate\n", __func__,
active ? WCNSS_PINCTRL_STATE_DEFAULT
: WCNSS_PINCTRL_STATE_SLEEP);
return PTR_ERR(pin_state);
@@ -1323,7 +1373,7 @@
penv->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(penv->pinctrl)) {
- pr_err("%s: failed to get pinctrl\n", __func__);
+ wcnss_log(ERR, "%s: failed to get pinctrl\n", __func__);
return PTR_ERR(penv->pinctrl);
}
@@ -1332,7 +1382,7 @@
WCNSS_PINCTRL_STATE_DEFAULT);
if (IS_ERR_OR_NULL(penv->wcnss_5wire_active)) {
- pr_err("%s: can not get default pinstate\n", __func__);
+ wcnss_log(ERR, "%s: can not get default pinstate\n", __func__);
return PTR_ERR(penv->wcnss_5wire_active);
}
@@ -1341,19 +1391,20 @@
WCNSS_PINCTRL_STATE_SLEEP);
if (IS_ERR_OR_NULL(penv->wcnss_5wire_suspend)) {
- pr_warn("%s: can not get sleep pinstate\n", __func__);
+ wcnss_log(WARN, "%s: can not get sleep pinstate\n", __func__);
return PTR_ERR(penv->wcnss_5wire_suspend);
}
penv->wcnss_gpio_active = pinctrl_lookup_state(penv->pinctrl,
WCNSS_PINCTRL_GPIO_STATE_DEFAULT);
if (IS_ERR_OR_NULL(penv->wcnss_gpio_active))
- pr_warn("%s: can not get gpio default pinstate\n", __func__);
+ wcnss_log(WARN, "%s: can not get gpio default pinstate\n",
+ __func__);
for (i = 0; i < WCNSS_WLAN_MAX_GPIO; i++) {
penv->gpios[i] = of_get_gpio(node, i);
if (penv->gpios[i] < 0)
- pr_warn("%s: Fail to get 5wire gpio: %d\n",
+ wcnss_log(WARN, "%s: Fail to get 5wire gpio: %d\n",
__func__, i);
}
@@ -1370,13 +1421,13 @@
/* Use Pinctrl to configure 5 wire GPIOs */
rc = wcnss_pinctrl_init(pdev);
if (rc) {
- pr_err("%s: failed to get pin resources\n", __func__);
+ wcnss_log(ERR, "%s: failed to get pin resources\n", __func__);
penv->pinctrl = NULL;
goto gpio_probe;
} else {
rc = wcnss_pinctrl_set_state(true);
if (rc)
- pr_err("%s: failed to set pin state\n",
+ wcnss_log(ERR, "%s: failed to set pin state\n",
__func__);
penv->use_pinctrl = true;
return rc;
@@ -1389,7 +1440,7 @@
if (enable) {
rc = gpio_request(gpio, "wcnss_wlan");
if (rc) {
- pr_err("WCNSS gpio_request %d err %d\n",
+ wcnss_log(ERR, "WCNSS gpio_request %d err %d\n",
gpio, rc);
goto fail;
}
@@ -1418,7 +1469,8 @@
if (enable) {
rc = gpio_request(i, gpios_5wire->name);
if (rc) {
- pr_err("WCNSS gpio_request %d err %d\n", i, rc);
+ wcnss_log(ERR,
+ "gpio_request %d err %d\n", i, rc);
goto fail;
}
} else {
@@ -1442,7 +1494,7 @@
penv->smd_channel_ready = 1;
- pr_info("%s: SMD ctrl channel up\n", __func__);
+ wcnss_log(INFO, "%s: SMD ctrl channel up\n", __func__);
return 0;
}
@@ -1452,7 +1504,7 @@
if (penv)
penv->smd_channel_ready = 0;
- pr_info("%s: SMD ctrl channel down\n", __func__);
+ wcnss_log(INFO, "%s: SMD ctrl channel down\n", __func__);
return 0;
}
@@ -1487,7 +1539,7 @@
&penv->smd_ch, penv,
wcnss_smd_notify_event);
if (ret < 0) {
- pr_err("wcnss: cannot open the smd command channel %s: %d\n",
+ wcnss_log(ERR, "cannot open the smd command channel %s: %d\n",
WCNSS_CTRL_CHANNEL, ret);
return -ENODEV;
}
@@ -1611,14 +1663,15 @@
{
if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
if (!penv->pm_ops) {
- pr_err("%s: pm_ops is already unregistered.\n",
+ wcnss_log(ERR, "%s: pm_ops is already unregistered.\n",
__func__);
return;
}
if (pm_ops->suspend != penv->pm_ops->suspend ||
pm_ops->resume != penv->pm_ops->resume)
- pr_err("PM APIs dont match with registered APIs\n");
+ wcnss_log(ERR,
+ "PM APIs dont match with registered APIs\n");
penv->pm_ops = NULL;
}
}
@@ -1637,7 +1690,7 @@
{
if (penv && tm_notify) {
if (tm_notify != penv->tm_notify)
- pr_err("tm_notify doesn't match registered\n");
+ wcnss_log(ERR, "tm_notify doesn't match registered\n");
penv->tm_notify = NULL;
}
}
@@ -1647,7 +1700,7 @@
{
if (penv) {
penv->serial_number = socinfo_get_serial_number();
- pr_info("%s: Device serial number: %u\n",
+ wcnss_log(INFO, "%s: Device serial number: %u\n",
__func__, penv->serial_number);
return penv->serial_number;
}
@@ -1662,7 +1715,7 @@
return -ENODEV;
memcpy(mac_addr, penv->wlan_nv_mac_addr, WLAN_MAC_ADDR_SIZE);
- pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
+ wcnss_log(DBG, "%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
penv->wlan_nv_mac_addr[0], penv->wlan_nv_mac_addr[1],
penv->wlan_nv_mac_addr[2], penv->wlan_nv_mac_addr[3],
penv->wlan_nv_mac_addr[4], penv->wlan_nv_mac_addr[5]);
@@ -1673,7 +1726,7 @@
static int enable_wcnss_suspend_notify;
static int enable_wcnss_suspend_notify_set(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
int ret;
@@ -1682,7 +1735,7 @@
return ret;
if (enable_wcnss_suspend_notify)
- pr_debug("Suspend notification activated for wcnss\n");
+ wcnss_log(DBG, "Suspend notification activated for wcnss\n");
return 0;
}
@@ -1771,6 +1824,15 @@
return 0;
}
+static int wcnss_wlan_suspend_noirq(struct device *dev)
+{
+ if (penv && dev && (dev == &penv->pdev->dev) &&
+ penv->smd_channel_ready &&
+ penv->pm_ops && penv->pm_ops->suspend_noirq)
+ return penv->pm_ops->suspend_noirq(dev);
+ return 0;
+}
+
static int wcnss_wlan_resume(struct device *dev)
{
if (penv && dev && (dev == &penv->pdev->dev) &&
@@ -1780,6 +1842,15 @@
return 0;
}
+static int wcnss_wlan_resume_noirq(struct device *dev)
+{
+ if (penv && dev && (dev == &penv->pdev->dev) &&
+ penv->smd_channel_ready &&
+ penv->pm_ops && penv->pm_ops->resume_noirq)
+ return penv->pm_ops->resume_noirq(dev);
+ return 0;
+}
+
void wcnss_prevent_suspend(void)
{
if (penv)
@@ -1857,12 +1928,12 @@
ret = smd_write_avail(penv->smd_ch);
if (ret < len) {
- pr_err("wcnss: no space available for smd frame\n");
+ wcnss_log(ERR, "no space available for smd frame\n");
return -ENOSPC;
}
ret = smd_write(penv->smd_ch, data, len);
if (ret < len) {
- pr_err("wcnss: failed to write Command %d", len);
+ wcnss_log(ERR, "failed to write Command %d", len);
ret = -ENODEV;
}
return ret;
@@ -1874,19 +1945,19 @@
struct qpnp_vadc_result adc_result;
if (!penv->vadc_dev) {
- pr_err("wcnss: not setting up vadc\n");
+ wcnss_log(ERR, "not setting up vadc\n");
return rc;
}
rc = qpnp_vadc_read(penv->vadc_dev, VBAT_SNS, &adc_result);
if (rc) {
- pr_err("error reading adc channel = %d, rc = %d\n",
+ wcnss_log(ERR, "error reading adc channel = %d, rc = %d\n",
VBAT_SNS, rc);
return rc;
}
- pr_info("Battery mvolts phy=%lld meas=0x%llx\n", adc_result.physical,
- adc_result.measurement);
+ wcnss_log(INFO, "Battery mvolts phy=%lld meas=0x%llx\n",
+ adc_result.physical, adc_result.measurement);
*result_uv = (int)adc_result.physical;
return 0;
@@ -1900,7 +1971,7 @@
cancel_delayed_work_sync(&penv->vbatt_work);
if (state == ADC_TM_LOW_STATE) {
- pr_debug("wcnss: low voltage notification triggered\n");
+ wcnss_log(DBG, "low voltage notification triggered\n");
penv->vbat_monitor_params.state_request =
ADC_TM_HIGH_THR_ENABLE;
penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD +
@@ -1912,14 +1983,14 @@
penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD -
WCNSS_VBATT_GUARD;
penv->vbat_monitor_params.high_thr = 0;
- pr_debug("wcnss: high voltage notification triggered\n");
+ wcnss_log(DBG, "high voltage notification triggered\n");
} else {
- pr_debug("wcnss: unknown voltage notification state: %d\n",
+ wcnss_log(DBG, "unknown voltage notification state: %d\n",
state);
mutex_unlock(&penv->vbat_monitor_mutex);
return;
}
- pr_debug("wcnss: set low thr to %d and high to %d\n",
+ wcnss_log(DBG, "set low thr to %d and high to %d\n",
penv->vbat_monitor_params.low_thr,
penv->vbat_monitor_params.high_thr);
@@ -1927,7 +1998,7 @@
&penv->vbat_monitor_params);
if (rc)
- pr_err("%s: tm setup failed: %d\n", __func__, rc);
+ wcnss_log(ERR, "%s: tm setup failed: %d\n", __func__, rc);
else
schedule_delayed_work(&penv->vbatt_work,
msecs_to_jiffies(2000));
@@ -1940,7 +2011,7 @@
int rc = -1;
if (!penv->adc_tm_dev) {
- pr_err("wcnss: not setting up vbatt\n");
+ wcnss_log(ERR, "not setting up vbatt\n");
return rc;
}
penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD;
@@ -1955,14 +2026,14 @@
penv->vbat_monitor_params.btm_ctx = (void *)penv;
penv->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
penv->vbat_monitor_params.threshold_notification = &wcnss_notify_vbat;
- pr_debug("wcnss: set low thr to %d and high to %d\n",
+ wcnss_log(DBG, "set low thr to %d and high to %d\n",
penv->vbat_monitor_params.low_thr,
penv->vbat_monitor_params.high_thr);
rc = qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
&penv->vbat_monitor_params);
if (rc)
- pr_err("%s: tm setup failed: %d\n", __func__, rc);
+ wcnss_log(ERR, "%s: tm setup failed: %d\n", __func__, rc);
return rc;
}
@@ -1979,12 +2050,12 @@
mutex_lock(&penv->vbat_monitor_mutex);
vbatt_msg.vbatt.curr_volt = penv->wlan_config.vbatt;
mutex_unlock(&penv->vbat_monitor_mutex);
- pr_debug("wcnss: send curr_volt: %d to FW\n",
+ wcnss_log(DBG, "send curr_volt: %d to FW\n",
vbatt_msg.vbatt.curr_volt);
ret = wcnss_smd_tx(&vbatt_msg, vbatt_msg.hdr.msg_len);
if (ret < 0)
- pr_err("wcnss: smd tx failed\n");
+ wcnss_log(ERR, "smd tx failed\n");
}
static void wcnss_update_vbatt(struct work_struct *work)
@@ -2002,13 +2073,13 @@
penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)) {
vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_HIGH;
penv->fw_vbatt_state = WCNSS_VBATT_HIGH;
- pr_debug("wcnss: send HIGH BATT to FW\n");
+ wcnss_log(DBG, "send HIGH BATT to FW\n");
} else if (!penv->vbat_monitor_params.low_thr &&
(penv->fw_vbatt_state == WCNSS_VBATT_HIGH ||
penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)){
vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_LOW;
penv->fw_vbatt_state = WCNSS_VBATT_LOW;
- pr_debug("wcnss: send LOW BATT to FW\n");
+ wcnss_log(DBG, "send LOW BATT to FW\n");
} else {
mutex_unlock(&penv->vbat_monitor_mutex);
return;
@@ -2016,7 +2087,7 @@
mutex_unlock(&penv->vbat_monitor_mutex);
ret = wcnss_smd_tx(&vbatt_msg, vbatt_msg.hdr.msg_len);
if (ret < 0)
- pr_err("wcnss: smd tx failed\n");
+ wcnss_log(ERR, "smd tx failed\n");
}
static unsigned char wcnss_fw_status(void)
@@ -2028,13 +2099,13 @@
len = smd_read_avail(penv->smd_ch);
if (len < 1) {
- pr_err("%s: invalid firmware status", __func__);
+ wcnss_log(ERR, "%s: invalid firmware status", __func__);
return fw_status;
}
rc = smd_read(penv->smd_ch, &fw_status, 1);
if (rc < 0) {
- pr_err("%s: incomplete data read from smd\n", __func__);
+ wcnss_log(ERR, "%s: incomplete data read from smd\n", __func__);
return fw_status;
}
return fw_status;
@@ -2057,7 +2128,7 @@
rc = wcnss_smd_tx(msg, rsphdr->msg_len);
if (rc < 0)
- pr_err("wcnss: smd tx failed\n");
+ wcnss_log(ERR, "smd tx failed\n");
kfree(msg);
}
@@ -2070,7 +2141,7 @@
unsigned char fw_status = WCNSS_RESP_FAIL;
if (len < sizeof(struct cal_data_params)) {
- pr_err("wcnss: incomplete cal header length\n");
+ wcnss_log(ERR, "incomplete cal header length\n");
return;
}
@@ -2078,18 +2149,18 @@
rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
sizeof(struct cal_data_params));
if (rc < sizeof(struct cal_data_params)) {
- pr_err("wcnss: incomplete cal header read from smd\n");
+ wcnss_log(ERR, "incomplete cal header read from smd\n");
mutex_unlock(&penv->dev_lock);
return;
}
if (penv->fw_cal_exp_frag != calhdr.frag_number) {
- pr_err("wcnss: Invalid frgament");
+ wcnss_log(ERR, "Invalid frgament");
goto unlock_exit;
}
if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
- pr_err("wcnss: Invalid fragment size");
+ wcnss_log(ERR, "Invalid fragment size");
goto unlock_exit;
}
@@ -2107,7 +2178,7 @@
if (calhdr.frag_number == 0) {
if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
- pr_err("wcnss: Invalid cal data size %d",
+ wcnss_log(ERR, "Invalid cal data size %d",
calhdr.total_size);
goto unlock_exit;
}
@@ -2123,7 +2194,7 @@
if (penv->fw_cal_rcvd + calhdr.frag_size >
MAX_CALIBRATED_DATA_SIZE) {
- pr_err("calibrated data size is more than expected %d",
+ wcnss_log(ERR, "calibrated data size is more than expected %d",
penv->fw_cal_rcvd + calhdr.frag_size);
penv->fw_cal_exp_frag = 0;
penv->fw_cal_rcvd = 0;
@@ -2142,7 +2213,7 @@
if (calhdr.msg_flags & LAST_FRAGMENT) {
penv->fw_cal_exp_frag = 0;
penv->fw_cal_available = true;
- pr_info("wcnss: cal data collection completed\n");
+ wcnss_log(INFO, "cal data collection completed\n");
}
mutex_unlock(&penv->dev_lock);
wake_up(&penv->read_wait);
@@ -2172,18 +2243,18 @@
len = smd_read_avail(penv->smd_ch);
if (len > WCNSS_MAX_FRAME_SIZE) {
- pr_err("wcnss: frame larger than the allowed size\n");
+ wcnss_log(ERR, "frame larger than the allowed size\n");
smd_read(penv->smd_ch, NULL, len);
return;
}
if (len < sizeof(struct smd_msg_hdr)) {
- pr_debug("wcnss: incomplete header available len = %d\n", len);
+ wcnss_log(DBG, "incomplete header available len = %d\n", len);
return;
}
rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
if (rc < sizeof(struct smd_msg_hdr)) {
- pr_err("wcnss: incomplete header read from smd\n");
+ wcnss_log(ERR, "incomplete header read from smd\n");
return;
}
len -= sizeof(struct smd_msg_hdr);
@@ -2194,14 +2265,14 @@
case WCNSS_VERSION_RSP:
if (len != sizeof(struct wcnss_version)
- sizeof(struct smd_msg_hdr)) {
- pr_err("wcnss: invalid version data from wcnss %d\n",
+ wcnss_log(ERR, "invalid version data from wcnss %d\n",
len);
return;
}
rc = smd_read(penv->smd_ch, buf + sizeof(struct smd_msg_hdr),
len);
if (rc < len) {
- pr_err("wcnss: incomplete data read from smd\n");
+ wcnss_log(ERR, "incomplete data read from smd\n");
return;
}
pversion = (struct wcnss_version *)buf;
@@ -2210,14 +2281,15 @@
snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
"%02x%02x%02x%02x", pversion->major, pversion->minor,
pversion->version, pversion->revision);
- pr_info("wcnss: version %s\n", penv->wcnss_version);
+ wcnss_log(INFO, "version %s\n", penv->wcnss_version);
/* schedule work to download nvbin to ccpu */
hw_type = wcnss_hardware_type();
switch (hw_type) {
case WCNSS_RIVA_HW:
/* supported only if riva major >= 1 and minor >= 4 */
if ((pversion->major >= 1) && (pversion->minor >= 4)) {
- pr_info("wcnss: schedule dnld work for riva\n");
+ wcnss_log(INFO,
+ "schedule download work for riva\n");
schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
}
break;
@@ -2227,41 +2299,43 @@
smd_msg.msg_len = sizeof(smd_msg);
rc = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
if (rc < 0)
- pr_err("wcnss: smd tx failed: %s\n", __func__);
+ wcnss_log(ERR, "smd tx failed: %s\n", __func__);
/* supported only if pronto major >= 1 and minor >= 4 */
if ((pversion->major >= 1) && (pversion->minor >= 4)) {
- pr_info("wcnss: schedule dnld work for pronto\n");
+ wcnss_log(INFO,
+ "schedule dnld work for pronto\n");
schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
}
break;
default:
- pr_info("wcnss: unknown hw type (%d), will not schedule dnld work\n",
- hw_type);
+ wcnss_log(INFO,
+ "unknown hw type (%d) will not schedule dnld work\n",
+ hw_type);
break;
}
break;
case WCNSS_BUILD_VER_RSP:
if (len > WCNSS_MAX_BUILD_VER_LEN) {
- pr_err("wcnss: invalid build version data from wcnss %d\n",
- len);
+ wcnss_log(ERR,
+ "invalid build version data from wcnss %d\n", len);
return;
}
rc = smd_read(penv->smd_ch, build, len);
if (rc < len) {
- pr_err("wcnss: incomplete data read from smd\n");
+ wcnss_log(ERR, "incomplete data read from smd\n");
return;
}
build[len] = 0;
- pr_info("wcnss: build version %s\n", build);
+ wcnss_log(INFO, "build version %s\n", build);
break;
case WCNSS_NVBIN_DNLD_RSP:
penv->nv_downloaded = true;
fw_status = wcnss_fw_status();
- pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
+ wcnss_log(DBG, "received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
fw_status);
if (fw_status != WAIT_FOR_CBC_IND)
penv->is_cbc_done = 1;
@@ -2271,12 +2345,12 @@
case WCNSS_CALDATA_DNLD_RSP:
penv->nv_downloaded = true;
fw_status = wcnss_fw_status();
- pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
+ wcnss_log(DBG, "received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
fw_status);
break;
case WCNSS_CBC_COMPLETE_IND:
penv->is_cbc_done = 1;
- pr_debug("wcnss: received WCNSS_CBC_COMPLETE_IND from FW\n");
+ wcnss_log(DBG, "received WCNSS_CBC_COMPLETE_IND from FW\n");
break;
case WCNSS_CALDATA_UPLD_REQ:
@@ -2284,7 +2358,7 @@
break;
default:
- pr_err("wcnss: invalid message type %d\n", phdr->msg_type);
+ wcnss_log(ERR, "invalid message type %d\n", phdr->msg_type);
}
}
@@ -2297,7 +2371,7 @@
smd_msg.msg_len = sizeof(smd_msg);
ret = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
if (ret < 0)
- pr_err("wcnss: smd tx failed\n");
+ wcnss_log(ERR, "smd tx failed\n");
}
static void wcnss_send_pm_config(struct work_struct *worker)
@@ -2322,12 +2396,12 @@
rc = of_property_read_u32_array(penv->pdev->dev.of_node,
"qcom,wcnss-pm", payload, prop_len);
if (rc < 0) {
- pr_err("wcnss: property read failed\n");
+ wcnss_log(ERR, "property read failed\n");
kfree(msg);
return;
}
- pr_debug("%s:size=%d: <%d, %d, %d, %d, %d %d>\n", __func__,
+ wcnss_log(DBG, "%s:size=%d: <%d, %d, %d, %d, %d %d>\n", __func__,
prop_len, *payload, *(payload + 1), *(payload + 2),
*(payload + 3), *(payload + 4), *(payload + 5));
@@ -2337,7 +2411,7 @@
rc = wcnss_smd_tx(msg, hdr->msg_len);
if (rc < 0)
- pr_err("wcnss: smd tx failed\n");
+ wcnss_log(ERR, "smd tx failed\n");
kfree(msg);
}
@@ -2368,7 +2442,8 @@
ret = request_firmware(&nv, NVBIN_FILE, dev);
if (ret || !nv || !nv->data || !nv->size) {
- pr_err("wcnss: %s: request_firmware failed for %s (ret = %d)\n",
+ wcnss_log(ERR,
+ "%s: request_firmware failed for %s (ret = %d)\n",
__func__, NVBIN_FILE, ret);
goto out;
}
@@ -2381,7 +2456,7 @@
total_fragments = TOTALFRAGMENTS(nv_blob_size);
- pr_info("wcnss: NV bin size: %d, total_fragments: %d\n",
+ wcnss_log(INFO, "NV bin size: %d, total_fragments: %d\n",
nv_blob_size, total_fragments);
/* get buffer for nv bin dnld req message */
@@ -2429,11 +2504,12 @@
retry_count = 0;
while ((ret == -ENOSPC) && (retry_count <= 3)) {
- pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
+ wcnss_log(DBG, "%s: smd tx failed, ENOSPC\n",
__func__);
- pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, dnld_req_msg->hdr.msg_len,
- total_fragments, retry_count);
+ wcnss_log(DBG, "fragment %d, len: %d,", count,
+ dnld_req_msg->hdr.msg_len);
+ wcnss_log(DBG, "TotFragments: %d, retry_count: %d\n",
+ total_fragments, retry_count);
/* wait and try again */
msleep(20);
@@ -2443,10 +2519,11 @@
}
if (ret < 0) {
- pr_err("wcnss: %s: smd tx failed\n", __func__);
- pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, dnld_req_msg->hdr.msg_len,
- total_fragments, retry_count);
+ wcnss_log(ERR, "%s: smd tx failed\n", __func__);
+ wcnss_log(ERR, "fragment %d, len: %d,", count,
+ dnld_req_msg->hdr.msg_len);
+ wcnss_log(ERR, "TotFragments: %d, retry_count: %d\n",
+ total_fragments, retry_count);
goto err_dnld;
}
}
@@ -2520,11 +2597,12 @@
retry_count = 0;
while ((ret == -ENOSPC) && (retry_count <= 3)) {
- pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
+ wcnss_log(DBG, "%s: smd tx failed, ENOSPC\n",
__func__);
- pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, cal_msg->hdr.msg_len,
- total_fragments, retry_count);
+ wcnss_log(DBG, "fragment: %d, len: %d",
+ count, cal_msg->hdr.msg_len);
+ wcnss_log(DBG, " TotFragments: %d, retry_count: %d\n",
+ total_fragments, retry_count);
/* wait and try again */
msleep(20);
@@ -2534,10 +2612,10 @@
}
if (ret < 0) {
- pr_err("wcnss: %s: smd tx failed\n", __func__);
- pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, cal_msg->hdr.msg_len,
- total_fragments, retry_count);
+ wcnss_log(ERR, "%s: smd tx failed: fragment %d, len:%d",
+ count, cal_msg->hdr.msg_len, __func__);
+ wcnss_log(ERR, " TotFragments: %d, retry_count: %d\n",
+ total_fragments, retry_count);
goto err_dnld;
}
}
@@ -2560,17 +2638,17 @@
msleep(500);
}
if (penv->fw_cal_available) {
- pr_info_ratelimited("wcnss: cal download, using fw cal");
+ wcnss_log(INFO, "cal download, using fw cal");
wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, true);
} else if (penv->user_cal_available) {
- pr_info_ratelimited("wcnss: cal download, using user cal");
+ wcnss_log(INFO, "cal download, using user cal");
wcnss_caldata_dnld(penv->user_cal_data,
penv->user_cal_rcvd, true);
}
nv_download:
- pr_info_ratelimited("wcnss: NV download");
+ wcnss_log(INFO, "NV download");
wcnss_nvbin_dnld();
}
@@ -2613,20 +2691,20 @@
switch (cmd) {
case WCNSS_USR_HAS_CAL_DATA:
if (buf[2] > 1)
- pr_err("%s: Invalid data for cal %d\n", __func__,
- buf[2]);
+ wcnss_log(ERR, "%s: Invalid data for cal %d\n",
+ __func__, buf[2]);
has_calibrated_data = buf[2];
break;
case WCNSS_USR_WLAN_MAC_ADDR:
memcpy(&penv->wlan_nv_mac_addr, &buf[2],
sizeof(penv->wlan_nv_mac_addr));
- pr_debug("%s: MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
+ wcnss_log(DBG, "%s: MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
penv->wlan_nv_mac_addr[0], penv->wlan_nv_mac_addr[1],
penv->wlan_nv_mac_addr[2], penv->wlan_nv_mac_addr[3],
penv->wlan_nv_mac_addr[4], penv->wlan_nv_mac_addr[5]);
break;
default:
- pr_err("%s: Invalid command %d\n", __func__, cmd);
+ wcnss_log(ERR, "%s: Invalid command %d\n", __func__, cmd);
break;
}
}
@@ -2685,7 +2763,7 @@
rc = wcnss_parse_voltage_regulator(&penv->wlan_config, &pdev->dev);
if (rc) {
- dev_err(&pdev->dev, "Failed to parse voltage regulators\n");
+ wcnss_log(ERR, "Failed to parse voltage regulators\n");
goto fail;
}
@@ -2724,7 +2802,7 @@
/* allocate 5-wire GPIO resources */
if (!penv->gpios_5wire) {
- dev_err(&pdev->dev, "insufficient IO resources\n");
+ wcnss_log(ERR, "insufficient IO resources\n");
ret = -ENOENT;
goto fail_gpio_res;
}
@@ -2734,7 +2812,7 @@
}
if (ret) {
- dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
+ wcnss_log(ERR, "gpios config failed.\n");
goto fail_gpio_res;
}
@@ -2747,7 +2825,7 @@
"wcnss_wlanrx_irq");
if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
- dev_err(&pdev->dev, "insufficient resources\n");
+ wcnss_log(ERR, "insufficient resources\n");
ret = -ENOENT;
goto fail_res;
}
@@ -2767,7 +2845,7 @@
"pronto_phy_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource pronto_phy_base failed\n",
+ wcnss_log(ERR, "%s: resource pronto_phy_base failed\n",
__func__);
goto fail_ioremap;
}
@@ -2779,7 +2857,7 @@
"riva_phy_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource riva_phy_base failed\n",
+ wcnss_log(ERR, "%s: resource riva_phy_base failed\n",
__func__);
goto fail_ioremap;
}
@@ -2789,7 +2867,7 @@
if (!penv->msm_wcnss_base) {
ret = -ENOMEM;
- pr_err("%s: ioremap wcnss physical failed\n", __func__);
+ wcnss_log(ERR, "%s: ioremap wcnss physical failed\n", __func__);
goto fail_ioremap;
}
@@ -2800,7 +2878,7 @@
"riva_ccu_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource riva_ccu_base failed\n",
+ wcnss_log(ERR, "%s: resource riva_ccu_base failed\n",
__func__);
goto fail_ioremap;
}
@@ -2809,7 +2887,7 @@
if (!penv->riva_ccu_base) {
ret = -ENOMEM;
- pr_err("%s: ioremap riva ccu physical failed\n",
+ wcnss_log(ERR, "%s: ioremap riva ccu physical failed\n",
__func__);
goto fail_ioremap;
}
@@ -2819,7 +2897,7 @@
"pronto_a2xb_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource pronto_a2xb_base failed\n",
+ wcnss_log(ERR, "%s: resource pronto_a2xb_base failed\n",
__func__);
goto fail_ioremap;
}
@@ -2828,8 +2906,8 @@
if (!penv->pronto_a2xb_base) {
ret = -ENOMEM;
- pr_err("%s: ioremap pronto a2xb physical failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: ioremap pronto a2xb physical failed\n", __func__);
goto fail_ioremap;
}
@@ -2838,7 +2916,7 @@
"pronto_ccpu_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource pronto_ccpu_base failed\n",
+ wcnss_log(ERR, "%s: resource pronto_ccpu_base failed\n",
__func__);
goto fail_ioremap;
}
@@ -2847,8 +2925,8 @@
if (!penv->pronto_ccpu_base) {
ret = -ENOMEM;
- pr_err("%s: ioremap pronto ccpu physical failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: ioremap pronto ccpu physical failed\n", __func__);
goto fail_ioremap;
}
@@ -2856,14 +2934,15 @@
res = platform_get_resource_byname(penv->pdev,
IORESOURCE_MEM, "wcnss_fiq");
if (!res) {
- dev_err(&pdev->dev, "insufficient irq mem resources\n");
+ wcnss_log(ERR, "insufficient irq mem resources\n");
ret = -ENOENT;
goto fail_ioremap;
}
penv->fiq_reg = ioremap_nocache(res->start, resource_size(res));
if (!penv->fiq_reg) {
- pr_err("wcnss: %s: ioremap_nocache() failed fiq_reg addr:%pr\n",
- __func__, &res->start);
+ wcnss_log(ERR, "%s", __func__,
+ "ioremap_nocache() failed fiq_reg addr:%pr\n",
+ &res->start);
ret = -ENOMEM;
goto fail_ioremap;
}
@@ -2873,7 +2952,7 @@
"pronto_saw2_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource pronto_saw2_base failed\n",
+ wcnss_log(ERR, "%s: resource pronto_saw2_base failed\n",
__func__);
goto fail_ioremap2;
}
@@ -2881,8 +2960,8 @@
devm_ioremap_resource(&pdev->dev, res);
if (!penv->pronto_saw2_base) {
- pr_err("%s: ioremap wcnss physical(saw2) failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: ioremap wcnss physical(saw2) failed\n", __func__);
ret = -ENOMEM;
goto fail_ioremap2;
}
@@ -2890,8 +2969,8 @@
penv->pronto_pll_base =
penv->msm_wcnss_base + PRONTO_PLL_MODE_OFFSET;
if (!penv->pronto_pll_base) {
- pr_err("%s: ioremap wcnss physical(pll) failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: ioremap wcnss physical(pll) failed\n", __func__);
ret = -ENOMEM;
goto fail_ioremap2;
}
@@ -2901,8 +2980,8 @@
"wlan_tx_phy_aborts");
if (!res) {
ret = -EIO;
- pr_err("%s: resource wlan_tx_phy_aborts failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: resource wlan_tx_phy_aborts failed\n", __func__);
goto fail_ioremap2;
}
penv->wlan_tx_phy_aborts =
@@ -2910,7 +2989,8 @@
if (!penv->wlan_tx_phy_aborts) {
ret = -ENOMEM;
- pr_err("%s: ioremap wlan TX PHY failed\n", __func__);
+ wcnss_log(ERR, "%s: ioremap wlan TX PHY failed\n",
+ __func__);
goto fail_ioremap2;
}
@@ -2919,8 +2999,8 @@
"wlan_brdg_err_source");
if (!res) {
ret = -EIO;
- pr_err("%s: resource wlan_brdg_err_source failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: get wlan_brdg_err_source res failed\n", __func__);
goto fail_ioremap2;
}
penv->wlan_brdg_err_source =
@@ -2928,7 +3008,8 @@
if (!penv->wlan_brdg_err_source) {
ret = -ENOMEM;
- pr_err("%s: ioremap wlan BRDG ERR failed\n", __func__);
+ wcnss_log(ERR, "%s: ioremap wlan BRDG ERR failed\n",
+ __func__);
goto fail_ioremap2;
}
@@ -2937,7 +3018,7 @@
"wlan_tx_status");
if (!res) {
ret = -EIO;
- pr_err("%s: resource wlan_tx_status failed\n",
+ wcnss_log(ERR, "%s: resource wlan_tx_status failed\n",
__func__);
goto fail_ioremap2;
}
@@ -2946,7 +3027,8 @@
if (!penv->wlan_tx_status) {
ret = -ENOMEM;
- pr_err("%s: ioremap wlan TX STATUS failed\n", __func__);
+ wcnss_log(ERR, "%s: ioremap wlan TX STATUS failed\n",
+ __func__);
goto fail_ioremap2;
}
@@ -2955,8 +3037,8 @@
"alarms_txctl");
if (!res) {
ret = -EIO;
- pr_err("%s: resource alarms_txctl failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: resource alarms_txctl failed\n", __func__);
goto fail_ioremap2;
}
penv->alarms_txctl =
@@ -2964,7 +3046,8 @@
if (!penv->alarms_txctl) {
ret = -ENOMEM;
- pr_err("%s: ioremap alarms TXCTL failed\n", __func__);
+ wcnss_log(ERR,
+ "%s: ioremap alarms TXCTL failed\n", __func__);
goto fail_ioremap2;
}
@@ -2973,8 +3056,8 @@
"alarms_tactl");
if (!res) {
ret = -EIO;
- pr_err("%s: resource alarms_tactl failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: resource alarms_tactl failed\n", __func__);
goto fail_ioremap2;
}
penv->alarms_tactl =
@@ -2982,7 +3065,8 @@
if (!penv->alarms_tactl) {
ret = -ENOMEM;
- pr_err("%s: ioremap alarms TACTL failed\n", __func__);
+ wcnss_log(ERR,
+ "%s: ioremap alarms TACTL failed\n", __func__);
goto fail_ioremap2;
}
@@ -2991,8 +3075,8 @@
"pronto_mcu_base");
if (!res) {
ret = -EIO;
- pr_err("%s: resource pronto_mcu_base failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: resource pronto_mcu_base failed\n", __func__);
goto fail_ioremap2;
}
penv->pronto_mcu_base =
@@ -3000,8 +3084,8 @@
if (!penv->pronto_mcu_base) {
ret = -ENOMEM;
- pr_err("%s: ioremap pronto mcu physical failed\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: ioremap pronto mcu physical failed\n", __func__);
goto fail_ioremap2;
}
@@ -3009,8 +3093,8 @@
"qcom,is-dual-band-disabled")) {
ret = wcnss_get_dual_band_capability_info(pdev);
if (ret) {
- pr_err("%s: failed to get dual band info\n",
- __func__);
+ wcnss_log(ERR,
+ "%s: failed to get dual band info\n", __func__);
goto fail_ioremap2;
}
}
@@ -3018,7 +3102,7 @@
penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
if (IS_ERR(penv->adc_tm_dev)) {
- pr_err("%s: adc get failed\n", __func__);
+ wcnss_log(ERR, "%s: adc get failed\n", __func__);
penv->adc_tm_dev = NULL;
} else {
INIT_DELAYED_WORK(&penv->vbatt_work, wcnss_update_vbatt);
@@ -3027,14 +3111,14 @@
penv->snoc_wcnss = devm_clk_get(&penv->pdev->dev, "snoc_wcnss");
if (IS_ERR(penv->snoc_wcnss)) {
- pr_err("%s: couldn't get snoc_wcnss\n", __func__);
+ wcnss_log(ERR, "%s: couldn't get snoc_wcnss\n", __func__);
penv->snoc_wcnss = NULL;
} else {
if (of_property_read_u32(pdev->dev.of_node,
"qcom,snoc-wcnss-clock-freq",
&penv->snoc_wcnss_clock_freq)) {
- pr_debug("%s: wcnss snoc clock frequency is not defined\n",
- __func__);
+ wcnss_log(DBG,
+ "%s: snoc clock frequency is not defined\n", __func__);
devm_clk_put(&penv->pdev->dev, penv->snoc_wcnss);
penv->snoc_wcnss = NULL;
}
@@ -3044,7 +3128,7 @@
penv->vadc_dev = qpnp_get_vadc(&penv->pdev->dev, "wcnss");
if (IS_ERR(penv->vadc_dev)) {
- pr_debug("%s: vadc get failed\n", __func__);
+ wcnss_log(DBG, "%s: vadc get failed\n", __func__);
penv->vadc_dev = NULL;
} else {
rc = wcnss_get_battery_volt(&penv->wlan_config.vbatt);
@@ -3052,8 +3136,8 @@
wcnss_send_vbatt_indication);
if (rc < 0)
- pr_err("Failed to get battery voltage with error= %d\n",
- rc);
+ wcnss_log(ERR,
+ "battery voltage get failed:err=%d\n", rc);
}
}
@@ -3061,7 +3145,7 @@
/* trigger initialization of the WCNSS */
penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
if (IS_ERR(penv->pil)) {
- dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
+ wcnss_log(ERR, "Peripheral Loader failed on WCNSS.\n");
ret = PTR_ERR(penv->pil);
wcnss_disable_pc_add_req();
wcnss_pronto_log_debug_regs();
@@ -3111,7 +3195,7 @@
int rc;
if (!penv->snoc_wcnss) {
- pr_err("%s: couldn't get clk snoc_wcnss\n", __func__);
+ wcnss_log(ERR, "%s: couldn't get clk snoc_wcnss\n", __func__);
return;
}
@@ -3119,13 +3203,15 @@
rc = clk_set_rate(penv->snoc_wcnss,
penv->snoc_wcnss_clock_freq);
if (rc) {
- pr_err("%s: snoc_wcnss_clk-clk_set_rate failed =%d\n",
- __func__, rc);
+ wcnss_log(ERR,
+ "%s: snoc_wcnss_clk-clk_set_rate failed=%d\n",
+ __func__, rc);
return;
}
if (clk_prepare_enable(penv->snoc_wcnss)) {
- pr_err("%s: snoc_wcnss clk enable failed\n", __func__);
+ wcnss_log(ERR, "%s: snoc_wcnss clk enable failed\n",
+ __func__);
return;
}
} else {
@@ -3201,7 +3287,7 @@
return -EFAULT;
if (!penv->triggered) {
- pr_info(DEVICE " triggered by userspace\n");
+ wcnss_log(INFO, DEVICE " triggered by userspace\n");
pdev = penv->pdev;
rc = wcnss_trigger_config(pdev);
if (rc)
@@ -3264,8 +3350,8 @@
user_buffer, 4);
if (!penv->user_cal_exp_size ||
penv->user_cal_exp_size > MAX_CALIBRATED_DATA_SIZE) {
- pr_err(DEVICE " invalid size to write %d\n",
- penv->user_cal_exp_size);
+ wcnss_log(ERR, DEVICE " invalid size to write %d\n",
+ penv->user_cal_exp_size);
penv->user_cal_exp_size = 0;
mutex_unlock(&penv->dev_lock);
return -EFAULT;
@@ -3279,8 +3365,8 @@
mutex_lock(&penv->dev_lock);
if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
(penv->user_cal_exp_size < count + penv->user_cal_rcvd)) {
- pr_err(DEVICE " invalid size to write %zu\n", count +
- penv->user_cal_rcvd);
+ wcnss_log(ERR, DEVICE " invalid size to write %zu\n",
+ count + penv->user_cal_rcvd);
mutex_unlock(&penv->dev_lock);
return -ENOMEM;
}
@@ -3302,7 +3388,7 @@
kfree(cal_data);
if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
penv->user_cal_available = true;
- pr_info_ratelimited("wcnss: user cal written");
+ wcnss_log(INFO, "user cal written");
}
mutex_unlock(&penv->dev_lock);
@@ -3324,12 +3410,12 @@
if (!(code >= SUBSYS_NOTIF_MIN_INDEX) &&
(code <= SUBSYS_NOTIF_MAX_INDEX)) {
- pr_debug("%s: Invaild subsystem notification code: %lu\n",
- __func__, code);
+ wcnss_log(DBG, "%s: Invaild subsystem notification code: %lu\n",
+ __func__, code);
return NOTIFY_DONE;
}
- pr_info("%s: wcnss notification event: %lu : %s\n",
+ wcnss_log(INFO, "%s: notification event: %lu : %s\n",
__func__, code, wcnss_subsys_notif_type[code]);
if (code == SUBSYS_PROXY_VOTE) {
@@ -3338,7 +3424,7 @@
WCNSS_WLAN_SWITCH_ON, &xo_mode);
wcnss_set_iris_xo_mode(xo_mode);
if (ret)
- pr_err("Failed to execute wcnss_wlan_power\n");
+ wcnss_log(ERR, "wcnss_wlan_power failed\n");
}
} else if (code == SUBSYS_PROXY_UNVOTE) {
if (pdev && pwlanconfig) {
@@ -3389,30 +3475,30 @@
ret = alloc_chrdev_region(&penv->dev_ctrl, 0, 1, CTRL_DEVICE);
if (ret < 0) {
- dev_err(&pdev->dev, "CTRL Device Registration failed\n");
+ wcnss_log(ERR, "CTRL Device Registration failed\n");
goto alloc_region_ctrl;
}
ret = alloc_chrdev_region(&penv->dev_node, 0, 1, DEVICE);
if (ret < 0) {
- dev_err(&pdev->dev, "NODE Device Registration failed\n");
+ wcnss_log(ERR, "NODE Device Registration failed\n");
goto alloc_region_node;
}
penv->node_class = class_create(THIS_MODULE, "wcnss");
if (!penv->node_class) {
- dev_err(&pdev->dev, "NODE Device Class Creation failed\n");
+ wcnss_log(ERR, "NODE Device Class Creation failed\n");
goto class_create_node;
}
if (device_create(penv->node_class, NULL, penv->dev_ctrl, NULL,
CTRL_DEVICE) == NULL) {
- dev_err(&pdev->dev, "CTRL Device Creation failed\n");
+ wcnss_log(ERR, "CTRL Device Creation failed\n");
goto device_create_ctrl;
}
if (device_create(penv->node_class, NULL, penv->dev_node, NULL,
DEVICE) == NULL) {
- dev_err(&pdev->dev, "NODE Device Creation failed\n");
+ wcnss_log(ERR, "NODE Device Creation failed\n");
goto device_create_node;
}
@@ -3420,11 +3506,11 @@
cdev_init(&penv->node_dev, &wcnss_node_fops);
if (cdev_add(&penv->ctrl_dev, penv->dev_ctrl, 1) == -1) {
- dev_err(&pdev->dev, "CTRL Device addition failed\n");
+ wcnss_log(ERR, "CTRL Device addition failed\n");
goto cdev_add_ctrl;
}
if (cdev_add(&penv->node_dev, penv->dev_node, 1) == -1) {
- dev_err(&pdev->dev, "NODE Device addition failed\n");
+ wcnss_log(ERR, "NODE Device addition failed\n");
goto cdev_add_node;
}
@@ -3448,7 +3534,7 @@
static void wcnss_cdev_unregister(struct platform_device *pdev)
{
- dev_err(&pdev->dev, "Unregistering cdev devices\n");
+ wcnss_log(ERR, "Unregistering cdev devices\n");
cdev_del(&penv->ctrl_dev);
cdev_del(&penv->node_dev);
device_destroy(penv->node_class, penv->dev_ctrl);
@@ -3465,7 +3551,7 @@
/* verify we haven't been called more than once */
if (penv) {
- dev_err(&pdev->dev, "cannot handle multiple devices.\n");
+ wcnss_log(ERR, "cannot handle multiple devices.\n");
return -ENODEV;
}
@@ -3479,7 +3565,7 @@
penv->user_cal_data =
devm_kzalloc(&pdev->dev, MAX_CALIBRATED_DATA_SIZE, GFP_KERNEL);
if (!penv->user_cal_data) {
- dev_err(&pdev->dev, "Failed to alloc memory for cal data.\n");
+ wcnss_log(ERR, "Failed to alloc memory for cal data.\n");
return -ENOMEM;
}
@@ -3493,7 +3579,7 @@
/* register wcnss event notification */
penv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
if (IS_ERR(penv->wcnss_notif_hdle)) {
- pr_err("wcnss: register event notification failed!\n");
+ wcnss_log(ERR, "register event notification failed!\n");
return PTR_ERR(penv->wcnss_notif_hdle);
}
@@ -3516,7 +3602,7 @@
* device so that we know that WCNSS configuration can take
* place
*/
- pr_info(DEVICE " probed in built-in mode\n");
+ wcnss_log(INFO, DEVICE " probed in built-in mode\n");
return wcnss_cdev_register(pdev);
}
@@ -3535,6 +3621,8 @@
static const struct dev_pm_ops wcnss_wlan_pm_ops = {
.suspend = wcnss_wlan_suspend,
.resume = wcnss_wlan_resume,
+ .suspend_noirq = wcnss_wlan_suspend_noirq,
+ .resume_noirq = wcnss_wlan_resume_noirq,
};
#ifdef CONFIG_WCNSS_CORE_PRONTO
@@ -3559,6 +3647,11 @@
static int __init wcnss_wlan_init(void)
{
+
+ wcnss_ipc_log = ipc_log_context_create(IPC_NUM_LOG_PAGES, "wcnss", 0);
+ if (!wcnss_ipc_log)
+ wcnss_log(ERR, "Unable to create log context\n");
+
platform_driver_register(&wcnss_wlan_driver);
platform_driver_register(&wcnss_wlan_ctrl_driver);
platform_driver_register(&wcnss_ctrl_driver);
@@ -3579,6 +3672,8 @@
platform_driver_unregister(&wcnss_ctrl_driver);
platform_driver_unregister(&wcnss_wlan_ctrl_driver);
platform_driver_unregister(&wcnss_wlan_driver);
+ ipc_log_context_destroy(wcnss_ipc_log);
+ wcnss_ipc_log = NULL;
}
module_init(wcnss_wlan_init);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6ee1da0..fc96f62 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -156,7 +156,6 @@
config SPI_BCM_QSPI
tristate "Broadcom BSPI and MSPI controller support"
depends on ARCH_BRCMSTB || ARCH_BCM || ARCH_BCM_IPROC || COMPILE_TEST
- depends on MTD_NORFLASH
default ARCH_BCM_IPROC
help
Enables support for the Broadcom SPI flash and MSPI controller.
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 8feac59..44be6b5 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1669,12 +1669,12 @@
pm_runtime_get_sync(&pdev->dev);
/* reset the hardware and block queue progress */
- spin_lock_irq(&as->lock);
if (as->use_dma) {
atmel_spi_stop_dma(as);
atmel_spi_release_dma(as);
}
+ spin_lock_irq(&as->lock);
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
spi_readl(as, SR);
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index 7d629b4..adc3f56 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -514,7 +514,7 @@
static void bcm_qspi_enable_bspi(struct bcm_qspi *qspi)
{
- if (!has_bspi(qspi) || (qspi->bspi_enabled))
+ if (!has_bspi(qspi))
return;
qspi->bspi_enabled = 1;
@@ -529,7 +529,7 @@
static void bcm_qspi_disable_bspi(struct bcm_qspi *qspi)
{
- if (!has_bspi(qspi) || (!qspi->bspi_enabled))
+ if (!has_bspi(qspi))
return;
qspi->bspi_enabled = 0;
@@ -543,16 +543,19 @@
static void bcm_qspi_chip_select(struct bcm_qspi *qspi, int cs)
{
- u32 data = 0;
+ u32 rd = 0;
+ u32 wr = 0;
- if (qspi->curr_cs == cs)
- return;
if (qspi->base[CHIP_SELECT]) {
- data = bcm_qspi_read(qspi, CHIP_SELECT, 0);
- data = (data & ~0xff) | (1 << cs);
- bcm_qspi_write(qspi, CHIP_SELECT, 0, data);
+ rd = bcm_qspi_read(qspi, CHIP_SELECT, 0);
+ wr = (rd & ~0xff) | (1 << cs);
+ if (rd == wr)
+ return;
+ bcm_qspi_write(qspi, CHIP_SELECT, 0, wr);
usleep_range(10, 20);
}
+
+ dev_dbg(&qspi->pdev->dev, "using cs:%d\n", cs);
qspi->curr_cs = cs;
}
@@ -770,8 +773,13 @@
dev_dbg(&qspi->pdev->dev, "WR %04x\n", val);
}
mspi_cdram = MSPI_CDRAM_CONT_BIT;
- mspi_cdram |= (~(1 << spi->chip_select) &
- MSPI_CDRAM_PCS);
+
+ if (has_bspi(qspi))
+ mspi_cdram &= ~1;
+ else
+ mspi_cdram |= (~(1 << spi->chip_select) &
+ MSPI_CDRAM_PCS);
+
mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 :
MSPI_CDRAM_BITSE_BIT);
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 02fb967..0d8f43a 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -646,7 +646,7 @@
buf = t->rx_buf;
t->rx_dma = dma_map_single(&spi->dev, buf,
t->len, DMA_FROM_DEVICE);
- if (dma_mapping_error(&spi->dev, !t->rx_dma)) {
+ if (dma_mapping_error(&spi->dev, t->rx_dma)) {
ret = -EFAULT;
goto err_rx_map;
}
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 447497e..d25cc40 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -115,8 +115,8 @@
{
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
- clk_disable_unprepare(dwsmmio->clk);
dw_spi_remove_host(&dwsmmio->dws);
+ clk_disable_unprepare(dwsmmio->clk);
return 0;
}
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index a074763..880fda4 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -89,7 +89,7 @@
#define TIMESTAMP_AFTER BIT(3)
#define POST_CMD_DELAY BIT(4)
-#define SPI_CORE2X_VOTE (10000)
+#define SPI_CORE2X_VOTE (7600)
/* GSI CONFIG0 TRE Params */
/* Flags bit fields */
#define GSI_LOOPBACK_EN (BIT(0))
@@ -311,7 +311,7 @@
struct spi_message *spi_msg)
{
struct spi_geni_master *mas = spi_master_get_devdata(spi);
- int mode = FIFO_MODE;
+ int mode = SE_DMA;
int fifo_disable = (geni_read_reg(mas->base, GENI_IF_FIFO_DISABLE_RO) &
FIFO_IF_DISABLE);
bool dma_chan_valid =
@@ -325,10 +325,10 @@
*/
if (fifo_disable && !dma_chan_valid)
mode = -EINVAL;
+ else if (!fifo_disable)
+ mode = SE_DMA;
else if (dma_chan_valid)
mode = GSI_DMA;
- else
- mode = FIFO_MODE;
return mode;
}
@@ -356,9 +356,6 @@
if (mode & SPI_CPHA)
flags |= GSI_CPHA;
- if (xfer->cs_change)
- flags |= GSI_CS_TOGGLE;
-
word_len = xfer->bits_per_word - MIN_WORD_LEN;
pack |= (GSI_TX_PACK_EN | GSI_RX_PACK_EN);
ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div);
@@ -586,8 +583,11 @@
}
cs |= spi_slv->chip_select;
- if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
- go_flags |= FRAGMENTATION;
+ if (!xfer->cs_change) {
+ if (!list_is_last(&xfer->transfer_list,
+ &spi->cur_msg->transfers))
+ go_flags |= FRAGMENTATION;
+ }
go_tre = setup_go_tre(cmd, cs, rx_len, go_flags, mas);
sg_init_table(xfer_tx_sg, tx_nent);
@@ -715,25 +715,20 @@
mas->cur_xfer_mode = select_xfer_mode(spi, spi_msg);
- if (mas->cur_xfer_mode == FIFO_MODE) {
- geni_se_select_mode(mas->base, FIFO_MODE);
- reinit_completion(&mas->xfer_done);
- ret = setup_fifo_params(spi_msg->spi, spi);
+ if (mas->cur_xfer_mode < 0) {
+ dev_err(mas->dev, "%s: Couldn't select mode %d", __func__,
+ mas->cur_xfer_mode);
+ ret = -EINVAL;
} else if (mas->cur_xfer_mode == GSI_DMA) {
- mas->num_tx_eot = 0;
- mas->num_rx_eot = 0;
- mas->num_xfers = 0;
- reinit_completion(&mas->tx_cb);
- reinit_completion(&mas->rx_cb);
memset(mas->gsi, 0,
(sizeof(struct spi_geni_gsi) * NUM_SPI_XFER));
geni_se_select_mode(mas->base, GSI_DMA);
ret = spi_geni_map_buf(mas, spi_msg);
} else {
- dev_err(mas->dev, "%s: Couldn't select mode %d", __func__,
- mas->cur_xfer_mode);
- ret = -EINVAL;
+ geni_se_select_mode(mas->base, mas->cur_xfer_mode);
+ ret = setup_fifo_params(spi_msg->spi, spi);
}
+
return ret;
}
@@ -756,9 +751,8 @@
u32 max_speed = spi->cur_msg->spi->max_speed_hz;
struct se_geni_rsc *rsc = &mas->spi_rsc;
- /* Adjust the AB/IB based on the max speed of the slave.*/
+ /* Adjust the IB based on the max speed of the slave.*/
rsc->ib = max_speed * DEFAULT_BUS_WIDTH;
- rsc->ab = max_speed * DEFAULT_BUS_WIDTH;
if (mas->shared_se) {
struct se_geni_rsc *rsc;
int ret = 0;
@@ -940,8 +934,6 @@
m_cmd = SPI_RX_ONLY;
spi_tx_cfg &= ~CS_TOGGLE;
- if (xfer->cs_change)
- spi_tx_cfg |= CS_TOGGLE;
if (!(mas->cur_word_len % MIN_WORD_LEN)) {
trans_len =
((xfer->len << 3) / mas->cur_word_len) & TRANS_LEN_MSK;
@@ -950,8 +942,12 @@
trans_len = (xfer->len / bytes_per_word) & TRANS_LEN_MSK;
}
- if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
- m_param |= FRAGMENTATION;
+
+ if (!xfer->cs_change) {
+ if (!list_is_last(&xfer->transfer_list,
+ &spi->cur_msg->transfers))
+ m_param |= FRAGMENTATION;
+ }
mas->cur_xfer = xfer;
if (m_cmd & SPI_TX_ONLY) {
@@ -963,25 +959,64 @@
geni_write_reg(trans_len, mas->base, SE_SPI_RX_TRANS_LEN);
mas->rx_rem_bytes = xfer->len;
}
+
+ if (trans_len > (mas->tx_fifo_depth * mas->tx_fifo_width)) {
+ if (mas->cur_xfer_mode != SE_DMA) {
+ mas->cur_xfer_mode = SE_DMA;
+ geni_se_select_mode(mas->base, mas->cur_xfer_mode);
+ }
+ } else {
+ if (mas->cur_xfer_mode != FIFO_MODE) {
+ mas->cur_xfer_mode = FIFO_MODE;
+ geni_se_select_mode(mas->base, mas->cur_xfer_mode);
+ }
+ }
+
geni_write_reg(spi_tx_cfg, mas->base, SE_SPI_TRANS_CFG);
geni_setup_m_cmd(mas->base, m_cmd, m_param);
GENI_SE_DBG(mas->ipc, false, mas->dev,
- "%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x\n",
- __func__, trans_len, xfer->len, spi_tx_cfg, m_cmd);
- if (m_cmd & SPI_TX_ONLY)
- geni_write_reg(mas->tx_wm, mas->base, SE_GENI_TX_WATERMARK_REG);
+ "%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x cs%d mode%d\n",
+ __func__, trans_len, xfer->len, spi_tx_cfg, m_cmd,
+ xfer->cs_change, mas->cur_xfer_mode);
+ if ((m_cmd & SPI_RX_ONLY) && (mas->cur_xfer_mode == SE_DMA)) {
+ int ret = 0;
+
+ ret = geni_se_rx_dma_prep(mas->wrapper_dev, mas->base,
+ xfer->rx_buf, xfer->len, &xfer->rx_dma);
+ if (ret)
+ GENI_SE_ERR(mas->ipc, true, mas->dev,
+ "Failed to setup Rx dma %d\n", ret);
+ }
+ if (m_cmd & SPI_TX_ONLY) {
+ if (mas->cur_xfer_mode == FIFO_MODE) {
+ geni_write_reg(mas->tx_wm, mas->base,
+ SE_GENI_TX_WATERMARK_REG);
+ } else if (mas->cur_xfer_mode == SE_DMA) {
+ int ret = 0;
+
+ ret = geni_se_tx_dma_prep(mas->wrapper_dev, mas->base,
+ (void *)xfer->tx_buf, xfer->len,
+ &xfer->tx_dma);
+ if (ret)
+ GENI_SE_ERR(mas->ipc, true, mas->dev,
+ "Failed to setup tx dma %d\n", ret);
+ }
+ }
+
/* Ensure all writes are done before the WM interrupt */
mb();
}
-static void handle_fifo_timeout(struct spi_geni_master *mas)
+static void handle_fifo_timeout(struct spi_geni_master *mas,
+ struct spi_transfer *xfer)
{
unsigned long timeout;
geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc);
reinit_completion(&mas->xfer_done);
geni_cancel_m_cmd(mas->base);
- geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
+ if (mas->cur_xfer_mode == FIFO_MODE)
+ geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
/* Ensure cmd cancel is written */
mb();
timeout = wait_for_completion_timeout(&mas->xfer_done, HZ);
@@ -996,6 +1031,15 @@
dev_err(mas->dev,
"Failed to cancel/abort m_cmd\n");
}
+ if (mas->cur_xfer_mode == SE_DMA) {
+ if (xfer->tx_buf)
+ geni_se_tx_dma_unprep(mas->wrapper_dev,
+ xfer->tx_dma, xfer->len);
+ if (xfer->rx_buf)
+ geni_se_rx_dma_unprep(mas->wrapper_dev,
+ xfer->rx_dma, xfer->len);
+ }
+
}
static int spi_geni_transfer_one(struct spi_master *spi,
@@ -1011,7 +1055,8 @@
return -EINVAL;
}
- if (mas->cur_xfer_mode == FIFO_MODE) {
+ if (mas->cur_xfer_mode != GSI_DMA) {
+ reinit_completion(&mas->xfer_done);
setup_fifo_xfer(xfer, mas, slv->mode, spi);
timeout = wait_for_completion_timeout(&mas->xfer_done,
msecs_to_jiffies(SPI_XFER_TIMEOUT_MS));
@@ -1025,7 +1070,22 @@
ret = -ETIMEDOUT;
goto err_fifo_geni_transfer_one;
}
+
+ if (mas->cur_xfer_mode == SE_DMA) {
+ if (xfer->tx_buf)
+ geni_se_tx_dma_unprep(mas->wrapper_dev,
+ xfer->tx_dma, xfer->len);
+ if (xfer->rx_buf)
+ geni_se_rx_dma_unprep(mas->wrapper_dev,
+ xfer->rx_dma, xfer->len);
+ }
} else {
+ mas->num_tx_eot = 0;
+ mas->num_rx_eot = 0;
+ mas->num_xfers = 0;
+ reinit_completion(&mas->tx_cb);
+ reinit_completion(&mas->rx_cb);
+
setup_gsi_xfer(xfer, mas, slv, spi);
if ((mas->num_xfers >= NUM_SPI_XFER) ||
(list_is_last(&xfer->transfer_list,
@@ -1069,7 +1129,7 @@
dmaengine_terminate_all(mas->tx);
return ret;
err_fifo_geni_transfer_one:
- handle_fifo_timeout(mas);
+ handle_fifo_timeout(mas, xfer);
return ret;
}
@@ -1185,33 +1245,59 @@
goto exit_geni_spi_irq;
}
m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);
- if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
- geni_spi_handle_rx(mas);
+ if (mas->cur_xfer_mode == FIFO_MODE) {
+ if ((m_irq & M_RX_FIFO_WATERMARK_EN) ||
+ (m_irq & M_RX_FIFO_LAST_EN))
+ geni_spi_handle_rx(mas);
- if ((m_irq & M_TX_FIFO_WATERMARK_EN))
- geni_spi_handle_tx(mas);
+ if ((m_irq & M_TX_FIFO_WATERMARK_EN))
+ geni_spi_handle_tx(mas);
- if ((m_irq & M_CMD_DONE_EN) || (m_irq & M_CMD_CANCEL_EN) ||
- (m_irq & M_CMD_ABORT_EN)) {
- complete(&mas->xfer_done);
- /*
- * If this happens, then a CMD_DONE came before all the buffer
- * bytes were sent out. This is unusual, log this condition and
- * disable the WM interrupt to prevent the system from stalling
- * due an interrupt storm.
- * If this happens when all Rx bytes haven't been received, log
- * the condition.
- */
- if (mas->tx_rem_bytes) {
- geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
- GENI_SE_DBG(mas->ipc, false, mas->dev,
- "%s:Premature Done.tx_rem%d bpw%d\n",
- __func__, mas->tx_rem_bytes, mas->cur_word_len);
+ if ((m_irq & M_CMD_DONE_EN) || (m_irq & M_CMD_CANCEL_EN) ||
+ (m_irq & M_CMD_ABORT_EN)) {
+ complete(&mas->xfer_done);
+ /*
+ * If this happens, then a CMD_DONE came before all the
+ * buffer bytes were sent out. This is unusual, log this
+ * condition and disable the WM interrupt to prevent the
+ * system from stalling due an interrupt storm.
+ * If this happens when all Rx bytes haven't been
+ * received, log the condition.
+ */
+ if (mas->tx_rem_bytes) {
+ geni_write_reg(0, mas->base,
+ SE_GENI_TX_WATERMARK_REG);
+ GENI_SE_DBG(mas->ipc, false, mas->dev,
+ "%s:Premature Done.tx_rem%d bpw%d\n",
+ __func__, mas->tx_rem_bytes,
+ mas->cur_word_len);
+ }
+ if (mas->rx_rem_bytes)
+ GENI_SE_DBG(mas->ipc, false, mas->dev,
+ "%s:Premature Done.rx_rem%d bpw%d\n",
+ __func__, mas->rx_rem_bytes,
+ mas->cur_word_len);
}
- if (mas->rx_rem_bytes)
- GENI_SE_DBG(mas->ipc, false, mas->dev,
- "%s:Premature Done.rx_rem%d bpw%d\n",
- __func__, mas->rx_rem_bytes, mas->cur_word_len);
+ } else if (mas->cur_xfer_mode == SE_DMA) {
+ u32 dma_tx_status = geni_read_reg(mas->base,
+ SE_DMA_TX_IRQ_STAT);
+ u32 dma_rx_status = geni_read_reg(mas->base,
+ SE_DMA_RX_IRQ_STAT);
+
+ if (dma_tx_status)
+ geni_write_reg(dma_tx_status, mas->base,
+ SE_DMA_TX_IRQ_CLR);
+ if (dma_rx_status)
+ geni_write_reg(dma_rx_status, mas->base,
+ SE_DMA_RX_IRQ_CLR);
+ if (dma_tx_status & TX_DMA_DONE)
+ mas->tx_rem_bytes = 0;
+ if (dma_rx_status & RX_DMA_DONE)
+ mas->rx_rem_bytes = 0;
+ if (!mas->tx_rem_bytes && !mas->rx_rem_bytes)
+ complete(&mas->xfer_done);
+ if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN))
+ complete(&mas->xfer_done);
}
exit_geni_spi_irq:
geni_write_reg(m_irq, mas->base, SE_GENI_M_IRQ_CLEAR);
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index d5157b2..a47cf63 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -454,6 +454,8 @@
int elements = 0;
int word_len, element_count;
struct omap2_mcspi_cs *cs = spi->controller_state;
+ void __iomem *chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
count = xfer->len;
@@ -549,8 +551,8 @@
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
elements--;
- if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
- & OMAP2_MCSPI_CHSTAT_RXS)) {
+ if (!mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_RXS)) {
u32 w;
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
@@ -568,8 +570,7 @@
return count;
}
}
- if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
- & OMAP2_MCSPI_CHSTAT_RXS)) {
+ if (!mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS)) {
u32 w;
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
index ce31b81..b8e004d 100644
--- a/drivers/spi/spi-pxa2xx.h
+++ b/drivers/spi/spi-pxa2xx.h
@@ -38,7 +38,7 @@
/* SSP register addresses */
void __iomem *ioaddr;
- u32 ssdr_physical;
+ phys_addr_t ssdr_physical;
/* SSP masks*/
u32 dma_cr1;
diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
index 9918a57..7e7da97 100644
--- a/drivers/spi/spi-sun6i.c
+++ b/drivers/spi/spi-sun6i.c
@@ -464,7 +464,7 @@
static int sun6i_spi_remove(struct platform_device *pdev)
{
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_force_suspend(&pdev->dev);
return 0;
}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 6db8063..c2e85e2 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -743,8 +743,14 @@
for (i = 0; i < sgs; i++) {
if (vmalloced_buf || kmap_buf) {
- min = min_t(size_t,
- len, desc_len - offset_in_page(buf));
+ /*
+ * Next scatterlist entry size is the minimum between
+ * the desc_len and the remaining buffer length that
+ * fits in a page.
+ */
+ min = min_t(size_t, desc_len,
+ min_t(size_t, len,
+ PAGE_SIZE - offset_in_page(buf)));
if (vmalloced_buf)
vm_page = vmalloc_to_page(buf);
else
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 6199523..0f0b7ba 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -1449,6 +1449,7 @@
.driver = {
.name = "spmi_pmic_arb",
.of_match_table = spmi_pmic_arb_match_table,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 7bd27a4..a17c483 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -33,6 +33,15 @@
/sys/module/lowmemorykiller/parameters/adj and convert them
to oom_score_adj values.
+config ANDROID_VSOC
+ tristate "Android Virtual SoC support"
+ default n
+ depends on PCI_MSI
+ ---help---
+ This option adds support for the Virtual SoC driver needed to boot
+ a 'cuttlefish' Android image inside QEmu. The driver interacts with
+ a QEmu ivshmem device. If built as a module, it will be called vsoc.
+
source "drivers/staging/android/ion/Kconfig"
source "drivers/staging/android/fiq_debugger/Kconfig"
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index 21b0ff4..93c5f5a 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_ASHMEM) += ashmem.o
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
+obj-$(CONFIG_ANDROID_VSOC) += vsoc.o
diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO
index 64d8c87..edfb680 100644
--- a/drivers/staging/android/TODO
+++ b/drivers/staging/android/TODO
@@ -33,5 +33,14 @@
- clean up and ABI check for security issues
- move it to drivers/base/dma-buf
+vsoc.c, uapi/vsoc_shm.h
+ - The current driver uses the same wait queue for all of the futexes in a
+ region. This will cause false wakeups in regions with a large number of
+ waiting threads. We should eventually use multiple queues and select the
+ queue based on the region.
+ - Add debugfs support for examining the permissions of regions.
+ - Remove VSOC_WAIT_FOR_INCOMING_INTERRUPT ioctl. This functionality has been
+ superseded by the futex and is there for legacy reasons.
+
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com>
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 35697e6..f4ffac4 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -343,24 +343,23 @@
mutex_lock(&ashmem_mutex);
if (asma->size == 0) {
- ret = -EINVAL;
- goto out;
+ mutex_unlock(&ashmem_mutex);
+ return -EINVAL;
}
if (!asma->file) {
- ret = -EBADF;
- goto out;
+ mutex_unlock(&ashmem_mutex);
+ return -EBADF;
}
+ mutex_unlock(&ashmem_mutex);
+
ret = vfs_llseek(asma->file, offset, origin);
if (ret < 0)
- goto out;
+ return ret;
/** Copy f_pos from backing file, since f_ops->llseek() sets it */
file->f_pos = asma->file->f_pos;
-
-out:
- mutex_unlock(&ashmem_mutex);
return ret;
}
@@ -711,16 +710,14 @@
size_t pgstart, pgend;
int ret = -EINVAL;
+ if (unlikely(copy_from_user(&pin, p, sizeof(pin))))
+ return -EFAULT;
+
mutex_lock(&ashmem_mutex);
if (unlikely(!asma->file))
goto out_unlock;
- if (unlikely(copy_from_user(&pin, p, sizeof(pin)))) {
- ret = -EFAULT;
- goto out_unlock;
- }
-
/* per custom, you can pass zero for len to mean "everything onward" */
if (!pin.len)
pin.len = PAGE_ALIGN(asma->size) - pin.offset;
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index c1103c7..6000707 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -1003,7 +1003,6 @@
struct ion_device *dev = client->dev;
struct rb_node *n;
- pr_debug("%s: %d\n", __func__, __LINE__);
down_write(&dev->lock);
rb_erase(&client->node, &dev->clients);
up_write(&dev->lock);
@@ -1213,9 +1212,6 @@
int pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
int i;
- pr_debug("%s: syncing for device %s\n", __func__,
- dev ? dev_name(dev) : "null");
-
if (!ion_buffer_fault_user_mappings(buffer))
return;
@@ -1269,7 +1265,6 @@
mutex_lock(&buffer->lock);
list_add(&vma_list->list, &buffer->vmas);
mutex_unlock(&buffer->lock);
- pr_debug("%s: adding %pK\n", __func__, vma);
}
static void ion_vm_close(struct vm_area_struct *vma)
@@ -1277,14 +1272,12 @@
struct ion_buffer *buffer = vma->vm_private_data;
struct ion_vma_list *vma_list, *tmp;
- pr_debug("%s\n", __func__);
mutex_lock(&buffer->lock);
list_for_each_entry_safe(vma_list, tmp, &buffer->vmas, list) {
if (vma_list->vma != vma)
continue;
list_del(&vma_list->list);
kfree(vma_list);
- pr_debug("%s: deleting %pK\n", __func__, vma);
break;
}
mutex_unlock(&buffer->lock);
@@ -1680,7 +1673,6 @@
{
struct ion_client *client = file->private_data;
- pr_debug("%s: %d\n", __func__, __LINE__);
ion_client_destroy(client);
return 0;
}
@@ -1692,7 +1684,6 @@
struct ion_client *client;
char debug_name[64];
- pr_debug("%s: %d\n", __func__, __LINE__);
snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
client = ion_client_create(dev, debug_name);
if (IS_ERR(client))
diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c
index 72f2b6a..d991b02 100644
--- a/drivers/staging/android/ion/ion_cma_heap.c
+++ b/drivers/staging/android/ion/ion_cma_heap.c
@@ -81,8 +81,6 @@
struct device *dev = heap->priv;
struct ion_cma_buffer_info *info;
- dev_dbg(dev, "Request buffer allocation len %ld\n", len);
-
info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL);
if (!info)
return ION_CMA_ALLOCATE_FAILED;
@@ -123,7 +121,6 @@
/* keep this for memory release */
buffer->priv_virt = info;
- dev_dbg(dev, "Allocate buffer %pK\n", buffer);
return 0;
err:
@@ -137,7 +134,6 @@
struct ion_cma_buffer_info *info = buffer->priv_virt;
unsigned long attrs = 0;
- dev_dbg(dev, "Release buffer %pK\n", buffer);
/* release memory */
if (info->is_cached)
attrs |= DMA_ATTR_FORCE_COHERENT;
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 800c167..3ac71ed 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_system_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -36,7 +36,11 @@
static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_NOWARN);
#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
-static const unsigned int orders[] = {9, 8, 4, 0};
+#if defined(CONFIG_IOMMU_IO_PGTABLE_ARMV7S)
+static const unsigned int orders[] = {8, 4, 0};
+#else
+static const unsigned int orders[] = {9, 4, 0};
+#endif
#else
static const unsigned int orders[] = {0};
#endif
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index a1602e4..027094f 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -63,6 +63,10 @@
#define CREATE_TRACE_POINTS
#include "trace/lowmemorykiller.h"
+/* to enable lowmemorykiller */
+static int enable_lmk = 1;
+module_param_named(enable_lmk, enable_lmk, int, 0644);
+
static u32 lowmem_debug_level = 1;
static short lowmem_adj[6] = {
0,
@@ -93,6 +97,9 @@
static unsigned long lowmem_count(struct shrinker *s,
struct shrink_control *sc)
{
+ if (!enable_lmk)
+ return 0;
+
return global_node_page_state(NR_ACTIVE_ANON) +
global_node_page_state(NR_ACTIVE_FILE) +
global_node_page_state(NR_INACTIVE_ANON) +
diff --git a/drivers/staging/android/uapi/vsoc_shm.h b/drivers/staging/android/uapi/vsoc_shm.h
new file mode 100644
index 0000000..741b138
--- /dev/null
+++ b/drivers/staging/android/uapi/vsoc_shm.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_VSOC_SHM_H
+#define _UAPI_LINUX_VSOC_SHM_H
+
+#include <linux/types.h>
+
+/**
+ * A permission is a token that permits a receiver to read and/or write an area
+ * of memory within a Vsoc region.
+ *
+ * An fd_scoped permission grants both read and write access, and can be
+ * attached to a file description (see open(2)).
+ * Ownership of the area can then be shared by passing a file descriptor
+ * among processes.
+ *
+ * begin_offset and end_offset define the area of memory that is controlled by
+ * the permission. owner_offset points to a word, also in shared memory, that
+ * controls ownership of the area.
+ *
+ * ownership of the region expires when the associated file description is
+ * released.
+ *
+ * At most one permission can be attached to each file description.
+ *
+ * This is useful when implementing HALs like gralloc that scope and pass
+ * ownership of shared resources via file descriptors.
+ *
+ * The caller is responsibe for doing any fencing.
+ *
+ * The calling process will normally identify a currently free area of
+ * memory. It will construct a proposed fd_scoped_permission_arg structure:
+ *
+ * begin_offset and end_offset describe the area being claimed
+ *
+ * owner_offset points to the location in shared memory that indicates the
+ * owner of the area.
+ *
+ * owned_value is the value that will be stored in owner_offset iff the
+ * permission can be granted. It must be different than VSOC_REGION_FREE.
+ *
+ * Two fd_scoped_permission structures are compatible if they vary only by
+ * their owned_value fields.
+ *
+ * The driver ensures that, for any group of simultaneous callers proposing
+ * compatible fd_scoped_permissions, it will accept exactly one of the
+ * propopsals. The other callers will get a failure with errno of EAGAIN.
+ *
+ * A process receiving a file descriptor can identify the region being
+ * granted using the VSOC_GET_FD_SCOPED_PERMISSION ioctl.
+ */
+struct fd_scoped_permission {
+ __u32 begin_offset;
+ __u32 end_offset;
+ __u32 owner_offset;
+ __u32 owned_value;
+};
+
+/*
+ * This value represents a free area of memory. The driver expects to see this
+ * value at owner_offset when creating a permission otherwise it will not do it,
+ * and will write this value back once the permission is no longer needed.
+ */
+#define VSOC_REGION_FREE ((__u32)0)
+
+/**
+ * ioctl argument for VSOC_CREATE_FD_SCOPE_PERMISSION
+ */
+struct fd_scoped_permission_arg {
+ struct fd_scoped_permission perm;
+ __s32 managed_region_fd;
+};
+
+#define VSOC_NODE_FREE ((__u32)0)
+
+/*
+ * Describes a signal table in shared memory. Each non-zero entry in the
+ * table indicates that the receiver should signal the futex at the given
+ * offset. Offsets are relative to the region, not the shared memory window.
+ *
+ * interrupt_signalled_offset is used to reliably signal interrupts across the
+ * vmm boundary. There are two roles: transmitter and receiver. For example,
+ * in the host_to_guest_signal_table the host is the transmitter and the
+ * guest is the receiver. The protocol is as follows:
+ *
+ * 1. The transmitter should convert the offset of the futex to an offset
+ * in the signal table [0, (1 << num_nodes_lg2))
+ * The transmitter can choose any appropriate hashing algorithm, including
+ * hash = futex_offset & ((1 << num_nodes_lg2) - 1)
+ *
+ * 3. The transmitter should atomically compare and swap futex_offset with 0
+ * at hash. There are 3 possible outcomes
+ * a. The swap fails because the futex_offset is already in the table.
+ * The transmitter should stop.
+ * b. Some other offset is in the table. This is a hash collision. The
+ * transmitter should move to another table slot and try again. One
+ * possible algorithm:
+ * hash = (hash + 1) & ((1 << num_nodes_lg2) - 1)
+ * c. The swap worked. Continue below.
+ *
+ * 3. The transmitter atomically swaps 1 with the value at the
+ * interrupt_signalled_offset. There are two outcomes:
+ * a. The prior value was 1. In this case an interrupt has already been
+ * posted. The transmitter is done.
+ * b. The prior value was 0, indicating that the receiver may be sleeping.
+ * The transmitter will issue an interrupt.
+ *
+ * 4. On waking the receiver immediately exchanges a 0 with the
+ * interrupt_signalled_offset. If it receives a 0 then this a spurious
+ * interrupt. That may occasionally happen in the current protocol, but
+ * should be rare.
+ *
+ * 5. The receiver scans the signal table by atomicaly exchanging 0 at each
+ * location. If a non-zero offset is returned from the exchange the
+ * receiver wakes all sleepers at the given offset:
+ * futex((int*)(region_base + old_value), FUTEX_WAKE, MAX_INT);
+ *
+ * 6. The receiver thread then does a conditional wait, waking immediately
+ * if the value at interrupt_signalled_offset is non-zero. This catches cases
+ * here additional signals were posted while the table was being scanned.
+ * On the guest the wait is handled via the VSOC_WAIT_FOR_INCOMING_INTERRUPT
+ * ioctl.
+ */
+struct vsoc_signal_table_layout {
+ /* log_2(Number of signal table entries) */
+ __u32 num_nodes_lg2;
+ /*
+ * Offset to the first signal table entry relative to the start of the
+ * region
+ */
+ __u32 futex_uaddr_table_offset;
+ /*
+ * Offset to an atomic_t / atomic uint32_t. A non-zero value indicates
+ * that one or more offsets are currently posted in the table.
+ * semi-unique access to an entry in the table
+ */
+ __u32 interrupt_signalled_offset;
+};
+
+#define VSOC_REGION_WHOLE ((__s32)0)
+#define VSOC_DEVICE_NAME_SZ 16
+
+/**
+ * Each HAL would (usually) talk to a single device region
+ * Mulitple entities care about these regions:
+ * - The ivshmem_server will populate the regions in shared memory
+ * - The guest kernel will read the region, create minor device nodes, and
+ * allow interested parties to register for FUTEX_WAKE events in the region
+ * - HALs will access via the minor device nodes published by the guest kernel
+ * - Host side processes will access the region via the ivshmem_server:
+ * 1. Pass name to ivshmem_server at a UNIX socket
+ * 2. ivshmemserver will reply with 2 fds:
+ * - host->guest doorbell fd
+ * - guest->host doorbell fd
+ * - fd for the shared memory region
+ * - region offset
+ * 3. Start a futex receiver thread on the doorbell fd pointed at the
+ * signal_nodes
+ */
+struct vsoc_device_region {
+ __u16 current_version;
+ __u16 min_compatible_version;
+ __u32 region_begin_offset;
+ __u32 region_end_offset;
+ __u32 offset_of_region_data;
+ struct vsoc_signal_table_layout guest_to_host_signal_table;
+ struct vsoc_signal_table_layout host_to_guest_signal_table;
+ /* Name of the device. Must always be terminated with a '\0', so
+ * the longest supported device name is 15 characters.
+ */
+ char device_name[VSOC_DEVICE_NAME_SZ];
+ /* There are two ways that permissions to access regions are handled:
+ * - When subdivided_by is VSOC_REGION_WHOLE, any process that can
+ * open the device node for the region gains complete access to it.
+ * - When subdivided is set processes that open the region cannot
+ * access it. Access to a sub-region must be established by invoking
+ * the VSOC_CREATE_FD_SCOPE_PERMISSION ioctl on the region
+ * referenced in subdivided_by, providing a fileinstance
+ * (represented by a fd) opened on this region.
+ */
+ __u32 managed_by;
+};
+
+/*
+ * The vsoc layout descriptor.
+ * The first 4K should be reserved for the shm header and region descriptors.
+ * The regions should be page aligned.
+ */
+
+struct vsoc_shm_layout_descriptor {
+ __u16 major_version;
+ __u16 minor_version;
+
+ /* size of the shm. This may be redundant but nice to have */
+ __u32 size;
+
+ /* number of shared memory regions */
+ __u32 region_count;
+
+ /* The offset to the start of region descriptors */
+ __u32 vsoc_region_desc_offset;
+};
+
+/*
+ * This specifies the current version that should be stored in
+ * vsoc_shm_layout_descriptor.major_version and
+ * vsoc_shm_layout_descriptor.minor_version.
+ * It should be updated only if the vsoc_device_region and
+ * vsoc_shm_layout_descriptor structures have changed.
+ * Versioning within each region is transferred
+ * via the min_compatible_version and current_version fields in
+ * vsoc_device_region. The driver does not consult these fields: they are left
+ * for the HALs and host processes and will change independently of the layout
+ * version.
+ */
+#define CURRENT_VSOC_LAYOUT_MAJOR_VERSION 2
+#define CURRENT_VSOC_LAYOUT_MINOR_VERSION 0
+
+#define VSOC_CREATE_FD_SCOPED_PERMISSION \
+ _IOW(0xF5, 0, struct fd_scoped_permission)
+#define VSOC_GET_FD_SCOPED_PERMISSION _IOR(0xF5, 1, struct fd_scoped_permission)
+
+/*
+ * This is used to signal the host to scan the guest_to_host_signal_table
+ * for new futexes to wake. This sends an interrupt if one is not already
+ * in flight.
+ */
+#define VSOC_MAYBE_SEND_INTERRUPT_TO_HOST _IO(0xF5, 2)
+
+/*
+ * When this returns the guest will scan host_to_guest_signal_table to
+ * check for new futexes to wake.
+ */
+/* TODO(ghartman): Consider moving this to the bottom half */
+#define VSOC_WAIT_FOR_INCOMING_INTERRUPT _IO(0xF5, 3)
+
+/*
+ * Guest HALs will use this to retrieve the region description after
+ * opening their device node.
+ */
+#define VSOC_DESCRIBE_REGION _IOR(0xF5, 4, struct vsoc_device_region)
+
+/*
+ * Wake any threads that may be waiting for a host interrupt on this region.
+ * This is mostly used during shutdown.
+ */
+#define VSOC_SELF_INTERRUPT _IO(0xF5, 5)
+
+/*
+ * This is used to signal the host to scan the guest_to_host_signal_table
+ * for new futexes to wake. This sends an interrupt unconditionally.
+ */
+#define VSOC_SEND_INTERRUPT_TO_HOST _IO(0xF5, 6)
+
+enum wait_types {
+ VSOC_WAIT_UNDEFINED = 0,
+ VSOC_WAIT_IF_EQUAL = 1,
+ VSOC_WAIT_IF_EQUAL_TIMEOUT = 2
+};
+
+/*
+ * Wait for a condition to be true
+ *
+ * Note, this is sized and aligned so the 32 bit and 64 bit layouts are
+ * identical.
+ */
+struct vsoc_cond_wait {
+ /* Input: Offset of the 32 bit word to check */
+ __u32 offset;
+ /* Input: Value that will be compared with the offset */
+ __u32 value;
+ /* Monotonic time to wake at in seconds */
+ __u64 wake_time_sec;
+ /* Input: Monotonic time to wait in nanoseconds */
+ __u32 wake_time_nsec;
+ /* Input: Type of wait */
+ __u32 wait_type;
+ /* Output: Number of times the thread woke before returning. */
+ __u32 wakes;
+ /* Ensure that we're 8-byte aligned and 8 byte length for 32/64 bit
+ * compatibility.
+ */
+ __u32 reserved_1;
+};
+
+#define VSOC_COND_WAIT _IOWR(0xF5, 7, struct vsoc_cond_wait)
+
+/* Wake any local threads waiting at the offset given in arg */
+#define VSOC_COND_WAKE _IO(0xF5, 8)
+
+#endif /* _UAPI_LINUX_VSOC_SHM_H */
diff --git a/drivers/staging/android/vsoc.c b/drivers/staging/android/vsoc.c
new file mode 100644
index 0000000..954ed2c
--- /dev/null
+++ b/drivers/staging/android/vsoc.c
@@ -0,0 +1,1165 @@
+/*
+ * drivers/android/staging/vsoc.c
+ *
+ * Android Virtual System on a Chip (VSoC) driver
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * Author: ghartman@google.com
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Based on drivers/char/kvm_ivshmem.c - driver for KVM Inter-VM shared memory
+ * Copyright 2009 Cam Macdonell <cam@cs.ualberta.ca>
+ *
+ * Based on cirrusfb.c and 8139cp.c:
+ * Copyright 1999-2001 Jeff Garzik
+ * Copyright 2001-2004 Jeff Garzik
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/futex.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/cdev.h>
+#include <linux/file.h>
+#include "uapi/vsoc_shm.h"
+
+#define VSOC_DEV_NAME "vsoc"
+
+/*
+ * Description of the ivshmem-doorbell PCI device used by QEmu. These
+ * constants follow docs/specs/ivshmem-spec.txt, which can be found in
+ * the QEmu repository. This was last reconciled with the version that
+ * came out with 2.8
+ */
+
+/*
+ * These constants are determined KVM Inter-VM shared memory device
+ * register offsets
+ */
+enum {
+ INTR_MASK = 0x00, /* Interrupt Mask */
+ INTR_STATUS = 0x04, /* Interrupt Status */
+ IV_POSITION = 0x08, /* VM ID */
+ DOORBELL = 0x0c, /* Doorbell */
+};
+
+static const int REGISTER_BAR; /* Equal to 0 */
+static const int MAX_REGISTER_BAR_LEN = 0x100;
+/*
+ * The MSI-x BAR is not used directly.
+ *
+ * static const int MSI_X_BAR = 1;
+ */
+static const int SHARED_MEMORY_BAR = 2;
+
+struct vsoc_region_data {
+ char name[VSOC_DEVICE_NAME_SZ + 1];
+ wait_queue_head_t interrupt_wait_queue;
+ /* TODO(b/73664181): Use multiple futex wait queues */
+ wait_queue_head_t futex_wait_queue;
+ /* Flag indicating that an interrupt has been signalled by the host. */
+ atomic_t *incoming_signalled;
+ /* Flag indicating the guest has signalled the host. */
+ atomic_t *outgoing_signalled;
+ bool irq_requested;
+ bool device_created;
+};
+
+struct vsoc_device {
+ /* Kernel virtual address of REGISTER_BAR. */
+ void __iomem *regs;
+ /* Physical address of SHARED_MEMORY_BAR. */
+ phys_addr_t shm_phys_start;
+ /* Kernel virtual address of SHARED_MEMORY_BAR. */
+ void __iomem *kernel_mapped_shm;
+ /* Size of the entire shared memory window in bytes. */
+ size_t shm_size;
+ /*
+ * Pointer to the virtual address of the shared memory layout structure.
+ * This is probably identical to kernel_mapped_shm, but saving this
+ * here saves a lot of annoying casts.
+ */
+ struct vsoc_shm_layout_descriptor *layout;
+ /*
+ * Points to a table of region descriptors in the kernel's virtual
+ * address space. Calculated from
+ * vsoc_shm_layout_descriptor.vsoc_region_desc_offset
+ */
+ struct vsoc_device_region *regions;
+ /* Head of a list of permissions that have been granted. */
+ struct list_head permissions;
+ struct pci_dev *dev;
+ /* Per-region (and therefore per-interrupt) information. */
+ struct vsoc_region_data *regions_data;
+ /*
+ * Table of msi-x entries. This has to be separated from struct
+ * vsoc_region_data because the kernel deals with them as an array.
+ */
+ struct msix_entry *msix_entries;
+ /* Mutex that protectes the permission list */
+ struct mutex mtx;
+ /* Major number assigned by the kernel */
+ int major;
+ /* Character device assigned by the kernel */
+ struct cdev cdev;
+ /* Device class assigned by the kernel */
+ struct class *class;
+ /*
+ * Flags that indicate what we've initialized. These are used to do an
+ * orderly cleanup of the device.
+ */
+ bool enabled_device;
+ bool requested_regions;
+ bool cdev_added;
+ bool class_added;
+ bool msix_enabled;
+};
+
+static struct vsoc_device vsoc_dev;
+
+/*
+ * TODO(ghartman): Add a /sys filesystem entry that summarizes the permissions.
+ */
+
+struct fd_scoped_permission_node {
+ struct fd_scoped_permission permission;
+ struct list_head list;
+};
+
+struct vsoc_private_data {
+ struct fd_scoped_permission_node *fd_scoped_permission_node;
+};
+
+static long vsoc_ioctl(struct file *, unsigned int, unsigned long);
+static int vsoc_mmap(struct file *, struct vm_area_struct *);
+static int vsoc_open(struct inode *, struct file *);
+static int vsoc_release(struct inode *, struct file *);
+static ssize_t vsoc_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t vsoc_write(struct file *, const char __user *, size_t, loff_t *);
+static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin);
+static int do_create_fd_scoped_permission(
+ struct vsoc_device_region *region_p,
+ struct fd_scoped_permission_node *np,
+ struct fd_scoped_permission_arg __user *arg);
+static void do_destroy_fd_scoped_permission(
+ struct vsoc_device_region *owner_region_p,
+ struct fd_scoped_permission *perm);
+static long do_vsoc_describe_region(struct file *,
+ struct vsoc_device_region __user *);
+static ssize_t vsoc_get_area(struct file *filp, __u32 *perm_off);
+
+/**
+ * Validate arguments on entry points to the driver.
+ */
+inline int vsoc_validate_inode(struct inode *inode)
+{
+ if (iminor(inode) >= vsoc_dev.layout->region_count) {
+ dev_err(&vsoc_dev.dev->dev,
+ "describe_region: invalid region %d\n", iminor(inode));
+ return -ENODEV;
+ }
+ return 0;
+}
+
+inline int vsoc_validate_filep(struct file *filp)
+{
+ int ret = vsoc_validate_inode(file_inode(filp));
+
+ if (ret)
+ return ret;
+ if (!filp->private_data) {
+ dev_err(&vsoc_dev.dev->dev,
+ "No private data on fd, region %d\n",
+ iminor(file_inode(filp)));
+ return -EBADFD;
+ }
+ return 0;
+}
+
+/* Converts from shared memory offset to virtual address */
+static inline void *shm_off_to_virtual_addr(__u32 offset)
+{
+ return (void __force *)vsoc_dev.kernel_mapped_shm + offset;
+}
+
+/* Converts from shared memory offset to physical address */
+static inline phys_addr_t shm_off_to_phys_addr(__u32 offset)
+{
+ return vsoc_dev.shm_phys_start + offset;
+}
+
+/**
+ * Convenience functions to obtain the region from the inode or file.
+ * Dangerous to call before validating the inode/file.
+ */
+static inline struct vsoc_device_region *vsoc_region_from_inode(
+ struct inode *inode)
+{
+ return &vsoc_dev.regions[iminor(inode)];
+}
+
+static inline struct vsoc_device_region *vsoc_region_from_filep(
+ struct file *inode)
+{
+ return vsoc_region_from_inode(file_inode(inode));
+}
+
+static inline uint32_t vsoc_device_region_size(struct vsoc_device_region *r)
+{
+ return r->region_end_offset - r->region_begin_offset;
+}
+
+static const struct file_operations vsoc_ops = {
+ .owner = THIS_MODULE,
+ .open = vsoc_open,
+ .mmap = vsoc_mmap,
+ .read = vsoc_read,
+ .unlocked_ioctl = vsoc_ioctl,
+ .compat_ioctl = vsoc_ioctl,
+ .write = vsoc_write,
+ .llseek = vsoc_lseek,
+ .release = vsoc_release,
+};
+
+static struct pci_device_id vsoc_id_table[] = {
+ {0x1af4, 0x1110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0},
+};
+
+MODULE_DEVICE_TABLE(pci, vsoc_id_table);
+
+static void vsoc_remove_device(struct pci_dev *pdev);
+static int vsoc_probe_device(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+
+static struct pci_driver vsoc_pci_driver = {
+ .name = "vsoc",
+ .id_table = vsoc_id_table,
+ .probe = vsoc_probe_device,
+ .remove = vsoc_remove_device,
+};
+
+static int do_create_fd_scoped_permission(
+ struct vsoc_device_region *region_p,
+ struct fd_scoped_permission_node *np,
+ struct fd_scoped_permission_arg __user *arg)
+{
+ struct file *managed_filp;
+ s32 managed_fd;
+ atomic_t *owner_ptr = NULL;
+ struct vsoc_device_region *managed_region_p;
+
+ if (copy_from_user(&np->permission, &arg->perm, sizeof(*np)) ||
+ copy_from_user(&managed_fd,
+ &arg->managed_region_fd, sizeof(managed_fd))) {
+ return -EFAULT;
+ }
+ managed_filp = fdget(managed_fd).file;
+ /* Check that it's a valid fd, */
+ if (!managed_filp || vsoc_validate_filep(managed_filp))
+ return -EPERM;
+ /* EEXIST if the given fd already has a permission. */
+ if (((struct vsoc_private_data *)managed_filp->private_data)->
+ fd_scoped_permission_node)
+ return -EEXIST;
+ managed_region_p = vsoc_region_from_filep(managed_filp);
+ /* Check that the provided region is managed by this one */
+ if (&vsoc_dev.regions[managed_region_p->managed_by] != region_p)
+ return -EPERM;
+ /* The area must be well formed and have non-zero size */
+ if (np->permission.begin_offset >= np->permission.end_offset)
+ return -EINVAL;
+ /* The area must fit in the memory window */
+ if (np->permission.end_offset >
+ vsoc_device_region_size(managed_region_p))
+ return -ERANGE;
+ /* The area must be in the region data section */
+ if (np->permission.begin_offset <
+ managed_region_p->offset_of_region_data)
+ return -ERANGE;
+ /* The area must be page aligned */
+ if (!PAGE_ALIGNED(np->permission.begin_offset) ||
+ !PAGE_ALIGNED(np->permission.end_offset))
+ return -EINVAL;
+ /* Owner offset must be naturally aligned in the window */
+ if (np->permission.owner_offset &
+ (sizeof(np->permission.owner_offset) - 1))
+ return -EINVAL;
+ /* The owner flag must reside in the owner memory */
+ if (np->permission.owner_offset + sizeof(np->permission.owner_offset) >
+ vsoc_device_region_size(region_p))
+ return -ERANGE;
+ /* The owner flag must reside in the data section */
+ if (np->permission.owner_offset < region_p->offset_of_region_data)
+ return -EINVAL;
+ /* The owner value must change to claim the memory */
+ if (np->permission.owned_value == VSOC_REGION_FREE)
+ return -EINVAL;
+ owner_ptr =
+ (atomic_t *)shm_off_to_virtual_addr(region_p->region_begin_offset +
+ np->permission.owner_offset);
+ /* We've already verified that this is in the shared memory window, so
+ * it should be safe to write to this address.
+ */
+ if (atomic_cmpxchg(owner_ptr,
+ VSOC_REGION_FREE,
+ np->permission.owned_value) != VSOC_REGION_FREE) {
+ return -EBUSY;
+ }
+ ((struct vsoc_private_data *)managed_filp->private_data)->
+ fd_scoped_permission_node = np;
+ /* The file offset needs to be adjusted if the calling
+ * process did any read/write operations on the fd
+ * before creating the permission.
+ */
+ if (managed_filp->f_pos) {
+ if (managed_filp->f_pos > np->permission.end_offset) {
+ /* If the offset is beyond the permission end, set it
+ * to the end.
+ */
+ managed_filp->f_pos = np->permission.end_offset;
+ } else {
+ /* If the offset is within the permission interval
+ * keep it there otherwise reset it to zero.
+ */
+ if (managed_filp->f_pos < np->permission.begin_offset) {
+ managed_filp->f_pos = 0;
+ } else {
+ managed_filp->f_pos -=
+ np->permission.begin_offset;
+ }
+ }
+ }
+ return 0;
+}
+
+static void do_destroy_fd_scoped_permission_node(
+ struct vsoc_device_region *owner_region_p,
+ struct fd_scoped_permission_node *node)
+{
+ if (node) {
+ do_destroy_fd_scoped_permission(owner_region_p,
+ &node->permission);
+ mutex_lock(&vsoc_dev.mtx);
+ list_del(&node->list);
+ mutex_unlock(&vsoc_dev.mtx);
+ kfree(node);
+ }
+}
+
+static void do_destroy_fd_scoped_permission(
+ struct vsoc_device_region *owner_region_p,
+ struct fd_scoped_permission *perm)
+{
+ atomic_t *owner_ptr = NULL;
+ int prev = 0;
+
+ if (!perm)
+ return;
+ owner_ptr = (atomic_t *)shm_off_to_virtual_addr(
+ owner_region_p->region_begin_offset + perm->owner_offset);
+ prev = atomic_xchg(owner_ptr, VSOC_REGION_FREE);
+ if (prev != perm->owned_value)
+ dev_err(&vsoc_dev.dev->dev,
+ "%x-%x: owner (%s) %x: expected to be %x was %x",
+ perm->begin_offset, perm->end_offset,
+ owner_region_p->device_name, perm->owner_offset,
+ perm->owned_value, prev);
+}
+
+static long do_vsoc_describe_region(struct file *filp,
+ struct vsoc_device_region __user *dest)
+{
+ struct vsoc_device_region *region_p;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ region_p = vsoc_region_from_filep(filp);
+ if (copy_to_user(dest, region_p, sizeof(*region_p)))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Implements the inner logic of cond_wait. Copies to and from userspace are
+ * done in the helper function below.
+ */
+static int handle_vsoc_cond_wait(struct file *filp, struct vsoc_cond_wait *arg)
+{
+ DEFINE_WAIT(wait);
+ u32 region_number = iminor(file_inode(filp));
+ struct vsoc_region_data *data = vsoc_dev.regions_data + region_number;
+ struct hrtimer_sleeper timeout, *to = NULL;
+ int ret = 0;
+ struct vsoc_device_region *region_p = vsoc_region_from_filep(filp);
+ atomic_t *address = NULL;
+ struct timespec ts;
+
+ /* Ensure that the offset is aligned */
+ if (arg->offset & (sizeof(uint32_t) - 1))
+ return -EADDRNOTAVAIL;
+ /* Ensure that the offset is within shared memory */
+ if (((uint64_t)arg->offset) + region_p->region_begin_offset +
+ sizeof(uint32_t) > region_p->region_end_offset)
+ return -E2BIG;
+ address = shm_off_to_virtual_addr(region_p->region_begin_offset +
+ arg->offset);
+
+ /* Ensure that the type of wait is valid */
+ switch (arg->wait_type) {
+ case VSOC_WAIT_IF_EQUAL:
+ break;
+ case VSOC_WAIT_IF_EQUAL_TIMEOUT:
+ to = &timeout;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (to) {
+ /* Copy the user-supplied timesec into the kernel structure.
+ * We do things this way to flatten differences between 32 bit
+ * and 64 bit timespecs.
+ */
+ ts.tv_sec = arg->wake_time_sec;
+ ts.tv_nsec = arg->wake_time_nsec;
+
+ if (!timespec_valid(&ts))
+ return -EINVAL;
+ hrtimer_init_on_stack(&to->timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS);
+ hrtimer_set_expires_range_ns(&to->timer, timespec_to_ktime(ts),
+ current->timer_slack_ns);
+
+ hrtimer_init_sleeper(to, current);
+ }
+
+ while (1) {
+ prepare_to_wait(&data->futex_wait_queue, &wait,
+ TASK_INTERRUPTIBLE);
+ /*
+ * Check the sentinel value after prepare_to_wait. If the value
+ * changes after this check the writer will call signal,
+ * changing the task state from INTERRUPTIBLE to RUNNING. That
+ * will ensure that schedule() will eventually schedule this
+ * task.
+ */
+ if (atomic_read(address) != arg->value) {
+ ret = 0;
+ break;
+ }
+ if (to) {
+ hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
+ if (likely(to->task))
+ freezable_schedule();
+ hrtimer_cancel(&to->timer);
+ if (!to->task) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ } else {
+ freezable_schedule();
+ }
+ /* Count the number of times that we woke up. This is useful
+ * for unit testing.
+ */
+ ++arg->wakes;
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ }
+ finish_wait(&data->futex_wait_queue, &wait);
+ if (to)
+ destroy_hrtimer_on_stack(&to->timer);
+ return ret;
+}
+
+/**
+ * Handles the details of copying from/to userspace to ensure that the copies
+ * happen on all of the return paths of cond_wait.
+ */
+static int do_vsoc_cond_wait(struct file *filp,
+ struct vsoc_cond_wait __user *untrusted_in)
+{
+ struct vsoc_cond_wait arg;
+ int rval = 0;
+
+ if (copy_from_user(&arg, untrusted_in, sizeof(arg)))
+ return -EFAULT;
+ /* wakes is an out parameter. Initialize it to something sensible. */
+ arg.wakes = 0;
+ rval = handle_vsoc_cond_wait(filp, &arg);
+ if (copy_to_user(untrusted_in, &arg, sizeof(arg)))
+ return -EFAULT;
+ return rval;
+}
+
+static int do_vsoc_cond_wake(struct file *filp, uint32_t offset)
+{
+ struct vsoc_device_region *region_p = vsoc_region_from_filep(filp);
+ u32 region_number = iminor(file_inode(filp));
+ struct vsoc_region_data *data = vsoc_dev.regions_data + region_number;
+ /* Ensure that the offset is aligned */
+ if (offset & (sizeof(uint32_t) - 1))
+ return -EADDRNOTAVAIL;
+ /* Ensure that the offset is within shared memory */
+ if (((uint64_t)offset) + region_p->region_begin_offset +
+ sizeof(uint32_t) > region_p->region_end_offset)
+ return -E2BIG;
+ /*
+ * TODO(b/73664181): Use multiple futex wait queues.
+ * We need to wake every sleeper when the condition changes. Typically
+ * only a single thread will be waiting on the condition, but there
+ * are exceptions. The worst case is about 10 threads.
+ */
+ wake_up_interruptible_all(&data->futex_wait_queue);
+ return 0;
+}
+
+static long vsoc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int rv = 0;
+ struct vsoc_device_region *region_p;
+ u32 reg_num;
+ struct vsoc_region_data *reg_data;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ region_p = vsoc_region_from_filep(filp);
+ reg_num = iminor(file_inode(filp));
+ reg_data = vsoc_dev.regions_data + reg_num;
+ switch (cmd) {
+ case VSOC_CREATE_FD_SCOPED_PERMISSION:
+ {
+ struct fd_scoped_permission_node *node = NULL;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ /* We can't allocate memory for the permission */
+ if (!node)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&node->list);
+ rv = do_create_fd_scoped_permission(
+ region_p,
+ node,
+ (struct fd_scoped_permission_arg __user *)arg);
+ if (!rv) {
+ mutex_lock(&vsoc_dev.mtx);
+ list_add(&node->list, &vsoc_dev.permissions);
+ mutex_unlock(&vsoc_dev.mtx);
+ } else {
+ kfree(node);
+ return rv;
+ }
+ }
+ break;
+
+ case VSOC_GET_FD_SCOPED_PERMISSION:
+ {
+ struct fd_scoped_permission_node *node =
+ ((struct vsoc_private_data *)filp->private_data)->
+ fd_scoped_permission_node;
+ if (!node)
+ return -ENOENT;
+ if (copy_to_user
+ ((struct fd_scoped_permission __user *)arg,
+ &node->permission, sizeof(node->permission)))
+ return -EFAULT;
+ }
+ break;
+
+ case VSOC_MAYBE_SEND_INTERRUPT_TO_HOST:
+ if (!atomic_xchg(
+ reg_data->outgoing_signalled,
+ 1)) {
+ writel(reg_num, vsoc_dev.regs + DOORBELL);
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+ break;
+
+ case VSOC_SEND_INTERRUPT_TO_HOST:
+ writel(reg_num, vsoc_dev.regs + DOORBELL);
+ return 0;
+
+ case VSOC_WAIT_FOR_INCOMING_INTERRUPT:
+ wait_event_interruptible(
+ reg_data->interrupt_wait_queue,
+ (atomic_read(reg_data->incoming_signalled) != 0));
+ break;
+
+ case VSOC_DESCRIBE_REGION:
+ return do_vsoc_describe_region(
+ filp,
+ (struct vsoc_device_region __user *)arg);
+
+ case VSOC_SELF_INTERRUPT:
+ atomic_set(reg_data->incoming_signalled, 1);
+ wake_up_interruptible(®_data->interrupt_wait_queue);
+ break;
+
+ case VSOC_COND_WAIT:
+ return do_vsoc_cond_wait(filp,
+ (struct vsoc_cond_wait __user *)arg);
+ case VSOC_COND_WAKE:
+ return do_vsoc_cond_wake(filp, arg);
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t vsoc_read(struct file *filp, char __user *buffer, size_t len,
+ loff_t *poffset)
+{
+ __u32 area_off;
+ const void *area_p;
+ ssize_t area_len;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ area_len = vsoc_get_area(filp, &area_off);
+ area_p = shm_off_to_virtual_addr(area_off);
+ area_p += *poffset;
+ area_len -= *poffset;
+ if (area_len <= 0)
+ return 0;
+ if (area_len < len)
+ len = area_len;
+ if (copy_to_user(buffer, area_p, len))
+ return -EFAULT;
+ *poffset += len;
+ return len;
+}
+
+static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin)
+{
+ ssize_t area_len = 0;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ area_len = vsoc_get_area(filp, NULL);
+ switch (origin) {
+ case SEEK_SET:
+ break;
+
+ case SEEK_CUR:
+ if (offset > 0 && offset + filp->f_pos < 0)
+ return -EOVERFLOW;
+ offset += filp->f_pos;
+ break;
+
+ case SEEK_END:
+ if (offset > 0 && offset + area_len < 0)
+ return -EOVERFLOW;
+ offset += area_len;
+ break;
+
+ case SEEK_DATA:
+ if (offset >= area_len)
+ return -EINVAL;
+ if (offset < 0)
+ offset = 0;
+ break;
+
+ case SEEK_HOLE:
+ /* Next hole is always the end of the region, unless offset is
+ * beyond that
+ */
+ if (offset < area_len)
+ offset = area_len;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (offset < 0 || offset > area_len)
+ return -EINVAL;
+ filp->f_pos = offset;
+
+ return offset;
+}
+
+static ssize_t vsoc_write(struct file *filp, const char __user *buffer,
+ size_t len, loff_t *poffset)
+{
+ __u32 area_off;
+ void *area_p;
+ ssize_t area_len;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ area_len = vsoc_get_area(filp, &area_off);
+ area_p = shm_off_to_virtual_addr(area_off);
+ area_p += *poffset;
+ area_len -= *poffset;
+ if (area_len <= 0)
+ return 0;
+ if (area_len < len)
+ len = area_len;
+ if (copy_from_user(area_p, buffer, len))
+ return -EFAULT;
+ *poffset += len;
+ return len;
+}
+
+static irqreturn_t vsoc_interrupt(int irq, void *region_data_v)
+{
+ struct vsoc_region_data *region_data =
+ (struct vsoc_region_data *)region_data_v;
+ int reg_num = region_data - vsoc_dev.regions_data;
+
+ if (unlikely(!region_data))
+ return IRQ_NONE;
+
+ if (unlikely(reg_num < 0 ||
+ reg_num >= vsoc_dev.layout->region_count)) {
+ dev_err(&vsoc_dev.dev->dev,
+ "invalid irq @%p reg_num=0x%04x\n",
+ region_data, reg_num);
+ return IRQ_NONE;
+ }
+ if (unlikely(vsoc_dev.regions_data + reg_num != region_data)) {
+ dev_err(&vsoc_dev.dev->dev,
+ "irq not aligned @%p reg_num=0x%04x\n",
+ region_data, reg_num);
+ return IRQ_NONE;
+ }
+ wake_up_interruptible(®ion_data->interrupt_wait_queue);
+ return IRQ_HANDLED;
+}
+
+static int vsoc_probe_device(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int result;
+ int i;
+ resource_size_t reg_size;
+ dev_t devt;
+
+ vsoc_dev.dev = pdev;
+ result = pci_enable_device(pdev);
+ if (result) {
+ dev_err(&pdev->dev,
+ "pci_enable_device failed %s: error %d\n",
+ pci_name(pdev), result);
+ return result;
+ }
+ vsoc_dev.enabled_device = true;
+ result = pci_request_regions(pdev, "vsoc");
+ if (result < 0) {
+ dev_err(&pdev->dev, "pci_request_regions failed\n");
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+ vsoc_dev.requested_regions = true;
+ /* Set up the control registers in BAR 0 */
+ reg_size = pci_resource_len(pdev, REGISTER_BAR);
+ if (reg_size > MAX_REGISTER_BAR_LEN)
+ vsoc_dev.regs =
+ pci_iomap(pdev, REGISTER_BAR, MAX_REGISTER_BAR_LEN);
+ else
+ vsoc_dev.regs = pci_iomap(pdev, REGISTER_BAR, reg_size);
+
+ if (!vsoc_dev.regs) {
+ dev_err(&pdev->dev,
+ "cannot map registers of size %zu\n",
+ (size_t)reg_size);
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+
+ /* Map the shared memory in BAR 2 */
+ vsoc_dev.shm_phys_start = pci_resource_start(pdev, SHARED_MEMORY_BAR);
+ vsoc_dev.shm_size = pci_resource_len(pdev, SHARED_MEMORY_BAR);
+
+ dev_info(&pdev->dev, "shared memory @ DMA %pa size=0x%zx\n",
+ &vsoc_dev.shm_phys_start, vsoc_dev.shm_size);
+ vsoc_dev.kernel_mapped_shm = pci_iomap_wc(pdev, SHARED_MEMORY_BAR, 0);
+ if (!vsoc_dev.kernel_mapped_shm) {
+ dev_err(&vsoc_dev.dev->dev, "cannot iomap region\n");
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+
+ vsoc_dev.layout = (struct vsoc_shm_layout_descriptor __force *)
+ vsoc_dev.kernel_mapped_shm;
+ dev_info(&pdev->dev, "major_version: %d\n",
+ vsoc_dev.layout->major_version);
+ dev_info(&pdev->dev, "minor_version: %d\n",
+ vsoc_dev.layout->minor_version);
+ dev_info(&pdev->dev, "size: 0x%x\n", vsoc_dev.layout->size);
+ dev_info(&pdev->dev, "regions: %d\n", vsoc_dev.layout->region_count);
+ if (vsoc_dev.layout->major_version !=
+ CURRENT_VSOC_LAYOUT_MAJOR_VERSION) {
+ dev_err(&vsoc_dev.dev->dev,
+ "driver supports only major_version %d\n",
+ CURRENT_VSOC_LAYOUT_MAJOR_VERSION);
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+ result = alloc_chrdev_region(&devt, 0, vsoc_dev.layout->region_count,
+ VSOC_DEV_NAME);
+ if (result) {
+ dev_err(&vsoc_dev.dev->dev, "alloc_chrdev_region failed\n");
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+ vsoc_dev.major = MAJOR(devt);
+ cdev_init(&vsoc_dev.cdev, &vsoc_ops);
+ vsoc_dev.cdev.owner = THIS_MODULE;
+ result = cdev_add(&vsoc_dev.cdev, devt, vsoc_dev.layout->region_count);
+ if (result) {
+ dev_err(&vsoc_dev.dev->dev, "cdev_add error\n");
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+ vsoc_dev.cdev_added = true;
+ vsoc_dev.class = class_create(THIS_MODULE, VSOC_DEV_NAME);
+ if (IS_ERR(vsoc_dev.class)) {
+ dev_err(&vsoc_dev.dev->dev, "class_create failed\n");
+ vsoc_remove_device(pdev);
+ return PTR_ERR(vsoc_dev.class);
+ }
+ vsoc_dev.class_added = true;
+ vsoc_dev.regions = (struct vsoc_device_region __force *)
+ ((void *)vsoc_dev.layout +
+ vsoc_dev.layout->vsoc_region_desc_offset);
+ vsoc_dev.msix_entries = kcalloc(
+ vsoc_dev.layout->region_count,
+ sizeof(vsoc_dev.msix_entries[0]), GFP_KERNEL);
+ if (!vsoc_dev.msix_entries) {
+ dev_err(&vsoc_dev.dev->dev,
+ "unable to allocate msix_entries\n");
+ vsoc_remove_device(pdev);
+ return -ENOSPC;
+ }
+ vsoc_dev.regions_data = kcalloc(
+ vsoc_dev.layout->region_count,
+ sizeof(vsoc_dev.regions_data[0]), GFP_KERNEL);
+ if (!vsoc_dev.regions_data) {
+ dev_err(&vsoc_dev.dev->dev,
+ "unable to allocate regions' data\n");
+ vsoc_remove_device(pdev);
+ return -ENOSPC;
+ }
+ for (i = 0; i < vsoc_dev.layout->region_count; ++i)
+ vsoc_dev.msix_entries[i].entry = i;
+
+ result = pci_enable_msix_exact(vsoc_dev.dev, vsoc_dev.msix_entries,
+ vsoc_dev.layout->region_count);
+ if (result) {
+ dev_info(&pdev->dev, "pci_enable_msix failed: %d\n", result);
+ vsoc_remove_device(pdev);
+ return -ENOSPC;
+ }
+ /* Check that all regions are well formed */
+ for (i = 0; i < vsoc_dev.layout->region_count; ++i) {
+ const struct vsoc_device_region *region = vsoc_dev.regions + i;
+
+ if (!PAGE_ALIGNED(region->region_begin_offset) ||
+ !PAGE_ALIGNED(region->region_end_offset)) {
+ dev_err(&vsoc_dev.dev->dev,
+ "region %d not aligned (%x:%x)", i,
+ region->region_begin_offset,
+ region->region_end_offset);
+ vsoc_remove_device(pdev);
+ return -EFAULT;
+ }
+ if (region->region_begin_offset >= region->region_end_offset ||
+ region->region_end_offset > vsoc_dev.shm_size) {
+ dev_err(&vsoc_dev.dev->dev,
+ "region %d offsets are wrong: %x %x %zx",
+ i, region->region_begin_offset,
+ region->region_end_offset, vsoc_dev.shm_size);
+ vsoc_remove_device(pdev);
+ return -EFAULT;
+ }
+ if (region->managed_by >= vsoc_dev.layout->region_count) {
+ dev_err(&vsoc_dev.dev->dev,
+ "region %d has invalid owner: %u",
+ i, region->managed_by);
+ vsoc_remove_device(pdev);
+ return -EFAULT;
+ }
+ }
+ vsoc_dev.msix_enabled = true;
+ for (i = 0; i < vsoc_dev.layout->region_count; ++i) {
+ const struct vsoc_device_region *region = vsoc_dev.regions + i;
+ size_t name_sz = sizeof(vsoc_dev.regions_data[i].name) - 1;
+ const struct vsoc_signal_table_layout *h_to_g_signal_table =
+ ®ion->host_to_guest_signal_table;
+ const struct vsoc_signal_table_layout *g_to_h_signal_table =
+ ®ion->guest_to_host_signal_table;
+
+ vsoc_dev.regions_data[i].name[name_sz] = '\0';
+ memcpy(vsoc_dev.regions_data[i].name, region->device_name,
+ name_sz);
+ dev_info(&pdev->dev, "region %d name=%s\n",
+ i, vsoc_dev.regions_data[i].name);
+ init_waitqueue_head(
+ &vsoc_dev.regions_data[i].interrupt_wait_queue);
+ init_waitqueue_head(&vsoc_dev.regions_data[i].futex_wait_queue);
+ vsoc_dev.regions_data[i].incoming_signalled =
+ shm_off_to_virtual_addr(region->region_begin_offset) +
+ h_to_g_signal_table->interrupt_signalled_offset;
+ vsoc_dev.regions_data[i].outgoing_signalled =
+ shm_off_to_virtual_addr(region->region_begin_offset) +
+ g_to_h_signal_table->interrupt_signalled_offset;
+ result = request_irq(
+ vsoc_dev.msix_entries[i].vector,
+ vsoc_interrupt, 0,
+ vsoc_dev.regions_data[i].name,
+ vsoc_dev.regions_data + i);
+ if (result) {
+ dev_info(&pdev->dev,
+ "request_irq failed irq=%d vector=%d\n",
+ i, vsoc_dev.msix_entries[i].vector);
+ vsoc_remove_device(pdev);
+ return -ENOSPC;
+ }
+ vsoc_dev.regions_data[i].irq_requested = true;
+ if (!device_create(vsoc_dev.class, NULL,
+ MKDEV(vsoc_dev.major, i),
+ NULL, vsoc_dev.regions_data[i].name)) {
+ dev_err(&vsoc_dev.dev->dev, "device_create failed\n");
+ vsoc_remove_device(pdev);
+ return -EBUSY;
+ }
+ vsoc_dev.regions_data[i].device_created = true;
+ }
+ return 0;
+}
+
+/*
+ * This should undo all of the allocations in the probe function in reverse
+ * order.
+ *
+ * Notes:
+ *
+ * The device may have been partially initialized, so double check
+ * that the allocations happened.
+ *
+ * This function may be called multiple times, so mark resources as freed
+ * as they are deallocated.
+ */
+static void vsoc_remove_device(struct pci_dev *pdev)
+{
+ int i;
+ /*
+ * pdev is the first thing to be set on probe and the last thing
+ * to be cleared here. If it's NULL then there is no cleanup.
+ */
+ if (!pdev || !vsoc_dev.dev)
+ return;
+ dev_info(&pdev->dev, "remove_device\n");
+ if (vsoc_dev.regions_data) {
+ for (i = 0; i < vsoc_dev.layout->region_count; ++i) {
+ if (vsoc_dev.regions_data[i].device_created) {
+ device_destroy(vsoc_dev.class,
+ MKDEV(vsoc_dev.major, i));
+ vsoc_dev.regions_data[i].device_created = false;
+ }
+ if (vsoc_dev.regions_data[i].irq_requested)
+ free_irq(vsoc_dev.msix_entries[i].vector, NULL);
+ vsoc_dev.regions_data[i].irq_requested = false;
+ }
+ kfree(vsoc_dev.regions_data);
+ vsoc_dev.regions_data = NULL;
+ }
+ if (vsoc_dev.msix_enabled) {
+ pci_disable_msix(pdev);
+ vsoc_dev.msix_enabled = false;
+ }
+ kfree(vsoc_dev.msix_entries);
+ vsoc_dev.msix_entries = NULL;
+ vsoc_dev.regions = NULL;
+ if (vsoc_dev.class_added) {
+ class_destroy(vsoc_dev.class);
+ vsoc_dev.class_added = false;
+ }
+ if (vsoc_dev.cdev_added) {
+ cdev_del(&vsoc_dev.cdev);
+ vsoc_dev.cdev_added = false;
+ }
+ if (vsoc_dev.major && vsoc_dev.layout) {
+ unregister_chrdev_region(MKDEV(vsoc_dev.major, 0),
+ vsoc_dev.layout->region_count);
+ vsoc_dev.major = 0;
+ }
+ vsoc_dev.layout = NULL;
+ if (vsoc_dev.kernel_mapped_shm) {
+ pci_iounmap(pdev, vsoc_dev.kernel_mapped_shm);
+ vsoc_dev.kernel_mapped_shm = NULL;
+ }
+ if (vsoc_dev.regs) {
+ pci_iounmap(pdev, vsoc_dev.regs);
+ vsoc_dev.regs = NULL;
+ }
+ if (vsoc_dev.requested_regions) {
+ pci_release_regions(pdev);
+ vsoc_dev.requested_regions = false;
+ }
+ if (vsoc_dev.enabled_device) {
+ pci_disable_device(pdev);
+ vsoc_dev.enabled_device = false;
+ }
+ /* Do this last: it indicates that the device is not initialized. */
+ vsoc_dev.dev = NULL;
+}
+
+static void __exit vsoc_cleanup_module(void)
+{
+ vsoc_remove_device(vsoc_dev.dev);
+ pci_unregister_driver(&vsoc_pci_driver);
+}
+
+static int __init vsoc_init_module(void)
+{
+ int err = -ENOMEM;
+
+ INIT_LIST_HEAD(&vsoc_dev.permissions);
+ mutex_init(&vsoc_dev.mtx);
+
+ err = pci_register_driver(&vsoc_pci_driver);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int vsoc_open(struct inode *inode, struct file *filp)
+{
+ /* Can't use vsoc_validate_filep because filp is still incomplete */
+ int ret = vsoc_validate_inode(inode);
+
+ if (ret)
+ return ret;
+ filp->private_data =
+ kzalloc(sizeof(struct vsoc_private_data), GFP_KERNEL);
+ if (!filp->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+static int vsoc_release(struct inode *inode, struct file *filp)
+{
+ struct vsoc_private_data *private_data = NULL;
+ struct fd_scoped_permission_node *node = NULL;
+ struct vsoc_device_region *owner_region_p = NULL;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ private_data = (struct vsoc_private_data *)filp->private_data;
+ if (!private_data)
+ return 0;
+
+ node = private_data->fd_scoped_permission_node;
+ if (node) {
+ owner_region_p = vsoc_region_from_inode(inode);
+ if (owner_region_p->managed_by != VSOC_REGION_WHOLE) {
+ owner_region_p =
+ &vsoc_dev.regions[owner_region_p->managed_by];
+ }
+ do_destroy_fd_scoped_permission_node(owner_region_p, node);
+ private_data->fd_scoped_permission_node = NULL;
+ }
+ kfree(private_data);
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+/*
+ * Returns the device relative offset and length of the area specified by the
+ * fd scoped permission. If there is no fd scoped permission set, a default
+ * permission covering the entire region is assumed, unless the region is owned
+ * by another one, in which case the default is a permission with zero size.
+ */
+static ssize_t vsoc_get_area(struct file *filp, __u32 *area_offset)
+{
+ __u32 off = 0;
+ ssize_t length = 0;
+ struct vsoc_device_region *region_p;
+ struct fd_scoped_permission *perm;
+
+ region_p = vsoc_region_from_filep(filp);
+ off = region_p->region_begin_offset;
+ perm = &((struct vsoc_private_data *)filp->private_data)->
+ fd_scoped_permission_node->permission;
+ if (perm) {
+ off += perm->begin_offset;
+ length = perm->end_offset - perm->begin_offset;
+ } else if (region_p->managed_by == VSOC_REGION_WHOLE) {
+ /* No permission set and the regions is not owned by another,
+ * default to full region access.
+ */
+ length = vsoc_device_region_size(region_p);
+ } else {
+ /* return zero length, access is denied. */
+ length = 0;
+ }
+ if (area_offset)
+ *area_offset = off;
+ return length;
+}
+
+static int vsoc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long len = vma->vm_end - vma->vm_start;
+ __u32 area_off;
+ phys_addr_t mem_off;
+ ssize_t area_len;
+ int retval = vsoc_validate_filep(filp);
+
+ if (retval)
+ return retval;
+ area_len = vsoc_get_area(filp, &area_off);
+ /* Add the requested offset */
+ area_off += (vma->vm_pgoff << PAGE_SHIFT);
+ area_len -= (vma->vm_pgoff << PAGE_SHIFT);
+ if (area_len < len)
+ return -EINVAL;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ mem_off = shm_off_to_phys_addr(area_off);
+ if (io_remap_pfn_range(vma, vma->vm_start, mem_off >> PAGE_SHIFT,
+ len, vma->vm_page_prot))
+ return -EAGAIN;
+ return 0;
+}
+
+module_init(vsoc_init_module);
+module_exit(vsoc_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Greg Hartman <ghartman@google.com>");
+MODULE_DESCRIPTION("VSoC interpretation of QEmu's ivshmem device");
+MODULE_VERSION("1.0");
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index a5bf2cc..1736248 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -484,8 +484,7 @@
struct comedi_cmd *cmd = &async->cmd;
if (cmd->stop_src == TRIG_COUNT) {
- unsigned int nscans = nsamples / cmd->scan_end_arg;
- unsigned int scans_left = __comedi_nscans_left(s, nscans);
+ unsigned int scans_left = __comedi_nscans_left(s, cmd->stop_arg);
unsigned int scan_pos =
comedi_bytes_to_samples(s, async->scan_progress);
unsigned long long samples_left = 0;
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index a574885..18c5312 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -1284,6 +1284,8 @@
ack |= NISTC_INTA_ACK_AI_START;
if (a_status & NISTC_AI_STATUS1_STOP)
ack |= NISTC_INTA_ACK_AI_STOP;
+ if (a_status & NISTC_AI_STATUS1_OVER)
+ ack |= NISTC_INTA_ACK_AI_ERR;
if (ack)
ni_stc_writew(dev, ack, NISTC_INTA_ACK_REG);
}
diff --git a/drivers/staging/lustre/lustre/include/obd.h b/drivers/staging/lustre/lustre/include/obd.h
index f6fc4dd..722c33f 100644
--- a/drivers/staging/lustre/lustre/include/obd.h
+++ b/drivers/staging/lustre/lustre/include/obd.h
@@ -253,7 +253,7 @@
struct sptlrpc_flavor cl_flvr_mgc; /* fixed flavor of mgc->mgs */
/* the grant values are protected by loi_list_lock below */
- unsigned long cl_dirty_pages; /* all _dirty_ in pahges */
+ unsigned long cl_dirty_pages; /* all _dirty_ in pages */
unsigned long cl_dirty_max_pages; /* allowed w/o rpc */
unsigned long cl_dirty_transit; /* dirty synchronous */
unsigned long cl_avail_grant; /* bytes of credit for ost */
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_obd.c b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
index cd19ce8..9e63171 100644
--- a/drivers/staging/lustre/lustre/lmv/lmv_obd.c
+++ b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
@@ -2928,7 +2928,7 @@
if (lsm && !lmm) {
int i;
- for (i = 1; i < lsm->lsm_md_stripe_count; i++) {
+ for (i = 0; i < lsm->lsm_md_stripe_count; i++) {
/*
* For migrating inode, the master stripe and master
* object will be the same, so do not need iput, see
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c
index f56ea64..6a146f4 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_request.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c
@@ -1219,9 +1219,9 @@
* in PAGE_SIZE (if PAGE_SIZE greater than LU_PAGE_SIZE), and the
* lu_dirpage for this integrated page will be adjusted.
**/
-static int mdc_read_page_remote(void *data, struct page *page0)
+static int mdc_read_page_remote(struct file *data, struct page *page0)
{
- struct readpage_param *rp = data;
+ struct readpage_param *rp = (struct readpage_param *)data;
struct page **page_pool;
struct page *page;
struct lu_dirpage *dp;
diff --git a/drivers/staging/lustre/lustre/osc/osc_cache.c b/drivers/staging/lustre/lustre/osc/osc_cache.c
index 4bbe219..1a8c9f5 100644
--- a/drivers/staging/lustre/lustre/osc/osc_cache.c
+++ b/drivers/staging/lustre/lustre/osc/osc_cache.c
@@ -1542,7 +1542,7 @@
if (rc < 0)
return 0;
- if (cli->cl_dirty_pages <= cli->cl_dirty_max_pages &&
+ if (cli->cl_dirty_pages < cli->cl_dirty_max_pages &&
atomic_long_read(&obd_dirty_pages) + 1 <= obd_max_dirty_pages) {
osc_consume_write_grant(cli, &oap->oap_brw_page);
if (transient) {
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec.c b/drivers/staging/lustre/lustre/ptlrpc/sec.c
index a7416cd..7b0587d 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec.c
@@ -838,7 +838,7 @@
if (req->rq_pool || !req->rq_reqbuf)
return;
- kfree(req->rq_reqbuf);
+ kvfree(req->rq_reqbuf);
req->rq_reqbuf = NULL;
req->rq_reqbuf_len = 0;
}
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index 457eeb5..5fe9593 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -1705,6 +1705,8 @@
priv->rx_urb[16] = usb_alloc_urb(0, GFP_KERNEL);
priv->oldaddr = kmalloc(16, GFP_KERNEL);
+ if (!priv->oldaddr)
+ return -ENOMEM;
oldaddr = priv->oldaddr;
align = ((long)oldaddr) & 3;
if (align) {
diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c
index e744aa9..dea018c 100644
--- a/drivers/staging/speakup/kobjects.c
+++ b/drivers/staging/speakup/kobjects.c
@@ -834,7 +834,9 @@
struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
unsigned long flags;
- BUG_ON(!group);
+ if (WARN_ON(!group))
+ return -EINVAL;
+
spin_lock_irqsave(&speakup_info.spinlock, flags);
retval = message_show_helper(buf, group->start, group->end);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
@@ -846,7 +848,9 @@
{
struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
- BUG_ON(!group);
+ if (WARN_ON(!group))
+ return -EINVAL;
+
return message_store_helper(buf, count, group);
}
diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c
index 5a7a87e..28b5392 100644
--- a/drivers/staging/unisys/visorhba/visorhba_main.c
+++ b/drivers/staging/unisys/visorhba/visorhba_main.c
@@ -842,7 +842,7 @@
do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
{
struct scsi_device *scsidev;
- unsigned char buf[36];
+ unsigned char *buf;
struct scatterlist *sg;
unsigned int i;
char *this_page;
@@ -857,6 +857,10 @@
if (cmdrsp->scsi.no_disk_result == 0)
return;
+ buf = kzalloc(sizeof(char) * 36, GFP_KERNEL);
+ if (!buf)
+ return;
+
/* Linux scsi code wants a device at Lun 0
* to issue report luns, but we don't want
* a disk there so we'll present a processor
@@ -868,6 +872,7 @@
if (scsi_sg_count(scsicmd) == 0) {
memcpy(scsi_sglist(scsicmd), buf,
cmdrsp->scsi.bufflen);
+ kfree(buf);
return;
}
@@ -879,6 +884,7 @@
memcpy(this_page, buf + bufind, sg[i].length);
kunmap_atomic(this_page_orig);
}
+ kfree(buf);
} else {
devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
for_each_vdisk_match(vdisk, devdata, scsidev) {
diff --git a/drivers/staging/wilc1000/host_interface.c b/drivers/staging/wilc1000/host_interface.c
index 6ab7443..6326375 100644
--- a/drivers/staging/wilc1000/host_interface.c
+++ b/drivers/staging/wilc1000/host_interface.c
@@ -1930,6 +1930,8 @@
wid.type = WID_STR;
wid.size = ETH_ALEN;
wid.val = kmalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ return -ENOMEM;
stamac = wid.val;
memcpy(stamac, strHostIfStaInactiveT->mac, ETH_ALEN);
diff --git a/drivers/staging/wilc1000/linux_mon.c b/drivers/staging/wilc1000/linux_mon.c
index 242f82f..d8ab4cb 100644
--- a/drivers/staging/wilc1000/linux_mon.c
+++ b/drivers/staging/wilc1000/linux_mon.c
@@ -197,6 +197,8 @@
if (skb->data[0] == 0xc0 && (!(memcmp(broadcast, &skb->data[4], 6)))) {
skb2 = dev_alloc_skb(skb->len + sizeof(struct wilc_wfi_radiotap_cb_hdr));
+ if (!skb2)
+ return -ENOMEM;
memcpy(skb_put(skb2, skb->len), skb->data, skb->len);
diff --git a/drivers/staging/wlan-ng/prism2mgmt.c b/drivers/staging/wlan-ng/prism2mgmt.c
index 170de1c..f815f9d 100644
--- a/drivers/staging/wlan-ng/prism2mgmt.c
+++ b/drivers/staging/wlan-ng/prism2mgmt.c
@@ -169,7 +169,7 @@
hw->ident_sta_fw.variant) >
HFA384x_FIRMWARE_VERSION(1, 5, 0)) {
if (msg->scantype.data != P80211ENUM_scantype_active)
- word = cpu_to_le16(msg->maxchanneltime.data);
+ word = msg->maxchanneltime.data;
else
word = 0;
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 97928b4..57a3d393 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -276,12 +276,11 @@
else
ret = vfs_iter_read(fd, &iter, &pos);
- kfree(bvec);
-
if (is_write) {
if (ret < 0 || ret != data_length) {
pr_err("%s() write returned %d\n", __func__, ret);
- return (ret < 0 ? ret : -EINVAL);
+ if (ret >= 0)
+ ret = -EINVAL;
}
} else {
/*
@@ -294,17 +293,29 @@
pr_err("%s() returned %d, expecting %u for "
"S_ISBLK\n", __func__, ret,
data_length);
- return (ret < 0 ? ret : -EINVAL);
+ if (ret >= 0)
+ ret = -EINVAL;
}
} else {
if (ret < 0) {
pr_err("%s() returned %d for non S_ISBLK\n",
__func__, ret);
- return ret;
+ } else if (ret != data_length) {
+ /*
+ * Short read case:
+ * Probably some one truncate file under us.
+ * We must explicitly zero sg-pages to prevent
+ * expose uninizialized pages to userspace.
+ */
+ if (ret < data_length)
+ ret += iov_iter_zero(data_length - ret, &iter);
+ else
+ ret = -EINVAL;
}
}
}
- return 1;
+ kfree(bvec);
+ return ret;
}
static sense_reason_t
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 1259654..ae24a68 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -58,4 +58,4 @@
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_THERMAL_QPNP_ADC_TM) += qpnp-adc-tm.o
-obj-$(CONFIG_THERMAL_TSENS) += msm-tsens.o tsens2xxx.o tsens-dbg.o tsens-mtc.o tsens1xxx.o
+obj-$(CONFIG_THERMAL_TSENS) += msm-tsens.o tsens2xxx.o tsens-dbg.o tsens-mtc.o tsens1xxx.o tsens_calib.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 2c4a63a..02f93f4 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -224,7 +224,7 @@
static int cpufreq_cooling_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
- struct cpufreq_cooling_device *cpufreq_dev;
+ struct cpufreq_cooling_device *cpufreq_dev, *next;
unsigned int cpu;
switch (mode) {
@@ -236,8 +236,8 @@
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
- mutex_lock(&cooling_list_lock);
- list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
+ list_for_each_entry_safe(cpufreq_dev, next, &cpufreq_dev_list,
+ node) {
mutex_lock(&core_isolate_lock);
if (cpufreq_dev->cpufreq_state ==
cpufreq_dev->max_level) {
@@ -259,7 +259,6 @@
}
mutex_unlock(&core_isolate_lock);
}
- mutex_unlock(&cooling_list_lock);
atomic_set(&in_suspend, 0);
break;
diff --git a/drivers/thermal/gov_low_limits.c b/drivers/thermal/gov_low_limits.c
index 278869c..d02ea26 100644
--- a/drivers/thermal/gov_low_limits.c
+++ b/drivers/thermal/gov_low_limits.c
@@ -62,19 +62,30 @@
dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
old_target, (int)instance->target);
- if (old_target == instance->target)
+ if (instance->initialized && old_target == instance->target)
continue;
- if (old_target == THERMAL_NO_TARGET &&
+ if (!instance->initialized) {
+ if (instance->target != THERMAL_NO_TARGET) {
+ trace_thermal_zone_trip(tz, trip, trip_type,
+ true);
+ tz->passive += 1;
+ }
+ } else {
+ if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) {
- trace_thermal_zone_trip(tz, trip, trip_type, true);
- tz->passive += 1;
- } else if (old_target != THERMAL_NO_TARGET &&
+ trace_thermal_zone_trip(tz, trip, trip_type,
+ true);
+ tz->passive += 1;
+ } else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) {
- trace_thermal_zone_trip(tz, trip, trip_type, false);
- tz->passive -= 1;
+ trace_thermal_zone_trip(tz, trip, trip_type,
+ false);
+ tz->passive -= 1;
+ }
}
+ instance->initialized = true;
instance->cdev->updated = false; /* cdev needs update */
}
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index 06912f0..b7cb49a 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -587,6 +587,9 @@
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+ data->irq_enabled = true;
+ data->mode = THERMAL_DEVICE_ENABLED;
+
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
0, "imx_thermal", data);
@@ -598,9 +601,6 @@
return ret;
}
- data->irq_enabled = true;
- data->mode = THERMAL_DEVICE_ENABLED;
-
return 0;
}
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index c137d3d..6241ef6 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -90,6 +90,9 @@
{ .compatible = "qcom,msm8937-tsens",
.data = &data_tsens14xx,
},
+ { .compatible = "qcom,msm8909-tsens",
+ .data = &data_tsens1xxx_8909,
+ },
{}
};
MODULE_DEVICE_TABLE(of, tsens_table);
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
index b4d3116..3055f9a 100644
--- a/drivers/thermal/power_allocator.c
+++ b/drivers/thermal/power_allocator.c
@@ -523,6 +523,7 @@
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
+ mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if ((instance->trip != params->trip_max_desired_temperature) ||
(!cdev_is_power_actor(instance->cdev)))
@@ -534,6 +535,7 @@
mutex_unlock(&instance->cdev->lock);
thermal_cdev_update(instance->cdev);
}
+ mutex_unlock(&tz->lock);
}
/**
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 5c0022e..5d345cc 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -191,6 +191,8 @@
#define QPNP_BTM_MEAS_INTERVAL_CTL 0x50
#define QPNP_BTM_MEAS_INTERVAL_CTL2 0x51
+#define QPNP_BTM_MEAS_INTERVAL_CTL_PM5 0x44
+#define QPNP_BTM_MEAS_INTERVAL_CTL2_PM5 0x45
#define QPNP_ADC_TM_MEAS_INTERVAL_TIME_SHIFT 0x3
#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT 0x4
#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_MASK 0xf0
@@ -742,6 +744,7 @@
bool chan_found = false;
u8 meas_interval_timer2 = 0, timer_interval_store = 0;
uint32_t btm_chan_idx = 0;
+ bool is_pmic_5 = chip->adc->adc_prop->is_pmic_5;
while (i < chip->max_channels_available) {
if (chip->sensor[i].btm_channel_num == btm_chan) {
@@ -763,10 +766,18 @@
rc = qpnp_adc_tm_write_reg(chip,
QPNP_ADC_TM_MEAS_INTERVAL_CTL,
chip->sensor[chan_idx].meas_interval, 1);
- else
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_MEAS_INTERVAL_CTL,
- chip->sensor[chan_idx].meas_interval, 1);
+ else {
+ if (!is_pmic_5)
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL,
+ chip->sensor[chan_idx].meas_interval,
+ 1);
+ else
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL_PM5,
+ chip->sensor[chan_idx].meas_interval,
+ 1);
+ }
if (rc < 0) {
pr_err("timer1 configure failed\n");
return rc;
@@ -778,10 +789,16 @@
rc = qpnp_adc_tm_read_reg(chip,
QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
&meas_interval_timer2, 1);
- else
- rc = qpnp_adc_tm_read_reg(chip,
+ else {
+ if (!is_pmic_5)
+ rc = qpnp_adc_tm_read_reg(chip,
QPNP_BTM_MEAS_INTERVAL_CTL2,
&meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2_PM5,
+ &meas_interval_timer2, 1);
+ }
if (rc < 0) {
pr_err("timer2 configure read failed\n");
return rc;
@@ -794,10 +811,16 @@
rc = qpnp_adc_tm_write_reg(chip,
QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
meas_interval_timer2, 1);
- else
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_MEAS_INTERVAL_CTL2,
- meas_interval_timer2, 1);
+ else {
+ if (!is_pmic_5)
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2,
+ meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2_PM5,
+ meas_interval_timer2, 1);
+ }
if (rc < 0) {
pr_err("timer2 configure failed\n");
return rc;
@@ -808,10 +831,16 @@
rc = qpnp_adc_tm_read_reg(chip,
QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
&meas_interval_timer2, 1);
- else
- rc = qpnp_adc_tm_read_reg(chip,
- QPNP_BTM_MEAS_INTERVAL_CTL2,
- &meas_interval_timer2, 1);
+ else {
+ if (!is_pmic_5)
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2,
+ &meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_read_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2_PM5,
+ &meas_interval_timer2, 1);
+ }
if (rc < 0) {
pr_err("timer3 read failed\n");
return rc;
@@ -823,10 +852,16 @@
rc = qpnp_adc_tm_write_reg(chip,
QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
meas_interval_timer2, 1);
- else
- rc = qpnp_adc_tm_write_reg(chip,
- QPNP_BTM_MEAS_INTERVAL_CTL2,
- meas_interval_timer2, 1);
+ else {
+ if (!is_pmic_5)
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2,
+ meas_interval_timer2, 1);
+ else
+ rc = qpnp_adc_tm_write_reg(chip,
+ QPNP_BTM_MEAS_INTERVAL_CTL2_PM5,
+ meas_interval_timer2, 1);
+ }
if (rc < 0) {
pr_err("timer3 configure failed\n");
return rc;
@@ -2997,6 +3032,7 @@
static const struct of_device_id qpnp_adc_tm_match_table[] = {
{ .compatible = "qcom,qpnp-adc-tm" },
{ .compatible = "qcom,qpnp-adc-tm-hc" },
+ { .compatible = "qcom,qpnp-adc-tm-hc-pm5" },
{}
};
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index ad1186d..a45810b 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -185,6 +185,7 @@
* @regulator: pointer to the TMU regulator structure.
* @reg_conf: pointer to structure to register with core thermal.
* @ntrip: number of supported trip points.
+ * @enabled: current status of TMU device
* @tmu_initialize: SoC specific TMU initialization method
* @tmu_control: SoC specific TMU control method
* @tmu_read: SoC specific TMU temperature read method
@@ -205,6 +206,7 @@
struct regulator *regulator;
struct thermal_zone_device *tzd;
unsigned int ntrip;
+ bool enabled;
int (*tmu_initialize)(struct platform_device *pdev);
void (*tmu_control)(struct platform_device *pdev, bool on);
@@ -398,6 +400,7 @@
mutex_lock(&data->lock);
clk_enable(data->clk);
data->tmu_control(pdev, on);
+ data->enabled = on;
clk_disable(data->clk);
mutex_unlock(&data->lock);
}
@@ -889,19 +892,24 @@
static int exynos_get_temp(void *p, int *temp)
{
struct exynos_tmu_data *data = p;
+ int value, ret = 0;
- if (!data || !data->tmu_read)
+ if (!data || !data->tmu_read || !data->enabled)
return -EINVAL;
mutex_lock(&data->lock);
clk_enable(data->clk);
- *temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS;
+ value = data->tmu_read(data);
+ if (value < 0)
+ ret = value;
+ else
+ *temp = code_to_temp(data, value) * MCELSIUS;
clk_disable(data->clk);
mutex_unlock(&data->lock);
- return 0;
+ return ret;
}
#ifdef CONFIG_THERMAL_EMULATION
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index 4bbb47a..4d75f6b 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -190,16 +190,26 @@
if (instance->initialized && old_target == instance->target)
continue;
- /* Activate a passive thermal instance */
- if (old_target == THERMAL_NO_TARGET &&
- instance->target != THERMAL_NO_TARGET) {
- update_passive_instance(tz, trip_type, 1);
- trace_thermal_zone_trip(tz, trip, trip_type, true);
- /* Deactivate a passive thermal instance */
- } else if (old_target != THERMAL_NO_TARGET &&
- instance->target == THERMAL_NO_TARGET) {
- update_passive_instance(tz, trip_type, -1);
- trace_thermal_zone_trip(tz, trip, trip_type, false);
+ if (!instance->initialized) {
+ if (instance->target != THERMAL_NO_TARGET) {
+ trace_thermal_zone_trip(tz, trip, trip_type,
+ true);
+ update_passive_instance(tz, trip_type, 1);
+ }
+ } else {
+ /* Activate a passive thermal instance */
+ if (old_target == THERMAL_NO_TARGET &&
+ instance->target != THERMAL_NO_TARGET) {
+ trace_thermal_zone_trip(tz, trip, trip_type,
+ true);
+ update_passive_instance(tz, trip_type, 1);
+ /* Deactivate a passive thermal instance */
+ } else if (old_target != THERMAL_NO_TARGET &&
+ instance->target == THERMAL_NO_TARGET) {
+ trace_thermal_zone_trip(tz, trip, trip_type,
+ false);
+ update_passive_instance(tz, trip_type, -1);
+ }
}
instance->initialized = true;
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index 730d124..c8e6233 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -23,7 +23,8 @@
#define DEBUG_SIZE 10
#define TSENS_MAX_SENSORS 16
-#define TSENS_1x_MAX_SENSORS 11
+#define TSENS_NUM_SENSORS_8937 11
+#define TSENS_NUM_SENSORS_8909 5
#define TSENS_CONTROLLER_ID(n) (n)
#define TSENS_CTRL_ADDR(n) (n)
#define TSENS_TM_SN_STATUS(n) ((n) + 0xa0)
@@ -32,6 +33,9 @@
#define ONE_PT_CALIB2 0x2
#define TWO_PT_CALIB 0x3
+#define SLOPE_FACTOR 1000
+#define SLOPE_DEFAULT 3200
+
enum tsens_dbg_type {
TSENS_DBG_POLL,
TSENS_DBG_LOG_TEMP_READS,
@@ -143,6 +147,7 @@
struct device *dev;
struct platform_device *pdev;
struct list_head list;
+ bool prev_reading_avail;
struct regmap *map;
struct regmap_field *status_field;
void __iomem *tsens_srot_addr;
@@ -153,12 +158,15 @@
spinlock_t tsens_crit_lock;
spinlock_t tsens_upp_low_lock;
const struct tsens_data *ctrl_data;
+ struct tsens_mtc_sysfs mtcsys;
struct tsens_sensor sensor[0];
- struct tsens_mtc_sysfs mtcsys;
};
extern const struct tsens_data data_tsens2xxx, data_tsens23xx, data_tsens24xx;
-extern const struct tsens_data data_tsens14xx;
+extern const struct tsens_data data_tsens14xx, data_tsens1xxx_8909;
extern struct list_head tsens_device_list;
+extern int calibrate_8937(struct tsens_device *tmdev);
+extern int calibrate_8909(struct tsens_device *tmdev);
+
#endif /* __QCOM_TSENS_H__ */
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
index d63fcd1..d698aed 100644
--- a/drivers/thermal/tsens1xxx.c
+++ b/drivers/thermal/tsens1xxx.c
@@ -33,6 +33,7 @@
#define TSENS_UPPER_THRESHOLD_SHIFT 10
#define TSENS_S0_STATUS_ADDR(n) ((n) + 0x30)
+#define TSENS_S0_TRDY_ADDR(n) ((n) + 0x5c)
#define TSENS_SN_ADDR_OFFSET 0x4
#define TSENS_SN_STATUS_TEMP_MASK 0x3ff
#define TSENS_SN_STATUS_LOWER_STATUS BIT(11)
@@ -55,102 +56,6 @@
#define TSENS_THRESHOLD_MIN_CODE 0x0
#define TSENS_SCALE_MILLIDEG 1000
-/* eeprom layout data for 8937 */
-#define BASE0_MASK 0x000000ff
-#define BASE1_MASK 0xff000000
-#define BASE1_SHIFT 24
-
-#define S0_P1_MASK 0x000001f8
-#define S1_P1_MASK 0x001f8000
-#define S2_P1_MASK_0_4 0xf8000000
-#define S2_P1_MASK_5 0x00000001
-#define S3_P1_MASK 0x00001f80
-#define S4_P1_MASK 0x01f80000
-#define S5_P1_MASK 0x00003f00
-#define S6_P1_MASK 0x03f00000
-#define S7_P1_MASK 0x0000003f
-#define S8_P1_MASK 0x0003f000
-#define S9_P1_MASK 0x0000003f
-#define S10_P1_MASK 0x0003f000
-
-#define S0_P2_MASK 0x00007e00
-#define S1_P2_MASK 0x07e00000
-#define S2_P2_MASK 0x0000007e
-#define S3_P2_MASK 0x0007e000
-#define S4_P2_MASK 0x7e000000
-#define S5_P2_MASK 0x000fc000
-#define S6_P2_MASK 0xfc000000
-#define S7_P2_MASK 0x00000fc0
-#define S8_P2_MASK 0x00fc0000
-#define S9_P2_MASK 0x00000fc0
-#define S10_P2_MASK 0x00fc0000
-
-#define S0_P1_SHIFT 3
-#define S1_P1_SHIFT 15
-#define S2_P1_SHIFT_0_4 27
-#define S2_P1_SHIFT_5 5
-#define S3_P1_SHIFT 7
-#define S4_P1_SHIFT 19
-#define S5_P1_SHIFT 8
-#define S6_P1_SHIFT 20
-#define S8_P1_SHIFT 12
-#define S10_P1_SHIFT 12
-
-#define S0_P2_SHIFT 9
-#define S1_P2_SHIFT 21
-#define S2_P2_SHIFT 1
-#define S3_P2_SHIFT 13
-#define S4_P2_SHIFT 25
-#define S5_P2_SHIFT 14
-#define S6_P2_SHIFT 26
-#define S7_P2_SHIFT 6
-#define S8_P2_SHIFT 18
-#define S9_P2_SHIFT 6
-#define S10_P2_SHIFT 18
-
-#define CAL_SEL_MASK 0x00000007
-
-#define CAL_DEGC_PT1 30
-#define CAL_DEGC_PT2 120
-#define SLOPE_FACTOR 1000
-#define SLOPE_DEFAULT 3200
-
-/*
- * Use this function on devices where slope and offset calculations
- * depend on calibration data read from qfprom. On others the slope
- * and offset values are derived from tz->tzp->slope and tz->tzp->offset
- * resp.
- */
-static void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
- u32 *p2, u32 mode)
-{
- int i;
- int num, den;
-
- for (i = 0; i < TSENS_1x_MAX_SENSORS; i++) {
- pr_debug(
- "sensor%d - data_point1:%#x data_point2:%#x\n",
- i, p1[i], p2[i]);
-
- tmdev->sensor[i].slope = SLOPE_DEFAULT;
- if (mode == TWO_PT_CALIB) {
- /*
- * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
- * temp_120_degc - temp_30_degc (x2 - x1)
- */
- num = p2[i] - p1[i];
- num *= SLOPE_FACTOR;
- den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
- tmdev->sensor[i].slope = num / den;
- }
-
- tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
- (CAL_DEGC_PT1 *
- tmdev->sensor[i].slope);
- pr_debug("offset:%d\n", tmdev->sensor[i].offset);
- }
-}
-
static int code_to_degc(u32 adc_code, const struct tsens_sensor *sensor)
{
int degc, num, den;
@@ -184,72 +89,6 @@
return code;
}
-static int calibrate_8937(struct tsens_device *tmdev)
-{
- int base0 = 0, base1 = 0, i;
- u32 p1[TSENS_1x_MAX_SENSORS], p2[TSENS_1x_MAX_SENSORS];
- int mode = 0, tmp = 0;
- u32 qfprom_cdata[5] = {0, 0, 0, 0, 0};
-
- qfprom_cdata[0] = readl_relaxed(tmdev->tsens_calib_addr + 0x1D8);
- qfprom_cdata[1] = readl_relaxed(tmdev->tsens_calib_addr + 0x1DC);
- qfprom_cdata[2] = readl_relaxed(tmdev->tsens_calib_addr + 0x210);
- qfprom_cdata[3] = readl_relaxed(tmdev->tsens_calib_addr + 0x214);
- qfprom_cdata[4] = readl_relaxed(tmdev->tsens_calib_addr + 0x230);
-
- mode = (qfprom_cdata[2] & CAL_SEL_MASK);
- pr_debug("calibration mode is %d\n", mode);
-
- switch (mode) {
- case TWO_PT_CALIB:
- base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
- p2[0] = (qfprom_cdata[2] & S0_P2_MASK) >> S0_P2_SHIFT;
- p2[1] = (qfprom_cdata[2] & S1_P2_MASK) >> S1_P2_SHIFT;
- p2[2] = (qfprom_cdata[3] & S2_P2_MASK) >> S2_P2_SHIFT;
- p2[3] = (qfprom_cdata[3] & S3_P2_MASK) >> S3_P2_SHIFT;
- p2[4] = (qfprom_cdata[3] & S4_P2_MASK) >> S4_P2_SHIFT;
- p2[5] = (qfprom_cdata[0] & S5_P2_MASK) >> S5_P2_SHIFT;
- p2[6] = (qfprom_cdata[0] & S6_P2_MASK) >> S6_P2_SHIFT;
- p2[7] = (qfprom_cdata[1] & S7_P2_MASK) >> S7_P2_SHIFT;
- p2[8] = (qfprom_cdata[1] & S8_P2_MASK) >> S8_P2_SHIFT;
- p2[9] = (qfprom_cdata[4] & S9_P2_MASK) >> S9_P2_SHIFT;
- p2[10] = (qfprom_cdata[4] & S10_P2_MASK) >> S10_P2_SHIFT;
-
- for (i = 0; i < TSENS_1x_MAX_SENSORS; i++)
- p2[i] = ((base1 + p2[i]) << 2);
- /* Fall through */
- case ONE_PT_CALIB2:
- base0 = (qfprom_cdata[0] & BASE0_MASK);
- p1[0] = (qfprom_cdata[2] & S0_P1_MASK) >> S0_P1_SHIFT;
- p1[1] = (qfprom_cdata[2] & S1_P1_MASK) >> S1_P1_SHIFT;
- p1[2] = (qfprom_cdata[2] & S2_P1_MASK_0_4) >> S2_P1_SHIFT_0_4;
- tmp = (qfprom_cdata[3] & S2_P1_MASK_5) << S2_P1_SHIFT_5;
- p1[2] |= tmp;
- p1[3] = (qfprom_cdata[3] & S3_P1_MASK) >> S3_P1_SHIFT;
- p1[4] = (qfprom_cdata[3] & S4_P1_MASK) >> S4_P1_SHIFT;
- p1[5] = (qfprom_cdata[0] & S5_P1_MASK) >> S5_P1_SHIFT;
- p1[6] = (qfprom_cdata[0] & S6_P1_MASK) >> S6_P1_SHIFT;
- p1[7] = (qfprom_cdata[1] & S7_P1_MASK);
- p1[8] = (qfprom_cdata[1] & S8_P1_MASK) >> S8_P1_SHIFT;
- p1[9] = (qfprom_cdata[4] & S9_P1_MASK);
- p1[10] = (qfprom_cdata[4] & S10_P1_MASK) >> S10_P1_SHIFT;
-
- for (i = 0; i < TSENS_1x_MAX_SENSORS; i++)
- p1[i] = (((base0) + p1[i]) << 2);
- break;
- default:
- for (i = 0; i < TSENS_1x_MAX_SENSORS; i++) {
- p1[i] = 500;
- p2[i] = 780;
- }
- break;
- }
-
- compute_intercept_slope(tmdev, p1, p2, mode);
-
- return 0;
-}
-
static int tsens1xxx_get_temp(struct tsens_sensor *sensor, int *temp)
{
struct tsens_device *tmdev = NULL;
@@ -265,8 +104,21 @@
tmdev = sensor->tmdev;
- trdy_addr = TSENS_TRDY_ADDR(tmdev->tsens_tm_addr);
- sensor_addr = TSENS_SN_STATUS_ADDR(tmdev->tsens_tm_addr);
+ if ((tmdev->ctrl_data->ver_major == 1) &&
+ (tmdev->ctrl_data->ver_minor == 1)) {
+ trdy_addr = TSENS_S0_TRDY_ADDR(tmdev->tsens_tm_addr);
+ sensor_addr = TSENS_S0_STATUS_ADDR(tmdev->tsens_tm_addr);
+
+ if (!(tmdev->prev_reading_avail)) {
+ while (!((readl_relaxed(trdy_addr)) & TSENS_TRDY_MASK))
+ usleep_range(TSENS_TRDY_RDY_MIN_TIME,
+ TSENS_TRDY_RDY_MAX_TIME);
+ tmdev->prev_reading_avail = true;
+ }
+ } else {
+ trdy_addr = TSENS_TRDY_ADDR(tmdev->tsens_tm_addr);
+ sensor_addr = TSENS_SN_STATUS_ADDR(tmdev->tsens_tm_addr);
+ }
code = readl_relaxed(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
@@ -486,11 +338,17 @@
void __iomem *sensor_status_ctrl_addr;
u32 rc = 0, addr_offset;
- sensor_status_addr = TSENS_SN_STATUS_ADDR(tm->tsens_tm_addr);
+
+ if ((tm->ctrl_data->ver_major == 1) &&
+ (tm->ctrl_data->ver_minor == 1))
+ sensor_status_addr = TSENS_S0_STATUS_ADDR(tm->tsens_tm_addr);
+ else
+ sensor_status_addr = TSENS_SN_STATUS_ADDR(tm->tsens_tm_addr);
+
sensor_status_ctrl_addr =
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tm->tsens_tm_addr);
- for (i = 0; i < TSENS_1x_MAX_SENSORS; i++) {
+ for (i = 0; i < tm->ctrl_data->num_sensors; i++) {
bool upper_thr = false, lower_thr = false;
if (IS_ERR(tm->sensor[i].tzd))
@@ -517,8 +375,8 @@
th_temp = code_to_degc((threshold &
TSENS_UPPER_THRESHOLD_MASK) >>
TSENS_UPPER_THRESHOLD_SHIFT,
- tm->sensor);
- if (th_temp > temp) {
+ (tm->sensor + i));
+ if (th_temp > (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm high threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
@@ -539,8 +397,8 @@
tm->tsens_tm_addr + addr_offset));
th_temp = code_to_degc((threshold &
TSENS_LOWER_THRESHOLD_MASK),
- tm->sensor);
- if (th_temp < temp) {
+ (tm->sensor + i));
+ if (th_temp < (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm Low threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
@@ -581,7 +439,12 @@
void __iomem *srot_addr;
unsigned int srot_val, sensor_en;
- srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+ if ((tmdev->ctrl_data->ver_major == 1) &&
+ (tmdev->ctrl_data->ver_minor == 1))
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr);
+ else
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+
srot_val = readl_relaxed(srot_addr);
srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val);
@@ -595,7 +458,12 @@
void __iomem *srot_addr;
unsigned int srot_val;
- srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+ if ((tmdev->ctrl_data->ver_major == 1) &&
+ (tmdev->ctrl_data->ver_minor == 1))
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr);
+ else
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+
srot_val = readl_relaxed(srot_addr);
if (!(srot_val & TSENS_EN)) {
pr_err("TSENS device is not enabled\n");
@@ -665,9 +533,27 @@
};
const struct tsens_data data_tsens14xx = {
+ .num_sensors = TSENS_NUM_SENSORS_8937,
.ops = &ops_tsens1xxx,
.valid_status_check = true,
.mtc = true,
.ver_major = 1,
.ver_minor = 4,
};
+
+static const struct tsens_ops ops_tsens1xxx_8909 = {
+ .hw_init = tsens1xxx_hw_init,
+ .get_temp = tsens1xxx_get_temp,
+ .set_trips = tsens1xxx_set_trip_temp,
+ .interrupts_reg = tsens1xxx_register_interrupts,
+ .sensor_en = tsens1xxx_hw_sensor_en,
+ .calibrate = calibrate_8909,
+ .dbg = tsens2xxx_dbg,
+};
+
+const struct tsens_data data_tsens1xxx_8909 = {
+ .num_sensors = TSENS_NUM_SENSORS_8909,
+ .ops = &ops_tsens1xxx_8909,
+ .ver_major = 1,
+ .ver_minor = 1,
+};
diff --git a/drivers/thermal/tsens_calib.c b/drivers/thermal/tsens_calib.c
new file mode 100644
index 0000000..04dd5c5
--- /dev/null
+++ b/drivers/thermal/tsens_calib.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include "tsens.h"
+
+/* eeprom layout data for 8937 */
+#define BASE0_MASK_8937 0x000000ff
+#define BASE1_MASK_8937 0xff000000
+#define BASE1_SHIFT_8937 24
+
+#define S0_P1_MASK_8937 0x000001f8
+#define S1_P1_MASK_8937 0x001f8000
+#define S2_P1_MASK_0_4_8937 0xf8000000
+#define S2_P1_MASK_5_8937 0x00000001
+#define S3_P1_MASK_8937 0x00001f80
+#define S4_P1_MASK_8937 0x01f80000
+#define S5_P1_MASK_8937 0x00003f00
+#define S6_P1_MASK_8937 0x03f00000
+#define S7_P1_MASK_8937 0x0000003f
+#define S8_P1_MASK_8937 0x0003f000
+#define S9_P1_MASK_8937 0x0000003f
+#define S10_P1_MASK_8937 0x0003f000
+
+#define S0_P2_MASK_8937 0x00007e00
+#define S1_P2_MASK_8937 0x07e00000
+#define S2_P2_MASK_8937 0x0000007e
+#define S3_P2_MASK_8937 0x0007e000
+#define S4_P2_MASK_8937 0x7e000000
+#define S5_P2_MASK_8937 0x000fc000
+#define S6_P2_MASK_8937 0xfc000000
+#define S7_P2_MASK_8937 0x00000fc0
+#define S8_P2_MASK_8937 0x00fc0000
+#define S9_P2_MASK_8937 0x00000fc0
+#define S10_P2_MASK_8937 0x00fc0000
+
+#define S0_P1_SHIFT_8937 3
+#define S1_P1_SHIFT_8937 15
+#define S2_P1_SHIFT_0_4_8937 27
+#define S2_P1_SHIFT_5_8937 5
+#define S3_P1_SHIFT_8937 7
+#define S4_P1_SHIFT_8937 19
+#define S5_P1_SHIFT_8937 8
+#define S6_P1_SHIFT_8937 20
+#define S8_P1_SHIFT_8937 12
+#define S10_P1_SHIFT_8937 12
+
+#define S0_P2_SHIFT_8937 9
+#define S1_P2_SHIFT_8937 21
+#define S2_P2_SHIFT_8937 1
+#define S3_P2_SHIFT_8937 13
+#define S4_P2_SHIFT_8937 25
+#define S5_P2_SHIFT_8937 14
+#define S6_P2_SHIFT_8937 26
+#define S7_P2_SHIFT_8937 6
+#define S8_P2_SHIFT_8937 18
+#define S9_P2_SHIFT_8937 6
+#define S10_P2_SHIFT_8937 18
+
+#define CAL_SEL_MASK_8937 0x00000007
+
+/* eeprom layout for 8909 */
+#define TSENS_EEPROM(n) ((n) + 0xa0)
+#define BASE0_MASK_8909 0x000000ff
+#define BASE1_MASK_8909 0x0000ff00
+
+#define S0_P1_MASK_8909 0x0000003f
+#define S1_P1_MASK_8909 0x0003f000
+#define S2_P1_MASK_8909 0x3f000000
+#define S3_P1_MASK_8909 0x000003f0
+#define S4_P1_MASK_8909 0x003f0000
+
+#define S0_P2_MASK_8909 0x00000fc0
+#define S1_P2_MASK_8909 0x00fc0000
+#define S2_P2_MASK_0_1_8909 0xc0000000
+#define S2_P2_MASK_2_5_8909 0x0000000f
+#define S3_P2_MASK_8909 0x0000fc00
+#define S4_P2_MASK_8909 0x0fc00000
+
+#define TSENS_CAL_SEL_8909 0x00070000
+#define CAL_SEL_SHIFT_8909 16
+#define BASE1_SHIFT_8909 8
+
+#define S1_P1_SHIFT_8909 12
+#define S2_P1_SHIFT_8909 24
+#define S3_P1_SHIFT_8909 4
+#define S4_P1_SHIFT_8909 16
+
+#define S0_P2_SHIFT_8909 6
+#define S1_P2_SHIFT_8909 18
+#define S2_P2_SHIFT_0_1_8909 30
+#define S2_P2_SHIFT_2_5_8909 2
+#define S3_P2_SHIFT_8909 10
+#define S4_P2_SHIFT_8909 22
+
+#define CAL_DEGC_PT1 30
+#define CAL_DEGC_PT2 120
+/*
+ * Use this function on devices where slope and offset calculations
+ * depend on calibration data read from qfprom. On others the slope
+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
+ * resp.
+ */
+static void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
+ u32 *p2, u32 mode)
+{
+ int i;
+ int num, den;
+
+ for (i = 0; i < tmdev->ctrl_data->num_sensors; i++) {
+ pr_debug(
+ "sensor%d - data_point1:%#x data_point2:%#x\n",
+ i, p1[i], p2[i]);
+
+ tmdev->sensor[i].slope = SLOPE_DEFAULT;
+ if (mode == TWO_PT_CALIB) {
+ /*
+ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
+ * temp_120_degc - temp_30_degc (x2 - x1)
+ */
+ num = p2[i] - p1[i];
+ num *= SLOPE_FACTOR;
+ den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
+ tmdev->sensor[i].slope = num / den;
+ }
+
+ tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
+ (CAL_DEGC_PT1 *
+ tmdev->sensor[i].slope);
+ pr_debug("offset:%d\n", tmdev->sensor[i].offset);
+ }
+}
+
+int calibrate_8937(struct tsens_device *tmdev)
+{
+ int base0 = 0, base1 = 0, i;
+ u32 p1[TSENS_NUM_SENSORS_8937], p2[TSENS_NUM_SENSORS_8937];
+ int mode = 0, tmp = 0;
+ u32 qfprom_cdata[5] = {0, 0, 0, 0, 0};
+
+ qfprom_cdata[0] = readl_relaxed(tmdev->tsens_calib_addr + 0x1D8);
+ qfprom_cdata[1] = readl_relaxed(tmdev->tsens_calib_addr + 0x1DC);
+ qfprom_cdata[2] = readl_relaxed(tmdev->tsens_calib_addr + 0x210);
+ qfprom_cdata[3] = readl_relaxed(tmdev->tsens_calib_addr + 0x214);
+ qfprom_cdata[4] = readl_relaxed(tmdev->tsens_calib_addr + 0x230);
+
+ mode = (qfprom_cdata[2] & CAL_SEL_MASK_8937);
+ pr_debug("calibration mode is %d\n", mode);
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base1 = (qfprom_cdata[1] &
+ BASE1_MASK_8937) >> BASE1_SHIFT_8937;
+ p2[0] = (qfprom_cdata[2] &
+ S0_P2_MASK_8937) >> S0_P2_SHIFT_8937;
+ p2[1] = (qfprom_cdata[2] &
+ S1_P2_MASK_8937) >> S1_P2_SHIFT_8937;
+ p2[2] = (qfprom_cdata[3] &
+ S2_P2_MASK_8937) >> S2_P2_SHIFT_8937;
+ p2[3] = (qfprom_cdata[3] &
+ S3_P2_MASK_8937) >> S3_P2_SHIFT_8937;
+ p2[4] = (qfprom_cdata[3] &
+ S4_P2_MASK_8937) >> S4_P2_SHIFT_8937;
+ p2[5] = (qfprom_cdata[0] &
+ S5_P2_MASK_8937) >> S5_P2_SHIFT_8937;
+ p2[6] = (qfprom_cdata[0] &
+ S6_P2_MASK_8937) >> S6_P2_SHIFT_8937;
+ p2[7] = (qfprom_cdata[1] &
+ S7_P2_MASK_8937) >> S7_P2_SHIFT_8937;
+ p2[8] = (qfprom_cdata[1] &
+ S8_P2_MASK_8937) >> S8_P2_SHIFT_8937;
+ p2[9] = (qfprom_cdata[4] &
+ S9_P2_MASK_8937) >> S9_P2_SHIFT_8937;
+ p2[10] = (qfprom_cdata[4] &
+ S10_P2_MASK_8937) >> S10_P2_SHIFT_8937;
+
+ for (i = 0; i < TSENS_NUM_SENSORS_8937; i++)
+ p2[i] = ((base1 + p2[i]) << 2);
+ /* Fall through */
+ case ONE_PT_CALIB2:
+ base0 = (qfprom_cdata[0] & BASE0_MASK_8937);
+ p1[0] = (qfprom_cdata[2] &
+ S0_P1_MASK_8937) >> S0_P1_SHIFT_8937;
+ p1[1] = (qfprom_cdata[2] &
+ S1_P1_MASK_8937) >> S1_P1_SHIFT_8937;
+ p1[2] = (qfprom_cdata[2] &
+ S2_P1_MASK_0_4_8937) >> S2_P1_SHIFT_0_4_8937;
+ tmp = (qfprom_cdata[3] &
+ S2_P1_MASK_5_8937) << S2_P1_SHIFT_5_8937;
+ p1[2] |= tmp;
+ p1[3] = (qfprom_cdata[3] &
+ S3_P1_MASK_8937) >> S3_P1_SHIFT_8937;
+ p1[4] = (qfprom_cdata[3] &
+ S4_P1_MASK_8937) >> S4_P1_SHIFT_8937;
+ p1[5] = (qfprom_cdata[0] &
+ S5_P1_MASK_8937) >> S5_P1_SHIFT_8937;
+ p1[6] = (qfprom_cdata[0] &
+ S6_P1_MASK_8937) >> S6_P1_SHIFT_8937;
+ p1[7] = (qfprom_cdata[1] & S7_P1_MASK_8937);
+ p1[8] = (qfprom_cdata[1] &
+ S8_P1_MASK_8937) >> S8_P1_SHIFT_8937;
+ p1[9] = (qfprom_cdata[4] & S9_P1_MASK_8937);
+ p1[10] = (qfprom_cdata[4] &
+ S10_P1_MASK_8937) >> S10_P1_SHIFT_8937;
+
+ for (i = 0; i < TSENS_NUM_SENSORS_8937; i++)
+ p1[i] = (((base0) + p1[i]) << 2);
+ break;
+ default:
+ for (i = 0; i < TSENS_NUM_SENSORS_8937; i++) {
+ p1[i] = 500;
+ p2[i] = 780;
+ }
+ break;
+ }
+
+ compute_intercept_slope(tmdev, p1, p2, mode);
+
+ return 0;
+}
+
+int calibrate_8909(struct tsens_device *tmdev)
+{
+ int i, base0 = 0, base1 = 0;
+ u32 p1[TSENS_NUM_SENSORS_8909], p2[TSENS_NUM_SENSORS_8909];
+ int mode = 0, temp = 0;
+ uint32_t calib_data[3] = {0, 0, 0};
+
+ calib_data[0] = readl_relaxed(
+ TSENS_EEPROM(tmdev->tsens_calib_addr));
+ calib_data[1] = readl_relaxed(
+ (TSENS_EEPROM(tmdev->tsens_calib_addr) + 0x4));
+ calib_data[2] = readl_relaxed(
+ (TSENS_EEPROM(tmdev->tsens_calib_addr) + 0x3c));
+ mode = (calib_data[2] & TSENS_CAL_SEL_8909) >> CAL_SEL_SHIFT_8909;
+
+ pr_debug("calib mode is %d\n", mode);
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base1 = (calib_data[2] & BASE1_MASK_8909) >> BASE1_SHIFT_8909;
+ p2[0] = (calib_data[0] & S0_P2_MASK_8909) >> S0_P2_SHIFT_8909;
+ p2[1] = (calib_data[0] & S1_P2_MASK_8909) >> S1_P2_SHIFT_8909;
+ p2[2] = (calib_data[0] &
+ S2_P2_MASK_0_1_8909) >> S2_P2_SHIFT_0_1_8909;
+ temp = (calib_data[1] &
+ S2_P2_MASK_2_5_8909) << S2_P2_SHIFT_2_5_8909;
+ p2[2] |= temp;
+ p2[3] = (calib_data[1] & S3_P2_MASK_8909) >> S3_P2_SHIFT_8909;
+ p2[4] = (calib_data[1] & S4_P2_MASK_8909) >> S4_P2_SHIFT_8909;
+
+ for (i = 0; i < TSENS_NUM_SENSORS_8909; i++)
+ p2[i] = ((base1 + p2[i]) << 2);
+ /* Fall through */
+ case ONE_PT_CALIB2:
+ base0 = (calib_data[2] & BASE0_MASK_8909);
+ p1[0] = (calib_data[0] & S0_P1_MASK_8909);
+ p1[1] = (calib_data[0] & S1_P1_MASK_8909) >> S1_P1_SHIFT_8909;
+ p1[2] = (calib_data[0] & S2_P1_MASK_8909) >> S2_P1_SHIFT_8909;
+ p1[3] = (calib_data[1] & S3_P1_MASK_8909) >> S3_P1_SHIFT_8909;
+ p1[4] = (calib_data[1] & S4_P1_MASK_8909) >> S4_P1_SHIFT_8909;
+ for (i = 0; i < TSENS_NUM_SENSORS_8909; i++)
+ p1[i] = (((base0) + p1[i]) << 2);
+ break;
+ default:
+ for (i = 0; i < TSENS_NUM_SENSORS_8909; i++) {
+ p1[i] = 500;
+ p2[i] = 780;
+ }
+ break;
+ }
+
+ compute_intercept_slope(tmdev, p1, p2, mode);
+ return 0;
+}
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index a8c2041..cba6bc6 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -628,6 +628,7 @@
* we just disable hotplug, the
* pci-tunnels stay alive.
*/
+ .thaw_noirq = nhi_resume_noirq,
.restore_noirq = nhi_resume_noirq,
};
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 54cab59..9e9016e 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -137,6 +137,9 @@
struct mutex mutex;
/* Link layer */
+ int mode;
+#define DLCI_MODE_ABM 0 /* Normal Asynchronous Balanced Mode */
+#define DLCI_MODE_ADM 1 /* Asynchronous Disconnected Mode */
spinlock_t lock; /* Protects the internal state */
struct timer_list t1; /* Retransmit timer for SABM and UA */
int retries;
@@ -1380,7 +1383,13 @@
ctrl->data = data;
ctrl->len = clen;
gsm->pending_cmd = ctrl;
- gsm->cretries = gsm->n2;
+
+ /* If DLCI0 is in ADM mode skip retries, it won't respond */
+ if (gsm->dlci[0]->mode == DLCI_MODE_ADM)
+ gsm->cretries = 1;
+ else
+ gsm->cretries = gsm->n2;
+
mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
gsm_control_transmit(gsm, ctrl);
spin_unlock_irqrestore(&gsm->control_lock, flags);
@@ -1467,6 +1476,10 @@
* in which case an opening port goes back to closed and a closing port
* is simply put into closed state (any further frames from the other
* end will get a DM response)
+ *
+ * Some control dlci can stay in ADM mode with other dlci working just
+ * fine. In that case we can just keep the control dlci open after the
+ * DLCI_OPENING retries time out.
*/
static void gsm_dlci_t1(unsigned long data)
@@ -1480,8 +1493,16 @@
if (dlci->retries) {
gsm_command(dlci->gsm, dlci->addr, SABM|PF);
mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
- } else
+ } else if (!dlci->addr && gsm->control == (DM | PF)) {
+ if (debug & 8)
+ pr_info("DLCI %d opening in ADM mode.\n",
+ dlci->addr);
+ dlci->mode = DLCI_MODE_ADM;
+ gsm_dlci_open(dlci);
+ } else {
gsm_dlci_close(dlci);
+ }
+
break;
case DLCI_CLOSING:
dlci->retries--;
@@ -1499,8 +1520,8 @@
* @dlci: DLCI to open
*
* Commence opening a DLCI from the Linux side. We issue SABM messages
- * to the modem which should then reply with a UA, at which point we
- * will move into open state. Opening is done asynchronously with retry
+ * to the modem which should then reply with a UA or ADM, at which point
+ * we will move into open state. Opening is done asynchronously with retry
* running off timers and the responses.
*/
@@ -2854,11 +2875,22 @@
static int gsm_carrier_raised(struct tty_port *port)
{
struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
+ struct gsm_mux *gsm = dlci->gsm;
+
/* Not yet open so no carrier info */
if (dlci->state != DLCI_OPEN)
return 0;
if (debug & 2)
return 1;
+
+ /*
+ * Basic mode with control channel in ADM mode may not respond
+ * to CMD_MSC at all and modem_rx is empty.
+ */
+ if (gsm->encoding == 0 && gsm->dlci[0]->mode == DLCI_MODE_ADM &&
+ !dlci->modem_rx)
+ return 1;
+
return dlci->modem_rx & TIOCM_CD;
}
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index faf50df..1c70541 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2182,6 +2182,12 @@
}
if (tty_hung_up_p(file))
break;
+ /*
+ * Abort readers for ttys which never actually
+ * get hung up. See __tty_hangup().
+ */
+ if (test_bit(TTY_HUPPING, &tty->flags))
+ break;
if (!timeout)
break;
if (file->f_flags & O_NONBLOCK) {
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 459d726..3eb01a71 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -464,7 +464,8 @@
/* If no clock rate is defined, fail. */
if (!p->uartclk) {
dev_err(dev, "clock rate not defined\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto err_clk;
}
data->pclk = devm_clk_get(dev, "apb_pclk");
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index da31159..e8b34f1 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -613,6 +613,10 @@
up->lsr_saved_flags = 0;
up->msr_saved_flags = 0;
+ /* Disable DMA for console UART */
+ if (uart_console(port))
+ up->dma = NULL;
+
if (up->dma) {
ret = serial8250_request_dma(up);
if (ret) {
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index b80ea87..e82b347 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -5100,6 +5100,17 @@
PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */
pbn_b2_4_115200 },
/*
+ * BrainBoxes UC-260
+ */
+ { PCI_VENDOR_ID_INTASHIELD, 0x0D21,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
+ pbn_b2_4_115200 },
+ { PCI_VENDOR_ID_INTASHIELD, 0x0E34,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
+ pbn_b2_4_115200 },
+ /*
* Perle PCI-RAS cards
*/
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index f6e4373..5d9038a 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1815,7 +1815,8 @@
status = serial_port_in(port, UART_LSR);
- if (status & (UART_LSR_DR | UART_LSR_BI)) {
+ if (status & (UART_LSR_DR | UART_LSR_BI) &&
+ iir & UART_IIR_RDI) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
}
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index e2c33b9..b42d7f1 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1302,14 +1302,15 @@
pl011_dma_tx_stop(uap);
}
-static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
+static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
/* Start TX with programmed I/O only (no DMA) */
static void pl011_start_tx_pio(struct uart_amba_port *uap)
{
- uap->im |= UART011_TXIM;
- pl011_write(uap->im, uap, REG_IMSC);
- pl011_tx_chars(uap, false);
+ if (pl011_tx_chars(uap, false)) {
+ uap->im |= UART011_TXIM;
+ pl011_write(uap->im, uap, REG_IMSC);
+ }
}
static void pl011_start_tx(struct uart_port *port)
@@ -1389,25 +1390,26 @@
return true;
}
-static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
+/* Returns true if tx interrupts have to be (kept) enabled */
+static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
{
struct circ_buf *xmit = &uap->port.state->xmit;
int count = uap->fifosize >> 1;
if (uap->port.x_char) {
if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
- return;
+ return true;
uap->port.x_char = 0;
--count;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
pl011_stop_tx(&uap->port);
- return;
+ return false;
}
/* If we are using DMA mode, try to send some characters. */
if (pl011_dma_tx_irq(uap))
- return;
+ return true;
do {
if (likely(from_irq) && count-- == 0)
@@ -1422,8 +1424,11 @@
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
- if (uart_circ_empty(xmit))
+ if (uart_circ_empty(xmit)) {
pl011_stop_tx(&uap->port);
+ return false;
+ }
+ return true;
}
static void pl011_modem_status(struct uart_amba_port *uap)
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 5ac06fc..fec48de 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -596,6 +596,11 @@
if (dev_id < 0)
dev_id = 0;
+ if (dev_id >= ARRAY_SIZE(arc_uart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", dev_id);
+ return -EINVAL;
+ }
+
uart = &arc_uart_ports[dev_id];
port = &uart->port;
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 4d079cd..addb287 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1780,6 +1780,7 @@
switch (version) {
case 0x302:
case 0x10213:
+ case 0x10302:
dev_dbg(port->dev, "This version is usart\n");
atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index c365154..b9a4625 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -172,7 +172,7 @@
*/
int __init setup_earlycon(char *buf)
{
- const struct earlycon_id *match;
+ const struct earlycon_id **p_match;
if (!buf || !buf[0])
return -EINVAL;
@@ -180,7 +180,9 @@
if (early_con.flags & CON_ENABLED)
return -EALREADY;
- for (match = __earlycon_table; match < __earlycon_table_end; match++) {
+ for (p_match = __earlycon_table; p_match < __earlycon_table_end;
+ p_match++) {
+ const struct earlycon_id *match = *p_match;
size_t len = strlen(match->name);
if (strncmp(buf, match->name, len))
@@ -253,11 +255,12 @@
}
port->mapbase = addr;
port->uartclk = BASE_BAUD * 16;
- port->membase = earlycon_map(port->mapbase, SZ_4K);
val = of_get_flat_dt_prop(node, "reg-offset", NULL);
if (val)
port->mapbase += be32_to_cpu(*val);
+ port->membase = earlycon_map(port->mapbase, SZ_4K);
+
val = of_get_flat_dt_prop(node, "reg-shift", NULL);
if (val)
port->regshift = be32_to_cpu(*val);
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 76103f2..937f5e1 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1902,6 +1902,10 @@
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
+ if (ret >= ARRAY_SIZE(lpuart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", ret);
+ return -EINVAL;
+ }
sport->port.line = ret;
sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart");
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 521a6e4..b24edf6 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1316,19 +1316,10 @@
if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
- /*
- * The effect of RI and DCD differs depending on the UFCR_DCEDTE
- * bit. In DCE mode they control the outputs, in DTE mode they
- * enable the respective irqs. At least the DCD irq cannot be
- * cleared on i.MX25 at least, so it's not usable and must be
- * disabled. I don't have test hardware to check if RI has the
- * same problem but I consider this likely so it's disabled for
- * now, too.
- */
- temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP |
- UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
+ temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
if (sport->dte_mode)
+ /* disable broken interrupts */
temp &= ~(UCR3_RI | UCR3_DCD);
writel(temp, sport->port.membase + UCR3);
@@ -1583,8 +1574,6 @@
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
- if (sport->dte_mode)
- ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
writel(num, sport->port.membase + UBIR);
@@ -2091,6 +2080,12 @@
else if (ret < 0)
return ret;
+ if (sport->port.line >= ARRAY_SIZE(imx_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n",
+ sport->port.line);
+ return -EINVAL;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
@@ -2149,6 +2144,37 @@
UCR1_TXMPTYEN | UCR1_RTSDEN);
writel_relaxed(reg, sport->port.membase + UCR1);
+ if (!is_imx1_uart(sport) && sport->dte_mode) {
+ /*
+ * The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
+ * and influences if UCR3_RI and UCR3_DCD changes the level of RI
+ * and DCD (when they are outputs) or enables the respective
+ * irqs. So set this bit early, i.e. before requesting irqs.
+ */
+ reg = readl(sport->port.membase + UFCR);
+ if (!(reg & UFCR_DCEDTE))
+ writel(reg | UFCR_DCEDTE, sport->port.membase + UFCR);
+
+ /*
+ * Disable UCR3_RI and UCR3_DCD irqs. They are also not
+ * enabled later because they cannot be cleared
+ * (confirmed on i.MX25) which makes them unusable.
+ */
+ writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
+ sport->port.membase + UCR3);
+
+ } else {
+ unsigned long ucr3 = UCR3_DSR;
+
+ reg = readl(sport->port.membase + UFCR);
+ if (reg & UFCR_DCEDTE)
+ writel(reg & ~UFCR_DCEDTE, sport->port.membase + UFCR);
+
+ if (!is_imx1_uart(sport))
+ ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
+ writel(ucr3, sport->port.membase + UCR3);
+ }
+
clk_disable_unprepare(sport->clk_ipg);
/*
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index a260cde..5532c44 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -245,7 +245,8 @@
kgdb_tty_line, chr);
}
-static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp)
+static int param_set_kgdboc_var(const char *kmessage,
+ const struct kernel_param *kp)
{
int len = strlen(kmessage);
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index f76b3db..4e895ee 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -617,7 +617,6 @@
static void msm_geni_serial_complete_rx_eot(struct uart_port *uport)
{
int poll_done = 0, tries = 0;
- u32 geni_status = 0;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
do {
@@ -626,11 +625,11 @@
tries++;
} while (!poll_done && tries < 5);
- geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
-
if (!poll_done)
- IPC_LOG_MSG(port->ipc_log_misc, "%s: RX_EOT, GENI:0x%x\n",
- __func__, geni_status);
+ IPC_LOG_MSG(port->ipc_log_misc,
+ "%s: RX_EOT, GENI:0x%x, DMA_DEBUG:0x%x\n", __func__,
+ geni_read_reg_nolog(uport->membase, SE_GENI_STATUS),
+ geni_read_reg_nolog(uport->membase, SE_DMA_DEBUG_REG0));
else
geni_write_reg_nolog(RX_EOT, uport->membase, SE_DMA_RX_IRQ_CLR);
}
@@ -1131,7 +1130,9 @@
* cancel control bit.
*/
mb();
- msm_geni_serial_complete_rx_eot(uport);
+ if (!uart_console(uport))
+ msm_geni_serial_complete_rx_eot(uport);
+
done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_CANCEL, false);
if (done) {
@@ -1660,7 +1661,7 @@
msm_port->rx_buf = devm_kzalloc(uport->dev, DMA_RX_BUF_SIZE,
GFP_KERNEL);
if (!msm_port->rx_buf) {
- kfree(msm_port->rx_fifo);
+ devm_kfree(uport->dev, msm_port->rx_fifo);
msm_port->rx_fifo = NULL;
ret = -ENOMEM;
goto exit_portsetup;
@@ -1856,6 +1857,7 @@
unsigned long ser_clk_cfg = 0;
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
unsigned long clk_rate;
+ unsigned long flags;
if (!uart_console(uport)) {
int ret = msm_geni_serial_power_on(uport);
@@ -1867,7 +1869,13 @@
return;
}
}
+ /* Take a spinlock else stop_rx causes a race with an ISR due to Cancel
+ * and FSM_RESET. This also has a potential race with the dma_map/unmap
+ * operations of ISR.
+ */
+ spin_lock_irqsave(&uport->lock, flags);
msm_geni_serial_stop_rx(uport);
+ spin_unlock_irqrestore(&uport->lock, flags);
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
port->cur_baud = baud;
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index efaac5e..69fa4dc 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -395,10 +395,22 @@
static inline void msm_wait_for_xmitr(struct uart_port *port)
{
+ u32 count = 500000;
+
while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) {
if (msm_read(port, UART_ISR) & UART_ISR_TX_READY)
break;
udelay(1);
+
+ /* At worst case, it is stuck in this loop for waiting
+ * TX ready, have a 500ms timeout to avoid stuck here
+ * and only miss some log to uart.
+ */
+ if (count-- == 0) {
+ msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
+ printk_deferred("uart may lost data, resetting TX!\n");
+ break;
+ }
}
msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR);
}
@@ -1858,10 +1870,35 @@
return 0;
}
+
+static int msm_serial_freeze(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct msm_port *msm_port = UART_TO_MSM(port);
+ int ret;
+
+ ret = msm_serial_suspend(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Set the rate as recommended to avoid issues where the clock
+ * driver skips reconfiguring the clock hardware during
+ * hibernation resume.
+ */
+ return clk_set_rate(msm_port->clk, 19200000);
+}
#endif
static const struct dev_pm_ops msm_serial_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(msm_serial_suspend, msm_serial_resume)
+#ifdef CONFIG_PM_SLEEP
+ .suspend = msm_serial_suspend,
+ .resume = msm_serial_resume,
+ .freeze = msm_serial_freeze,
+ .thaw = msm_serial_resume,
+ .poweroff = msm_serial_suspend,
+ .restore = msm_serial_resume,
+#endif
};
static struct platform_driver msm_platform_driver = {
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 792fb3b..a2522c6 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -3,7 +3,7 @@
* MSM 7k High speed uart driver
*
* Copyright (c) 2008 Google Inc.
- * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2018, The Linux Foundation. All rights reserved.
* Modified: Nick Pelly <npelly@google.com>
*
* All source code in this file is licensed under the following license
@@ -2617,6 +2617,33 @@
/* turn on uart clk */
msm_hs_resource_vote(msm_uport);
+ /* Set up Uart Receive */
+ msm_hs_write(uport, UART_DM_RFWR, 32);
+ /* Write to BADR explicitly to set up FIFO sizes */
+ msm_hs_write(uport, UARTDM_BADR_ADDR, 64);
+
+ /* configure the CR Protection to Enable */
+ msm_hs_write(uport, UART_DM_CR, CR_PROTECTION_EN);
+
+ /*
+ * Enable Command register protection before going ahead as this hw
+ * configuration makes sure that issued cmd to CR register gets complete
+ * before next issued cmd start. Hence mb() requires here.
+ */
+ mb();
+
+ /*
+ * Set RX_BREAK_ZERO_CHAR_OFF and RX_ERROR_CHAR_OFF
+ * so any rx_break and character having parity of framing
+ * error don't enter inside UART RX FIFO.
+ */
+ data = msm_hs_read(uport, UART_DM_MR2);
+ data |= (UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF |
+ UARTDM_MR2_RX_ERROR_CHAR_OFF);
+ msm_hs_write(uport, UART_DM_MR2, data);
+ /* Ensure register IO completion */
+ mb();
+
if (is_use_low_power_wakeup(msm_uport)) {
ret = request_threaded_irq(msm_uport->wakeup.irq, NULL,
msm_hs_wakeup_isr,
@@ -2797,10 +2824,6 @@
goto exit_lh_init;
}
- /* Set up Uart Receive */
- msm_hs_write(uport, UART_DM_RFWR, 32);
- /* Write to BADR explicitly to set up FIFO sizes */
- msm_hs_write(uport, UARTDM_BADR_ADDR, 64);
INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
@@ -3331,7 +3354,6 @@
struct resource *bam_resource;
int core_irqres, bam_irqres, wakeup_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
- unsigned long data;
char name[30];
if (pdev->dev.of_node) {
@@ -3551,27 +3573,6 @@
if (unlikely(ret))
goto err_clock;
- /* configure the CR Protection to Enable */
- msm_hs_write(uport, UART_DM_CR, CR_PROTECTION_EN);
-
- /*
- * Enable Command register protection before going ahead as this hw
- * configuration makes sure that issued cmd to CR register gets complete
- * before next issued cmd start. Hence mb() requires here.
- */
- mb();
-
- /*
- * Set RX_BREAK_ZERO_CHAR_OFF and RX_ERROR_CHAR_OFF
- * so any rx_break and character having parity of framing
- * error don't enter inside UART RX FIFO.
- */
- data = msm_hs_read(uport, UART_DM_MR2);
- data |= (UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF |
- UARTDM_MR2_RX_ERROR_CHAR_OFF);
- msm_hs_write(uport, UART_DM_MR2, data);
- /* Ensure register IO completion */
- mb();
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
if (unlikely(ret)) {
@@ -3761,8 +3762,8 @@
.runtime_suspend = msm_hs_runtime_suspend,
.runtime_resume = msm_hs_runtime_resume,
.runtime_idle = NULL,
- .suspend_noirq = msm_hs_pm_sys_suspend_noirq,
- .resume_noirq = msm_hs_pm_sys_resume_noirq,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(msm_hs_pm_sys_suspend_noirq,
+ msm_hs_pm_sys_resume_noirq)
};
static struct platform_driver msm_serial_hs_platform_driver = {
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 07390f8..1d9d778 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1664,6 +1664,10 @@
s->port.line = pdev->id < 0 ? 0 : pdev->id;
else if (ret < 0)
return ret;
+ if (s->port.line >= ARRAY_SIZE(auart_port)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", s->port.line);
+ return -EINVAL;
+ }
if (of_id) {
pdev->id_entry = of_id->data;
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index d65f92b..f2ab6d8a 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1813,6 +1813,10 @@
dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
+ if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", index);
+ return -EINVAL;
+ }
ourport = &s3c24xx_serial_ports[index];
ourport->drv_data = s3c24xx_get_driver_data(pdev);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index fcf803f..b9c7a90 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -884,14 +884,28 @@
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
- if (PTR_ERR(clk) == -EPROBE_DEFER) {
- ret = -EPROBE_DEFER;
+ ret = PTR_ERR(clk);
+ if (ret == -EPROBE_DEFER)
goto err_out;
- }
+ uartclk = 0;
+ } else {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto err_out;
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ (void(*)(void *))clk_disable_unprepare,
+ clk);
+ if (ret)
+ goto err_out;
+
+ uartclk = clk_get_rate(clk);
+ }
+
+ if (!uartclk) {
dev_notice(&pdev->dev, "Using default clock frequency\n");
uartclk = s->chip->freq_std;
- } else
- uartclk = clk_get_rate(clk);
+ }
/* Check input frequency */
if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
@@ -983,7 +997,7 @@
uart_unregister_driver(&s->uart);
err_out:
if (!IS_ERR(s->regulator))
- return regulator_disable(s->regulator);
+ regulator_disable(s->regulator);
return ret;
}
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index dce39de..839cee4 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1138,6 +1138,8 @@
uport->ops->config_port(uport, flags);
ret = uart_startup(tty, state, 1);
+ if (ret == 0)
+ tty_port_set_initialized(port, true);
if (ret > 0)
ret = 0;
}
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 15eaea5..107f0d1 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -935,6 +935,8 @@
/* Tell the rest of the system the news. New characters! */
tty_flip_buffer_push(tport);
} else {
+ /* TTY buffers full; read from RX reg to prevent lockup */
+ serial_port_in(port, SCxRDR);
serial_port_in(port, SCxSR); /* dummy read */
sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
}
@@ -1543,7 +1545,16 @@
if (s->chan_rx)
sci_rx_dma_release(s, false);
}
-#else
+
+static void sci_flush_buffer(struct uart_port *port)
+{
+ /*
+ * In uart_flush_buffer(), the xmit circular buffer has just been
+ * cleared, so we have to reset tx_dma_len accordingly.
+ */
+ to_sci_port(port)->tx_dma_len = 0;
+}
+#else /* !CONFIG_SERIAL_SH_SCI_DMA */
static inline void sci_request_dma(struct uart_port *port)
{
}
@@ -1551,7 +1562,9 @@
static inline void sci_free_dma(struct uart_port *port)
{
}
-#endif
+
+#define sci_flush_buffer NULL
+#endif /* !CONFIG_SERIAL_SH_SCI_DMA */
static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
{
@@ -2549,6 +2562,7 @@
.break_ctl = sci_break_ctl,
.startup = sci_startup,
.shutdown = sci_shutdown,
+ .flush_buffer = sci_flush_buffer,
.set_termios = sci_set_termios,
.pm = sci_pm,
.type = sci_type,
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index dd4c02f..7497f1d 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1106,7 +1106,7 @@
struct uart_port *port;
/* Try the given port id if failed use default method */
- if (cdns_uart_port[id].mapbase != 0) {
+ if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) {
/* Find the next unused port */
for (id = 0; id < CDNS_UART_NR_PORTS; id++)
if (cdns_uart_port[id].mapbase == 0)
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 8d9f9a8..789c814 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -709,6 +709,14 @@
return;
}
+ /*
+ * Some console devices aren't actually hung up for technical and
+ * historical reasons, which can lead to indefinite interruptible
+ * sleep in n_tty_read(). The following explicitly tells
+ * n_tty_read() to abort readers.
+ */
+ set_bit(TTY_HUPPING, &tty->flags);
+
/* inuse_filps is protected by the single tty lock,
this really needs to change if we want to flush the
workqueue with the lock held */
@@ -763,6 +771,7 @@
* from the ldisc side, which is now guaranteed.
*/
set_bit(TTY_HUPPED, &tty->flags);
+ clear_bit(TTY_HUPPING, &tty->flags);
tty_unlock(tty);
if (f)
@@ -1702,6 +1711,8 @@
if (tty->link)
tty->link->port->itty = NULL;
tty_buffer_cancel_work(tty->port);
+ if (tty->link)
+ tty_buffer_cancel_work(tty->link->port);
tty_kref_put(tty->link);
tty_kref_put(tty);
@@ -3159,7 +3170,10 @@
kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
- tty_ldisc_init(tty);
+ if (tty_ldisc_init(tty)) {
+ kfree(tty);
+ return NULL;
+ }
tty->session = NULL;
tty->pgrp = NULL;
mutex_init(&tty->legacy_mutex);
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 3a9e2a2..4ab518d 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -175,12 +175,11 @@
return ERR_CAST(ldops);
}
- ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
- if (ld == NULL) {
- put_ldops(ldops);
- return ERR_PTR(-ENOMEM);
- }
-
+ /*
+ * There is no way to handle allocation failure of only 16 bytes.
+ * Let's simplify error handling and save more memory.
+ */
+ ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL | __GFP_NOFAIL);
ld->ops = ldops;
ld->tty = tty;
@@ -753,12 +752,13 @@
* the tty structure is not completely set up when this call is made.
*/
-void tty_ldisc_init(struct tty_struct *tty)
+int tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
- panic("n_tty: init_tty");
+ return PTR_ERR(ld);
tty->ldisc = ld;
+ return 0;
}
/**
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index ce2c3c6..9e1ac58 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1354,6 +1354,11 @@
case 3:
vc->vc_italic = 1;
break;
+ case 21:
+ /*
+ * No console drivers support double underline, so
+ * convert it to a single underline.
+ */
case 4:
vc->vc_underline = 1;
break;
@@ -1389,7 +1394,6 @@
vc->vc_disp_ctrl = 1;
vc->vc_toggle_meta = 1;
break;
- case 21:
case 22:
vc->vc_intensity = 1;
break;
@@ -1727,7 +1731,7 @@
default_attr(vc);
update_attr(vc);
- vc->vc_tab_stop[0] = 0x01010100;
+ vc->vc_tab_stop[0] =
vc->vc_tab_stop[1] =
vc->vc_tab_stop[2] =
vc->vc_tab_stop[3] =
@@ -1771,7 +1775,7 @@
vc->vc_pos -= (vc->vc_x << 1);
while (vc->vc_x < vc->vc_cols - 1) {
vc->vc_x++;
- if (vc->vc_tab_stop[vc->vc_x >> 5] & (1 << (vc->vc_x & 31)))
+ if (vc->vc_tab_stop[7 & (vc->vc_x >> 5)] & (1 << (vc->vc_x & 31)))
break;
}
vc->vc_pos += (vc->vc_x << 1);
@@ -1831,7 +1835,7 @@
lf(vc);
return;
case 'H':
- vc->vc_tab_stop[vc->vc_x >> 5] |= (1 << (vc->vc_x & 31));
+ vc->vc_tab_stop[7 & (vc->vc_x >> 5)] |= (1 << (vc->vc_x & 31));
return;
case 'Z':
respond_ID(tty);
@@ -2024,7 +2028,7 @@
return;
case 'g':
if (!vc->vc_par[0])
- vc->vc_tab_stop[vc->vc_x >> 5] &= ~(1 << (vc->vc_x & 31));
+ vc->vc_tab_stop[7 & (vc->vc_x >> 5)] &= ~(1 << (vc->vc_x & 31));
else if (vc->vc_par[0] == 3) {
vc->vc_tab_stop[0] =
vc->vc_tab_stop[1] =
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index fba021f..208bc52 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -279,7 +279,7 @@
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map) {
ret = -ENOMEM;
- goto err_map_kobj;
+ goto err_map;
}
kobject_init(&map->kobj, &map_attr_type);
map->mem = mem;
@@ -289,7 +289,7 @@
goto err_map_kobj;
ret = kobject_uevent(&map->kobj, KOBJ_ADD);
if (ret)
- goto err_map;
+ goto err_map_kobj;
}
for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) {
@@ -308,7 +308,7 @@
portio = kzalloc(sizeof(*portio), GFP_KERNEL);
if (!portio) {
ret = -ENOMEM;
- goto err_portio_kobj;
+ goto err_portio;
}
kobject_init(&portio->kobj, &portio_attr_type);
portio->port = port;
@@ -319,7 +319,7 @@
goto err_portio_kobj;
ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
if (ret)
- goto err_portio;
+ goto err_portio_kobj;
}
return 0;
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 6e0d614..64c6af2c 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -839,7 +839,7 @@
{
ci_hdrc_gadget_destroy(ci);
ci_hdrc_host_destroy(ci);
- if (ci->is_otg)
+ if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
ci_hdrc_otg_destroy(ci);
}
@@ -939,27 +939,35 @@
/* initialize role(s) before the interrupt is requested */
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
ret = ci_hdrc_host_init(ci);
- if (ret)
- dev_info(dev, "doesn't support host\n");
+ if (ret) {
+ if (ret == -ENXIO)
+ dev_info(dev, "doesn't support host\n");
+ else
+ goto deinit_phy;
+ }
}
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
ret = ci_hdrc_gadget_init(ci);
- if (ret)
- dev_info(dev, "doesn't support gadget\n");
+ if (ret) {
+ if (ret == -ENXIO)
+ dev_info(dev, "doesn't support gadget\n");
+ else
+ goto deinit_host;
+ }
}
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
dev_err(dev, "no supported roles\n");
ret = -ENODEV;
- goto deinit_phy;
+ goto deinit_gadget;
}
if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
ret = ci_hdrc_otg_init(ci);
if (ret) {
dev_err(dev, "init otg fails, ret = %d\n", ret);
- goto stop;
+ goto deinit_gadget;
}
}
@@ -1024,7 +1032,12 @@
ci_extcon_unregister(ci);
stop:
- ci_role_destroy(ci);
+ if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
+ ci_hdrc_otg_destroy(ci);
+deinit_gadget:
+ ci_hdrc_gadget_destroy(ci);
+deinit_host:
+ ci_hdrc_host_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 34d23cc..fe22ac7 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -174,6 +174,7 @@
wb = &acm->wb[wbn];
if (!wb->use) {
wb->use = 1;
+ wb->len = 0;
return wbn;
}
wbn = (wbn + 1) % ACM_NW;
@@ -731,16 +732,18 @@
static void acm_tty_flush_chars(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- struct acm_wb *cur = acm->putbuffer;
+ struct acm_wb *cur;
int err;
unsigned long flags;
+ spin_lock_irqsave(&acm->write_lock, flags);
+
+ cur = acm->putbuffer;
if (!cur) /* nothing to do */
- return;
+ goto out;
acm->putbuffer = NULL;
err = usb_autopm_get_interface_async(acm->control);
- spin_lock_irqsave(&acm->write_lock, flags);
if (err < 0) {
cur->use = 0;
acm->putbuffer = cur;
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 0dce6ab..876679a 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -186,7 +186,9 @@
static const unsigned short high_speed_maxpacket_maxes[4] = {
[USB_ENDPOINT_XFER_CONTROL] = 64,
[USB_ENDPOINT_XFER_ISOC] = 1024,
- [USB_ENDPOINT_XFER_BULK] = 512,
+
+ /* Bulk should be 512, but some devices use 1024: we will warn below */
+ [USB_ENDPOINT_XFER_BULK] = 1024,
[USB_ENDPOINT_XFER_INT] = 1024,
};
static const unsigned short super_speed_maxpacket_maxes[4] = {
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ee33c0d..5532246 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1495,9 +1495,10 @@
* Some buses would like to keep their devices in suspend
* state after system resume. Their resume happen when
* a remote wakeup is detected or interface driver start
- * I/O.
+ * I/O. And in the case when the system is restoring from
+ * hibernation, make sure all the devices are resumed.
*/
- if (udev->bus->skip_resume)
+ if (udev->bus->skip_resume && msg.event != PM_EVENT_RESTORE)
return 0;
/* For all calls, take the device back to full power and
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 0f10ff2..8df85f6 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -240,8 +240,13 @@
if (!udev->parent)
rc = hcd_bus_suspend(udev, msg);
- /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
- else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
+ /*
+ * Non-root USB2 devices don't need to do anything for FREEZE
+ * or PRETHAW. USB3 devices don't support global suspend and
+ * needs to be selectively suspended.
+ */
+ else if ((msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
+ && (udev->speed < USB_SPEED_SUPER))
rc = 0;
else
rc = usb_port_suspend(udev, msg);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index e4b39a7..6a4ea98 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2430,6 +2430,7 @@
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered) {
+ pm_wakeup_event(&hcd->self.root_hub->dev, 0);
set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
queue_work(pm_wq, &hcd->wakeup_work);
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a9117ee..a9b3bbd 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -659,12 +659,17 @@
unsigned int portnum)
{
struct usb_hub *hub;
+ struct usb_port *port_dev;
if (!hdev)
return;
hub = usb_hub_to_struct_hub(hdev);
if (hub) {
+ port_dev = hub->ports[portnum - 1];
+ if (port_dev && port_dev->child)
+ pm_wakeup_event(&port_dev->child->dev, 0);
+
set_bit(portnum, hub->wakeup_bits);
kick_hub_wq(hub);
}
@@ -3428,8 +3433,11 @@
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
- if (status == 0 && !port_is_suspended(hub, portstatus))
+ if (status == 0 && !port_is_suspended(hub, portstatus)) {
+ if (portchange & USB_PORT_STAT_C_SUSPEND)
+ pm_wakeup_event(&udev->dev, 0);
goto SuspendCleared;
+ }
/* see 7.1.7.7; affects power usage, but not budgeting */
if (hub_is_superspeed(hub->hdev))
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 4f8221e..9016a9b 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -148,6 +148,10 @@
ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+ /* Linger a bit, prior to the next control message. */
+ if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
+ msleep(200);
+
kfree(dr);
return ret;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index c05c4f8..40ce175 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -45,6 +45,9 @@
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
+ /* HP v222w 16GB Mini USB Drive */
+ { USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -225,8 +228,12 @@
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Corsair K70 RGB */
+ { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Corsair Strafe RGB */
- { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT |
+ USB_QUIRK_DELAY_CTRL_MSG },
/* Corsair K70 LUX */
{ USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 2a21a04..0f45a2f 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -209,7 +209,7 @@
unsigned char dir_in;
unsigned char index;
unsigned char mc;
- unsigned char interval;
+ u16 interval;
unsigned int halted:1;
unsigned int periodic:1;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index cfdd5c3..09921ef 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -2642,12 +2642,6 @@
dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
- dwc2_hsotg_enqueue_setup(hsotg);
-
- dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- dwc2_readl(hsotg->regs + DIEPCTL0),
- dwc2_readl(hsotg->regs + DOEPCTL0));
-
/* clear global NAKs */
val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
if (!is_usb_reset)
@@ -2658,6 +2652,12 @@
mdelay(3);
hsotg->lx_state = DWC2_L0;
+
+ dwc2_hsotg_enqueue_setup(hsotg);
+
+ dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
+ dwc2_readl(hsotg->regs + DIEPCTL0),
+ dwc2_readl(hsotg->regs + DOEPCTL0));
}
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index df5a065..0a0cf15 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2268,10 +2268,22 @@
*/
static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
{
- u32 hcfg, hfir, otgctl;
+ u32 hcfg, hfir, otgctl, usbcfg;
dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
+ /* Set HS/FS Timeout Calibration to 7 (max available value).
+ * The number of PHY clocks that the application programs in
+ * this field is added to the high/full speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This can be required, because the delay
+ * introduced by the PHY in generating the linestate condition
+ * can vary from one PHY to another.
+ */
+ usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg |= GUSBCFG_TOUTCAL(7);
+ dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+
/* Restart the Phy Clock */
dwc2_writel(0, hsotg->regs + PCGCTL);
@@ -3237,8 +3249,12 @@
if (count > 250)
dev_err(hsotg->dev,
"Connection id status change timed out\n");
- hsotg->op_state = OTG_STATE_A_HOST;
+ spin_lock_irqsave(&hsotg->lock, flags);
+ dwc2_hsotg_disconnect(hsotg);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ hsotg->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2095579..93d8e14 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -252,7 +252,7 @@
/* core exits U1/U2/U3 only in PHY power state P1/P2/P3 respectively */
if (dwc->revision <= DWC3_REVISION_310A)
- reg |= DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX;
+ reg |= DWC3_GUSB3PIPECTL_UX_EXIT_PX;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
@@ -571,6 +571,12 @@
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
+ * Make sure UX_EXIT_PX is cleared as that causes issues with some
+ * PHYs. Also, this bit is not supposed to be used in normal operation.
+ */
+ reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
+
+ /*
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
* to '0' during coreConsultant configuration. So default value
* will be '0' when the core is reset. Application needs to set it
@@ -904,6 +910,21 @@
}
/*
+ * Workaround for STAR 9001285599 which affects dwc3 core version 3.20a
+ * only. If the PM TIMER ECN is enabled thru GUCTL2[19], then link
+ * compliance test (TD7.21) may fail. If the ECN is not enabled
+ * GUCTL2[19] = 0), the controller will use the old timer value (5us),
+ * which is still fine for Link Compliance test. Hence Do not enable
+ * PM TIMER ECN in V3.20a by setting GUCTL2[19] by default,
+ * instead use GUCTL2[19] = 0.
+ */
+ if (dwc->revision == DWC3_REVISION_320A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg &= ~DWC3_GUCTL2_LC_TIMER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
+ /*
* Enable hardware control of sending remote wakeup in HS when
* the device is in the L1 state.
*/
@@ -913,6 +934,17 @@
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ /*
+ * Enable evicting endpoint cache after flow control for bulk
+ * endpoints for dwc3 core version 3.00a and 3.20a
+ */
+ if (dwc->revision == DWC3_REVISION_300A ||
+ dwc->revision == DWC3_REVISION_320A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg |= DWC3_GUCTL2_ENABLE_EP_CACHE_EVICT;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
return 0;
err2:
@@ -1550,6 +1582,19 @@
return 0;
}
+static int dwc3_pm_restore(struct device *dev)
+{
+ /*
+ * Set the core as runtime active to prevent the runtime
+ * PM ops being called before the PM restore is completed.
+ */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
static int dwc3_resume(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
@@ -1574,7 +1619,12 @@
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dwc3_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+ .suspend = dwc3_suspend,
+ .resume = dwc3_resume,
+ .freeze = dwc3_suspend,
+ .thaw = dwc3_pm_restore,
+ .poweroff = dwc3_suspend,
+ .restore = dwc3_pm_restore,
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
dwc3_runtime_idle)
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4407a83..0963aa3 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -171,13 +171,15 @@
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
-#define DWC3_TXFIFOQ 1
-#define DWC3_RXFIFOQ 3
-#define DWC3_TXREQQ 5
-#define DWC3_RXREQQ 7
-#define DWC3_RXINFOQ 9
-#define DWC3_DESCFETCHQ 13
-#define DWC3_EVENTQ 15
+#define DWC3_TXFIFOQ 0
+#define DWC3_RXFIFOQ 1
+#define DWC3_TXREQQ 2
+#define DWC3_RXREQQ 3
+#define DWC3_RXINFOQ 4
+#define DWC3_PSTATQ 5
+#define DWC3_DESCFETCHQ 6
+#define DWC3_EVENTQ 7
+#define DWC3_AUXEVENTQ 8
/* Global RX Threshold Configuration Register */
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
@@ -220,6 +222,9 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+/* Global User Control 2 Register */
+#define DWC3_GUCTL2_ENABLE_EP_CACHE_EVICT (1 << 12)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
@@ -249,7 +254,7 @@
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
-#define DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX (1 << 27)
+#define DWC3_GUSB3PIPECTL_UX_EXIT_PX (1 << 27)
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
@@ -264,6 +269,8 @@
#define DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE (1 << 0)
/* Global TX Fifo Size Register */
+#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
+#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
@@ -328,6 +335,7 @@
#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
#define DWC3_GUCTL2_HP_TIMER(n) ((n) << 21)
#define DWC3_GUCTL2_HP_TIMER_MASK DWC3_GUCTL2_HP_TIMER(0x1f)
+#define DWC3_GUCTL2_LC_TIMER (1 << 19)
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index 7266470..12ee23f 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -107,6 +107,10 @@
return PTR_ERR(kdwc->usbss);
kdwc->clk = devm_clk_get(kdwc->dev, "usb");
+ if (IS_ERR(kdwc->clk)) {
+ dev_err(kdwc->dev, "unable to get usb clock\n");
+ return PTR_ERR(kdwc->clk);
+ }
error = clk_prepare_enable(kdwc->clk);
if (error < 0) {
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 989956d..bf1baf6 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -73,6 +73,8 @@
/* XHCI registers */
#define USB3_HCSPARAMS1 (0x4)
+#define USB3_HCCPARAMS2 (0x1c)
+#define HCC_CTC(p) ((p) & (1 << 3))
#define USB3_PORTSC (0x420)
/**
@@ -255,6 +257,7 @@
struct notifier_block host_restart_nb;
struct notifier_block host_nb;
+ bool xhci_ss_compliance_enable;
atomic_t in_p3;
unsigned int lpm_to_suspend_delay;
@@ -2093,6 +2096,19 @@
dwc3_core_init(dwc);
/* Re-configure event buffers */
dwc3_event_buffers_setup(dwc);
+
+ /* Get initial P3 status and enable IN_P3 event */
+ val = dwc3_msm_read_reg_field(mdwc->base,
+ DWC3_GDBGLTSSM, DWC3_GDBGLTSSM_LINKSTATE_MASK);
+ atomic_set(&mdwc->in_p3, val == DWC3_LINK_STATE_U3);
+ dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG,
+ PWR_EVNT_POWERDOWN_IN_P3_MASK, 1);
+
+ if (mdwc->otg_state == OTG_STATE_A_HOST) {
+ dev_dbg(mdwc->dev, "%s: set the core in host mode\n",
+ __func__);
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+ }
}
static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
@@ -2246,7 +2262,7 @@
}
}
-static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
+static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation)
{
int ret;
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
@@ -2363,8 +2379,8 @@
clk_disable_unprepare(mdwc->xo_clk);
/* Perform controller power collapse */
- if (!mdwc->in_host_mode && (!mdwc->in_device_mode ||
- mdwc->in_restart)) {
+ if ((!mdwc->in_host_mode && (!mdwc->in_device_mode ||
+ mdwc->in_restart)) || hibernation) {
mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE;
dev_dbg(mdwc->dev, "%s: power collapse\n", __func__);
dwc3_msm_config_gdsc(mdwc, 0);
@@ -2520,8 +2536,6 @@
/* Recover from controller power collapse */
if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
- u32 tmp;
-
if (mdwc->iommu_map) {
ret = arm_iommu_attach_device(mdwc->dev,
mdwc->iommu_map);
@@ -2536,13 +2550,6 @@
dwc3_msm_power_collapse_por(mdwc);
- /* Get initial P3 status and enable IN_P3 event */
- tmp = dwc3_msm_read_reg_field(mdwc->base,
- DWC3_GDBGLTSSM, DWC3_GDBGLTSSM_LINKSTATE_MASK);
- atomic_set(&mdwc->in_p3, tmp == DWC3_LINK_STATE_U3);
- dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG,
- PWR_EVNT_POWERDOWN_IN_P3_MASK, 1);
-
mdwc->lpm_flags &= ~MDWC3_POWER_COLLAPSE;
}
@@ -2821,8 +2828,11 @@
int ret;
mdwc->dwc3_gdsc = devm_regulator_get(mdwc->dev, "USB3_GDSC");
- if (IS_ERR(mdwc->dwc3_gdsc))
+ if (IS_ERR(mdwc->dwc3_gdsc)) {
+ if (PTR_ERR(mdwc->dwc3_gdsc) == -EPROBE_DEFER)
+ return PTR_ERR(mdwc->dwc3_gdsc);
mdwc->dwc3_gdsc = NULL;
+ }
mdwc->xo_clk = devm_clk_get(mdwc->dev, "xo");
if (IS_ERR(mdwc->xo_clk)) {
@@ -3279,6 +3289,35 @@
static DEVICE_ATTR_RW(usb_compliance_mode);
+static ssize_t xhci_link_compliance_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
+ if (mdwc->xhci_ss_compliance_enable)
+ return snprintf(buf, PAGE_SIZE, "y\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "n\n");
+}
+
+static ssize_t xhci_link_compliance_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ bool value;
+ int ret;
+
+ ret = strtobool(buf, &value);
+ if (!ret) {
+ mdwc->xhci_ss_compliance_enable = value;
+ return count;
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(xhci_link_compliance);
+
static int dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node, *dwc3_node;
@@ -3593,6 +3632,8 @@
mutex_init(&mdwc->suspend_resume_mutex);
/* Mark type-C as true by default */
mdwc->type_c = true;
+ if (of_property_read_bool(node, "qcom,connector-type-uAB"))
+ mdwc->type_c = false;
mdwc->usb_psy = power_supply_get_by_name("usb");
if (!mdwc->usb_psy) {
@@ -3634,6 +3675,7 @@
device_create_file(&pdev->dev, &dev_attr_mode);
device_create_file(&pdev->dev, &dev_attr_speed);
device_create_file(&pdev->dev, &dev_attr_usb_compliance_mode);
+ device_create_file(&pdev->dev, &dev_attr_xhci_link_compliance);
host_mode = usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST;
if (!dwc->is_drd && host_mode) {
@@ -3672,6 +3714,7 @@
int ret_pm;
device_remove_file(&pdev->dev, &dev_attr_mode);
+ device_remove_file(&pdev->dev, &dev_attr_xhci_link_compliance);
if (mdwc->usb_psy)
power_supply_put(mdwc->usb_psy);
@@ -3938,6 +3981,25 @@
}
/*
+ * If the Compliance Transition Capability(CTC) flag of
+ * HCCPARAMS2 register is set and xhci_link_compliance sysfs
+ * param has been enabled by the user for the SuperSpeed host
+ * controller, then write 10 (Link in Compliance Mode State)
+ * onto the Port Link State(PLS) field of the PORTSC register
+ * for 3.0 host controller which is at an offset of USB3_PORTSC
+ * + 0x10 from the DWC3 base address. Also, disable the runtime
+ * PM of 3.0 root hub (root hub of shared_hcd of xhci device)
+ */
+ if (HCC_CTC(dwc3_msm_read_reg(mdwc->base, USB3_HCCPARAMS2))
+ && mdwc->xhci_ss_compliance_enable
+ && dwc->maximum_speed == USB_SPEED_SUPER) {
+ dwc3_msm_write_reg(mdwc->base, USB3_PORTSC + 0x10,
+ 0x10340);
+ pm_runtime_disable(&hcd_to_xhci(platform_get_drvdata(
+ dwc->xhci))->shared_hcd->self.root_hub->dev);
+ }
+
+ /*
* In some cases it is observed that USB PHY is not going into
* suspend with host mode suspend functionality. Hence disable
* XHCI's runtime PM here if disable_host_mode_pm is set.
@@ -4399,7 +4461,39 @@
return -EBUSY;
}
- ret = dwc3_msm_suspend(mdwc);
+ ret = dwc3_msm_suspend(mdwc, false);
+ if (!ret)
+ atomic_set(&mdwc->pm_suspended, 1);
+
+ return ret;
+}
+
+static int dwc3_msm_pm_freeze(struct device *dev)
+{
+ int ret = 0;
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+
+ dev_dbg(dev, "dwc3-msm PM freeze\n");
+ dbg_event(0xFF, "PM Freeze", 0);
+
+ flush_workqueue(mdwc->dwc3_wq);
+
+ /* Resume the core to make sure we can power collapse it */
+ ret = dwc3_msm_resume(mdwc);
+
+ /*
+ * PHYs also needed to be power collapsed, so call the notify_disconnect
+ * before suspend to ensure it.
+ */
+ usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
+ mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
+ if (mdwc->ss_phy) {
+ usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER);
+ mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
+ }
+
+ ret = dwc3_msm_suspend(mdwc, true);
if (!ret)
atomic_set(&mdwc->pm_suspended, 1);
@@ -4423,6 +4517,35 @@
return 0;
}
+
+static int dwc3_msm_pm_restore(struct device *dev)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+
+ dev_dbg(dev, "dwc3-msm PM restore\n");
+ dbg_event(0xFF, "PM Restore", 0);
+
+ atomic_set(&mdwc->pm_suspended, 0);
+
+ dwc3_msm_resume(mdwc);
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /* Restore PHY flags if hibernated in host mode */
+ if (mdwc->otg_state == OTG_STATE_A_HOST) {
+ usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
+ mdwc->hs_phy->flags |= PHY_HOST_MODE;
+ if (mdwc->ss_phy) {
+ usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER);
+ mdwc->ss_phy->flags |= PHY_HOST_MODE;
+ }
+ }
+
+
+ return 0;
+}
#endif
#ifdef CONFIG_PM
@@ -4445,7 +4568,7 @@
dev_dbg(dev, "DWC3-msm runtime suspend\n");
dbg_event(0xFF, "RT Sus", 0);
- return dwc3_msm_suspend(mdwc);
+ return dwc3_msm_suspend(mdwc, false);
}
static int dwc3_msm_runtime_resume(struct device *dev)
@@ -4461,7 +4584,12 @@
#endif
static const struct dev_pm_ops dwc3_msm_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_msm_pm_suspend, dwc3_msm_pm_resume)
+ .suspend = dwc3_msm_pm_suspend,
+ .resume = dwc3_msm_pm_resume,
+ .freeze = dwc3_msm_pm_freeze,
+ .restore = dwc3_msm_pm_restore,
+ .thaw = dwc3_msm_pm_restore,
+ .poweroff = dwc3_msm_pm_suspend,
SET_RUNTIME_PM_OPS(dwc3_msm_runtime_suspend, dwc3_msm_runtime_resume,
dwc3_msm_runtime_idle)
};
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 35b6351..f221cb4 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -598,9 +598,25 @@
return 0;
}
+static void dwc3_omap_complete(struct device *dev)
+{
+ struct dwc3_omap *omap = dev_get_drvdata(dev);
+
+ if (extcon_get_state(omap->edev, EXTCON_USB))
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
+ else
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_OFF);
+
+ if (extcon_get_state(omap->edev, EXTCON_USB_HOST))
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
+ else
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_FLOAT);
+}
+
static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume)
+ .complete = dwc3_omap_complete,
};
#define DEV_PM_OPS (&dwc3_omap_dev_pm_ops)
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 427291a..d6493ab 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -173,7 +173,7 @@
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
- return ret;
+ goto err;
}
dwc3->dev.parent = dev;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 902d36e..d9baac6 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -43,6 +43,8 @@
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
+static int dwc3_ep0_delegate_req(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl);
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
@@ -358,12 +360,14 @@
static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req)
{
}
+
/*
* ch 9.4.5
*/
static int dwc3_ep0_handle_status(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl)
{
+ int ret;
struct dwc3_ep *dep;
u32 recip;
u32 reg;
@@ -397,7 +401,8 @@
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
- break;
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ return ret;
case USB_RECIP_ENDPOINT:
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
@@ -524,6 +529,9 @@
if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
/* XXX enable remote wakeup */
;
+ ret = dwc3_ep0_delegate_req(dwc, ctrl);
+ if (ret)
+ return ret;
break;
default:
return -EINVAL;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 243c078..f878b8d1 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -414,7 +414,7 @@
dwc3_trace(trace_dwc3_gadget, "Command Timed Out");
dev_err(dwc->dev, "%s command timeout for %s\n",
dwc3_gadget_ep_cmd_string(cmd), dep->name);
- if (cmd != DWC3_DEPCMD_ENDTRANSFER) {
+ if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_ENDTRANSFER) {
dwc->ep_cmd_timeout_cnt++;
dwc3_notify_event(dwc,
DWC3_CONTROLLER_RESTART_USB_SESSION, 0);
@@ -1307,7 +1307,7 @@
if (req->request.status == -EINPROGRESS) {
ret = -EBUSY;
- dev_err_ratelimited(dwc->dev, "%s: %p request already in queue",
+ dev_err_ratelimited(dwc->dev, "%s: %pK request already in queue",
dep->name, req);
return ret;
}
@@ -3263,6 +3263,8 @@
break;
}
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+
/* Enable USB2 LPM Capability */
if ((dwc->revision > DWC3_REVISION_194A) &&
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d3e0ca5..90cbb61 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -568,6 +568,7 @@
config USB_CONFIGFS_F_GSI
bool "USB GSI function"
select USB_F_GSI
+ select USB_U_ETHER
depends on USB_CONFIGFS
help
Generic function driver to support h/w acceleration to IPA over GSI.
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index d4c243c..42936f1 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -70,18 +70,11 @@
struct usb_phy *phy = udc->transceiver;
if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP)) {
- u32 temp;
-
usb_phy_io_write(phy,
ULPI_MISC_A_VBUSVLDEXT |
ULPI_MISC_A_VBUSVLDEXTSEL,
ULPI_CLR(ULPI_MISC_A));
- /* Notify LINK of VBUS LOW */
- temp = readl_relaxed(USB_USBCMD);
- temp &= ~USBCMD_SESS_VLD_CTRL;
- writel_relaxed(temp, USB_USBCMD);
-
/*
* Add memory barrier as it is must to complete
* above USB PHY and Link register writes before
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 939c219..703fb24 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -3951,7 +3951,7 @@
usb_del_gadget_udc(&udc->gadget);
remove_trans:
if (udc->transceiver)
- otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
+ otg_set_peripheral(udc->transceiver->otg, NULL);
err("error = %i", retval);
put_transceiver:
@@ -3989,7 +3989,7 @@
usb_del_gadget_udc(&udc->gadget);
if (udc->transceiver) {
- otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
+ otg_set_peripheral(udc->transceiver->otg, NULL);
usb_put_phy(udc->transceiver);
}
destroy_eps(udc);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index ab2c623..3de95d5 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1563,7 +1563,7 @@
return res;
}
-static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
+static int fill_ext_compat(struct usb_configuration *c, u8 *buf)
{
int i, count;
@@ -1590,10 +1590,12 @@
buf += 23;
}
count += 24;
- if (count >= 4096)
- return;
+ if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
}
}
+
+ return count;
}
static int count_ext_prop(struct usb_configuration *c, int interface)
@@ -1638,25 +1640,20 @@
struct usb_os_desc *d;
struct usb_os_desc_ext_prop *ext_prop;
int j, count, n, ret;
- u8 *start = buf;
f = c->interface[interface];
+ count = 10; /* header length */
for (j = 0; j < f->os_desc_n; ++j) {
if (interface != f->os_desc_table[j].if_id)
continue;
d = f->os_desc_table[j].os_desc;
if (d)
list_for_each_entry(ext_prop, &d->ext_prop, entry) {
- /* 4kB minus header length */
- n = buf - start;
- if (n >= 4086)
- return 0;
-
- count = ext_prop->data_len +
+ n = ext_prop->data_len +
ext_prop->name_len + 14;
- if (count > 4086 - n)
- return -EINVAL;
- usb_ext_prop_put_size(buf, count);
+ if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
+ usb_ext_prop_put_size(buf, n);
usb_ext_prop_put_type(buf, ext_prop->type);
ret = usb_ext_prop_put_name(buf, ext_prop->name,
ext_prop->name_len);
@@ -1682,11 +1679,12 @@
default:
return -EINVAL;
}
- buf += count;
+ buf += n;
+ count += n;
}
}
- return 0;
+ return count;
}
/*
@@ -1980,6 +1978,7 @@
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
+ w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
memset(buf, 0, w_length);
buf[5] = 0x01;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
@@ -2003,8 +2002,8 @@
count += 16; /* header */
put_unaligned_le32(count, buf);
buf += 16;
- fill_ext_compat(os_desc_cfg, buf);
- value = w_length;
+ value = fill_ext_compat(os_desc_cfg, buf);
+ value = min_t(u16, w_length, value);
}
break;
case USB_RECIP_INTERFACE:
@@ -2033,8 +2032,7 @@
interface, buf);
if (value < 0)
return value;
-
- value = w_length;
+ value = min_t(u16, w_length, value);
}
break;
}
@@ -2336,8 +2334,8 @@
goto end;
}
- /* OS feature descriptor length <= 4kB */
- cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
+ cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ,
+ GFP_KERNEL);
if (!cdev->os_desc_req->buf) {
ret = -ENOMEM;
usb_ep_free_request(ep0, cdev->os_desc_req);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index f779fdc30..14c18f3 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1257,9 +1257,9 @@
cfg = container_of(c, struct config_usb_cfg, c);
- list_for_each_entry_safe(f, tmp, &c->functions, list) {
+ list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) {
- list_move_tail(&f->list, &cfg->func_list);
+ list_move(&f->list, &cfg->func_list);
if (f->unbind) {
dev_dbg(&gi->cdev.gadget->dev,
"unbind function '%s'/%pK\n",
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index a9344a5..b8fa93e 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -64,7 +64,7 @@
obj-$(CONFIG_USB_F_GSI) += usb_f_gsi.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
-usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o
+usb_f_qcrndis-y := f_qc_rndis.o
obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o
-usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o u_bam_dmux.o
+usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o u_bam_dmux.o u_data_ipa.o
obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o
diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c
index 4d4f039..a7166e2 100644
--- a/drivers/usb/gadget/function/f_cdev.c
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -1256,6 +1256,7 @@
ret = -EFAULT;
} else {
req->length = xfer_size;
+ req->zero = 1;
ret = usb_ep_queue(in, req, GFP_KERNEL);
if (ret) {
pr_err("EP QUEUE failed:%d\n", ret);
diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c
index f08e443..b1bccd0 100644
--- a/drivers/usb/gadget/function/f_diag.c
+++ b/drivers/usb/gadget/function/f_diag.c
@@ -2,7 +2,7 @@
* Diag Function Device - Route ARM9 and ARM11 DIAG messages
* between HOST and DEVICE.
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -492,6 +492,7 @@
}
EXPORT_SYMBOL(usb_diag_alloc_req);
#define DWC3_MAX_REQUEST_SIZE (16 * 1024 * 1024)
+#define CI_MAX_REQUEST_SIZE (16 * 1024)
/**
* usb_diag_request_size - Max request size for controller
* @ch: Channel handler
@@ -501,6 +502,12 @@
*/
int usb_diag_request_size(struct usb_diag_ch *ch)
{
+ struct diag_context *ctxt = ch->priv_usb;
+ struct usb_composite_dev *cdev = ctxt->cdev;
+
+ if (cdev->gadget->is_chipidea)
+ return CI_MAX_REQUEST_SIZE;
+
return DWC3_MAX_REQUEST_SIZE;
}
EXPORT_SYMBOL(usb_diag_request_size);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 7897f68..b9c19d4 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1097,7 +1097,7 @@
spin_unlock_irq(&epfile->ffs->eps_lock);
extra_buf_alloc = ffs->gadget->extra_buf_alloc;
- if (io_data->read)
+ if (!io_data->read)
data = kmalloc(data_len + extra_buf_alloc,
GFP_KERNEL);
else
@@ -3415,8 +3415,8 @@
struct ffs_data *ffs = func->ffs;
const int full = !!func->ffs->fs_descs_count;
- const int high = func->ffs->hs_descs_count;
- const int super = func->ffs->ss_descs_count;
+ const int high = !!func->ffs->hs_descs_count;
+ const int super = !!func->ffs->ss_descs_count;
int fs_len, hs_len, ss_len, ret, i;
struct ffs_ep *eps_ptr;
@@ -3734,7 +3734,7 @@
ffs_log("exit");
- return 0;
+ return USB_GADGET_DELAYED_STATUS;
}
static bool ffs_func_req_match(struct usb_function *f,
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 5ffbf12..7170dc9 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -14,12 +14,18 @@
#include "f_gsi.h"
#include "rndis.h"
+struct usb_gsi_debugfs {
+ struct dentry *debugfs_root;
+};
+
+static struct usb_gsi_debugfs debugfs;
+
static bool qti_packet_debug;
module_param(qti_packet_debug, bool, 0644);
MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
static struct workqueue_struct *ipa_usb_wq;
-static struct f_gsi *__gsi[IPA_USB_MAX_TETH_PROT_SIZE];
+static struct f_gsi *__gsi[USB_PROT_MAX];
static void *ipc_log_ctxt;
#define NUM_LOG_PAGES 15
@@ -56,6 +62,15 @@
static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned int len, gfp_t flags);
static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);
+static inline bool is_ext_prot_ether(int prot_id)
+{
+ if (prot_id == USB_PROT_RMNET_ETHER ||
+ prot_id == USB_PROT_DPL_ETHER)
+ return true;
+
+ return false;
+}
+
static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
{
bool remote_wakeup_allowed;
@@ -196,6 +211,223 @@
return ret;
}
+static void debugfs_rw_timer_func(unsigned long arg)
+{
+ struct f_gsi *gsi;
+
+ gsi = (struct f_gsi *)arg;
+
+ if (!atomic_read(&gsi->connected)) {
+ log_event_dbg("%s: gsi not connected..del timer\n", __func__);
+ gsi->debugfs_rw_enable = 0;
+ del_timer(&gsi->debugfs_rw_timer);
+ return;
+ }
+
+ log_event_dbg("%s: calling gsi_wakeup_host\n", __func__);
+ gsi_wakeup_host(gsi);
+
+ if (gsi->debugfs_rw_enable) {
+ log_event_dbg("%s: re-arm the timer\n", __func__);
+ mod_timer(&gsi->debugfs_rw_timer,
+ jiffies + msecs_to_jiffies(gsi->debugfs_rw_interval));
+ }
+}
+
+static struct f_gsi *get_connected_gsi(void)
+{
+ struct f_gsi *connected_gsi;
+ bool gsi_connected = false;
+ unsigned int i;
+
+ for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++) {
+ connected_gsi = __gsi[i];
+ if (connected_gsi && atomic_read(&connected_gsi->connected)) {
+ gsi_connected = true;
+ break;
+ }
+ }
+
+ if (!gsi_connected)
+ connected_gsi = NULL;
+
+ return connected_gsi;
+}
+
+#define DEFAULT_RW_TIMER_INTERVAL 500 /* in ms */
+static ssize_t usb_gsi_rw_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct f_gsi *gsi;
+ u8 input;
+ int ret;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ goto err;
+ }
+
+ if (ubuf == NULL) {
+ log_event_dbg("%s: buffer is Null.\n", __func__);
+ goto err;
+ }
+
+ ret = kstrtou8_from_user(ubuf, count, 0, &input);
+ if (ret) {
+ log_event_err("%s: Invalid value. err:%d\n", __func__, ret);
+ goto err;
+ }
+
+ if (gsi->debugfs_rw_enable == !!input) {
+ if (!!input)
+ log_event_dbg("%s: RW already enabled\n", __func__);
+ else
+ log_event_dbg("%s: RW already disabled\n", __func__);
+ goto err;
+ }
+
+ gsi->debugfs_rw_enable = !!input;
+ if (gsi->debugfs_rw_enable) {
+ init_timer(&gsi->debugfs_rw_timer);
+ gsi->debugfs_rw_timer.data = (unsigned long) gsi;
+ gsi->debugfs_rw_timer.function = debugfs_rw_timer_func;
+
+ /* Use default remote wakeup timer interval if it is not set */
+ if (!gsi->debugfs_rw_interval)
+ gsi->debugfs_rw_interval = DEFAULT_RW_TIMER_INTERVAL;
+ gsi->debugfs_rw_timer.expires = jiffies +
+ msecs_to_jiffies(gsi->debugfs_rw_interval);
+ add_timer(&gsi->debugfs_rw_timer);
+ log_event_dbg("%s: timer initialized\n", __func__);
+ } else {
+ del_timer_sync(&gsi->debugfs_rw_timer);
+ log_event_dbg("%s: timer deleted\n", __func__);
+ }
+
+err:
+ return count;
+}
+
+static int usb_gsi_rw_show(struct seq_file *s, void *unused)
+{
+
+ struct f_gsi *gsi;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ return 0;
+ }
+
+ seq_printf(s, "%d\n", gsi->debugfs_rw_enable);
+
+ return 0;
+}
+
+static int usb_gsi_rw_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, usb_gsi_rw_show, inode->i_private);
+}
+
+static const struct file_operations fops_usb_gsi_rw = {
+ .open = usb_gsi_rw_open,
+ .read = seq_read,
+ .write = usb_gsi_rw_write,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static ssize_t usb_gsi_rw_timer_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct f_gsi *gsi;
+ u16 timer_val;
+ int ret;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ goto err;
+ }
+
+ if (ubuf == NULL) {
+ log_event_dbg("%s: buffer is NULL.\n", __func__);
+ goto err;
+ }
+
+ ret = kstrtou16_from_user(ubuf, count, 0, &timer_val);
+ if (ret) {
+ log_event_err("%s: Invalid value. err:%d\n", __func__, ret);
+ goto err;
+ }
+
+ if (timer_val <= 0 || timer_val > 10000) {
+ log_event_err("%s: value must be > 0 and < 10000.\n", __func__);
+ goto err;
+ }
+
+ gsi->debugfs_rw_interval = timer_val;
+err:
+ return count;
+}
+
+static int usb_gsi_rw_timer_show(struct seq_file *s, void *unused)
+{
+ struct f_gsi *gsi;
+ unsigned int timer_interval;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ return 0;
+ }
+
+ timer_interval = DEFAULT_RW_TIMER_INTERVAL;
+ if (gsi->debugfs_rw_interval)
+ timer_interval = gsi->debugfs_rw_interval;
+
+ seq_printf(s, "%ums\n", timer_interval);
+
+ return 0;
+}
+
+static int usb_gsi_rw_timer_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, usb_gsi_rw_timer_show, inode->i_private);
+}
+
+static const struct file_operations fops_usb_gsi_rw_timer = {
+ .open = usb_gsi_rw_timer_open,
+ .read = seq_read,
+ .write = usb_gsi_rw_timer_write,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int usb_gsi_debugfs_init(void)
+{
+ debugfs.debugfs_root = debugfs_create_dir("usb_gsi", NULL);
+ if (!debugfs.debugfs_root)
+ return -ENOMEM;
+
+ debugfs_create_file("remote_wakeup_enable", 0600,
+ debugfs.debugfs_root,
+ __gsi, &fops_usb_gsi_rw);
+ debugfs_create_file("remote_wakeup_interval", 0600,
+ debugfs.debugfs_root,
+ __gsi,
+ &fops_usb_gsi_rw_timer);
+ return 0;
+}
+
+static void usb_gsi_debugfs_exit(void)
+{
+ debugfs_remove_recursive(debugfs.debugfs_root);
+}
+
/*
* Callback for when when network interface is up
* and userspace is ready to answer DHCP requests, or remote wakeup
@@ -226,7 +458,7 @@
log_event_err("%s: Set net_ready_trigger", __func__);
gsi->d_port.net_ready_trigger = true;
- if (gsi->prot_id == IPA_USB_ECM) {
+ if (gsi->prot_id == USB_PROT_ECM_IPA) {
cpkt_notify_connect = gsi_ctrl_pkt_alloc(0, GFP_ATOMIC);
if (IS_ERR(cpkt_notify_connect)) {
spin_unlock_irqrestore(&gsi->d_port.lock,
@@ -260,7 +492,7 @@
* Do not post EVT_CONNECTED for RNDIS.
* Data path for RNDIS is enabled on EVT_HOST_READY.
*/
- if (gsi->prot_id != IPA_USB_RNDIS) {
+ if (gsi->prot_id != USB_PROT_RNDIS_IPA) {
post_event(&gsi->d_port, EVT_CONNECTED);
queue_work(gsi->d_port.ipa_usb_wq,
&gsi->d_port.usb_ipa_w);
@@ -311,7 +543,7 @@
log_event_dbg("%s: USB GSI IN OPS Completed", __func__);
in_params->client =
- (gsi->prot_id != IPA_USB_DIAG) ? IPA_CLIENT_USB_CONS :
+ (gsi->prot_id != USB_PROT_DIAG_IPA) ? IPA_CLIENT_USB_CONS :
IPA_CLIENT_USB_DPL_CONS;
in_params->ipa_ep_cfg.mode.mode = IPA_BASIC;
in_params->teth_prot = gsi->prot_id;
@@ -395,7 +627,7 @@
conn_params->usb_to_ipa_xferrscidx =
d_port->out_xfer_rsc_index;
conn_params->usb_to_ipa_xferrscidx_valid =
- (gsi->prot_id != IPA_USB_DIAG) ? true : false;
+ (gsi->prot_id != USB_PROT_DIAG_IPA) ? true : false;
conn_params->ipa_to_usb_xferrscidx_valid = true;
conn_params->teth_prot = gsi->prot_id;
conn_params->teth_prot_params.max_xfer_size_bytes_to_dev = 23700;
@@ -440,7 +672,7 @@
d_port->in_request.db_reg_phs_addr_msb =
ipa_in_channel_out_params.db_reg_phs_addr_msb;
- if (gsi->prot_id != IPA_USB_DIAG) {
+ if (gsi->prot_id != USB_PROT_DIAG_IPA) {
d_port->out_channel_handle =
ipa_out_channel_out_params.clnt_hdl;
d_port->out_request.db_reg_phs_addr_lsb =
@@ -690,6 +922,11 @@
log_event_dbg("%s: ST_CON_IN_PROG_EVT_HOST_READY",
__func__);
} else if (event == EVT_CONNECTED) {
+ if (peek_event(d_port) == EVT_SUSPEND) {
+ log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUSPEND",
+ __func__);
+ break;
+ }
ipa_data_path_enable(d_port);
d_port->sm_state = STATE_CONNECTED;
log_event_dbg("%s: ST_CON_IN_PROG_EVT_CON %d",
@@ -1159,7 +1396,8 @@
switch (cmd) {
case QTI_CTRL_MODEM_OFFLINE:
- if (gsi->prot_id == IPA_USB_DIAG) {
+ if (gsi->prot_id == USB_PROT_DIAG_IPA ||
+ gsi->prot_id == USB_PROT_DPL_ETHER) {
log_event_dbg("%s:Modem Offline not handled", __func__);
goto exit_ioctl;
}
@@ -1177,7 +1415,8 @@
gsi_ctrl_send_notification(gsi);
break;
case QTI_CTRL_MODEM_ONLINE:
- if (gsi->prot_id == IPA_USB_DIAG) {
+ if (gsi->prot_id == USB_PROT_DIAG_IPA ||
+ gsi->prot_id == USB_PROT_DPL_ETHER) {
log_event_dbg("%s:Modem Online not handled", __func__);
goto exit_ioctl;
}
@@ -1186,7 +1425,8 @@
break;
case QTI_CTRL_GET_LINE_STATE:
val = atomic_read(&gsi->connected);
- if (gsi->prot_id == IPA_USB_RMNET)
+ if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_ETHER)
val = gsi->rmnet_dtr_status;
ret = copy_to_user((void __user *)arg, &val, sizeof(val));
@@ -1207,32 +1447,43 @@
break;
}
- if (gsi->prot_id == IPA_USB_DIAG &&
+ if ((gsi->prot_id == USB_PROT_DIAG_IPA ||
+ gsi->prot_id == USB_PROT_DPL_ETHER) &&
(gsi->d_port.in_channel_handle == -EINVAL)) {
ret = -EAGAIN;
break;
}
- if (gsi->d_port.in_channel_handle == -EINVAL &&
- gsi->d_port.out_channel_handle == -EINVAL) {
- ret = -EAGAIN;
- break;
+ if (gsi->prot_id != USB_PROT_GPS_CTRL) {
+ if (gsi->d_port.in_channel_handle == -EINVAL &&
+ gsi->d_port.out_channel_handle == -EINVAL) {
+ ret = -EAGAIN;
+ break;
+ }
+ info.ph_ep_info.ep_type = GSI_MBIM_DATA_EP_TYPE_HSUSB;
+ info.ph_ep_info.peripheral_iface_id = gsi->data_id;
+ } else {
+ info.ph_ep_info.ep_type = GSI_MBIM_DATA_EP_TYPE_HSUSB;
+ info.ph_ep_info.peripheral_iface_id = gsi->ctrl_id;
}
- info.ph_ep_info.ep_type = GSI_MBIM_DATA_EP_TYPE_HSUSB;
- info.ph_ep_info.peripheral_iface_id = gsi->data_id;
- info.ipa_ep_pair.cons_pipe_num =
- (gsi->prot_id == IPA_USB_DIAG) ? -1 :
- gsi->d_port.out_channel_handle;
- info.ipa_ep_pair.prod_pipe_num = gsi->d_port.in_channel_handle;
-
log_event_dbg("%s: prot id :%d ep_type:%d intf:%d",
__func__, gsi->prot_id, info.ph_ep_info.ep_type,
info.ph_ep_info.peripheral_iface_id);
+ if (gsi->prot_id != USB_PROT_GPS_CTRL) {
+ info.ipa_ep_pair.cons_pipe_num =
+ (gsi->prot_id == USB_PROT_DIAG_IPA ||
+ gsi->prot_id == USB_PROT_DPL_ETHER) ? -1 :
+ gsi->d_port.out_channel_handle;
+ info.ipa_ep_pair.prod_pipe_num =
+ gsi->d_port.in_channel_handle;
- log_event_dbg("%s: ipa_cons_idx:%d ipa_prod_idx:%d",
- __func__, info.ipa_ep_pair.cons_pipe_num,
- info.ipa_ep_pair.prod_pipe_num);
+
+ log_event_dbg("%s: ipa_cons_idx:%d ipa_prod_idx:%d",
+ __func__,
+ info.ipa_ep_pair.cons_pipe_num,
+ info.ipa_ep_pair.prod_pipe_num);
+ }
ret = copy_to_user((void __user *)arg, &info,
sizeof(info));
@@ -1328,8 +1579,8 @@
static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
{
int ret;
+ char *cdev_name = NULL;
int sz = GSI_CTRL_NAME_LEN;
- bool ctrl_dev_create = true;
INIT_LIST_HEAD(&gsi->c_port.cpkt_req_q);
INIT_LIST_HEAD(&gsi->c_port.cpkt_resp_q);
@@ -1338,17 +1589,33 @@
init_waitqueue_head(&gsi->c_port.read_wq);
- if (gsi->prot_id == IPA_USB_RMNET)
- strlcat(gsi->c_port.name, GSI_RMNET_CTRL_NAME, sz);
- else if (gsi->prot_id == IPA_USB_MBIM)
- strlcat(gsi->c_port.name, GSI_MBIM_CTRL_NAME, sz);
- else if (gsi->prot_id == IPA_USB_DIAG)
- strlcat(gsi->c_port.name, GSI_DPL_CTRL_NAME, sz);
- else
- ctrl_dev_create = false;
+ switch (gsi->prot_id) {
+ case USB_PROT_RMNET_IPA:
+ cdev_name = GSI_RMNET_CTRL_NAME;
+ break;
+ case USB_PROT_RMNET_ETHER:
+ cdev_name = ETHER_RMNET_CTRL_NAME;
+ break;
+ case USB_PROT_MBIM_IPA:
+ cdev_name = GSI_MBIM_CTRL_NAME;
+ break;
+ case USB_PROT_DIAG_IPA:
+ cdev_name = GSI_DPL_CTRL_NAME;
+ break;
+ case USB_PROT_DPL_ETHER:
+ cdev_name = ETHER_DPL_CTRL_NAME;
+ break;
+ case USB_PROT_GPS_CTRL:
+ cdev_name = GSI_GPS_CTRL_NAME;
+ break;
+ default:
+ break;
+ }
- if (!ctrl_dev_create)
+ if (!cdev_name)
return 0;
+ else
+ strlcat(gsi->c_port.name, cdev_name, sz);
gsi->c_port.ctrl_device.name = gsi->c_port.name;
gsi->c_port.ctrl_device.fops = &gsi_ctrl_dev_fops;
@@ -1419,6 +1686,12 @@
} else {
log_event_dbg("%s: posting HOST_READY\n", __func__);
post_event(d_port, EVT_HOST_READY);
+ /*
+ * If host supports flow control with RNDIS_MSG_INIT then
+ * mark the flag to true. This flag will be used further to
+ * enable the flow control on resume path.
+ */
+ gsi->host_supports_flow_control = true;
}
queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
@@ -1512,7 +1785,7 @@
event->wValue = cpu_to_le16(0);
event->wLength = cpu_to_le16(0);
- if (gsi->prot_id == IPA_USB_RNDIS) {
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
data = req->buf;
data[0] = cpu_to_le32(1);
data[1] = cpu_to_le32(0);
@@ -1737,7 +2010,7 @@
/* read the request; process it later */
value = w_length;
req->context = gsi;
- if (gsi->prot_id == IPA_USB_RNDIS)
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA)
req->complete = gsi_rndis_command_complete;
else
req->complete = gsi_ctrl_cmd_complete;
@@ -1749,7 +2022,7 @@
if (w_value || w_index != id)
goto invalid;
- if (gsi->prot_id == IPA_USB_RNDIS) {
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
/* return the result */
buf = rndis_get_next_response(gsi->params, &n);
if (buf) {
@@ -1785,7 +2058,8 @@
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
line_state = (w_value & GSI_CTRL_DTR ? true : false);
- if (gsi->prot_id == IPA_USB_RMNET)
+ if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_ETHER)
gsi->rmnet_dtr_status = line_state;
log_event_dbg("%s: USB_CDC_REQ_SET_CONTROL_LINE_STATE DTR:%d\n",
__func__, line_state);
@@ -1882,9 +2156,10 @@
struct f_gsi *gsi = func_to_gsi(f);
/* RNDIS, RMNET and DPL only support alt 0*/
- if (intf == gsi->ctrl_id || gsi->prot_id == IPA_USB_RNDIS ||
- gsi->prot_id == IPA_USB_RMNET ||
- gsi->prot_id == IPA_USB_DIAG)
+ if (intf == gsi->ctrl_id || gsi->prot_id == USB_PROT_RNDIS_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_DIAG_IPA ||
+ is_ext_prot_ether(gsi->prot_id))
return 0;
else if (intf == gsi->data_id)
return gsi->data_interface_up;
@@ -2003,7 +2278,8 @@
log_event_dbg("intf=%u, alt=%u", intf, alt);
/* Control interface has only altsetting 0 */
- if (intf == gsi->ctrl_id || gsi->prot_id == IPA_USB_RMNET) {
+ if (intf == gsi->ctrl_id || gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_ETHER) {
if (alt != 0)
goto fail;
@@ -2037,10 +2313,11 @@
if (intf == gsi->data_id) {
gsi->d_port.net_ready_trigger = false;
/* for rndis and rmnet alt is always 0 update alt accordingly */
- if (gsi->prot_id == IPA_USB_RNDIS ||
- gsi->prot_id == IPA_USB_RMNET ||
- gsi->prot_id == IPA_USB_DIAG)
- alt = 1;
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_DIAG_IPA ||
+ is_ext_prot_ether(gsi->prot_id))
+ alt = 1;
if (alt > 1)
goto notify_ep_disable;
@@ -2067,8 +2344,9 @@
}
/* Configure EPs for GSI */
- if (gsi->d_port.in_ep) {
- if (gsi->prot_id == IPA_USB_DIAG)
+ if (gsi->d_port.in_ep &&
+ gsi->prot_id <= USB_PROT_DIAG_IPA) {
+ if (gsi->prot_id == USB_PROT_DIAG_IPA)
gsi->d_port.in_ep->ep_intr_num = 3;
else
gsi->d_port.in_ep->ep_intr_num = 2;
@@ -2077,7 +2355,8 @@
GSI_EP_OP_CONFIG);
}
- if (gsi->d_port.out_ep) {
+ if (gsi->d_port.out_ep &&
+ gsi->prot_id <= USB_PROT_DIAG_IPA) {
gsi->d_port.out_ep->ep_intr_num = 1;
usb_gsi_ep_op(gsi->d_port.out_ep,
&gsi->d_port.out_request,
@@ -2086,7 +2365,17 @@
gsi->d_port.gadget = cdev->gadget;
- if (gsi->prot_id == IPA_USB_RNDIS) {
+ if (is_ext_prot_ether(gsi->prot_id)) {
+ net = gether_connect(&gsi->d_port.gether_port);
+ if (IS_ERR(net)) {
+ pr_err("%s:gether_connect err:%ld\n",
+ __func__, PTR_ERR(net));
+ goto notify_ep_disable;
+ }
+ gsi->d_port.gether_port.cdc_filter = 0;
+ }
+
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
gsi_rndis_open(gsi);
net = gsi_rndis_get_netdev("rndis0");
if (IS_ERR(net))
@@ -2098,7 +2387,7 @@
&gsi->d_port.cdc_filter);
}
- if (gsi->prot_id == IPA_USB_ECM)
+ if (gsi->prot_id == USB_PROT_ECM_IPA)
gsi->d_port.cdc_filter = DEFAULT_FILTER;
/*
@@ -2106,7 +2395,7 @@
* handler which is invoked when the host sends the
* GEN_CURRENT_PACKET_FILTER message.
*/
- if (gsi->prot_id != IPA_USB_RNDIS)
+ if (gsi->prot_id != USB_PROT_RNDIS_IPA)
post_event(&gsi->d_port,
EVT_CONNECT_IN_PROGRESS);
queue_work(gsi->d_port.ipa_usb_wq,
@@ -2127,7 +2416,8 @@
atomic_set(&gsi->connected, 1);
/* send 0 len pkt to qti to notify state change */
- if (gsi->prot_id == IPA_USB_DIAG)
+ if (gsi->prot_id == USB_PROT_DIAG_IPA ||
+ gsi->prot_id == USB_PROT_DPL_ETHER)
gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
return 0;
@@ -2145,10 +2435,11 @@
atomic_set(&gsi->connected, 0);
- if (gsi->prot_id == IPA_USB_RNDIS)
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA)
rndis_uninit(gsi->params);
- if (gsi->prot_id == IPA_USB_RMNET)
+ if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_RMNET_ETHER)
gsi->rmnet_dtr_status = false;
/* Disable Control Path */
@@ -2170,7 +2461,15 @@
gsi->data_interface_up = false;
+ gsi->host_supports_flow_control = false;
+
log_event_dbg("%s deactivated", gsi->function.name);
+
+ if (is_ext_prot_ether(gsi->prot_id)) {
+ gether_disconnect(&gsi->d_port.gether_port);
+ return;
+ }
+
ipa_disconnect_handler(&gsi->d_port);
post_event(&gsi->d_port, EVT_DISCONNECTED);
queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
@@ -2181,12 +2480,25 @@
bool block_db;
struct f_gsi *gsi = func_to_gsi(f);
- /* Check if function is already suspended in gsi_func_suspend() */
- if (f->func_is_suspended) {
+ /* Check if function is already suspended in gsi_func_suspend()
+ * Or func_suspend would have bailed out earlier if func_remote_wakeup
+ * wasn't enabled.
+ */
+ if (f->func_is_suspended && (gsi->d_port.sm_state == STATE_SUSPENDED ||
+ gsi->d_port.sm_state == STATE_SUSPEND_IN_PROGRESS)) {
log_event_dbg("%s: func already suspended, return\n", __func__);
return;
}
+ /*
+ * GPS doesn't use any data interface, hence bail out as there is no
+ * GSI specific handling needed.
+ */
+ if (gsi->prot_id == USB_PROT_GPS_CTRL) {
+ log_event_dbg("%s: suspend done\n", __func__);
+ return;
+ }
+
block_db = true;
usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
GSI_EP_OP_SET_CLR_BLOCK_DBL);
@@ -2216,13 +2528,20 @@
/* Check any pending cpkt, and queue immediately on resume */
gsi_ctrl_send_notification(gsi);
+ if (gsi->prot_id == USB_PROT_GPS_CTRL) {
+ log_event_dbg("%s: resume done\n", __func__);
+ return;
+ }
+
/*
* Linux host does not send RNDIS_MSG_INIT or non-zero
* RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
+ * Check whether host supports flow_control are not. If yes
* Trigger state machine explicitly on resume.
*/
- if (gsi->prot_id == IPA_USB_RNDIS &&
- !usb_gsi_remote_wakeup_allowed(f))
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA &&
+ !usb_gsi_remote_wakeup_allowed(f) &&
+ gsi->host_supports_flow_control)
rndis_flow_control(gsi->params, false);
post_event(&gsi->d_port, EVT_RESUMED);
@@ -2231,6 +2550,14 @@
log_event_dbg("%s: completed", __func__);
}
+static int gsi_get_status(struct usb_function *f)
+{
+ unsigned int remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0;
+
+ return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) |
+ (1 << FUNC_WAKEUP_CAPABLE_SHIFT);
+}
+
static int gsi_func_suspend(struct usb_function *f, u8 options)
{
bool func_wakeup_allowed;
@@ -2327,7 +2654,7 @@
info->data_nop_desc->bInterfaceNumber = gsi->data_id;
/* allocate instance-specific endpoints */
- if (info->fs_in_desc) {
+ if (info->fs_in_desc && gsi->prot_id <= USB_PROT_DIAG_IPA) {
ep = usb_ep_autoconfig_by_name(cdev->gadget,
info->fs_in_desc, info->in_epname);
if (!ep)
@@ -2335,9 +2662,17 @@
gsi->d_port.in_ep = ep;
msm_ep_config(gsi->d_port.in_ep, NULL);
ep->driver_data = cdev; /* claim */
+ } else {
+ if (info->fs_in_desc) {
+ ep = usb_ep_autoconfig(cdev->gadget, info->fs_in_desc);
+ if (!ep)
+ goto fail;
+ gsi->d_port.in_ep = ep;
+ ep->driver_data = cdev; /* claim */
+ }
}
- if (info->fs_out_desc) {
+ if (info->fs_out_desc && gsi->prot_id <= USB_PROT_DIAG_IPA) {
ep = usb_ep_autoconfig_by_name(cdev->gadget,
info->fs_out_desc, info->out_epname);
if (!ep)
@@ -2345,6 +2680,14 @@
gsi->d_port.out_ep = ep;
msm_ep_config(gsi->d_port.out_ep, NULL);
ep->driver_data = cdev; /* claim */
+ } else {
+ if (info->fs_out_desc) {
+ ep = usb_ep_autoconfig(cdev->gadget, info->fs_out_desc);
+ if (!ep)
+ goto fail;
+ gsi->d_port.out_ep = ep;
+ ep->driver_data = cdev; /* claim */
+ }
}
if (info->fs_notify_desc) {
@@ -2475,14 +2818,17 @@
struct gsi_function_bind_info info = {0};
struct f_gsi *gsi = func_to_gsi(f);
struct rndis_params *params;
+ struct net_device *net;
+ char *name = NULL;
int status;
__u8 class;
__u8 subclass;
__u8 proto;
- if (gsi->prot_id == IPA_USB_RMNET ||
- gsi->prot_id == IPA_USB_DIAG)
+ if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+ gsi->prot_id == USB_PROT_DIAG_IPA ||
+ is_ext_prot_ether(gsi->prot_id))
gsi->ctrl_id = -ENODEV;
else {
status = gsi->ctrl_id = usb_interface_id(c, f);
@@ -2490,12 +2836,14 @@
goto fail;
}
- status = gsi->data_id = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
+ if (gsi->prot_id != USB_PROT_GPS_CTRL) {
+ status = gsi->data_id = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ }
switch (gsi->prot_id) {
- case IPA_USB_RNDIS:
+ case USB_PROT_RNDIS_IPA:
info.string_defs = rndis_gsi_string_defs;
info.ctrl_desc = &rndis_gsi_control_intf;
info.ctrl_str_idx = 0;
@@ -2641,7 +2989,7 @@
info.ctrl_desc->bInterfaceProtocol = proto;
break;
- case IPA_USB_MBIM:
+ case USB_PROT_MBIM_IPA:
info.string_defs = mbim_gsi_string_defs;
info.ctrl_desc = &mbim_gsi_control_intf;
info.ctrl_str_idx = 0;
@@ -2688,7 +3036,8 @@
c->bConfigurationValue + '0';
}
break;
- case IPA_USB_RMNET:
+ case USB_PROT_RMNET_IPA:
+ case USB_PROT_RMNET_ETHER:
info.string_defs = rmnet_gsi_string_defs;
info.data_desc = &rmnet_gsi_interface_desc;
info.data_str_idx = 0;
@@ -2713,8 +3062,9 @@
info.out_req_buf_len = GSI_OUT_RMNET_BUF_LEN;
info.out_req_num_buf = GSI_NUM_OUT_BUFFERS;
info.notify_buf_len = sizeof(struct usb_cdc_notification);
+ name = "usb_rmnet";
break;
- case IPA_USB_ECM:
+ case USB_PROT_ECM_IPA:
info.string_defs = ecm_gsi_string_defs;
info.ctrl_desc = &ecm_gsi_control_intf;
info.ctrl_str_idx = 0;
@@ -2763,7 +3113,8 @@
gsi->d_port.ipa_init_params.host_ethaddr[5]);
info.string_defs[1].s = gsi->ethaddr;
break;
- case IPA_USB_DIAG:
+ case USB_PROT_DIAG_IPA:
+ case USB_PROT_DPL_ETHER:
info.string_defs = qdss_gsi_string_defs;
info.data_desc = &qdss_gsi_data_intf_desc;
info.data_str_idx = 0;
@@ -2778,6 +3129,19 @@
info.in_req_buf_len = 16384;
info.in_req_num_buf = GSI_NUM_IN_BUFFERS;
info.notify_buf_len = sizeof(struct usb_cdc_notification);
+ name = "dpl_usb";
+ break;
+ case USB_PROT_GPS_CTRL:
+ info.string_defs = gps_string_defs;
+ info.ctrl_str_idx = 0;
+ info.ctrl_desc = &gps_interface_desc;
+ info.fs_notify_desc = &gps_fs_notify_desc;
+ info.hs_notify_desc = &gps_hs_notify_desc;
+ info.ss_notify_desc = &gps_ss_notify_desc;
+ info.fs_desc_hdr = gps_fs_function;
+ info.hs_desc_hdr = gps_hs_function;
+ info.ss_desc_hdr = gps_ss_function;
+ info.notify_buf_len = sizeof(struct usb_cdc_notification);
break;
default:
log_event_err("%s: Invalid prot id %d", __func__,
@@ -2789,6 +3153,32 @@
if (status)
goto dereg_rndis;
+ if (gsi->prot_id == USB_PROT_GPS_CTRL)
+ goto skip_ipa_init;
+
+ if (is_ext_prot_ether(gsi->prot_id)) {
+ if (!name)
+ return -EINVAL;
+
+ gsi->d_port.gether_port.in_ep = gsi->d_port.in_ep;
+ gsi->d_port.gether_port.out_ep = gsi->d_port.out_ep;
+ net = gether_setup_name_default(name);
+ if (IS_ERR(net)) {
+ pr_err("%s: gether_setup failed\n", __func__);
+ return PTR_ERR(net);
+ }
+ gsi->d_port.gether_port.ioport = netdev_priv(net);
+ gether_set_gadget(net, c->cdev->gadget);
+ status = gether_register_netdev(net);
+ if (status < 0) {
+ pr_err("%s: gether_register_netdev failed\n",
+ __func__);
+ free_netdev(net);
+ return status;
+ }
+ goto skip_ipa_init;
+ }
+
status = ipa_register_ipa_ready_cb(ipa_ready_callback, gsi);
if (!status) {
log_event_info("%s: ipa is not ready", __func__);
@@ -2814,6 +3204,7 @@
gsi->d_port.sm_state = STATE_INITIALIZED;
+skip_ipa_init:
DBG(cdev, "%s: %s speed IN/%s OUT/%s NOTIFY/%s\n",
f->name,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
@@ -2836,6 +3227,12 @@
{
struct f_gsi *gsi = func_to_gsi(f);
+ if (is_ext_prot_ether(gsi->prot_id)) {
+ gether_cleanup(gsi->d_port.gether_port.ioport);
+ gsi->d_port.gether_port.ioport = NULL;
+ goto skip_ipa_dinit;
+ }
+
/*
* Use drain_workqueue to accomplish below conditions:
* 1. Make sure that any running work completed
@@ -2847,12 +3244,13 @@
drain_workqueue(gsi->d_port.ipa_usb_wq);
ipa_usb_deinit_teth_prot(gsi->prot_id);
- if (gsi->prot_id == IPA_USB_RNDIS) {
+skip_ipa_dinit:
+ if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
gsi->d_port.sm_state = STATE_UNINITIALIZED;
rndis_deregister(gsi->params);
}
- if (gsi->prot_id == IPA_USB_MBIM)
+ if (gsi->prot_id == USB_PROT_MBIM_IPA)
mbim_gsi_ext_config_desc.function.subCompatibleID[0] = 0;
if (gadget_is_superspeed(c->cdev->gadget)) {
@@ -2883,33 +3281,38 @@
static int gsi_bind_config(struct f_gsi *gsi)
{
int status = 0;
- enum ipa_usb_teth_prot prot_id = gsi->prot_id;
- log_event_dbg("%s: prot id %d", __func__, prot_id);
+ log_event_dbg("%s: prot id %d", __func__, gsi->prot_id);
- switch (prot_id) {
- case IPA_USB_RNDIS:
+ switch (gsi->prot_id) {
+ case USB_PROT_RNDIS_IPA:
gsi->function.name = "rndis";
gsi->function.strings = rndis_gsi_strings;
break;
- case IPA_USB_ECM:
+ case USB_PROT_ECM_IPA:
gsi->function.name = "cdc_ethernet";
gsi->function.strings = ecm_gsi_strings;
break;
- case IPA_USB_RMNET:
+ case USB_PROT_RMNET_IPA:
+ case USB_PROT_RMNET_ETHER:
gsi->function.name = "rmnet";
gsi->function.strings = rmnet_gsi_strings;
break;
- case IPA_USB_MBIM:
+ case USB_PROT_MBIM_IPA:
gsi->function.name = "mbim";
gsi->function.strings = mbim_gsi_strings;
break;
- case IPA_USB_DIAG:
+ case USB_PROT_DIAG_IPA:
+ case USB_PROT_DPL_ETHER:
gsi->function.name = "dpl";
gsi->function.strings = qdss_gsi_strings;
break;
+ case USB_PROT_GPS_CTRL:
+ gsi->function.name = "gps";
+ gsi->function.strings = gps_strings;
+ break;
default:
- log_event_err("%s: invalid prot id %d", __func__, prot_id);
+ log_event_err("%s: invalid prot id %d", __func__, gsi->prot_id);
return -EINVAL;
}
@@ -2922,6 +3325,7 @@
gsi->function.disable = gsi_disable;
gsi->function.free_func = gsi_free_func;
gsi->function.suspend = gsi_suspend;
+ gsi->function.get_status = gsi_get_status;
gsi->function.func_suspend = gsi_func_suspend;
gsi->function.resume = gsi_resume;
@@ -3164,7 +3568,7 @@
static int gsi_set_inst_name(struct usb_function_instance *fi,
const char *name)
{
- int prot_id, name_len, ret = 0;
+ int name_len, prot_id, ret = 0;
struct gsi_opts *opts;
struct f_gsi *gsi;
@@ -3181,7 +3585,7 @@
return -EINVAL;
}
- if (prot_id == IPA_USB_RNDIS)
+ if (prot_id == USB_PROT_RNDIS_IPA)
config_group_init_type_name(&opts->func_inst.group, "",
&gsi_func_rndis_type);
@@ -3249,7 +3653,7 @@
return -ENOMEM;
}
- for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++) {
+ for (i = 0; i < USB_PROT_MAX; i++) {
__gsi[i] = gsi_function_init();
if (IS_ERR(__gsi[i]))
return PTR_ERR(__gsi[i]);
@@ -3259,6 +3663,7 @@
if (!ipc_log_ctxt)
pr_err("%s: Err allocating ipc_log_ctxt\n", __func__);
+ usb_gsi_debugfs_init();
return usb_function_register(&gsiusb_func);
}
module_init(fgsi_init);
@@ -3272,9 +3677,10 @@
if (ipc_log_ctxt)
ipc_log_context_destroy(ipc_log_ctxt);
- for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++)
+ for (i = 0; i < USB_PROT_MAX; i++)
kfree(__gsi[i]);
+ usb_gsi_debugfs_exit();
usb_function_unregister(&gsiusb_func);
}
module_exit(fgsi_exit);
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index c6e64fd..cd146a0 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -26,10 +26,17 @@
#include <linux/debugfs.h>
#include <linux/ipa_usb.h>
#include <linux/ipc_logging.h>
+#include <linux/timer.h>
+
+#include "u_ether.h"
#define GSI_RMNET_CTRL_NAME "rmnet_ctrl"
#define GSI_MBIM_CTRL_NAME "android_mbim"
#define GSI_DPL_CTRL_NAME "dpl_ctrl"
+#define ETHER_RMNET_CTRL_NAME "rmnet_ctrl0"
+#define ETHER_DPL_CTRL_NAME "dpl_ctrl0"
+#define GSI_GPS_CTRL_NAME "gps"
+
#define GSI_CTRL_NAME_LEN (sizeof(GSI_MBIM_CTRL_NAME)+2)
#define GSI_MAX_CTRL_PKT_SIZE 4096
#define GSI_CTRL_DTR (1 << 0)
@@ -114,6 +121,22 @@
RNDIS_ID_MAX,
};
+enum usb_prot_id {
+ /* accelerated: redefined from ipa_usb.h, do not change order */
+ USB_PROT_RNDIS_IPA,
+ USB_PROT_ECM_IPA,
+ USB_PROT_RMNET_IPA,
+ USB_PROT_MBIM_IPA,
+ USB_PROT_DIAG_IPA,
+
+ /* non-accelerated */
+ USB_PROT_RMNET_ETHER,
+ USB_PROT_DPL_ETHER,
+ USB_PROT_GPS_CTRL,
+
+ USB_PROT_MAX,
+};
+
#define MAXQUEUELEN 128
struct event_queue {
u8 event[MAXQUEUELEN];
@@ -228,6 +251,7 @@
enum connection_state sm_state;
struct event_queue evt_q;
wait_queue_head_t wait_for_ipa_ready;
+ struct gether gether_port;
/* Track these for debugfs */
struct ipa_usb_xdci_chan_params ipa_in_channel_params;
@@ -237,7 +261,7 @@
struct f_gsi {
struct usb_function function;
- enum ipa_usb_teth_prot prot_id;
+ enum usb_prot_id prot_id;
int ctrl_id;
int data_id;
u32 vendorID;
@@ -254,6 +278,11 @@
struct gsi_data_port d_port;
struct gsi_ctrl_port c_port;
bool rmnet_dtr_status;
+ /* To test remote wakeup using debugfs */
+ struct timer_list debugfs_rw_timer;
+ u8 debugfs_rw_enable;
+ u16 debugfs_rw_interval;
+ bool host_supports_flow_control;
};
static inline struct f_gsi *func_to_gsi(struct usb_function *f)
@@ -285,21 +314,27 @@
func_inst.group);
}
-static enum ipa_usb_teth_prot name_to_prot_id(const char *name)
+static int name_to_prot_id(const char *name)
{
if (!name)
goto error;
if (!strncasecmp(name, "rndis", MAX_INST_NAME_LEN))
- return IPA_USB_RNDIS;
+ return USB_PROT_RNDIS_IPA;
if (!strncasecmp(name, "ecm", MAX_INST_NAME_LEN))
- return IPA_USB_ECM;
+ return USB_PROT_ECM_IPA;
if (!strncasecmp(name, "rmnet", MAX_INST_NAME_LEN))
- return IPA_USB_RMNET;
+ return USB_PROT_RMNET_IPA;
if (!strncasecmp(name, "mbim", MAX_INST_NAME_LEN))
- return IPA_USB_MBIM;
+ return USB_PROT_MBIM_IPA;
if (!strncasecmp(name, "dpl", MAX_INST_NAME_LEN))
- return IPA_USB_DIAG;
+ return USB_PROT_DIAG_IPA;
+ if (!strncasecmp(name, "rmnet.ether", MAX_INST_NAME_LEN))
+ return USB_PROT_RMNET_ETHER;
+ if (!strncasecmp(name, "dpl.ether", MAX_INST_NAME_LEN))
+ return USB_PROT_DPL_ETHER;
+ if (!strncasecmp(name, "gps", MAX_INST_NAME_LEN))
+ return USB_PROT_GPS_CTRL;
error:
return -EINVAL;
@@ -309,6 +344,7 @@
#define LOG2_STATUS_INTERVAL_MSEC 5
#define MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification)
+#define GPS_MAX_NOTIFY_SIZE 64
/* rmnet device descriptors */
@@ -1399,4 +1435,91 @@
&qdss_gsi_string_table,
NULL,
};
+
+/* gps device descriptor */
+static struct usb_interface_descriptor gps_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
+ /* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor gps_fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GPS_MAX_NOTIFY_SIZE),
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_descriptor_header *gps_fs_function[] = {
+ (struct usb_descriptor_header *) &gps_interface_desc,
+ (struct usb_descriptor_header *) &gps_fs_notify_desc,
+ NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor gps_hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GPS_MAX_NOTIFY_SIZE),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_descriptor_header *gps_hs_function[] = {
+ (struct usb_descriptor_header *) &gps_interface_desc,
+ (struct usb_descriptor_header *) &gps_hs_notify_desc,
+ NULL,
+};
+
+/* Super speed support */
+static struct usb_endpoint_descriptor gps_ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GPS_MAX_NOTIFY_SIZE),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor gps_ss_notify_comp_desc = {
+ .bLength = sizeof(gps_ss_notify_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(GPS_MAX_NOTIFY_SIZE),
+};
+
+static struct usb_descriptor_header *gps_ss_function[] = {
+ (struct usb_descriptor_header *) &gps_interface_desc,
+ (struct usb_descriptor_header *) &gps_ss_notify_desc,
+ (struct usb_descriptor_header *) &gps_ss_notify_comp_desc,
+ NULL,
+};
+
+/* String descriptors */
+
+static struct usb_string gps_string_defs[] = {
+ [0].s = "GPS",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings gps_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = gps_string_defs,
+};
+
+static struct usb_gadget_strings *gps_strings[] = {
+ &gps_string_table,
+ NULL,
+};
#endif
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index b6d4b48..5815120 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -284,6 +284,7 @@
size_t count, loff_t *offp)
{
struct f_hidg *hidg = file->private_data;
+ struct usb_request *req;
unsigned long flags;
ssize_t status = -ENOMEM;
@@ -293,7 +294,7 @@
spin_lock_irqsave(&hidg->write_spinlock, flags);
#define WRITE_COND (!hidg->write_pending)
-
+try_again:
/* write queue */
while (!WRITE_COND) {
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
@@ -308,6 +309,7 @@
}
hidg->write_pending = 1;
+ req = hidg->req;
count = min_t(unsigned, count, hidg->report_length);
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
@@ -320,24 +322,38 @@
goto release_write_pending;
}
- hidg->req->status = 0;
- hidg->req->zero = 0;
- hidg->req->length = count;
- hidg->req->complete = f_hidg_req_complete;
- hidg->req->context = hidg;
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+
+ /* we our function has been disabled by host */
+ if (!hidg->req) {
+ free_ep_req(hidg->in_ep, hidg->req);
+ /*
+ * TODO
+ * Should we fail with error here?
+ */
+ goto try_again;
+ }
+
+ req->status = 0;
+ req->zero = 0;
+ req->length = count;
+ req->complete = f_hidg_req_complete;
+ req->context = hidg;
status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
if (status < 0) {
ERROR(hidg->func.config->cdev,
"usb_ep_queue error on int endpoint %zd\n", status);
- goto release_write_pending;
+ goto release_write_pending_unlocked;
} else {
status = count;
}
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
return status;
release_write_pending:
spin_lock_irqsave(&hidg->write_spinlock, flags);
+release_write_pending_unlocked:
hidg->write_pending = 0;
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
@@ -541,12 +557,23 @@
kfree(list);
}
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+ if (!hidg->write_pending) {
+ free_ep_req(hidg->in_ep, hidg->req);
+ hidg->write_pending = 1;
+ }
+
+ hidg->req = NULL;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
}
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_hidg *hidg = func_to_hidg(f);
+ struct usb_request *req_in = NULL;
+ unsigned long flags;
int i, status = 0;
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
@@ -567,6 +594,12 @@
goto fail;
}
hidg->in_ep->driver_data = hidg;
+
+ req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length);
+ if (!req_in) {
+ status = -ENOMEM;
+ goto disable_ep_in;
+ }
}
@@ -578,12 +611,12 @@
hidg->out_ep);
if (status) {
ERROR(cdev, "config_ep_by_speed FAILED!\n");
- goto fail;
+ goto free_req_in;
}
status = usb_ep_enable(hidg->out_ep);
if (status < 0) {
ERROR(cdev, "Enable OUT endpoint FAILED!\n");
- goto fail;
+ goto free_req_in;
}
hidg->out_ep->driver_data = hidg;
@@ -599,17 +632,37 @@
req->context = hidg;
status = usb_ep_queue(hidg->out_ep, req,
GFP_ATOMIC);
- if (status)
+ if (status) {
ERROR(cdev, "%s queue req --> %d\n",
hidg->out_ep->name, status);
+ free_ep_req(hidg->out_ep, req);
+ }
} else {
- usb_ep_disable(hidg->out_ep);
status = -ENOMEM;
- goto fail;
+ goto disable_out_ep;
}
}
}
+ if (hidg->in_ep != NULL) {
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+ hidg->req = req_in;
+ hidg->write_pending = 0;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+ wake_up(&hidg->write_queue);
+ }
+ return 0;
+disable_out_ep:
+ usb_ep_disable(hidg->out_ep);
+free_req_in:
+ if (req_in)
+ free_ep_req(hidg->in_ep, req_in);
+
+disable_ep_in:
+ if (hidg->in_ep)
+ usb_ep_disable(hidg->in_ep);
+
fail:
return status;
}
@@ -658,12 +711,6 @@
goto fail;
hidg->out_ep = ep;
- /* preallocate request and buffer */
- status = -ENOMEM;
- hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
- if (!hidg->req)
- goto fail;
-
/* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@@ -690,6 +737,8 @@
goto fail;
spin_lock_init(&hidg->write_spinlock);
+ hidg->write_pending = 1;
+ hidg->req = NULL;
spin_lock_init(&hidg->read_spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
@@ -954,10 +1003,6 @@
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
- /* disable/free request and end point */
- usb_ep_disable(hidg->in_ep);
- free_ep_req(hidg->in_ep, hidg->req);
-
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 1979156..4b4ce0e 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -413,7 +413,8 @@
if (err) {
ERROR(midi, "%s: couldn't enqueue request: %d\n",
midi->out_ep->name, err);
- free_ep_req(midi->out_ep, req);
+ if (req->buf != NULL)
+ free_ep_req(midi->out_ep, req);
return err;
}
}
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 651776d..79ef286 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -137,6 +137,9 @@
} perf[MAX_ITERATION];
unsigned int dbg_read_index;
unsigned int dbg_write_index;
+ unsigned int mtp_rx_req_len;
+ unsigned int mtp_tx_req_len;
+ unsigned int mtp_tx_reqs;
struct mutex read_mutex;
};
@@ -531,16 +534,16 @@
retry_tx_alloc:
/* now allocate requests for our endpoints */
- for (i = 0; i < mtp_tx_reqs; i++) {
+ for (i = 0; i < dev->mtp_tx_reqs; i++) {
req = mtp_request_new(dev->ep_in,
- mtp_tx_req_len + extra_buf_alloc);
+ dev->mtp_tx_req_len + extra_buf_alloc);
if (!req) {
- if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE)
+ if (dev->mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE)
goto fail;
while ((req = mtp_req_get(dev, &dev->tx_idle)))
mtp_request_free(req, dev->ep_in);
- mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
- mtp_tx_reqs = MTP_TX_REQ_MAX;
+ dev->mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
+ dev->mtp_tx_reqs = MTP_TX_REQ_MAX;
goto retry_tx_alloc;
}
req->complete = mtp_complete_in;
@@ -553,18 +556,18 @@
* operational speed. Hence assuming super speed max
* packet size.
*/
- if (mtp_rx_req_len % 1024)
- mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+ if (dev->mtp_rx_req_len % 1024)
+ dev->mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
retry_rx_alloc:
for (i = 0; i < RX_REQ_MAX; i++) {
- req = mtp_request_new(dev->ep_out, mtp_rx_req_len);
+ req = mtp_request_new(dev->ep_out, dev->mtp_rx_req_len);
if (!req) {
- if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE)
+ if (dev->mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE)
goto fail;
for (--i; i >= 0; i--)
mtp_request_free(dev->rx_req[i], dev->ep_out);
- mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+ dev->mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
goto retry_rx_alloc;
}
req->complete = mtp_complete_out;
@@ -609,11 +612,21 @@
}
len = ALIGN(count, dev->ep_out->maxpacket);
- if (len > mtp_rx_req_len)
+ if (len > dev->mtp_rx_req_len)
return -EINVAL;
spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+
if (dev->ep_out->desc) {
+ if (!cdev) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+
len = usb_ep_align_maybe(cdev->gadget, dev->ep_out, count);
if (len > MTP_BULK_BUFFER_SIZE) {
spin_unlock_irq(&dev->lock);
@@ -750,8 +763,8 @@
break;
}
- if (count > mtp_tx_req_len)
- xfer = mtp_tx_req_len;
+ if (count > dev->mtp_tx_req_len)
+ xfer = dev->mtp_tx_req_len;
else
xfer = count;
if (xfer && copy_from_user(req->buf, buf, xfer)) {
@@ -847,8 +860,8 @@
break;
}
- if (count > mtp_tx_req_len)
- xfer = mtp_tx_req_len;
+ if (count > dev->mtp_tx_req_len)
+ xfer = dev->mtp_tx_req_len;
else
xfer = count;
@@ -944,7 +957,7 @@
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
/* some h/w expects size to be aligned to ep's MTU */
- read_req->length = mtp_rx_req_len;
+ read_req->length = dev->mtp_rx_req_len;
dev->rx_done = 0;
mutex_unlock(&dev->read_mutex);
@@ -1421,6 +1434,9 @@
mtp_tx_req_len = 16384;
}
+ dev->mtp_rx_req_len = mtp_rx_req_len;
+ dev->mtp_tx_req_len = mtp_tx_req_len;
+ dev->mtp_tx_reqs = mtp_tx_reqs;
/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
@@ -1498,7 +1514,10 @@
while ((req = mtp_req_get(dev, &dev->intr_idle)))
mtp_request_free(req, dev->ep_intr);
mutex_unlock(&dev->read_mutex);
+ spin_lock_irq(&dev->lock);
dev->state = STATE_OFFLINE;
+ dev->cdev = NULL;
+ spin_unlock_irq(&dev->lock);
kfree(f->os_desc_table);
f->os_desc_n = 0;
fi_mtp->func_inst.f = NULL;
@@ -1554,7 +1573,9 @@
struct usb_composite_dev *cdev = dev->cdev;
DBG(cdev, "mtp_function_disable\n");
+ spin_lock_irq(&dev->lock);
dev->state = STATE_OFFLINE;
+ spin_unlock_irq(&dev->lock);
usb_ep_disable(dev->ep_in);
usb_ep_disable(dev->ep_out);
usb_ep_disable(dev->ep_intr);
@@ -1581,7 +1602,7 @@
seq_printf(s, "vfs write: bytes:%ld\t\t time:%d\n",
dev->perf[i].vfs_wbytes,
dev->perf[i].vfs_wtime);
- if (dev->perf[i].vfs_wbytes == mtp_rx_req_len) {
+ if (dev->perf[i].vfs_wbytes == dev->mtp_rx_req_len) {
sum += dev->perf[i].vfs_wtime;
if (min > dev->perf[i].vfs_wtime)
min = dev->perf[i].vfs_wtime;
@@ -1603,7 +1624,7 @@
seq_printf(s, "vfs read: bytes:%ld\t\t time:%d\n",
dev->perf[i].vfs_rbytes,
dev->perf[i].vfs_rtime);
- if (dev->perf[i].vfs_rbytes == mtp_tx_req_len) {
+ if (dev->perf[i].vfs_rbytes == dev->mtp_tx_req_len) {
sum += dev->perf[i].vfs_rtime;
if (min > dev->perf[i].vfs_rtime)
min = dev->perf[i].vfs_rtime;
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index d1c3741..312ae24 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -1,7 +1,7 @@
/*
* f_qdss.c -- QDSS function Driver
*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -117,6 +117,39 @@
.wBytesPerInterval = 0,
};
+/* Full speed support */
+static struct usb_endpoint_descriptor qdss_fs_data_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor qdss_fs_ctrl_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor qdss_fs_ctrl_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *qdss_fs_desc[] = {
+ (struct usb_descriptor_header *) &qdss_data_intf_desc,
+ (struct usb_descriptor_header *) &qdss_fs_data_desc,
+ (struct usb_descriptor_header *) &qdss_ctrl_intf_desc,
+ (struct usb_descriptor_header *) &qdss_fs_ctrl_in_desc,
+ (struct usb_descriptor_header *) &qdss_fs_ctrl_out_desc,
+ NULL,
+};
static struct usb_descriptor_header *qdss_hs_desc[] = {
(struct usb_descriptor_header *) &qdss_data_intf_desc,
(struct usb_descriptor_header *) &qdss_hs_data_desc,
@@ -138,6 +171,11 @@
NULL,
};
+static struct usb_descriptor_header *qdss_fs_data_only_desc[] = {
+ (struct usb_descriptor_header *) &qdss_data_intf_desc,
+ (struct usb_descriptor_header *) &qdss_fs_data_desc,
+ NULL,
+};
static struct usb_descriptor_header *qdss_hs_data_only_desc[] = {
(struct usb_descriptor_header *) &qdss_data_intf_desc,
(struct usb_descriptor_header *) &qdss_hs_data_desc,
@@ -367,6 +405,9 @@
if (gadget_is_dualspeed(gadget) && f->hs_descriptors)
usb_free_descriptors(f->hs_descriptors);
+
+ if (f->fs_descriptors)
+ usb_free_descriptors(f->fs_descriptors);
}
static int qdss_bind(struct usb_configuration *c, struct usb_function *f)
@@ -378,11 +419,6 @@
pr_debug("qdss_bind\n");
- if (!gadget_is_dualspeed(gadget) && !gadget_is_superspeed(gadget)) {
- pr_err("qdss_bind: full-speed is not supported\n");
- return -ENOTSUPP;
- }
-
/* Allocate data I/F */
iface = usb_interface_id(c, f);
if (iface < 0) {
@@ -447,6 +483,18 @@
ep->driver_data = qdss;
}
+ /*update fs descriptors*/
+ qdss_fs_data_desc.bEndpointAddress =
+ qdss_ss_data_desc.bEndpointAddress;
+ if (qdss->debug_inface_enabled) {
+ qdss_fs_ctrl_in_desc.bEndpointAddress =
+ qdss_ss_ctrl_in_desc.bEndpointAddress;
+ qdss_fs_ctrl_out_desc.bEndpointAddress =
+ qdss_ss_ctrl_out_desc.bEndpointAddress;
+ f->fs_descriptors = usb_copy_descriptors(qdss_fs_desc);
+ } else
+ f->fs_descriptors = usb_copy_descriptors(
+ qdss_fs_data_only_desc);
/*update descriptors*/
qdss_hs_data_desc.bEndpointAddress =
qdss_ss_data_desc.bEndpointAddress;
@@ -650,7 +698,7 @@
goto fail1;
}
- if (intf == qdss->data_iface_id) {
+ if (intf == qdss->data_iface_id && !qdss->data_enabled) {
/* Increment usage count on connect */
usb_gadget_autopm_get_async(qdss->gadget);
@@ -1144,7 +1192,7 @@
struct f_qdss *usb_qdss = opts->usb_qdss;
usb_qdss->port.function.name = "usb_qdss";
- usb_qdss->port.function.fs_descriptors = qdss_hs_desc;
+ usb_qdss->port.function.fs_descriptors = qdss_fs_desc;
usb_qdss->port.function.hs_descriptors = qdss_hs_desc;
usb_qdss->port.function.strings = qdss_strings;
usb_qdss->port.function.bind = qdss_bind;
@@ -1157,7 +1205,7 @@
return &usb_qdss->port.function;
}
-DECLARE_USB_FUNCTION_INIT(qdss, qdss_alloc_inst, qdss_alloc);
+DECLARE_USB_FUNCTION(qdss, qdss_alloc_inst, qdss_alloc);
static int __init usb_qdss_init(void)
{
int ret;
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 6153c54..24bc2d4 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -386,10 +386,31 @@
struct sk_buff *skb)
{
struct sk_buff *skb2;
+ struct rndis_packet_msg_type *header = NULL;
+ struct f_rndis *rndis = func_to_rndis(&port->func);
if (!skb)
return NULL;
+ if (rndis->port.multi_pkt_xfer) {
+ if (port->header) {
+ header = port->header;
+ header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
+ header->MessageLength = cpu_to_le32(skb->len +
+ sizeof(*header));
+ header->DataOffset = cpu_to_le32(36);
+ header->DataLength = cpu_to_le32(skb->len);
+ pr_debug("MessageLength:%d DataLength:%d\n",
+ header->MessageLength,
+ header->DataLength);
+ return skb;
+ }
+
+ dev_kfree_skb(skb);
+ pr_err("RNDIS header is NULL.\n");
+ return NULL;
+ }
+
skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
rndis_add_hdr(skb2);
@@ -766,18 +787,31 @@
* with regard to rndis_opts->bound access
*/
if (!rndis_opts->bound) {
+ mutex_lock(&rndis_opts->lock);
+ rndis_opts->net = gether_setup_name_default("rndis");
+ if (IS_ERR(rndis_opts->net)) {
+ status = PTR_ERR(rndis_opts->net);
+ mutex_unlock(&rndis_opts->lock);
+ goto error;
+ }
gether_set_gadget(rndis_opts->net, cdev->gadget);
status = gether_register_netdev(rndis_opts->net);
- if (status)
- goto fail;
+ mutex_unlock(&rndis_opts->lock);
+ if (status) {
+ free_netdev(rndis_opts->net);
+ goto error;
+ }
rndis_opts->bound = true;
}
+ gether_get_host_addr_u8(rndis_opts->net, rndis->ethaddr);
+ rndis->port.ioport = netdev_priv(rndis_opts->net);
+
us = usb_gstrings_attach(cdev, rndis_strings,
ARRAY_SIZE(rndis_string_defs));
if (IS_ERR(us)) {
status = PTR_ERR(us);
- goto fail;
+ goto netdev_cleanup;
}
rndis_control_intf.iInterface = us[0].id;
rndis_data_intf.iInterface = us[1].id;
@@ -805,6 +839,27 @@
rndis_data_intf.bInterfaceNumber = status;
rndis_union_desc.bSlaveInterface0 = status;
+ if (rndis_opts->wceis) {
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ rndis_iad_descriptor.bFunctionClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ rndis_iad_descriptor.bFunctionSubClass = 0x01;
+ rndis_iad_descriptor.bFunctionProtocol = 0x03;
+ rndis_control_intf.bInterfaceClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ rndis_control_intf.bInterfaceSubClass = 0x01;
+ rndis_control_intf.bInterfaceProtocol = 0x03;
+ } else {
+ rndis_iad_descriptor.bFunctionClass = USB_CLASS_COMM;
+ rndis_iad_descriptor.bFunctionSubClass =
+ USB_CDC_SUBCLASS_ETHERNET;
+ rndis_iad_descriptor.bFunctionProtocol = USB_CDC_PROTO_NONE;
+ rndis_control_intf.bInterfaceClass = USB_CLASS_COMM;
+ rndis_control_intf.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM;
+ rndis_control_intf.bInterfaceProtocol =
+ USB_CDC_ACM_PROTO_VENDOR;
+ }
+
status = -ENODEV;
/* allocate instance-specific endpoints */
@@ -894,7 +949,9 @@
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
}
-
+netdev_cleanup:
+ gether_cleanup(rndis->port.ioport);
+error:
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
@@ -935,11 +992,15 @@
/* f_rndis_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis);
+/* f_rndis_opts_wceis */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_WCEIS(rndis);
+
static struct configfs_attribute *rndis_attrs[] = {
&rndis_opts_attr_dev_addr,
&rndis_opts_attr_host_addr,
&rndis_opts_attr_qmult,
&rndis_opts_attr_ifname,
+ &rndis_opts_attr_wceis,
NULL,
};
@@ -957,8 +1018,6 @@
if (!opts->borrowed_net) {
if (opts->bound)
gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
}
kfree(opts->rndis_interf_group); /* single VLA chunk */
@@ -980,12 +1039,6 @@
mutex_init(&opts->lock);
spin_lock_init(&_rndis_lock);
opts->func_inst.free_func_inst = rndis_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
descs[0] = &opts->rndis_os_desc;
@@ -1001,6 +1054,9 @@
}
opts->rndis_interf_group = rndis_interf_group;
+ /* Enable "Wireless" RNDIS by default */
+ opts->wceis = true;
+
return &opts->func_inst;
}
@@ -1025,6 +1081,8 @@
static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
+ struct f_rndis_opts *opts = container_of(f->fi, struct f_rndis_opts,
+ func_inst);
kfree(f->os_desc_table);
f->os_desc_n = 0;
@@ -1032,6 +1090,8 @@
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
+ gether_cleanup(rndis->port.ioport);
+ opts->bound = false;
}
static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
@@ -1051,11 +1111,9 @@
mutex_lock(&opts->lock);
opts->refcnt++;
- gether_get_host_addr_u8(opts->net, rndis->ethaddr);
rndis->vendorID = opts->vendor_id;
rndis->manufacturer = opts->manufacturer;
- rndis->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
/* RNDIS activates when the host changes this filter */
rndis->port.cdc_filter = 0;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index c330814..626e020 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1156,6 +1156,8 @@
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
+ iad_desc.bFirstInterface = ret;
+
std_ac_if_desc.bInterfaceNumber = ret;
iad_desc.bFirstInterface = ret;
agdev->ac_intf = ret;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 9e38809..c75e790 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -246,6 +246,7 @@
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+static void tx_complete(struct usb_ep *ep, struct usb_request *req);
static int
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
@@ -307,7 +308,6 @@
req->buf = skb->data;
req->length = size;
- req->complete = rx_complete;
req->context = skb;
retval = usb_ep_queue(out, req, gfp_flags);
@@ -410,6 +410,7 @@
{
unsigned i;
struct usb_request *req;
+ bool usb_in;
if (!n)
return -ENOMEM;
@@ -420,10 +421,22 @@
if (i-- == 0)
goto extra;
}
+
+ if (ep->desc->bEndpointAddress & USB_DIR_IN)
+ usb_in = true;
+ else
+ usb_in = false;
+
while (i--) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (!req)
return list_empty(list) ? -ENOMEM : 0;
+ /* update completion handler */
+ if (usb_in)
+ req->complete = tx_complete;
+ else
+ req->complete = rx_complete;
+
list_add(&req->list, list);
}
return 0;
@@ -575,7 +588,7 @@
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct sk_buff *skb = req->context;
+ struct sk_buff *skb;
struct eth_dev *dev = ep->driver_data;
struct net_device *net = dev->net;
struct usb_request *new_req;
@@ -683,6 +696,7 @@
spin_unlock(&dev->req_lock);
}
} else {
+ skb = req->context;
/* Is aggregation already enabled and buffers allocated ? */
if (dev->port_usb->multi_pkt_xfer && dev->tx_req_bufsize) {
req->buf = kzalloc(dev->tx_req_bufsize
@@ -725,7 +739,7 @@
list_for_each(act, &dev->tx_reqs) {
req = container_of(act, struct usb_request, list);
if (!req->buf)
- req->buf = kmalloc(dev->tx_req_bufsize
+ req->buf = kzalloc(dev->tx_req_bufsize
+ dev->gadget->extra_buf_alloc, GFP_ATOMIC);
if (!req->buf)
@@ -804,29 +818,6 @@
}
dev->tx_pkts_rcvd++;
- /*
- * no buffer copies needed, unless the network stack did it
- * or the hardware can't use skb buffers.
- * or there's not enough space for extra headers we need
- */
- spin_lock_irqsave(&dev->lock, flags);
- if (dev->wrap && dev->port_usb)
- skb = dev->wrap(dev->port_usb, skb);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- if (!skb) {
- if (dev->port_usb && dev->port_usb->supports_multi_frame) {
- /*
- * Multi frame CDC protocols may store the frame for
- * later which is not a dropped frame.
- */
- } else {
- dev->net->stats.tx_dropped++;
- }
-
- /* no error code for dropped packets */
- return NETDEV_TX_OK;
- }
/* Allocate memory for tx_reqs to support multi packet transfer */
spin_lock_irqsave(&dev->req_lock, flags);
@@ -861,13 +852,45 @@
dev->tx_throttle++;
netif_stop_queue(net);
}
-
- dev->tx_skb_hold_count++;
spin_unlock_irqrestore(&dev->req_lock, flags);
+ /* no buffer copies needed, unless the network stack did it
+ * or the hardware can't use skb buffers.
+ * or there's not enough space for extra headers we need
+ */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->wrap) {
+ if (dev->port_usb)
+ skb = dev->wrap(dev->port_usb, skb);
+ if (!skb) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /* Multi frame CDC protocols may store the frame for
+ * later which is not a dropped frame.
+ */
+ if (dev->port_usb &&
+ dev->port_usb->supports_multi_frame)
+ goto multiframe;
+ goto drop;
+ }
+ }
+
+ dev->tx_skb_hold_count++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
if (multi_pkt_xfer) {
+ pr_debug("req->length:%d header_len:%u\n"
+ "skb->len:%d skb->data_len:%d\n",
+ req->length, dev->header_len,
+ skb->len, skb->data_len);
+ /* Add RNDIS Header */
+ memcpy(req->buf + req->length, dev->port_usb->header,
+ dev->header_len);
+ /* Increment req length by header size */
+ req->length += dev->header_len;
+ /* Copy received IP data from SKB */
memcpy(req->buf + req->length, skb->data, skb->len);
- req->length = req->length + skb->len;
+ /* Increment req length by skb data length */
+ req->length += skb->len;
length = req->length;
dev_kfree_skb_any(skb);
@@ -927,8 +950,6 @@
req->context = skb;
}
- req->complete = tx_complete;
-
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
if (is_fixed && length == fixed_in_len &&
(length % in->maxpacket) == 0)
@@ -977,11 +998,13 @@
dev_kfree_skb_any(skb);
else
req->length = 0;
+drop:
dev->net->stats.tx_dropped++;
+multiframe:
spin_lock_irqsave(&dev->req_lock, flags);
if (list_empty(&dev->tx_reqs))
netif_start_queue(net);
- list_add(&req->list, &dev->tx_reqs);
+ list_add_tail(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
}
success:
@@ -1443,6 +1466,8 @@
else
INFO(dev, "MAC %pM\n", dev->dev_mac);
+ uether_debugfs_init(dev);
+
return status;
}
EXPORT_SYMBOL_GPL(gether_register_netdev);
@@ -1594,6 +1619,11 @@
if (!dev)
return ERR_PTR(-EINVAL);
+ /* size of rndis_packet_msg_type is 44 */
+ link->header = kzalloc(44, GFP_ATOMIC);
+ if (!link->header)
+ return ERR_PTR(-ENOMEM);
+
if (link->in_ep) {
link->in_ep->driver_data = dev;
result = usb_ep_enable(link->in_ep);
@@ -1657,8 +1687,12 @@
}
fail0:
/* caller is responsible for cleanup on error */
- if (result < 0)
+ if (result < 0) {
+ kfree(link->header);
+
return ERR_PTR(result);
+ }
+
return dev->net;
}
EXPORT_SYMBOL_GPL(gether_connect);
@@ -1711,6 +1745,9 @@
spin_lock(&dev->req_lock);
}
spin_unlock(&dev->req_lock);
+ /* Free rndis header buffer memory */
+ kfree(link->header);
+ link->header = NULL;
link->in_ep->desc = NULL;
}
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 3ff09d1..a10503a 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -87,6 +87,7 @@
/* called on network open/close */
void (*open)(struct gether *);
void (*close)(struct gether *);
+ struct rndis_packet_msg_type *header;
};
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index 0468459..1dd2ff4 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -188,4 +188,50 @@
\
CONFIGFS_ATTR_RO(_f_##_opts_, ifname)
+#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_WCEIS(_f_) \
+ static ssize_t _f_##_opts_wceis_show(struct config_item *item, \
+ char *page) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ bool wceis; \
+ \
+ if (opts->bound == false) { \
+ pr_err("Gadget function do not bind yet.\n"); \
+ return -ENODEV; \
+ } \
+ \
+ mutex_lock(&opts->lock); \
+ wceis = opts->wceis; \
+ mutex_unlock(&opts->lock); \
+ return snprintf(page, PAGE_SIZE, "%d", wceis); \
+ } \
+ \
+ static ssize_t _f_##_opts_wceis_store(struct config_item *item, \
+ const char *page, size_t len)\
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ bool wceis; \
+ int ret; \
+ \
+ if (opts->bound == false) { \
+ pr_err("Gadget function do not bind yet.\n"); \
+ return -ENODEV; \
+ } \
+ \
+ mutex_lock(&opts->lock); \
+ \
+ ret = kstrtobool(page, &wceis); \
+ if (ret) \
+ goto out; \
+ \
+ opts->wceis = wceis; \
+ ret = len; \
+out: \
+ mutex_unlock(&opts->lock); \
+ \
+ return ret; \
+ } \
+ \
+ CONFIGFS_ATTR(_f_##_opts_, wceis)
+
#endif /* __U_ETHER_CONFIGFS_H */
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index 4e2ad04..f829a5e 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -38,6 +38,9 @@
*/
struct mutex lock;
int refcnt;
+
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ bool wceis;
};
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
index 7d53a47..2f03334 100644
--- a/drivers/usb/gadget/u_f.h
+++ b/drivers/usb/gadget/u_f.h
@@ -64,7 +64,9 @@
/* Frees a usb_request previously allocated by alloc_ep_req() */
static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
{
+ WARN_ON(req->buf == NULL);
kfree(req->buf);
+ req->buf = NULL;
usb_ep_free_request(ep, req);
}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index ccb9c21..e9bd8d4 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -475,7 +475,7 @@
bdc->dev = dev;
dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
- temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
if ((temp & BDC_P64) &&
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
dev_dbg(bdc->dev, "Using 64-bit address\n");
diff --git a/drivers/usb/gadget/udc/bdc/bdc_pci.c b/drivers/usb/gadget/udc/bdc/bdc_pci.c
index 0296884..708e36f 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_pci.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_pci.c
@@ -82,6 +82,7 @@
if (ret) {
dev_err(&pci->dev,
"couldn't add resources to bdc device\n");
+ platform_device_put(bdc);
return ret;
}
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 6959071..0c71938 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -139,10 +139,8 @@
goto out;
ret = ep->ops->disable(ep);
- if (ret) {
- ret = ret;
+ if (ret)
goto out;
- }
ep->enabled = false;
@@ -250,6 +248,9 @@
* arranges to poll once per interval, and the gadget driver usually will
* have queued some data to transfer at that time.
*
+ * Note that @req's ->complete() callback must never be called from
+ * within usb_ep_queue() as that can create deadlock situations.
+ *
* Returns zero, or a negative error code. Endpoints that are not enabled
* report errors; errors will also be
* reported when the usb peripheral is disconnected.
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index b62a3de..ff4d6ca 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -2103,16 +2103,13 @@
}
break;
case USB_PORT_FEAT_POWER:
- if (hcd->speed == HCD_USB3) {
- if (dum_hcd->port_status & USB_PORT_STAT_POWER)
- dev_dbg(dummy_dev(dum_hcd),
- "power-off\n");
- } else
- if (dum_hcd->port_status &
- USB_SS_PORT_STAT_POWER)
- dev_dbg(dummy_dev(dum_hcd),
- "power-off\n");
- /* FALLS THROUGH */
+ dev_dbg(dummy_dev(dum_hcd), "power-off\n");
+ if (hcd->speed == HCD_USB3)
+ dum_hcd->port_status &= ~USB_SS_PORT_STAT_POWER;
+ else
+ dum_hcd->port_status &= ~USB_PORT_STAT_POWER;
+ set_link_state(dum_hcd);
+ break;
default:
dum_hcd->port_status &= ~(1 << wValue);
set_link_state(dum_hcd);
@@ -2283,14 +2280,13 @@
if ((dum_hcd->port_status &
USB_SS_PORT_STAT_POWER) != 0) {
dum_hcd->port_status |= (1 << wValue);
- set_link_state(dum_hcd);
}
} else
if ((dum_hcd->port_status &
USB_PORT_STAT_POWER) != 0) {
dum_hcd->port_status |= (1 << wValue);
- set_link_state(dum_hcd);
}
+ set_link_state(dum_hcd);
}
break;
case GetPortErrorCount:
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index aac0ce8..8991a40 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1310,7 +1310,7 @@
{
struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
- if (ep->name)
+ if (ep->ep.name)
nuke(ep, -ESHUTDOWN);
}
@@ -1698,7 +1698,7 @@
curr_ep = get_ep_by_pipe(udc, i);
/* If the ep is configured */
- if (curr_ep->name == NULL) {
+ if (!curr_ep->ep.name) {
WARNING("Invalid EP?");
continue;
}
diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
index 86d2ada..64eb0f2 100644
--- a/drivers/usb/gadget/udc/goku_udc.h
+++ b/drivers/usb/gadget/udc/goku_udc.h
@@ -28,7 +28,7 @@
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
-#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */
+#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 142fe51..e79ac2a 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -377,6 +377,9 @@
if (!ehci->sbrn)
return;
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ return;
+
spin_lock_irq(&ehci->lock);
ehci->shutdown = true;
ehci->rh_state = EHCI_RH_STOPPING;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 5635a33..216ddee 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -73,6 +73,7 @@
#define STATECHANGE_DELAY msecs_to_jiffies(300)
#define IO_WATCHDOG_DELAY msecs_to_jiffies(275)
+#define IO_WATCHDOG_OFF 0xffffff00
#include "ohci.h"
#include "pci-quirks.h"
@@ -230,7 +231,7 @@
}
/* Start up the I/O watchdog timer, if it's not running */
- if (!timer_pending(&ohci->io_watchdog) &&
+ if (ohci->prev_frame_no == IO_WATCHDOG_OFF &&
list_empty(&ohci->eds_in_use) &&
!(ohci->flags & OHCI_QUIRK_QEMU)) {
ohci->prev_frame_no = ohci_frame_no(ohci);
@@ -445,7 +446,8 @@
struct usb_hcd *hcd = ohci_to_hcd(ohci);
/* Accept arbitrarily long scatter-gather lists */
- hcd->self.sg_tablesize = ~0;
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
if (distrust_firmware)
ohci->flags |= OHCI_QUIRK_HUB_POWER;
@@ -501,6 +503,7 @@
setup_timer(&ohci->io_watchdog, io_watchdog_func,
(unsigned long) ohci);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
ohci->hcca = dma_alloc_coherent (hcd->self.controller,
sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
@@ -730,7 +733,7 @@
u32 head;
struct ed *ed;
struct td *td, *td_start, *td_next;
- unsigned frame_no;
+ unsigned frame_no, prev_frame_no = IO_WATCHDOG_OFF;
unsigned long flags;
spin_lock_irqsave(&ohci->lock, flags);
@@ -835,7 +838,7 @@
}
}
if (!list_empty(&ohci->eds_in_use)) {
- ohci->prev_frame_no = frame_no;
+ prev_frame_no = frame_no;
ohci->prev_wdh_cnt = ohci->wdh_cnt;
ohci->prev_donehead = ohci_readl(ohci,
&ohci->regs->donehead);
@@ -845,6 +848,7 @@
}
done:
+ ohci->prev_frame_no = prev_frame_no;
spin_unlock_irqrestore(&ohci->lock, flags);
}
@@ -973,6 +977,7 @@
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
del_timer_sync(&ohci->io_watchdog);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset(ohci);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index ed678c17..798b2d2 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -310,8 +310,10 @@
rc = ohci_rh_suspend (ohci, 0);
spin_unlock_irq (&ohci->lock);
- if (rc == 0)
+ if (rc == 0) {
del_timer_sync(&ohci->io_watchdog);
+ ohci->prev_frame_no = IO_WATCHDOG_OFF;
+ }
return rc;
}
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 4365dc3..48200a8 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -1018,6 +1018,8 @@
* have modified this list. normally it's just prepending
* entries (which we'd ignore), but paranoia won't hurt.
*/
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
modified = 0;
/* unlink urbs as requested, but rescan the list after
@@ -1076,21 +1078,22 @@
goto rescan_this;
/*
- * If no TDs are queued, take ED off the ed_rm_list.
+ * If no TDs are queued, ED is now idle.
* Otherwise, if the HC is running, reschedule.
- * If not, leave it on the list for further dequeues.
+ * If the HC isn't running, add ED back to the
+ * start of the list for later processing.
*/
if (list_empty(&ed->td_list)) {
- *last = ed->ed_next;
- ed->ed_next = NULL;
ed->state = ED_IDLE;
list_del(&ed->in_use_list);
} else if (ohci->rh_state == OHCI_RH_RUNNING) {
- *last = ed->ed_next;
- ed->ed_next = NULL;
ed_schedule(ohci, ed);
} else {
- last = &ed->ed_next;
+ ed->ed_next = ohci->ed_rm_list;
+ ohci->ed_rm_list = ed;
+ /* Don't loop on the same ED */
+ if (last == &ohci->ed_rm_list)
+ last = &ed->ed_next;
}
if (modified)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 588e053..040feda 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -975,6 +975,8 @@
if (dev->out_ctx)
xhci_free_container_ctx(xhci, dev->out_ctx);
+ if (dev->udev && dev->udev->slot_id)
+ dev->udev->slot_id = 0;
kfree(xhci->devs[slot_id]);
xhci->devs[slot_id] = NULL;
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index a1dedf0..75afa8f 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -439,6 +439,39 @@
return -EBUSY;
}
+static int xhci_plat_pm_freeze(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ if (!xhci)
+ return 0;
+
+ dev_dbg(dev, "xhci-plat freeze\n");
+
+ return xhci_suspend(xhci, false);
+}
+
+static int xhci_plat_pm_restore(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ret;
+
+ if (!xhci)
+ return 0;
+
+ dev_dbg(dev, "xhci-plat restore\n");
+
+ ret = xhci_resume(xhci, true);
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_mark_last_busy(dev);
+
+ return ret;
+}
+
static int xhci_plat_runtime_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -470,7 +503,9 @@
}
static const struct dev_pm_ops xhci_plat_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(NULL, NULL)
+ .freeze = xhci_plat_pm_freeze,
+ .restore = xhci_plat_pm_restore,
+ .thaw = xhci_plat_pm_restore,
SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume,
xhci_plat_runtime_idle)
};
@@ -489,7 +524,6 @@
static struct platform_driver usb_xhci_driver = {
.probe = xhci_plat_probe,
.remove = xhci_plat_remove,
- .shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "xhci-hcd",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index fe2bbfb..9cba037 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -947,7 +947,7 @@
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
- if (!hcd->state)
+ if (!hcd->state || xhci->suspended)
return 0;
if (hcd->state != HC_STATE_SUSPENDED ||
@@ -1017,6 +1017,7 @@
/* step 5: remove core well power */
/* synchronize irq when using MSI-X */
xhci_msix_sync_irqs(xhci);
+ xhci->suspended = true;
return rc;
}
@@ -1036,7 +1037,7 @@
int retval = 0;
bool comp_timer_running = false;
- if (!hcd->state)
+ if (!hcd->state || !xhci->suspended)
return 0;
/* Wait a bit if either of the roothubs need to settle from the
@@ -1173,6 +1174,7 @@
/* Re-enable port polling. */
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
+ xhci->suspended = false;
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
usb_hcd_poll_rh_status(xhci->shared_hcd);
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 4c1f556..a5153ca 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1697,6 +1697,7 @@
/* Compliance Mode Recovery Data */
struct timer_list comp_mode_recovery_timer;
u32 port_status_u0;
+ bool suspended;
/* Compliance Mode Timer Triggered every 2 seconds */
#define COMP_MODE_RCVRY_MSECS 2000
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 9ca5956..c5e3032 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -46,6 +46,9 @@
#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 /* USB Product ID of Micro-CASSY Time (reserved) */
#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 /* USB Product ID of Micro-CASSY Temperature */
#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 /* USB Product ID of Micro-CASSY pH */
+#define USB_DEVICE_ID_LD_POWERANALYSERCASSY 0x1040 /* USB Product ID of Power Analyser CASSY */
+#define USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY 0x1042 /* USB Product ID of Converter Controller CASSY */
+#define USB_DEVICE_ID_LD_MACHINETESTCASSY 0x1043 /* USB Product ID of Machine Test CASSY */
#define USB_DEVICE_ID_LD_JWM 0x1080 /* USB Product ID of Joule and Wattmeter */
#define USB_DEVICE_ID_LD_DMMP 0x1081 /* USB Product ID of Digital Multimeter P (reserved) */
#define USB_DEVICE_ID_LD_UMIP 0x1090 /* USB Product ID of UMI P */
@@ -88,6 +91,9 @@
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) },
+ { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERANALYSERCASSY) },
+ { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CONVERTERCONTROLLERCASSY) },
+ { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETESTCASSY) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 934e10f..baa57bc 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -481,6 +481,7 @@
struct lvs_rh *lvs = usb_get_intfdata(intf);
sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
+ usb_poison_urb(lvs->urb); /* used in scheduled work */
flush_work(&lvs->rh_work);
usb_free_urb(lvs->urb);
}
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 57e038d..794c553 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -83,6 +83,8 @@
wait_queue_head_t wait;
int printf_size;
+ size_t printf_offset;
+ size_t printf_togo;
char *printf_buf;
struct mutex printf_lock;
@@ -374,75 +376,103 @@
return rc;
}
-/*
- * For simplicity, we read one record in one system call and throw out
- * what does not fit. This means that the following does not work:
- * dd if=/dbg/usbmon/0t bs=10
- * Also, we do not allow seeks and do not bother advancing the offset.
- */
-static ssize_t mon_text_read_t(struct file *file, char __user *buf,
- size_t nbytes, loff_t *ppos)
+static ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
+ char __user * const buf, const size_t nbytes)
{
- struct mon_reader_text *rp = file->private_data;
- struct mon_event_text *ep;
- struct mon_text_ptr ptr;
+ const size_t togo = min(nbytes, rp->printf_togo);
- ep = mon_text_read_wait(rp, file);
- if (IS_ERR(ep))
- return PTR_ERR(ep);
- mutex_lock(&rp->printf_lock);
- ptr.cnt = 0;
- ptr.pbuf = rp->printf_buf;
- ptr.limit = rp->printf_size;
-
- mon_text_read_head_t(rp, &ptr, ep);
- mon_text_read_statset(rp, &ptr, ep);
- ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
- " %d", ep->length);
- mon_text_read_data(rp, &ptr, ep);
-
- if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
- ptr.cnt = -EFAULT;
- mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
- return ptr.cnt;
+ if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo))
+ return -EFAULT;
+ rp->printf_togo -= togo;
+ rp->printf_offset += togo;
+ return togo;
}
-static ssize_t mon_text_read_u(struct file *file, char __user *buf,
- size_t nbytes, loff_t *ppos)
+/* ppos is not advanced since the llseek operation is not permitted. */
+static ssize_t mon_text_read_t(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
{
struct mon_reader_text *rp = file->private_data;
struct mon_event_text *ep;
struct mon_text_ptr ptr;
+ ssize_t ret;
- ep = mon_text_read_wait(rp, file);
- if (IS_ERR(ep))
- return PTR_ERR(ep);
mutex_lock(&rp->printf_lock);
- ptr.cnt = 0;
- ptr.pbuf = rp->printf_buf;
- ptr.limit = rp->printf_size;
- mon_text_read_head_u(rp, &ptr, ep);
- if (ep->type == 'E') {
+ if (rp->printf_togo == 0) {
+
+ ep = mon_text_read_wait(rp, file);
+ if (IS_ERR(ep)) {
+ mutex_unlock(&rp->printf_lock);
+ return PTR_ERR(ep);
+ }
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_t(rp, &ptr, ep);
mon_text_read_statset(rp, &ptr, ep);
- } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
- mon_text_read_isostat(rp, &ptr, ep);
- mon_text_read_isodesc(rp, &ptr, ep);
- } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
- mon_text_read_intstat(rp, &ptr, ep);
- } else {
- mon_text_read_statset(rp, &ptr, ep);
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ rp->printf_togo = ptr.cnt;
+ rp->printf_offset = 0;
+
+ kmem_cache_free(rp->e_slab, ep);
}
- ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
- " %d", ep->length);
- mon_text_read_data(rp, &ptr, ep);
- if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
- ptr.cnt = -EFAULT;
+ ret = mon_text_copy_to_user(rp, buf, nbytes);
mutex_unlock(&rp->printf_lock);
- kmem_cache_free(rp->e_slab, ep);
- return ptr.cnt;
+ return ret;
+}
+
+/* ppos is not advanced since the llseek operation is not permitted. */
+static ssize_t mon_text_read_u(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct mon_reader_text *rp = file->private_data;
+ struct mon_event_text *ep;
+ struct mon_text_ptr ptr;
+ ssize_t ret;
+
+ mutex_lock(&rp->printf_lock);
+
+ if (rp->printf_togo == 0) {
+
+ ep = mon_text_read_wait(rp, file);
+ if (IS_ERR(ep)) {
+ mutex_unlock(&rp->printf_lock);
+ return PTR_ERR(ep);
+ }
+ ptr.cnt = 0;
+ ptr.pbuf = rp->printf_buf;
+ ptr.limit = rp->printf_size;
+
+ mon_text_read_head_u(rp, &ptr, ep);
+ if (ep->type == 'E') {
+ mon_text_read_statset(rp, &ptr, ep);
+ } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
+ mon_text_read_isostat(rp, &ptr, ep);
+ mon_text_read_isodesc(rp, &ptr, ep);
+ } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
+ mon_text_read_intstat(rp, &ptr, ep);
+ } else {
+ mon_text_read_statset(rp, &ptr, ep);
+ }
+ ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ " %d", ep->length);
+ mon_text_read_data(rp, &ptr, ep);
+
+ rp->printf_togo = ptr.cnt;
+ rp->printf_offset = 0;
+
+ kmem_cache_free(rp->e_slab, ep);
+ }
+
+ ret = mon_text_copy_to_user(rp, buf, nbytes);
+ mutex_unlock(&rp->printf_lock);
+ return ret;
}
static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 2d9a806..579aa9a 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1774,6 +1774,7 @@
int vbus;
u8 devctl;
+ pm_runtime_get_sync(dev);
spin_lock_irqsave(&musb->lock, flags);
val = musb->a_wait_bcon;
vbus = musb_platform_get_vbus_status(musb);
@@ -1787,6 +1788,7 @@
vbus = 0;
}
spin_unlock_irqrestore(&musb->lock, flags);
+ pm_runtime_put_sync(dev);
return sprintf(buf, "Vbus %s, timeout %lu msec\n",
vbus ? "on" : "off", val);
@@ -2483,10 +2485,11 @@
musb_generic_disable(musb);
spin_unlock_irqrestore(&musb->lock, flags);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
+ musb_platform_exit(musb);
+
pm_runtime_dont_use_autosuspend(musb->controller);
pm_runtime_put_sync(musb->controller);
pm_runtime_disable(musb->controller);
- musb_platform_exit(musb);
musb_phy_callback = NULL;
if (musb->dma_controller)
musb_dma_controller_destroy(musb->dma_controller);
@@ -2710,7 +2713,8 @@
if ((devctl & mask) != (musb->context.devctl & mask))
musb->port1_status = 0;
- musb_start(musb);
+ musb_enable_interrupts(musb);
+ musb_platform_enable(musb);
spin_lock_irqsave(&musb->lock, flags);
error = musb_run_resume_work(musb);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index a55173c..f1219f6 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -442,7 +442,6 @@
req = next_request(musb_ep);
request = &req->request;
- trace_musb_req_tx(req);
csr = musb_readw(epio, MUSB_TXCSR);
musb_dbg(musb, "<== %s, txcsr %04x", musb_ep->end_point.name, csr);
@@ -481,6 +480,8 @@
u8 is_dma = 0;
bool short_packet = false;
+ trace_musb_req_tx(req);
+
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
is_dma = 1;
csr |= MUSB_TXCSR_P_WZC_BITS;
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 844a309..e85b9c2 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -114,15 +114,19 @@
}
is_in = epnum & USB_DIR_IN;
- if (is_in) {
- epnum &= 0x0f;
- ep = &musb->endpoints[epnum].ep_in;
- } else {
- ep = &musb->endpoints[epnum].ep_out;
+ epnum &= 0x0f;
+ if (epnum >= MUSB_C_NUM_EPS) {
+ handled = -EINVAL;
+ break;
}
+
+ if (is_in)
+ ep = &musb->endpoints[epnum].ep_in;
+ else
+ ep = &musb->endpoints[epnum].ep_out;
regs = musb->endpoints[epnum].regs;
- if (epnum >= MUSB_C_NUM_EPS || !ep->desc) {
+ if (!ep->desc) {
handled = -EINVAL;
break;
}
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 55c624f..e2bc915 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -418,13 +418,7 @@
}
}
- /*
- * The pipe must be broken if current urb->status is set, so don't
- * start next urb.
- * TODO: to minimize the risk of regression, only check urb->status
- * for RX, until we have a test case to understand the behavior of TX.
- */
- if ((!status || !is_in) && qh && qh->is_ready) {
+ if (qh != NULL && qh->is_ready) {
musb_dbg(musb, "... next ep%d %cX urb %p",
hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
musb_start_urb(musb, is_in, qh);
@@ -1029,7 +1023,9 @@
/* set tx_reinit and schedule the next qh */
ep->tx_reinit = 1;
}
- musb_start_urb(musb, is_in, next_qh);
+
+ if (next_qh)
+ musb_start_urb(musb, is_in, next_qh);
}
}
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 6111d39..6685f05 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -753,6 +753,7 @@
{
int i;
union power_supply_propval val;
+ bool pps_found = false;
u32 first_pdo = pd->received_pdos[0];
if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) {
@@ -768,10 +769,8 @@
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val);
- if (pd->spec_rev == USBPD_REV_30 && !rev3_sink_only) {
- bool pps_found = false;
-
- /* downgrade to 2.0 if no PPS */
+ /* Check for PPS APDOs */
+ if (pd->spec_rev == USBPD_REV_30) {
for (i = 1; i < PD_MAX_DATA_OBJ; i++) {
if ((PD_SRC_PDO_TYPE(pd->received_pdos[i]) ==
PD_SRC_PDO_TYPE_AUGMENTED) &&
@@ -780,10 +779,18 @@
break;
}
}
- if (!pps_found)
+
+ /* downgrade to 2.0 if no PPS */
+ if (!pps_found && !rev3_sink_only)
pd->spec_rev = USBPD_REV_20;
}
+ val.intval = pps_found ?
+ POWER_SUPPLY_PD_PPS_ACTIVE :
+ POWER_SUPPLY_PD_ACTIVE;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+
/* Select the first PDO (vSafe5V) immediately. */
pd_select_pdo(pd, 1, 0, 0);
@@ -942,7 +949,7 @@
/* check against received length to avoid overrun */
if (bytes_to_copy > len - sizeof(ext_hdr)) {
- usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%lu\n",
+ usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%zu\n",
bytes_to_copy, len - sizeof(ext_hdr));
bytes_to_copy = len - sizeof(ext_hdr);
}
@@ -2140,7 +2147,7 @@
usbpd_dbg(&pd->dev, "Src CapsCounter exceeded, disabling PD\n");
usbpd_set_state(pd, PE_SRC_DISABLED);
- val.intval = 0;
+ val.intval = POWER_SUPPLY_PD_INACTIVE;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE,
&val);
@@ -2160,7 +2167,7 @@
pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT;
kick_sm(pd, SENDER_RESPONSE_TIME);
- val.intval = 1;
+ val.intval = POWER_SUPPLY_PD_ACTIVE;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
break;
@@ -2352,10 +2359,6 @@
pd->src_cap_id++;
usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
-
- val.intval = 1;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_ACTIVE, &val);
} else if (pd->hard_reset_count < 3) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
} else {
@@ -2366,7 +2369,7 @@
POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
&val);
- val.intval = 0;
+ val.intval = POWER_SUPPLY_PD_INACTIVE;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
}
@@ -3577,7 +3580,7 @@
{
struct usbpd *pd = dev_get_drvdata(dev);
int pos = PD_RDO_OBJ_POS(pd->rdo);
- int type = PD_SRC_PDO_TYPE(pd->received_pdos[pos]);
+ int type = PD_SRC_PDO_TYPE(pd->received_pdos[pos - 1]);
int len;
len = scnprintf(buf, PAGE_SIZE, "Request Data Object\n"
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index e28173b..33a7f6a 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -149,6 +149,7 @@
/* override TUNEX registers value */
struct dentry *root;
u8 tune[5];
+ u8 bias_ctrl2;
struct hrtimer timer;
int soc_min_rev;
@@ -494,6 +495,10 @@
writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL,
qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+ if (qphy->bias_ctrl2)
+ writel_relaxed(qphy->bias_ctrl2,
+ qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
/* Ensure above write is completed before turning ON ref clk */
wmb();
@@ -585,6 +590,10 @@
writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL,
qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+ if (qphy->bias_ctrl2)
+ writel_relaxed(qphy->bias_ctrl2,
+ qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
/* ensure above writes are completed before re-enabling PHY */
wmb();
@@ -946,11 +955,21 @@
dev_err(qphy->phy.dev,
"can't create debugfs entry for %s\n", name);
debugfs_remove_recursive(qphy->root);
- ret = ENOMEM;
+ ret = -ENOMEM;
goto create_err;
}
}
+ file = debugfs_create_x8("bias_ctrl2", 0644, qphy->root,
+ &qphy->bias_ctrl2);
+ if (IS_ERR_OR_NULL(file)) {
+ dev_err(qphy->phy.dev,
+ "can't create debugfs entry for bias_ctrl2\n");
+ debugfs_remove_recursive(qphy->root);
+ ret = -ENOMEM;
+ goto create_err;
+ }
+
create_err:
return ret;
}
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 9c33c6e..47db12b 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -706,7 +706,9 @@
qphy->base + QUSB2PHY_PORT_INTR_CTRL);
/* Disable PHY */
- writel_relaxed(POWER_DOWN,
+ writel_relaxed(POWER_DOWN |
+ readl_relaxed(qphy->base +
+ QUSB2PHY_PORT_POWERDOWN),
qphy->base + QUSB2PHY_PORT_POWERDOWN);
/* Make sure that above write is completed */
wmb();
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index ad3bbf7..1795d24 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -25,10 +25,10 @@
#include <linux/clk.h>
#include <linux/reset.h>
-enum core_ldo_levels {
- CORE_LEVEL_NONE = 0,
- CORE_LEVEL_MIN,
- CORE_LEVEL_MAX,
+enum ldo_levels {
+ VOLTAGE_LEVEL_NONE = 0,
+ VOLTAGE_LEVEL_MIN,
+ VOLTAGE_LEVEL_MAX,
};
#define INIT_MAX_TIME_USEC 1000
@@ -38,6 +38,8 @@
#define USB_SSPHY_1P2_VOL_MAX 1200000 /* uV */
#define USB_SSPHY_HPM_LOAD 23000 /* uA */
+#define USB_SSPHY_LOAD_DEFAULT -1
+
/* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */
#define PHYSTATUS BIT(6)
@@ -119,6 +121,9 @@
int vdd_levels[3]; /* none, low, high */
struct regulator *core_ldo;
int core_voltage_levels[3];
+ struct regulator *fpc_redrive_ldo;
+ int redrive_voltage_levels[3];
+ int redrive_load;
struct clk *ref_clk_src;
struct clk *ref_clk;
struct clk *aux_clk;
@@ -226,6 +231,33 @@
}
}
+static int msm_ldo_enable(struct msm_ssphy_qmp *phy,
+ struct regulator *ldo, int *voltage_levels, int load)
+{
+ int ret = 0;
+
+ dev_dbg(phy->phy.dev,
+ "ldo: min_vol:%duV max_vol:%duV\n",
+ voltage_levels[VOLTAGE_LEVEL_MIN],
+ voltage_levels[VOLTAGE_LEVEL_MAX]);
+
+ if (load > 0) {
+ ret = regulator_set_load(ldo, load);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = regulator_set_voltage(ldo,
+ voltage_levels[VOLTAGE_LEVEL_MIN],
+ voltage_levels[VOLTAGE_LEVEL_MAX]);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(ldo);
+
+ return ret;
+}
+
static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
{
int min, rc = 0;
@@ -245,74 +277,65 @@
if (!on)
goto disable_regulators;
- rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (rc) {
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
- return rc;
+ if (phy->fpc_redrive_ldo) {
+ rc = msm_ldo_enable(phy, phy->fpc_redrive_ldo,
+ phy->redrive_voltage_levels,
+ phy->redrive_load);
+ if (rc < 0) {
+ dev_err(phy->phy.dev,
+ "enable phy->fpc_redrive_ldo failed\n");
+ return rc;
+ }
+
+ dev_dbg(phy->phy.dev,
+ "fpc redrive ldo: min_vol:%duV max_vol:%duV\n",
+ phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN],
+ phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]);
}
- dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
- phy->vdd_levels[min], phy->vdd_levels[2]);
-
- rc = regulator_enable(phy->vdd);
- if (rc) {
- dev_err(phy->phy.dev,
- "regulator_enable(phy->vdd) failed, ret=%d",
- rc);
- goto unconfig_vdd;
- }
-
- rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD);
+ rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels,
+ USB_SSPHY_LOAD_DEFAULT);
if (rc < 0) {
- dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n");
+ dev_err(phy->phy.dev, "enable phy->vdd failed\n");
+ goto disable_fpc_redrive;
+ }
+
+ dev_dbg(phy->phy.dev,
+ "vdd ldo: min_vol:%duV max_vol:%duV\n",
+ phy->vdd_levels[VOLTAGE_LEVEL_MIN],
+ phy->vdd_levels[VOLTAGE_LEVEL_MAX]);
+
+ rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels,
+ USB_SSPHY_HPM_LOAD);
+ if (rc < 0) {
+ dev_err(phy->phy.dev, "enable phy->core_ldo failed\n");
goto disable_vdd;
}
- rc = regulator_set_voltage(phy->core_ldo,
- phy->core_voltage_levels[CORE_LEVEL_MIN],
- phy->core_voltage_levels[CORE_LEVEL_MAX]);
- if (rc) {
- dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
- goto put_core_ldo_lpm;
- }
-
- rc = regulator_enable(phy->core_ldo);
- if (rc) {
- dev_err(phy->phy.dev, "Unable to enable core_ldo\n");
- goto unset_core_ldo;
- }
+ dev_dbg(phy->phy.dev,
+ "core ldo: min_vol:%duV max_vol:%duV\n",
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MIN],
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]);
return 0;
disable_regulators:
rc = regulator_disable(phy->core_ldo);
if (rc)
- dev_err(phy->phy.dev, "Unable to disable core_ldo\n");
-
-unset_core_ldo:
- rc = regulator_set_voltage(phy->core_ldo,
- phy->core_voltage_levels[CORE_LEVEL_NONE],
- phy->core_voltage_levels[CORE_LEVEL_MAX]);
- if (rc)
- dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
-
-put_core_ldo_lpm:
- rc = regulator_set_load(phy->core_ldo, 0);
- if (rc < 0)
- dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n");
+ dev_err(phy->phy.dev, "disable phy->core_ldo failed\n");
disable_vdd:
rc = regulator_disable(phy->vdd);
if (rc)
- dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
- rc);
+ dev_err(phy->phy.dev, "disable phy->vdd failed\n");
-unconfig_vdd:
- rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (rc)
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
+disable_fpc_redrive:
+ if (phy->fpc_redrive_ldo) {
+ rc = regulator_disable(phy->fpc_redrive_ldo);
+ if (rc)
+ dev_err(phy->phy.dev,
+ "disable phy->fpc_redrive_ldo failed\n");
+ }
return rc < 0 ? rc : 0;
}
@@ -634,6 +657,9 @@
if (suspend) {
if (phy->cable_connected)
msm_ssusb_qmp_enable_autonomous(phy, 1);
+ else
+ writel_relaxed(0x00,
+ phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
/* Make sure above write completed with PHY */
wmb();
@@ -944,9 +970,9 @@
}
/* Set default core voltage values */
- phy->core_voltage_levels[CORE_LEVEL_NONE] = 0;
- phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
- phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_NONE] = 0;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) &&
len == sizeof(phy->core_voltage_levels)) {
@@ -990,6 +1016,39 @@
goto err;
}
+ phy->fpc_redrive_ldo = devm_regulator_get_optional(dev, "fpc-redrive");
+ if (IS_ERR(phy->fpc_redrive_ldo)) {
+ phy->fpc_redrive_ldo = NULL;
+ dev_dbg(dev, "no FPC re-drive ldo regulator\n");
+ } else {
+ if (of_get_property(dev->of_node,
+ "qcom,redrive-voltage-level", &len) &&
+ len == sizeof(phy->redrive_voltage_levels)) {
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,redrive-voltage-level",
+ (u32 *) phy->redrive_voltage_levels,
+ len / sizeof(u32));
+ if (ret) {
+ dev_err(dev,
+ "err qcom,redrive-voltage-level\n");
+ goto err;
+ }
+ } else {
+ ret = -EINVAL;
+ dev_err(dev, "err inputs for redrive-voltage-level\n");
+ goto err;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "qcom,redrive-load",
+ &phy->redrive_load);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read redrive load\n");
+ goto err;
+ }
+
+ dev_dbg(dev, "Get FPC re-drive ldo regulator\n");
+ }
+
platform_set_drvdata(pdev, phy);
if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override"))
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 7706a48..a6326d5 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -213,8 +213,11 @@
bool enable_streaming;
bool enable_axi_prefetch;
bool vbus_low_as_hostmode;
+ bool phy_id_high_as_peripheral;
};
+#define SDP_CHECK_DELAY_MS 10000 /* in ms */
+
#define MSM_USB_BASE (motg->regs)
#define MSM_USB_PHY_CSR_BASE (motg->phy_csr_regs)
@@ -255,11 +258,26 @@
MODULE_PARM_DESC(lpm_disconnect_thresh,
"Delay before entering LPM on USB disconnect");
+static bool floated_charger_enable;
+module_param(floated_charger_enable, bool, 0644);
+MODULE_PARM_DESC(floated_charger_enable,
+ "Whether to enable floated charger");
+
/* by default debugging is enabled */
static unsigned int enable_dbg_log = 1;
module_param(enable_dbg_log, uint, 0644);
MODULE_PARM_DESC(enable_dbg_log, "Debug buffer events");
+/* Max current to be drawn for DCP charger */
+static int dcp_max_current = IDEV_CHG_MAX;
+module_param(dcp_max_current, int, 0644);
+MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
+
+static bool chg_detection_for_float_charger;
+module_param(chg_detection_for_float_charger, bool, 0644);
+MODULE_PARM_DESC(chg_detection_for_float_charger,
+ "Whether to do PHY based charger detection for float chargers");
+
static struct msm_otg *the_msm_otg;
static bool debug_bus_voting_enabled;
@@ -1322,6 +1340,7 @@
struct msm_otg_platform_data *pdata = motg->pdata;
int cnt;
bool host_bus_suspend, device_bus_suspend, sm_work_busy;
+ bool host_pc_charger;
u32 cmd_val;
u32 portsc, config2;
u32 func_ctrl;
@@ -1349,6 +1368,9 @@
if (host_bus_suspend)
msm_otg_perf_vote_update(motg, false);
+ host_pc_charger = (motg->chg_type == USB_SDP_CHARGER) ||
+ (motg->chg_type == USB_CDP_CHARGER);
+
/* !BSV, but its handling is in progress by otg sm_work */
sm_work_busy = !test_bit(B_SESS_VLD, &motg->inputs) &&
phy->otg->state == OTG_STATE_B_PERIPHERAL;
@@ -1375,8 +1397,8 @@
* Don't abort suspend in case of dcp detected by PMIC
*/
- if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend) ||
- sm_work_busy) {
+ if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
+ host_pc_charger) || sm_work_busy) {
msm_otg_dbg_log_event(phy, "LPM ENTER ABORTED",
motg->inputs, 0);
enable_irq(motg->irq);
@@ -1808,23 +1830,87 @@
}
#endif
-static void msm_otg_notify_chg_current(struct msm_otg *motg, unsigned int mA)
+static int get_psy_type(struct msm_otg *motg)
+{
+ union power_supply_propval pval = {0};
+
+ if (!psy) {
+ dev_err(motg->phy.dev, "no usb power supply registered\n");
+ return -ENODEV;
+ }
+
+ power_supply_get_property(psy, POWER_SUPPLY_PROP_REAL_TYPE, &pval);
+
+ return pval.intval;
+}
+
+static int msm_otg_notify_chg_type(struct msm_otg *motg)
+{
+ static int charger_type;
+ union power_supply_propval propval;
+ int ret = 0;
+ /*
+ * TODO
+ * Unify OTG driver charger types and power supply charger types
+ */
+ if (charger_type == motg->chg_type)
+ return 0;
+
+ if (motg->chg_type == USB_SDP_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB;
+ else if (motg->chg_type == USB_CDP_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB_CDP;
+ else if (motg->chg_type == USB_DCP_CHARGER ||
+ motg->chg_type == USB_NONCOMPLIANT_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB_DCP;
+ else if (motg->chg_type == USB_FLOATED_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB_FLOAT;
+ else
+ charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ if (!psy) {
+ dev_err(motg->phy.dev, "no usb power supply registered\n");
+ return -ENODEV;
+ }
+
+ pr_debug("Trying to set usb power supply type %d\n", charger_type);
+
+ propval.intval = charger_type;
+ ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_REAL_TYPE,
+ &propval);
+ if (ret)
+ dev_dbg(motg->phy.dev, "power supply error when setting property\n");
+
+ msm_otg_dbg_log_event(&motg->phy, "SET USB PWR SUPPLY TYPE",
+ motg->chg_type, charger_type);
+ return ret;
+}
+
+static void msm_otg_notify_charger(struct msm_otg *motg, unsigned int mA)
{
struct usb_gadget *g = motg->phy.otg->gadget;
union power_supply_propval pval = {0};
+ int psy_type;
if (g && g->is_a_peripheral)
return;
dev_dbg(motg->phy.dev, "Requested curr from USB = %u\n", mA);
- if (!psy) {
- dev_dbg(motg->phy.dev, "no usb power supply registered\n");
- return;
+
+ if (msm_otg_notify_chg_type(motg))
+ dev_dbg(motg->phy.dev, "Failed notifying %d charger type to PMIC\n",
+ motg->chg_type);
+
+ psy_type = get_psy_type(motg);
+ if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
+ if (!mA)
+ pval.intval = -ETIMEDOUT;
+ else
+ pval.intval = 1000 * mA;
+ goto set_prop;
}
- power_supply_get_property(psy, POWER_SUPPLY_PROP_REAL_TYPE, &pval);
-
- if (motg->cur_power == mA || pval.intval != POWER_SUPPLY_TYPE_USB)
+ if (motg->cur_power == mA)
return;
dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
@@ -1833,6 +1919,7 @@
/* Set max current limit in uA */
pval.intval = 1000 * mA;
+set_prop:
if (power_supply_set_property(psy, POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
&pval)) {
dev_dbg(motg->phy.dev, "power supply error when setting property\n");
@@ -1842,15 +1929,12 @@
motg->cur_power = mA;
}
-static void msm_otg_notify_chg_current_work(struct work_struct *w)
+static void msm_otg_notify_charger_work(struct work_struct *w)
{
struct msm_otg *motg = container_of(w,
- struct msm_otg, notify_chg_current_work);
- /*
- * Gadget driver uses set_power method to notify about the
- * available current based on suspend/configured states.
- */
- msm_otg_notify_chg_current(motg, motg->notify_current_mA);
+ struct msm_otg, notify_charger_work);
+
+ msm_otg_notify_charger(motg, motg->notify_current_mA);
}
static int msm_otg_set_power(struct usb_phy *phy, unsigned int mA)
@@ -1858,7 +1942,14 @@
struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
motg->notify_current_mA = mA;
- schedule_work(&motg->notify_chg_current_work);
+ /*
+ * Gadget driver uses set_power method to notify about the
+ * available current based on suspend/configured states.
+ */
+ if (motg->chg_type == USB_SDP_CHARGER ||
+ get_psy_type(motg) == POWER_SUPPLY_TYPE_USB ||
+ get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_FLOAT)
+ queue_work(motg->otg_wq, &motg->notify_charger_work);
return 0;
}
@@ -2259,6 +2350,301 @@
return true;
}
+static bool msm_chg_check_secondary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 chg_det;
+
+ chg_det = ulpi_read(phy, 0x87);
+
+ return (chg_det & 1);
+}
+
+static void msm_chg_enable_secondary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ /*
+ * Configure DM as current source, DP as current sink
+ * and enable battery charging comparators.
+ */
+ ulpi_write(phy, 0x8, 0x85);
+ ulpi_write(phy, 0x2, 0x85);
+ ulpi_write(phy, 0x1, 0x85);
+}
+
+static bool msm_chg_check_primary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 chg_det;
+ bool ret = false;
+
+ chg_det = ulpi_read(phy, 0x87);
+ ret = chg_det & 1;
+ /* Turn off VDP_SRC */
+ ulpi_write(phy, 0x3, 0x86);
+ msleep(20);
+
+ return ret;
+}
+
+static void msm_chg_enable_primary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ /*
+ * Configure DP as current source, DM as current sink
+ * and enable battery charging comparators.
+ */
+ ulpi_write(phy, 0x2, 0x85);
+ ulpi_write(phy, 0x1, 0x85);
+}
+
+static bool msm_chg_check_dcd(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 line_state;
+
+ line_state = ulpi_read(phy, 0x87);
+
+ return line_state & 2;
+}
+
+static void msm_chg_disable_dcd(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ ulpi_write(phy, 0x10, 0x86);
+ /*
+ * Disable the Rdm_down after
+ * the DCD is completed.
+ */
+ ulpi_write(phy, 0x04, 0x0C);
+}
+
+static void msm_chg_enable_dcd(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ /*
+ * Idp_src and Rdm_down are de-coupled
+ * on Femto PHY. If Idp_src alone is
+ * enabled, DCD timeout is observed with
+ * wall charger. But a genuine DCD timeout
+ * may be incorrectly interpreted. Also
+ * BC1.2 compliance testers expect Rdm_down
+ * to enabled during DCD. Enable Rdm_down
+ * explicitly before enabling the DCD.
+ */
+ ulpi_write(phy, 0x04, 0x0B);
+ ulpi_write(phy, 0x10, 0x85);
+}
+
+static void msm_chg_block_on(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 func_ctrl;
+
+ /* put the controller in non-driving mode */
+ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+
+ /* disable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xC);
+ /* Clear charger detecting control bits */
+ ulpi_write(phy, 0x1F, 0x86);
+ /* Clear alt interrupt latch and enable bits */
+ ulpi_write(phy, 0x1F, 0x92);
+ ulpi_write(phy, 0x1F, 0x95);
+ udelay(100);
+}
+
+static void msm_chg_block_off(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 func_ctrl;
+
+ /* Clear charger detecting control bits */
+ ulpi_write(phy, 0x3F, 0x86);
+ /* Clear alt interrupt latch and enable bits */
+ ulpi_write(phy, 0x1F, 0x92);
+ ulpi_write(phy, 0x1F, 0x95);
+ /* re-enable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xB);
+
+ /* put the controller in normal mode */
+ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+}
+
+#define MSM_CHG_DCD_TIMEOUT (750 * HZ/1000) /* 750 msec */
+#define MSM_CHG_DCD_POLL_TIME (50 * HZ/1000) /* 50 msec */
+#define MSM_CHG_PRIMARY_DET_TIME (50 * HZ/1000) /* TVDPSRC_ON */
+#define MSM_CHG_SECONDARY_DET_TIME (50 * HZ/1000) /* TVDMSRC_ON */
+
+static void msm_chg_detect_work(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
+ struct usb_phy *phy = &motg->phy;
+ bool is_dcd = false, tmout, vout, queue_sm_work = false;
+ static bool dcd;
+ u32 line_state, dm_vlgc;
+ unsigned long delay = 0;
+
+ dev_dbg(phy->dev, "chg detection work\n");
+ msm_otg_dbg_log_event(phy, "CHG DETECTION WORK",
+ motg->chg_state, get_pm_runtime_counter(phy->dev));
+
+ switch (motg->chg_state) {
+ case USB_CHG_STATE_UNDEFINED:
+ pm_runtime_get_sync(phy->dev);
+ msm_chg_block_on(motg);
+ case USB_CHG_STATE_IN_PROGRESS:
+ if (!motg->vbus_state) {
+ motg->chg_state = USB_CHG_STATE_UNDEFINED;
+ motg->chg_type = USB_INVALID_CHARGER;
+ msm_chg_block_off(motg);
+ pm_runtime_put_sync(phy->dev);
+ return;
+ }
+
+ msm_chg_enable_dcd(motg);
+ motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+ motg->dcd_time = 0;
+ delay = MSM_CHG_DCD_POLL_TIME;
+ break;
+ case USB_CHG_STATE_WAIT_FOR_DCD:
+ if (!motg->vbus_state) {
+ motg->chg_state = USB_CHG_STATE_IN_PROGRESS;
+ break;
+ }
+
+ is_dcd = msm_chg_check_dcd(motg);
+ motg->dcd_time += MSM_CHG_DCD_POLL_TIME;
+ tmout = motg->dcd_time >= MSM_CHG_DCD_TIMEOUT;
+ if (is_dcd || tmout) {
+ if (is_dcd)
+ dcd = true;
+ else
+ dcd = false;
+ msm_chg_disable_dcd(motg);
+ msm_chg_enable_primary_det(motg);
+ delay = MSM_CHG_PRIMARY_DET_TIME;
+ motg->chg_state = USB_CHG_STATE_DCD_DONE;
+ } else {
+ delay = MSM_CHG_DCD_POLL_TIME;
+ }
+ break;
+ case USB_CHG_STATE_DCD_DONE:
+ if (!motg->vbus_state) {
+ motg->chg_state = USB_CHG_STATE_IN_PROGRESS;
+ break;
+ }
+
+ vout = msm_chg_check_primary_det(motg);
+ line_state = readl_relaxed(USB_PORTSC) & PORTSC_LS;
+ dm_vlgc = line_state & PORTSC_LS_DM;
+ if (vout && !dm_vlgc) { /* VDAT_REF < DM < VLGC */
+ if (line_state) { /* DP > VLGC */
+ motg->chg_type = USB_NONCOMPLIANT_CHARGER;
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ } else {
+ msm_chg_enable_secondary_det(motg);
+ delay = MSM_CHG_SECONDARY_DET_TIME;
+ motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+ }
+ } else { /* DM < VDAT_REF || DM > VLGC */
+ if (line_state) /* DP > VLGC or/and DM > VLGC */
+ motg->chg_type = USB_NONCOMPLIANT_CHARGER;
+ else if (!dcd && floated_charger_enable)
+ motg->chg_type = USB_FLOATED_CHARGER;
+ else
+ motg->chg_type = USB_SDP_CHARGER;
+
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ }
+ break;
+ case USB_CHG_STATE_PRIMARY_DONE:
+ if (!motg->vbus_state) {
+ motg->chg_state = USB_CHG_STATE_IN_PROGRESS;
+ break;
+ }
+
+ vout = msm_chg_check_secondary_det(motg);
+ if (vout)
+ motg->chg_type = USB_DCP_CHARGER;
+ else
+ motg->chg_type = USB_CDP_CHARGER;
+ motg->chg_state = USB_CHG_STATE_SECONDARY_DONE;
+ /* fall through */
+ case USB_CHG_STATE_SECONDARY_DONE:
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ case USB_CHG_STATE_DETECTED:
+ if (!motg->vbus_state) {
+ motg->chg_state = USB_CHG_STATE_IN_PROGRESS;
+ break;
+ }
+
+ msm_chg_block_off(motg);
+
+ /* Enable VDP_SRC in case of DCP charger */
+ if (motg->chg_type == USB_DCP_CHARGER) {
+ ulpi_write(phy, 0x2, 0x85);
+ msm_otg_notify_charger(motg, dcp_max_current);
+ } else if (motg->chg_type == USB_NONCOMPLIANT_CHARGER)
+ msm_otg_notify_charger(motg, dcp_max_current);
+ else if (motg->chg_type == USB_FLOATED_CHARGER ||
+ motg->chg_type == USB_CDP_CHARGER)
+ msm_otg_notify_charger(motg, IDEV_CHG_MAX);
+
+ msm_otg_dbg_log_event(phy, "CHG WORK PUT: CHG_TYPE",
+ motg->chg_type, get_pm_runtime_counter(phy->dev));
+ /* to match _get at the start of chg_det_work */
+ pm_runtime_mark_last_busy(phy->dev);
+ pm_runtime_put_autosuspend(phy->dev);
+ motg->chg_state = USB_CHG_STATE_QUEUE_SM_WORK;
+ break;
+ case USB_CHG_STATE_QUEUE_SM_WORK:
+ if (!motg->vbus_state) {
+ pm_runtime_get_sync(phy->dev);
+ /* Turn off VDP_SRC if charger is DCP type */
+ if (motg->chg_type == USB_DCP_CHARGER)
+ ulpi_write(phy, 0x2, 0x86);
+
+ motg->chg_state = USB_CHG_STATE_UNDEFINED;
+ if (motg->chg_type == USB_SDP_CHARGER ||
+ motg->chg_type == USB_CDP_CHARGER)
+ queue_sm_work = true;
+
+ motg->chg_type = USB_INVALID_CHARGER;
+ msm_otg_notify_charger(motg, 0);
+ motg->cur_power = 0;
+ msm_chg_block_off(motg);
+ pm_runtime_mark_last_busy(phy->dev);
+ pm_runtime_put_autosuspend(phy->dev);
+ if (queue_sm_work)
+ queue_work(motg->otg_wq, &motg->sm_work);
+ else
+ return;
+ }
+
+ if (motg->chg_type == USB_CDP_CHARGER ||
+ motg->chg_type == USB_SDP_CHARGER)
+ queue_work(motg->otg_wq, &motg->sm_work);
+
+ return;
+ default:
+ return;
+ }
+
+ msm_otg_dbg_log_event(phy, "CHG WORK: QUEUE", motg->chg_type, delay);
+ queue_delayed_work(motg->otg_wq, &motg->chg_work, delay);
+}
+
/*
* We support OTG, Peripheral only and Host only configurations. In case
* of OTG, mode switch (host-->peripheral/peripheral-->host) can happen
@@ -2304,10 +2690,17 @@
else
clear_bit(ID, &motg->inputs);
} else if (motg->phy_irq) {
- if (msm_otg_read_phy_id_state(motg))
+ if (msm_otg_read_phy_id_state(motg)) {
set_bit(ID, &motg->inputs);
- else
+ if (pdata->phy_id_high_as_peripheral)
+ set_bit(B_SESS_VLD,
+ &motg->inputs);
+ } else {
clear_bit(ID, &motg->inputs);
+ if (pdata->phy_id_high_as_peripheral)
+ clear_bit(B_SESS_VLD,
+ &motg->inputs);
+ }
}
}
break;
@@ -2335,35 +2728,49 @@
USB_ID_GROUND;
}
+static void check_for_sdp_connection(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg, sdp_check.work);
+
+ /* Cable disconnected or device enumerated as SDP */
+ if (!motg->vbus_state || motg->phy.otg->gadget->state >=
+ USB_STATE_DEFAULT)
+ return;
+
+ /* floating D+/D- lines detected */
+ motg->vbus_state = 0;
+ msm_otg_dbg_log_event(&motg->phy, "Q RW SDP CHK", motg->vbus_state, 0);
+ msm_otg_set_vbus_state(motg->vbus_state);
+}
+
static void msm_otg_sm_work(struct work_struct *w)
{
struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
+ struct usb_phy *phy = &motg->phy;
struct usb_otg *otg = motg->phy.otg;
struct device *dev = otg->usb_phy->dev;
bool work = 0;
int ret;
pr_debug("%s work\n", usb_otg_state_string(otg->state));
- msm_otg_dbg_log_event(&motg->phy, "SM WORK:",
- otg->state, motg->inputs);
+ msm_otg_dbg_log_event(phy, "SM WORK:", otg->state, motg->inputs);
/* Just resume h/w if reqd, pm_count is handled based on state/inputs */
if (motg->resume_pending) {
- pm_runtime_get_sync(otg->usb_phy->dev);
+ pm_runtime_get_sync(dev);
if (atomic_read(&motg->in_lpm)) {
dev_err(dev, "SM WORK: USB is in LPM\n");
- msm_otg_dbg_log_event(&motg->phy,
- "SM WORK: USB IS IN LPM",
+ msm_otg_dbg_log_event(phy, "SM WORK: USB IS IN LPM",
otg->state, motg->inputs);
msm_otg_resume(motg);
}
motg->resume_pending = false;
- pm_runtime_put_noidle(otg->usb_phy->dev);
+ pm_runtime_put_noidle(dev);
}
switch (otg->state) {
case OTG_STATE_UNDEFINED:
- pm_runtime_get_sync(otg->usb_phy->dev);
+ pm_runtime_get_sync(dev);
msm_otg_reset(otg->usb_phy);
/* Add child device only after block reset */
ret = of_platform_populate(motg->pdev->dev.of_node, NULL, NULL,
@@ -2375,21 +2782,20 @@
otg->state = OTG_STATE_B_IDLE;
if (!test_bit(B_SESS_VLD, &motg->inputs) &&
test_bit(ID, &motg->inputs)) {
- msm_otg_dbg_log_event(&motg->phy,
- "PM RUNTIME: UNDEF PUT",
- get_pm_runtime_counter(otg->usb_phy->dev), 0);
- pm_runtime_put_sync(otg->usb_phy->dev);
+ msm_otg_dbg_log_event(phy, "PM RUNTIME: UNDEF PUT",
+ get_pm_runtime_counter(dev), 0);
+ pm_runtime_put_sync(dev);
break;
}
- pm_runtime_put(otg->usb_phy->dev);
+ pm_runtime_put(dev);
/* FALL THROUGH */
case OTG_STATE_B_IDLE:
if (!test_bit(ID, &motg->inputs) && otg->host) {
pr_debug("!id\n");
- msm_otg_dbg_log_event(&motg->phy, "!ID",
+ msm_otg_dbg_log_event(phy, "!ID",
motg->inputs, otg->state);
if (!otg->host) {
- msm_otg_dbg_log_event(&motg->phy,
+ msm_otg_dbg_log_event(phy,
"SM WORK: Host Not Set",
otg->state, motg->inputs);
break;
@@ -2399,30 +2805,35 @@
otg->state = OTG_STATE_A_HOST;
} else if (test_bit(B_SESS_VLD, &motg->inputs)) {
pr_debug("b_sess_vld\n");
- msm_otg_dbg_log_event(&motg->phy, "B_SESS_VLD",
+ msm_otg_dbg_log_event(phy, "B_SESS_VLD",
motg->inputs, otg->state);
if (!otg->gadget) {
- msm_otg_dbg_log_event(&motg->phy,
+ msm_otg_dbg_log_event(phy,
"SM WORK: Gadget Not Set",
otg->state, motg->inputs);
break;
}
+ if (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_FLOAT)
+ queue_delayed_work(motg->otg_wq,
+ &motg->sdp_check,
+ msecs_to_jiffies(SDP_CHECK_DELAY_MS));
+
pm_runtime_get_sync(otg->usb_phy->dev);
msm_otg_start_peripheral(otg, 1);
otg->state = OTG_STATE_B_PERIPHERAL;
} else {
pr_debug("Cable disconnected\n");
- msm_otg_dbg_log_event(&motg->phy, "RT: Cable DISC",
- get_pm_runtime_counter(otg->usb_phy->dev), 0);
-
- msm_otg_notify_chg_current(motg, 0);
+ msm_otg_dbg_log_event(phy, "RT: Cable DISC",
+ get_pm_runtime_counter(dev), 0);
+ msm_otg_notify_charger(motg, 0);
}
break;
case OTG_STATE_B_PERIPHERAL:
if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ cancel_delayed_work_sync(&motg->sdp_check);
msm_otg_start_peripheral(otg, 0);
- msm_otg_dbg_log_event(&motg->phy, "RT PM: B_PERI A PUT",
+ msm_otg_dbg_log_event(phy, "RT PM: B_PERI A PUT",
get_pm_runtime_counter(dev), 0);
/* _put for _get done on cable connect in B_IDLE */
pm_runtime_mark_last_busy(dev);
@@ -2432,8 +2843,7 @@
work = 1;
} else if (test_bit(A_BUS_SUSPEND, &motg->inputs)) {
pr_debug("a_bus_suspend\n");
- msm_otg_dbg_log_event(&motg->phy,
- "BUS_SUSPEND: PM RT PUT",
+ msm_otg_dbg_log_event(phy, "BUS_SUSPEND: PM RT PUT",
get_pm_runtime_counter(dev), 0);
otg->state = OTG_STATE_B_SUSPEND;
/* _get on connect in B_IDLE or host resume in B_SUSP */
@@ -2443,6 +2853,7 @@
break;
case OTG_STATE_B_SUSPEND:
if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ cancel_delayed_work_sync(&motg->sdp_check);
msm_otg_start_peripheral(otg, 0);
otg->state = OTG_STATE_B_IDLE;
/* Schedule work to finish cable disconnect processing*/
@@ -2450,8 +2861,7 @@
} else if (!test_bit(A_BUS_SUSPEND, &motg->inputs)) {
pr_debug("!a_bus_suspend\n");
otg->state = OTG_STATE_B_PERIPHERAL;
- msm_otg_dbg_log_event(&motg->phy,
- "BUS_RESUME: PM RT GET",
+ msm_otg_dbg_log_event(phy, "BUS_RESUME: PM RT GET",
get_pm_runtime_counter(dev), 0);
pm_runtime_get_sync(dev);
}
@@ -2568,7 +2978,26 @@
else
set_bit(ID, &motg->inputs);
}
- msm_otg_kick_sm_work(motg);
+
+ /*
+ * Enable PHY based charger detection in 2 cases:
+ * 1. PMI not capable of doing charger detection and provides VBUS
+ * notification with UNKNOWN psy type.
+ * 2. Data lines have been cut off from PMI, in which case it provides
+ * VBUS notification with FLOAT psy type and we want to do PHY based
+ * charger detection by setting 'chg_detection_for_float_charger'.
+ */
+ if (test_bit(B_SESS_VLD, &motg->inputs) && !motg->chg_detection) {
+ if ((get_psy_type(motg) == POWER_SUPPLY_TYPE_UNKNOWN) ||
+ (get_psy_type(motg) == POWER_SUPPLY_TYPE_USB_FLOAT &&
+ chg_detection_for_float_charger))
+ motg->chg_detection = true;
+ }
+
+ if (motg->chg_detection)
+ queue_delayed_work(motg->otg_wq, &motg->chg_work, 0);
+ else
+ msm_otg_kick_sm_work(motg);
}
static void msm_id_status_w(struct work_struct *w)
@@ -2594,6 +3023,8 @@
gpio_direction_input(motg->pdata->switch_sel_gpio);
if (!test_and_set_bit(ID, &motg->inputs)) {
pr_debug("ID set\n");
+ if (motg->pdata->phy_id_high_as_peripheral)
+ set_bit(B_SESS_VLD, &motg->inputs);
msm_otg_dbg_log_event(&motg->phy, "ID SET",
motg->inputs, motg->phy.otg->state);
work = 1;
@@ -2603,6 +3034,8 @@
gpio_direction_output(motg->pdata->switch_sel_gpio, 1);
if (test_and_clear_bit(ID, &motg->inputs)) {
pr_debug("ID clear\n");
+ if (motg->pdata->phy_id_high_as_peripheral)
+ clear_bit(B_SESS_VLD, &motg->inputs);
msm_otg_dbg_log_event(&motg->phy, "ID CLEAR",
motg->inputs, motg->phy.otg->state);
work = 1;
@@ -3381,6 +3814,10 @@
pdata->vbus_low_as_hostmode = of_property_read_bool(node,
"qcom,vbus-low-as-hostmode");
+
+ pdata->phy_id_high_as_peripheral = of_property_read_bool(node,
+ "qcom,phy-id-high-as-peripheral");
+
return pdata;
}
@@ -3822,10 +4259,11 @@
motg->id_state = USB_ID_FLOAT;
set_bit(ID, &motg->inputs);
INIT_WORK(&motg->sm_work, msm_otg_sm_work);
+ INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
INIT_DELAYED_WORK(&motg->id_status_work, msm_id_status_w);
INIT_DELAYED_WORK(&motg->perf_vote_work, msm_otg_perf_vote_work);
- INIT_WORK(&motg->notify_chg_current_work,
- msm_otg_notify_chg_current_work);
+ INIT_DELAYED_WORK(&motg->sdp_check, check_for_sdp_connection);
+ INIT_WORK(&motg->notify_charger_work, msm_otg_notify_charger_work);
motg->otg_wq = alloc_ordered_workqueue("k_otg", 0);
if (!motg->otg_wq) {
pr_err("%s: Unable to create workqueue otg_wq\n",
@@ -4176,11 +4614,13 @@
if (psy)
power_supply_put(psy);
msm_otg_debugfs_cleanup();
+ cancel_delayed_work_sync(&motg->chg_work);
+ cancel_delayed_work_sync(&motg->sdp_check);
cancel_delayed_work_sync(&motg->id_status_work);
cancel_delayed_work_sync(&motg->perf_vote_work);
msm_otg_perf_vote_update(motg, false);
cancel_work_sync(&motg->sm_work);
- cancel_work_sync(&motg->notify_chg_current_work);
+ cancel_work_sync(&motg->notify_charger_work);
destroy_workqueue(motg->otg_wq);
pm_runtime_resume(&pdev->dev);
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 6c6a3a8..968ade5 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -1001,6 +1001,10 @@
if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
goto usbhsf_pio_prepare_pop;
+ /* return at this time if the pipe is running */
+ if (usbhs_pipe_is_running(pipe))
+ return 0;
+
usbhs_pipe_config_change_bfre(pipe, 1);
ret = usbhsf_fifo_select(pipe, fifo, 0);
@@ -1191,6 +1195,7 @@
usbhsf_fifo_clear(pipe, fifo);
pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
+ usbhs_pipe_running(pipe, 0);
usbhsf_dma_stop(pipe, fifo);
usbhsf_dma_unmap(pkt);
usbhsf_fifo_unselect(pipe, pipe->fifo);
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 584ae8c..77c3ebe 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -62,6 +62,7 @@
- Fundamental Software dongle.
- Google USB serial devices
- HP4x calculators
+ - Libtransistor USB console
- a number of Motorola phones
- Motorola Tetra devices
- Novatel Wireless GPS receivers
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 3178d8a..90d7c6e 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -152,6 +152,7 @@
{ USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
+ { USB_DEVICE(0x155A, 0x1006) }, /* ELDAT Easywave RX09 */
{ USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
{ USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */
{ USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
@@ -210,6 +211,7 @@
{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
{ USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
+ { USB_DEVICE(0x3923, 0x7A0B) }, /* National Instruments USB Serial Console */
{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
{ } /* Terminating Entry */
};
@@ -743,7 +745,7 @@
unsigned int cflag;
struct cp210x_flow_ctl flow_ctl;
u32 baud;
- u16 bits;
+ u16 bits = 0;
u32 ctl_hs;
cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 0c743e4..2e2f7363 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -773,6 +773,7 @@
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) },
+ { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S03_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_59_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57A_PID) },
@@ -935,6 +936,7 @@
{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) },
{ USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
@@ -1909,7 +1911,8 @@
return ftdi_jtag_probe(serial);
if (udev->product &&
- (!strcmp(udev->product, "BeagleBone/XDS100V2") ||
+ (!strcmp(udev->product, "Arrow USB Blaster") ||
+ !strcmp(udev->product, "BeagleBone/XDS100V2") ||
!strcmp(udev->product, "SNAP Connect E10")))
return ftdi_jtag_probe(serial);
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 543d280..76a10b2 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -922,6 +922,9 @@
/*
* RT Systems programming cables for various ham radios
*/
+/* This device uses the VID of FTDI */
+#define RTSYSTEMS_USB_VX8_PID 0x9e50 /* USB-VX8 USB to 7 pin modular plug for Yaesu VX-8 radio */
+
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
#define RTSYSTEMS_USB_S03_PID 0x9001 /* RTS-03 USB to Serial Adapter */
#define RTSYSTEMS_USB_59_PID 0x9e50 /* USB-59 USB to 8 pin plug */
@@ -1441,6 +1444,12 @@
#define FTDI_CINTERION_MC55I_PID 0xA951
/*
+ * Product: FirmwareHubEmulator
+ * Manufacturer: Harman Becker Automotive Systems
+ */
+#define FTDI_FHE_PID 0xA9A0
+
+/*
* Product: Comet Caller ID decoder
* Manufacturer: Crucible Technologies
*/
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 1799aa0..d982c45 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -236,6 +236,8 @@
/* These Quectel products use Qualcomm's vendor ID */
#define QUECTEL_PRODUCT_UC20 0x9003
#define QUECTEL_PRODUCT_UC15 0x9090
+/* These u-blox products use Qualcomm's vendor ID */
+#define UBLOX_PRODUCT_R410M 0x90b2
/* These Yuga products use Qualcomm's vendor ID */
#define YUGA_PRODUCT_CLM920_NC5 0x9625
@@ -244,6 +246,7 @@
#define QUECTEL_PRODUCT_EC21 0x0121
#define QUECTEL_PRODUCT_EC25 0x0125
#define QUECTEL_PRODUCT_BG96 0x0296
+#define QUECTEL_PRODUCT_EP06 0x0306
#define CMOTECH_VENDOR_ID 0x16d8
#define CMOTECH_PRODUCT_6001 0x6001
@@ -550,147 +553,15 @@
#define WETELECOM_PRODUCT_6802 0x6802
#define WETELECOM_PRODUCT_WMD300 0x6803
-struct option_blacklist_info {
- /* bitmask of interface numbers blacklisted for send_setup */
- const unsigned long sendsetup;
- /* bitmask of interface numbers that are reserved */
- const unsigned long reserved;
-};
-static const struct option_blacklist_info four_g_w14_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
-};
+/* Device flags */
-static const struct option_blacklist_info four_g_w100_blacklist = {
- .sendsetup = BIT(1) | BIT(2),
- .reserved = BIT(3),
-};
+/* Interface does not support modem-control requests */
+#define NCTRL(ifnum) ((BIT(ifnum) & 0xff) << 8)
-static const struct option_blacklist_info alcatel_x200_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
- .reserved = BIT(4),
-};
+/* Interface is reserved */
+#define RSVD(ifnum) ((BIT(ifnum) & 0xff) << 0)
-static const struct option_blacklist_info zte_0037_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
-};
-
-static const struct option_blacklist_info zte_k3765_z_blacklist = {
- .sendsetup = BIT(0) | BIT(1) | BIT(2),
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info zte_ad3812_z_blacklist = {
- .sendsetup = BIT(0) | BIT(1) | BIT(2),
-};
-
-static const struct option_blacklist_info zte_mc2718_z_blacklist = {
- .sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4),
-};
-
-static const struct option_blacklist_info zte_mc2716_z_blacklist = {
- .sendsetup = BIT(1) | BIT(2) | BIT(3),
-};
-
-static const struct option_blacklist_info zte_me3620_mbim_blacklist = {
- .reserved = BIT(2) | BIT(3) | BIT(4),
-};
-
-static const struct option_blacklist_info zte_me3620_xl_blacklist = {
- .reserved = BIT(3) | BIT(4) | BIT(5),
-};
-
-static const struct option_blacklist_info zte_zm8620_x_blacklist = {
- .reserved = BIT(3) | BIT(4) | BIT(5),
-};
-
-static const struct option_blacklist_info huawei_cdc12_blacklist = {
- .reserved = BIT(1) | BIT(2),
-};
-
-static const struct option_blacklist_info net_intf0_blacklist = {
- .reserved = BIT(0),
-};
-
-static const struct option_blacklist_info net_intf1_blacklist = {
- .reserved = BIT(1),
-};
-
-static const struct option_blacklist_info net_intf2_blacklist = {
- .reserved = BIT(2),
-};
-
-static const struct option_blacklist_info net_intf3_blacklist = {
- .reserved = BIT(3),
-};
-
-static const struct option_blacklist_info net_intf4_blacklist = {
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info net_intf5_blacklist = {
- .reserved = BIT(5),
-};
-
-static const struct option_blacklist_info net_intf6_blacklist = {
- .reserved = BIT(6),
-};
-
-static const struct option_blacklist_info zte_mf626_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info zte_1255_blacklist = {
- .reserved = BIT(3) | BIT(4),
-};
-
-static const struct option_blacklist_info simcom_sim7100e_blacklist = {
- .reserved = BIT(5) | BIT(6),
-};
-
-static const struct option_blacklist_info telit_me910_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(3),
-};
-
-static const struct option_blacklist_info telit_me910_dual_modem_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(3),
-};
-
-static const struct option_blacklist_info telit_le910_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(2),
-};
-
-static const struct option_blacklist_info telit_le920_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(5),
-};
-
-static const struct option_blacklist_info telit_le920a4_blacklist_1 = {
- .sendsetup = BIT(0),
- .reserved = BIT(1),
-};
-
-static const struct option_blacklist_info telit_le922_blacklist_usbcfg0 = {
- .sendsetup = BIT(2),
- .reserved = BIT(0) | BIT(1) | BIT(3),
-};
-
-static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(2) | BIT(3),
-};
-
-static const struct option_blacklist_info cinterion_rmnet2_blacklist = {
- .reserved = BIT(4) | BIT(5),
-};
-
-static const struct option_blacklist_info yuga_clm920_nc5_blacklist = {
- .reserved = BIT(1) | BIT(4),
-};
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
@@ -724,26 +595,26 @@
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, 0xea42),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S6, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ .driver_info = RSVD(1) | RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ .driver_info = RSVD(1) | RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x14ac, 0xff, 0xff, 0xff), /* Huawei E1820 */
- .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ .driver_info = RSVD(1) | RSVD(2) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) },
@@ -1188,65 +1059,70 @@
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, 0x6001, 0xff, 0xff, 0xff), /* 4G LTE usb-modem U901 */
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
/* Quectel products using Qualcomm vendor ID */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
/* Yuga products use Qualcomm vendor ID */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5),
- .driver_info = (kernel_ulong_t)&yuga_clm920_nc5_blacklist },
+ .driver_info = RSVD(1) | RSVD(4) },
+ /* u-blox products using Qualcomm vendor ID */
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, UBLOX_PRODUCT_R410M),
+ .driver_info = RSVD(1) | RSVD(3) },
/* Quectel products using Quectel vendor ID */
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06),
+ .driver_info = RSVD(4) | RSVD(5) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6004) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6005) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_628A) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHE_628S),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_301),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628S) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_680) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_685A) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720S),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7002),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629K),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7004),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7005) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_629),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629S),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720I),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7212),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7213),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7251),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7252),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7253),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) },
@@ -1254,38 +1130,38 @@
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
- .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
- .driver_info = (kernel_ulong_t)&telit_me910_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
- .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist },
+ .driver_info = NCTRL(0) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
- .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
- .driver_info = (kernel_ulong_t)&telit_le920_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(5) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1207) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1208),
- .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 },
+ .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1211),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1212),
- .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 },
+ .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) },
@@ -1301,58 +1177,58 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff,
- 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff),
+ .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_0037_blacklist },
+ .driver_info = NCTRL(0) | NCTRL(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) },
@@ -1377,26 +1253,26 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0135, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0136, 0xff, 0xff, 0xff) },
@@ -1412,50 +1288,50 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0189, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0196, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0197, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0199, 0xff, 0xff, 0xff), /* ZTE MF820S */
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0200, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0201, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0330, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0395, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0412, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0414, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0417, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1018, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1021, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) },
@@ -1572,23 +1448,23 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_1255_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) },
@@ -1603,7 +1479,7 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1268, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1269, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1271, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) },
@@ -1639,17 +1515,17 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1303, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1333, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff), /* ZTE MF91 */
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G v2 */
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
@@ -1667,8 +1543,8 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1596, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1598, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1600, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff,
- 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff),
+ .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */
@@ -1679,20 +1555,20 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) },
@@ -1844,19 +1720,19 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist },
+ .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) | NCTRL(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
+ .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
+ .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L),
- .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM),
- .driver_info = (kernel_ulong_t)&zte_me3620_mbim_blacklist },
+ .driver_info = RSVD(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X),
- .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X),
- .driver_info = (kernel_ulong_t)&zte_zm8620_x_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
@@ -1876,37 +1752,34 @@
{ USB_DEVICE(ALINK_VENDOR_ID, ALINK_PRODUCT_PH300) },
{ USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
{ USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E),
- .driver_info = (kernel_ulong_t)&simcom_sim7100e_blacklist },
+ .driver_info = RSVD(5) | RSVD(6) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
- .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist
- },
+ .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x0052),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b6),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b7),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L800MA),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) },
{ USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14),
- .driver_info = (kernel_ulong_t)&four_g_w14_blacklist
- },
+ .driver_info = NCTRL(0) | NCTRL(1) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100),
- .driver_info = (kernel_ulong_t)&four_g_w100_blacklist
- },
+ .driver_info = NCTRL(1) | NCTRL(2) | RSVD(3) },
{USB_DEVICE(LONGCHEER_VENDOR_ID, FUJISOFT_PRODUCT_FS040U),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist},
+ .driver_info = RSVD(3)},
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) },
{ USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) },
@@ -1932,14 +1805,14 @@
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_E) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_P) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
- .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist },
+ .driver_info = RSVD(4) | RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
@@ -1949,20 +1822,20 @@
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, /* HC28 enumerates with Siemens or Cinterion VID depending on FW revision */
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD120),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD140),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD155),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD160),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD500),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/
{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM600) },
@@ -2039,9 +1912,9 @@
{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) },
{ USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) }, /* TP-Link LTE Module */
{ USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d01, 0xff, 0x02, 0x01) }, /* D-Link DWM-156 (variant) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d01, 0xff, 0x00, 0x00) }, /* D-Link DWM-156 (variant) */
@@ -2052,9 +1925,9 @@
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) }, /* D-Link DWM-157 C1 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
@@ -2114,7 +1987,7 @@
struct usb_interface_descriptor *iface_desc =
&serial->interface->cur_altsetting->desc;
struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
- const struct option_blacklist_info *blacklist;
+ unsigned long device_flags = id->driver_info;
/* Never bind to the CD-Rom emulation interface */
if (iface_desc->bInterfaceClass == 0x08)
@@ -2125,9 +1998,7 @@
* the same class/subclass/protocol as the serial interfaces. Look at
* the Windows driver .INF files for reserved interface numbers.
*/
- blacklist = (void *)id->driver_info;
- if (blacklist && test_bit(iface_desc->bInterfaceNumber,
- &blacklist->reserved))
+ if (device_flags & RSVD(iface_desc->bInterfaceNumber))
return -ENODEV;
/*
* Don't bind network interface on Samsung GT-B3730, it is handled by
@@ -2138,8 +2009,8 @@
iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
return -ENODEV;
- /* Store the blacklist info so we can use it during attach. */
- usb_set_serial_data(serial, (void *)blacklist);
+ /* Store the device flags so we can use them during attach. */
+ usb_set_serial_data(serial, (void *)device_flags);
return 0;
}
@@ -2147,22 +2018,21 @@
static int option_attach(struct usb_serial *serial)
{
struct usb_interface_descriptor *iface_desc;
- const struct option_blacklist_info *blacklist;
struct usb_wwan_intf_private *data;
+ unsigned long device_flags;
data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
if (!data)
return -ENOMEM;
- /* Retrieve blacklist info stored at probe. */
- blacklist = usb_get_serial_data(serial);
+ /* Retrieve device flags stored at probe. */
+ device_flags = (unsigned long)usb_get_serial_data(serial);
iface_desc = &serial->interface->cur_altsetting->desc;
- if (!blacklist || !test_bit(iface_desc->bInterfaceNumber,
- &blacklist->sendsetup)) {
+ if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber)))
data->use_send_setup = 1;
- }
+
spin_lock_init(&data->susp_lock);
usb_set_serial_data(serial, data);
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 6aa7ff2..2674da4 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -66,6 +66,11 @@
0x01) }
DEVICE(google, GOOGLE_IDS);
+/* Libtransistor USB console */
+#define LIBTRANSISTOR_IDS() \
+ { USB_DEVICE(0x1209, 0x8b00) }
+DEVICE(libtransistor, LIBTRANSISTOR_IDS);
+
/* ViVOpay USB Serial Driver */
#define VIVOPAY_IDS() \
{ USB_DEVICE(0x1d5f, 0x1004) } /* ViVOpay 8800 */
@@ -113,6 +118,7 @@
&funsoft_device,
&flashloader_device,
&google_device,
+ &libtransistor_device,
&vivopay_device,
&moto_modem_device,
&motorola_tetra_device,
@@ -129,6 +135,7 @@
FUNSOFT_IDS(),
FLASHLOADER_IDS(),
GOOGLE_IDS(),
+ LIBTRANSISTOR_IDS(),
VIVOPAY_IDS(),
MOTO_IDS(),
MOTOROLA_TETRA_IDS(),
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 337a0be..dbc3801 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -338,47 +338,48 @@
goto exit;
}
- if (retval == sizeof(*connection_info)) {
- connection_info = (struct visor_connection_info *)
- transfer_buffer;
-
- num_ports = le16_to_cpu(connection_info->num_ports);
- for (i = 0; i < num_ports; ++i) {
- switch (
- connection_info->connections[i].port_function_id) {
- case VISOR_FUNCTION_GENERIC:
- string = "Generic";
- break;
- case VISOR_FUNCTION_DEBUGGER:
- string = "Debugger";
- break;
- case VISOR_FUNCTION_HOTSYNC:
- string = "HotSync";
- break;
- case VISOR_FUNCTION_CONSOLE:
- string = "Console";
- break;
- case VISOR_FUNCTION_REMOTE_FILE_SYS:
- string = "Remote File System";
- break;
- default:
- string = "unknown";
- break;
- }
- dev_info(dev, "%s: port %d, is for %s use\n",
- serial->type->description,
- connection_info->connections[i].port, string);
- }
+ if (retval != sizeof(*connection_info)) {
+ dev_err(dev, "Invalid connection information received from device\n");
+ retval = -ENODEV;
+ goto exit;
}
- /*
- * Handle devices that report invalid stuff here.
- */
+
+ connection_info = (struct visor_connection_info *)transfer_buffer;
+
+ num_ports = le16_to_cpu(connection_info->num_ports);
+
+ /* Handle devices that report invalid stuff here. */
if (num_ports == 0 || num_ports > 2) {
dev_warn(dev, "%s: No valid connect info available\n",
serial->type->description);
num_ports = 2;
}
+ for (i = 0; i < num_ports; ++i) {
+ switch (connection_info->connections[i].port_function_id) {
+ case VISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case VISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case VISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case VISOR_FUNCTION_CONSOLE:
+ string = "Console";
+ break;
+ case VISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ dev_info(dev, "%s: port %d, is for %s use\n",
+ serial->type->description,
+ connection_info->connections[i].port, string);
+ }
dev_info(dev, "%s: Number of ports: %d\n", serial->type->description,
num_ports);
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 4340b49..4d6eb48 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -1942,6 +1942,8 @@
bcb->CDB[0] = 0xEF;
result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0);
+ if (us->srb != NULL)
+ scsi_set_resid(us->srb, 0);
info->BIN_FLAG = flag;
kfree(buf);
@@ -2295,21 +2297,22 @@
static int ene_transport(struct scsi_cmnd *srb, struct us_data *us)
{
- int result = 0;
+ int result = USB_STOR_XFER_GOOD;
struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
/*US_DEBUG(usb_stor_show_command(us, srb)); */
scsi_set_resid(srb, 0);
- if (unlikely(!(info->SD_Status.Ready || info->MS_Status.Ready))) {
+ if (unlikely(!(info->SD_Status.Ready || info->MS_Status.Ready)))
result = ene_init(us);
- } else {
+ if (result == USB_STOR_XFER_GOOD) {
+ result = USB_STOR_TRANSPORT_ERROR;
if (info->SD_Status.Ready)
result = sd_scsi_irp(us, srb);
if (info->MS_Status.Ready)
result = ms_scsi_irp(us, srb);
}
- return 0;
+ return result;
}
static struct scsi_host_template ene_ub6250_host_template;
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 6891e90..a96dcc6 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -1076,7 +1076,7 @@
return 0;
err = uas_configure_endpoints(devinfo);
- if (err && err != ENODEV)
+ if (err && err != -ENODEV)
shost_printk(KERN_ERR, shost,
"%s: alloc streams error %d after reset",
__func__, err);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index b605115..ca3a5d4 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2137,6 +2137,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_BROKEN_FUA ),
+/* Reported by Teijo Kinnunen <teijo.kinnunen@code-q.fi> */
+UNUSUAL_DEV( 0x152d, 0x2567, 0x0117, 0x0117,
+ "JMicron",
+ "USB to ATA/ATAPI Bridge",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_BROKEN_FUA ),
+
/* Reported-by George Cherian <george.cherian@cavium.com> */
UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999,
"JMicron",
diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h
index 910f027..84c0599 100644
--- a/drivers/usb/usbip/stub.h
+++ b/drivers/usb/usbip/stub.h
@@ -87,6 +87,7 @@
struct stub_device *sdev;
struct usb_device *udev;
char shutdown_busid;
+ spinlock_t busid_lock;
};
/* stub_priv is allocated from stub_priv_cache */
@@ -97,6 +98,7 @@
/* stub_main.c */
struct bus_id_priv *get_busid_priv(const char *busid);
+void put_busid_priv(struct bus_id_priv *bid);
int del_match_busid(char *busid);
void stub_device_cleanup_urbs(struct stub_device *sdev);
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index 3550224..8e629b6 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -314,9 +314,9 @@
struct stub_device *sdev = NULL;
const char *udev_busid = dev_name(&udev->dev);
struct bus_id_priv *busid_priv;
- int rc;
+ int rc = 0;
- dev_dbg(&udev->dev, "Enter\n");
+ dev_dbg(&udev->dev, "Enter probe\n");
/* check we should claim or not by busid_table */
busid_priv = get_busid_priv(udev_busid);
@@ -331,13 +331,15 @@
* other matched drivers by the driver core.
* See driver_probe_device() in driver/base/dd.c
*/
- return -ENODEV;
+ rc = -ENODEV;
+ goto call_put_busid_priv;
}
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
udev_busid);
- return -ENODEV;
+ rc = -ENODEV;
+ goto call_put_busid_priv;
}
if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
@@ -345,13 +347,16 @@
"%s is attached on vhci_hcd... skip!\n",
udev_busid);
- return -ENODEV;
+ rc = -ENODEV;
+ goto call_put_busid_priv;
}
/* ok, this is my device */
sdev = stub_device_alloc(udev);
- if (!sdev)
- return -ENOMEM;
+ if (!sdev) {
+ rc = -ENOMEM;
+ goto call_put_busid_priv;
+ }
dev_info(&udev->dev,
"usbip-host: register new device (bus %u dev %u)\n",
@@ -383,7 +388,9 @@
}
busid_priv->status = STUB_BUSID_ALLOC;
- return 0;
+ rc = 0;
+ goto call_put_busid_priv;
+
err_files:
usb_hub_release_port(udev->parent, udev->portnum,
(struct usb_dev_state *) udev);
@@ -393,6 +400,9 @@
busid_priv->sdev = NULL;
stub_device_free(sdev);
+
+call_put_busid_priv:
+ put_busid_priv(busid_priv);
return rc;
}
@@ -418,7 +428,7 @@
struct bus_id_priv *busid_priv;
int rc;
- dev_dbg(&udev->dev, "Enter\n");
+ dev_dbg(&udev->dev, "Enter disconnect\n");
busid_priv = get_busid_priv(udev_busid);
if (!busid_priv) {
@@ -431,7 +441,7 @@
/* get stub_device */
if (!sdev) {
dev_err(&udev->dev, "could not get device");
- return;
+ goto call_put_busid_priv;
}
dev_set_drvdata(&udev->dev, NULL);
@@ -446,12 +456,12 @@
(struct usb_dev_state *) udev);
if (rc) {
dev_dbg(&udev->dev, "unable to release port\n");
- return;
+ goto call_put_busid_priv;
}
/* If usb reset is called from event handler */
if (usbip_in_eh(current))
- return;
+ goto call_put_busid_priv;
/* shutdown the current connection */
shutdown_busid(busid_priv);
@@ -462,12 +472,11 @@
busid_priv->sdev = NULL;
stub_device_free(sdev);
- if (busid_priv->status == STUB_BUSID_ALLOC) {
+ if (busid_priv->status == STUB_BUSID_ALLOC)
busid_priv->status = STUB_BUSID_ADDED;
- } else {
- busid_priv->status = STUB_BUSID_OTHER;
- del_match_busid((char *)udev_busid);
- }
+
+call_put_busid_priv:
+ put_busid_priv(busid_priv);
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index 325b4c0..fa90496 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -28,6 +28,7 @@
#define DRIVER_DESC "USB/IP Host Driver"
struct kmem_cache *stub_priv_cache;
+
/*
* busid_tables defines matching busids that usbip can grab. A user can change
* dynamically what device is locally used and what device is exported to a
@@ -39,6 +40,8 @@
static void init_busid_table(void)
{
+ int i;
+
/*
* This also sets the bus_table[i].status to
* STUB_BUSID_OTHER, which is 0.
@@ -46,6 +49,9 @@
memset(busid_table, 0, sizeof(busid_table));
spin_lock_init(&busid_table_lock);
+
+ for (i = 0; i < MAX_BUSID; i++)
+ spin_lock_init(&busid_table[i].busid_lock);
}
/*
@@ -57,15 +63,20 @@
int i;
int idx = -1;
- for (i = 0; i < MAX_BUSID; i++)
+ for (i = 0; i < MAX_BUSID; i++) {
+ spin_lock(&busid_table[i].busid_lock);
if (busid_table[i].name[0])
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
idx = i;
+ spin_unlock(&busid_table[i].busid_lock);
break;
}
+ spin_unlock(&busid_table[i].busid_lock);
+ }
return idx;
}
+/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
struct bus_id_priv *get_busid_priv(const char *busid)
{
int idx;
@@ -73,13 +84,22 @@
spin_lock(&busid_table_lock);
idx = get_busid_idx(busid);
- if (idx >= 0)
+ if (idx >= 0) {
bid = &(busid_table[idx]);
+ /* get busid_lock before returning */
+ spin_lock(&bid->busid_lock);
+ }
spin_unlock(&busid_table_lock);
return bid;
}
+void put_busid_priv(struct bus_id_priv *bid)
+{
+ if (bid)
+ spin_unlock(&bid->busid_lock);
+}
+
static int add_match_busid(char *busid)
{
int i;
@@ -92,15 +112,19 @@
goto out;
}
- for (i = 0; i < MAX_BUSID; i++)
+ for (i = 0; i < MAX_BUSID; i++) {
+ spin_lock(&busid_table[i].busid_lock);
if (!busid_table[i].name[0]) {
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
(busid_table[i].status != STUB_BUSID_REMOV))
busid_table[i].status = STUB_BUSID_ADDED;
ret = 0;
+ spin_unlock(&busid_table[i].busid_lock);
break;
}
+ spin_unlock(&busid_table[i].busid_lock);
+ }
out:
spin_unlock(&busid_table_lock);
@@ -121,6 +145,8 @@
/* found */
ret = 0;
+ spin_lock(&busid_table[idx].busid_lock);
+
if (busid_table[idx].status == STUB_BUSID_OTHER)
memset(busid_table[idx].name, 0, BUSID_SIZE);
@@ -128,6 +154,7 @@
(busid_table[idx].status != STUB_BUSID_ADDED))
busid_table[idx].status = STUB_BUSID_REMOV;
+ spin_unlock(&busid_table[idx].busid_lock);
out:
spin_unlock(&busid_table_lock);
@@ -140,9 +167,12 @@
char *out = buf;
spin_lock(&busid_table_lock);
- for (i = 0; i < MAX_BUSID; i++)
+ for (i = 0; i < MAX_BUSID; i++) {
+ spin_lock(&busid_table[i].busid_lock);
if (busid_table[i].name[0])
out += sprintf(out, "%s ", busid_table[i].name);
+ spin_unlock(&busid_table[i].busid_lock);
+ }
spin_unlock(&busid_table_lock);
out += sprintf(out, "\n");
@@ -184,6 +214,51 @@
static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid,
store_match_busid);
+static int do_rebind(char *busid, struct bus_id_priv *busid_priv)
+{
+ int ret;
+
+ /* device_attach() callers should hold parent lock for USB */
+ if (busid_priv->udev->dev.parent)
+ device_lock(busid_priv->udev->dev.parent);
+ ret = device_attach(&busid_priv->udev->dev);
+ if (busid_priv->udev->dev.parent)
+ device_unlock(busid_priv->udev->dev.parent);
+ if (ret < 0) {
+ dev_err(&busid_priv->udev->dev, "rebind failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static void stub_device_rebind(void)
+{
+#if IS_MODULE(CONFIG_USBIP_HOST)
+ struct bus_id_priv *busid_priv;
+ int i;
+
+ /* update status to STUB_BUSID_OTHER so probe ignores the device */
+ spin_lock(&busid_table_lock);
+ for (i = 0; i < MAX_BUSID; i++) {
+ if (busid_table[i].name[0] &&
+ busid_table[i].shutdown_busid) {
+ busid_priv = &(busid_table[i]);
+ busid_priv->status = STUB_BUSID_OTHER;
+ }
+ }
+ spin_unlock(&busid_table_lock);
+
+ /* now run rebind - no need to hold locks. driver files are removed */
+ for (i = 0; i < MAX_BUSID; i++) {
+ if (busid_table[i].name[0] &&
+ busid_table[i].shutdown_busid) {
+ busid_priv = &(busid_table[i]);
+ do_rebind(busid_table[i].name, busid_priv);
+ }
+ }
+#endif
+}
+
static ssize_t rebind_store(struct device_driver *dev, const char *buf,
size_t count)
{
@@ -201,11 +276,17 @@
if (!bid)
return -ENODEV;
- ret = device_attach(&bid->udev->dev);
- if (ret < 0) {
- dev_err(&bid->udev->dev, "rebind failed\n");
+ /* mark the device for deletion so probe ignores it during rescan */
+ bid->status = STUB_BUSID_OTHER;
+ /* release the busid lock */
+ put_busid_priv(bid);
+
+ ret = do_rebind((char *) buf, bid);
+ if (ret < 0)
return ret;
- }
+
+ /* delete device from busid_table */
+ del_match_busid((char *) buf);
return count;
}
@@ -328,6 +409,9 @@
*/
usb_deregister_device_driver(&stub_driver);
+ /* initiate scan to attach devices */
+ stub_device_rebind();
+
kmem_cache_destroy(stub_priv_cache);
}
diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
index f0b955f..109e65b 100644
--- a/drivers/usb/usbip/usbip_common.h
+++ b/drivers/usb/usbip/usbip_common.h
@@ -258,7 +258,7 @@
#define VUDC_EVENT_ERROR_USB (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
#define VUDC_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
-#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE)
+#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE)
#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c
index f163566..f8f7f38 100644
--- a/drivers/usb/usbip/usbip_event.c
+++ b/drivers/usb/usbip/usbip_event.c
@@ -105,10 +105,6 @@
unset_event(ud, USBIP_EH_UNUSABLE);
}
- /* Stop the error handler. */
- if (ud->event & USBIP_EH_BYE)
- usbip_dbg_eh("removed %p\n", ud);
-
wake_up(&ud->eh_waitq);
}
}
diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c
index 0f98f2c..7efa374 100644
--- a/drivers/usb/usbip/vudc_sysfs.c
+++ b/drivers/usb/usbip/vudc_sysfs.c
@@ -117,10 +117,14 @@
if (rv != 0)
return -EINVAL;
+ if (!udc) {
+ dev_err(dev, "no device");
+ return -ENODEV;
+ }
spin_lock_irqsave(&udc->lock, flags);
/* Don't export what we don't have */
- if (!udc || !udc->driver || !udc->pullup) {
- dev_err(dev, "no device or gadget not bound");
+ if (!udc->driver || !udc->pullup) {
+ dev_err(dev, "gadget not bound");
ret = -ENODEV;
goto unlock;
}
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 9f1ec43..7b8a957 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -810,6 +810,7 @@
{
__le16 *ctrl = (__le16 *)(vdev->vconfig + pos -
offset + PCI_EXP_DEVCTL);
+ int readrq = le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ;
count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
if (count < 0)
@@ -835,6 +836,27 @@
pci_try_reset_function(vdev->pdev);
}
+ /*
+ * MPS is virtualized to the user, writes do not change the physical
+ * register since determining a proper MPS value requires a system wide
+ * device view. The MRRS is largely independent of MPS, but since the
+ * user does not have that system-wide view, they might set a safe, but
+ * inefficiently low value. Here we allow writes through to hardware,
+ * but we set the floor to the physical device MPS setting, so that
+ * we can at least use full TLPs, as defined by the MPS value.
+ *
+ * NB, if any devices actually depend on an artificially low MRRS
+ * setting, this will need to be revisited, perhaps with a quirk
+ * though pcie_set_readrq().
+ */
+ if (readrq != (le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ)) {
+ readrq = 128 <<
+ ((le16_to_cpu(*ctrl) & PCI_EXP_DEVCTL_READRQ) >> 12);
+ readrq = max(readrq, pcie_get_mps(vdev->pdev));
+
+ pcie_set_readrq(vdev->pdev, readrq);
+ }
+
return count;
}
@@ -853,11 +875,12 @@
* Allow writes to device control fields, except devctl_phantom,
* which could confuse IOMMU, MPS, which can break communication
* with other physical devices, and the ARI bit in devctl2, which
- * is set at probe time. FLR gets virtualized via our writefn.
+ * is set at probe time. FLR and MRRS get virtualized via our
+ * writefn.
*/
p_setw(perm, PCI_EXP_DEVCTL,
- PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_PAYLOAD,
- ~PCI_EXP_DEVCTL_PHANTOM);
+ PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_PAYLOAD |
+ PCI_EXP_DEVCTL_READRQ, ~PCI_EXP_DEVCTL_PHANTOM);
p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI);
return 0;
}
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 59b3f62..70c748a 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -195,6 +195,11 @@
return ret;
tcemem = kzalloc(sizeof(*tcemem), GFP_KERNEL);
+ if (!tcemem) {
+ mm_iommu_put(container->mm, mem);
+ return -ENOMEM;
+ }
+
tcemem->mem = mem;
list_add(&tcemem->next, &container->prereg_list);
@@ -1332,8 +1337,16 @@
if (!table_group->ops || !table_group->ops->take_ownership ||
!table_group->ops->release_ownership) {
+ if (container->v2) {
+ ret = -EPERM;
+ goto unlock_exit;
+ }
ret = tce_iommu_take_ownership(container, table_group);
} else {
+ if (!container->v2) {
+ ret = -EPERM;
+ goto unlock_exit;
+ }
ret = tce_iommu_take_ownership_ddw(container, table_group);
if (!tce_groups_attached(container) && !container->tables[0])
container->def_window_pending = true;
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index e5b7652..487586e 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -524,7 +524,7 @@
if (!len && vq->busyloop_timeout) {
/* Both tx vq and rx socket were polled here */
- mutex_lock(&vq->mutex);
+ mutex_lock_nested(&vq->mutex, 1);
vhost_disable_notify(&net->dev, vq);
preempt_disable();
@@ -657,7 +657,7 @@
struct iov_iter fixup;
__virtio16 num_buffers;
- mutex_lock(&vq->mutex);
+ mutex_lock_nested(&vq->mutex, 0);
sock = vq->private_data;
if (!sock)
goto out;
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index cd38f5a..fce49eb 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -211,8 +211,7 @@
if (mask)
vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask);
if (mask & POLLERR) {
- if (poll->wqh)
- remove_wait_queue(poll->wqh, &poll->wait);
+ vhost_poll_stop(poll);
ret = -EINVAL;
}
@@ -1176,14 +1175,14 @@
/* Caller should have vq mutex and device mutex */
int vhost_vq_access_ok(struct vhost_virtqueue *vq)
{
- if (vq->iotlb) {
- /* When device IOTLB was used, the access validation
- * will be validated during prefetching.
- */
+ if (!vq_log_access_ok(vq, vq->log_base))
+ return 0;
+
+ /* Access validation occurs at prefetch time with IOTLB */
+ if (vq->iotlb)
return 1;
- }
- return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used) &&
- vq_log_access_ok(vq, vq->log_base);
+
+ return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used);
}
EXPORT_SYMBOL_GPL(vhost_vq_access_ok);
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 16c8fcf..8c835f1 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -134,7 +134,7 @@
{
int rc;
struct backlight_device *bd = to_backlight_device(dev);
- unsigned long power;
+ unsigned long power, old_power;
rc = kstrtoul(buf, 0, &power);
if (rc)
@@ -145,10 +145,16 @@
if (bd->ops) {
pr_debug("set power to %lu\n", power);
if (bd->props.power != power) {
+ old_power = bd->props.power;
bd->props.power = power;
- backlight_update_status(bd);
+ rc = backlight_update_status(bd);
+ if (rc)
+ bd->props.power = old_power;
+ else
+ rc = count;
+ } else {
+ rc = count;
}
- rc = count;
}
mutex_unlock(&bd->ops_lock);
@@ -176,8 +182,7 @@
else {
pr_debug("set brightness to %lu\n", brightness);
bd->props.brightness = brightness;
- backlight_update_status(bd);
- rc = 0;
+ rc = backlight_update_status(bd);
}
}
mutex_unlock(&bd->ops_lock);
diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
index d7c239e..f557406 100644
--- a/drivers/video/backlight/corgi_lcd.c
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -177,7 +177,7 @@
struct spi_message msg;
struct spi_transfer xfer = {
.len = 1,
- .cs_change = 1,
+ .cs_change = 0,
.tx_buf = lcd->buf,
};
diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c
index eab1f84..e4bd63e 100644
--- a/drivers/video/backlight/tdo24m.c
+++ b/drivers/video/backlight/tdo24m.c
@@ -369,7 +369,7 @@
spi_message_init(m);
- x->cs_change = 1;
+ x->cs_change = 0;
x->tx_buf = &lcd->buf[0];
spi_message_add_tail(x, m);
diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c
index 6a41ea9..4dc5ee8 100644
--- a/drivers/video/backlight/tosa_lcd.c
+++ b/drivers/video/backlight/tosa_lcd.c
@@ -49,7 +49,7 @@
struct spi_message msg;
struct spi_transfer xfer = {
.len = 1,
- .cs_change = 1,
+ .cs_change = 0,
.tx_buf = buf,
};
diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c
index b90ef96..25d5bc3 100644
--- a/drivers/video/console/dummycon.c
+++ b/drivers/video/console/dummycon.c
@@ -41,12 +41,55 @@
vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS);
}
-static int dummycon_dummy(void)
+static void dummycon_deinit(struct vc_data *vc)
+{
+}
+
+static void dummycon_clear(struct vc_data *vc, int a, int b, int c, int d)
+{
+}
+
+static void dummycon_putc(struct vc_data *vc, int a, int b, int c)
+{
+}
+
+static void dummycon_putcs(struct vc_data *vc, const unsigned short *s, int a, int b, int c)
+{
+}
+
+static void dummycon_cursor(struct vc_data *vc, int a)
+{
+}
+
+static int dummycon_scroll(struct vc_data *vc, int a, int b, int c, int d)
{
return 0;
}
-#define DUMMY (void *)dummycon_dummy
+static int dummycon_switch(struct vc_data *vc)
+{
+ return 0;
+}
+
+static int dummycon_blank(struct vc_data *vc, int a, int b)
+{
+ return 0;
+}
+
+static int dummycon_font_set(struct vc_data *vc, struct console_font *f, unsigned u)
+{
+ return 0;
+}
+
+static int dummycon_font_default(struct vc_data *vc, struct console_font *f, char *c)
+{
+ return 0;
+}
+
+static int dummycon_font_copy(struct vc_data *vc, int a)
+{
+ return 0;
+}
/*
* The console `switch' structure for the dummy console
@@ -58,16 +101,16 @@
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
- .con_deinit = DUMMY,
- .con_clear = DUMMY,
- .con_putc = DUMMY,
- .con_putcs = DUMMY,
- .con_cursor = DUMMY,
- .con_scroll = DUMMY,
- .con_switch = DUMMY,
- .con_blank = DUMMY,
- .con_font_set = DUMMY,
- .con_font_default = DUMMY,
- .con_font_copy = DUMMY,
+ .con_deinit = dummycon_deinit,
+ .con_clear = dummycon_clear,
+ .con_putc = dummycon_putc,
+ .con_putcs = dummycon_putcs,
+ .con_cursor = dummycon_cursor,
+ .con_scroll = dummycon_scroll,
+ .con_switch = dummycon_switch,
+ .con_blank = dummycon_blank,
+ .con_font_set = dummycon_font_set,
+ .con_font_default = dummycon_font_default,
+ .con_font_copy = dummycon_font_copy,
};
EXPORT_SYMBOL_GPL(dummy_con);
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 1157661..dda1c4b 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -405,7 +405,10 @@
vga_video_port_val = VGA_CRT_DM;
if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
static struct resource ega_console_resource =
- { .name = "ega", .start = 0x3B0, .end = 0x3BF };
+ { .name = "ega",
+ .flags = IORESOURCE_IO,
+ .start = 0x3B0,
+ .end = 0x3BF };
vga_video_type = VIDEO_TYPE_EGAM;
vga_vram_size = 0x8000;
display_desc = "EGA+";
@@ -413,9 +416,15 @@
&ega_console_resource);
} else {
static struct resource mda1_console_resource =
- { .name = "mda", .start = 0x3B0, .end = 0x3BB };
+ { .name = "mda",
+ .flags = IORESOURCE_IO,
+ .start = 0x3B0,
+ .end = 0x3BB };
static struct resource mda2_console_resource =
- { .name = "mda", .start = 0x3BF, .end = 0x3BF };
+ { .name = "mda",
+ .flags = IORESOURCE_IO,
+ .start = 0x3BF,
+ .end = 0x3BF };
vga_video_type = VIDEO_TYPE_MDA;
vga_vram_size = 0x2000;
display_desc = "*MDA";
@@ -437,15 +446,21 @@
vga_vram_size = 0x8000;
if (!screen_info.orig_video_isVGA) {
- static struct resource ega_console_resource
- = { .name = "ega", .start = 0x3C0, .end = 0x3DF };
+ static struct resource ega_console_resource =
+ { .name = "ega",
+ .flags = IORESOURCE_IO,
+ .start = 0x3C0,
+ .end = 0x3DF };
vga_video_type = VIDEO_TYPE_EGAC;
display_desc = "EGA";
request_resource(&ioport_resource,
&ega_console_resource);
} else {
- static struct resource vga_console_resource
- = { .name = "vga+", .start = 0x3C0, .end = 0x3DF };
+ static struct resource vga_console_resource =
+ { .name = "vga+",
+ .flags = IORESOURCE_IO,
+ .start = 0x3C0,
+ .end = 0x3DF };
vga_video_type = VIDEO_TYPE_VGAC;
display_desc = "VGA+";
request_resource(&ioport_resource,
@@ -489,7 +504,10 @@
}
} else {
static struct resource cga_console_resource =
- { .name = "cga", .start = 0x3D4, .end = 0x3D5 };
+ { .name = "cga",
+ .flags = IORESOURCE_IO,
+ .start = 0x3D4,
+ .end = 0x3D5 };
vga_video_type = VIDEO_TYPE_CGA;
vga_vram_size = 0x2000;
display_desc = "*CGA";
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index ec2671d..89880b7 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -892,8 +892,8 @@
if (err)
return err;
- framesize = fb->panel->mode.xres * fb->panel->mode.yres *
- fb->panel->bpp / 8;
+ framesize = PAGE_ALIGN(fb->panel->mode.xres * fb->panel->mode.yres *
+ fb->panel->bpp / 8);
fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize,
&dma, GFP_KERNEL);
if (!fb->fb.screen_base)
diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig
index e8f902b..a5e0662 100644
--- a/drivers/video/fbdev/msm/Kconfig
+++ b/drivers/video/fbdev/msm/Kconfig
@@ -82,6 +82,16 @@
MHL (Mobile High-Definition Link) technology
uses USB connector to output HDMI content
+config FB_MSM_MDSS_SPI_PANEL
+ depends on FB_MSM_MDSS
+ bool "Support SPI panel feature"
+ default n
+ ---help---
+ The MDSS SPI Panel provides support for transmittimg SPI signals of
+ MDSS frame buffer data to connected panel. Limited by SPI rate, the
+ current max fps only reach to 27 fps, and limited by MDP hardware
+ architecture only supply on MDP3
+
config FB_MSM_MDSS_MHL3
depends on FB_MSM_MDSS_HDMI_PANEL
bool "MHL3 SII8620 Support"
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index 81d4953..26a940c 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -46,6 +46,11 @@
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
+ifeq ($(CONFIG_SPI_QUP), y)
+obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_client.o
+obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_panel.o
+endif
+
ifneq ($(CONFIG_FB_MSM_MDSS_MDP3), y)
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o
diff --git a/drivers/video/fbdev/msm/dsi_host_v2.c b/drivers/video/fbdev/msm/dsi_host_v2.c
index 33775ec..2cca469 100644
--- a/drivers/video/fbdev/msm/dsi_host_v2.c
+++ b/drivers/video/fbdev/msm/dsi_host_v2.c
@@ -753,8 +753,8 @@
}
if (dchdr->wait)
- usleep_range(dchdr->wait * 1000,
- dchdr->wait * 1000);
+ usleep_range((dchdr->wait * 1000),
+ (dchdr->wait * 1000) + 10);
mdss_dsi_buf_init(tp);
len = 0;
diff --git a/drivers/video/fbdev/msm/dsi_status_v2.c b/drivers/video/fbdev/msm/dsi_status_v2.c
index 35b0984..87720cf 100644
--- a/drivers/video/fbdev/msm/dsi_status_v2.c
+++ b/drivers/video/fbdev/msm/dsi_status_v2.c
@@ -18,6 +18,7 @@
#include "mdss_dsi.h"
#include "mdp3_ctrl.h"
+#include "mdss_spi_panel.h"
/*
* mdp3_check_te_status() - Check the status of panel for TE based ESD.
@@ -165,3 +166,79 @@
mdss_fb_report_panel_dead(pdsi_status->mfd);
}
+#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL)
+void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval)
+{
+ struct dsi_status_data *pdsi_status = NULL;
+ struct mdss_panel_data *pdata = NULL;
+ struct spi_panel_data *ctrl_pdata = NULL;
+ struct mdp3_session_data *mdp3_session = NULL;
+ int ret = 0;
+
+ pdsi_status = container_of(to_delayed_work(work),
+ struct dsi_status_data, check_status);
+
+ if (!pdsi_status || !(pdsi_status->mfd)) {
+ pr_err("%s: mfd not available\n", __func__);
+ return;
+ }
+
+ pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev);
+ if (!pdata) {
+ pr_err("%s: panel data not available\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data, panel_data);
+ if (!ctrl_pdata || !ctrl_pdata->check_status) {
+ pr_err("%s: Dsi ctrl or status_check callback not available\n",
+ __func__);
+ return;
+ }
+
+ mdp3_session = pdsi_status->mfd->mdp.private1;
+ if (!mdp3_session) {
+ pr_err("%s: Display is off\n", __func__);
+ return;
+ }
+
+ if (mdp3_session->in_splash_screen) {
+ schedule_delayed_work(&pdsi_status->check_status,
+ msecs_to_jiffies(interval));
+ pr_debug("%s: cont splash is on\n", __func__);
+ return;
+ }
+
+ mutex_lock(&mdp3_session->lock);
+ if (!mdp3_session->status) {
+ pr_debug("%s: display off already\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+ return;
+ }
+
+ if (!ret)
+ ret = ctrl_pdata->check_status(ctrl_pdata);
+ else
+ pr_err("%s:wait_for_dma_done error\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+
+ if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) {
+ if (ret > 0) {
+ schedule_delayed_work(&pdsi_status->check_status,
+ msecs_to_jiffies(interval));
+ } else {
+ char *envp[2] = {"PANEL_ALIVE=0", NULL};
+
+ pdata->panel_info.panel_dead = true;
+ ret = kobject_uevent_env(
+ &pdsi_status->mfd->fbi->dev->kobj, KOBJ_CHANGE, envp);
+ pr_err("%s:panel has gone bad, sending uevent - %s\n",
+ __func__, envp[0]);
+ }
+ }
+}
+#else
+void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval)
+{
+}
+#endif
diff --git a/drivers/video/fbdev/msm/dsi_v2.c b/drivers/video/fbdev/msm/dsi_v2.c
index 74c0726..11c7144 100644
--- a/drivers/video/fbdev/msm/dsi_v2.c
+++ b/drivers/video/fbdev/msm/dsi_v2.c
@@ -233,6 +233,10 @@
pr_err("%s:%d, bklt_en gpio not specified\n",
__func__, __LINE__);
+ ctrl_pdata->bklt_en_gpio_invert =
+ of_property_read_bool(ctrl_pdev->dev.of_node,
+ "qcom,platform-bklight-en-gpio-invert");
+
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c
index 591c240..54e9919 100644
--- a/drivers/video/fbdev/msm/mdp3.c
+++ b/drivers/video/fbdev/msm/mdp3.c
@@ -56,6 +56,7 @@
#include "mdss_debug.h"
#include "mdss_smmu.h"
#include "mdss.h"
+#include "mdss_spi_panel.h"
#ifndef EXPORT_COMPAT
#define EXPORT_COMPAT(x)
@@ -109,6 +110,7 @@
static struct mdss_panel_intf pan_types[] = {
{"dsi", MDSS_PANEL_INTF_DSI},
+ {"spi", MDSS_PANEL_INTF_SPI},
};
static char mdss_mdp3_panel[MDSS_MAX_PANEL_LEN];
@@ -127,6 +129,13 @@
},
};
+#ifndef CONFIG_FB_MSM_MDSS_SPI_PANEL
+void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, u32 bl_level)
+{
+
+}
+#endif
+
static irqreturn_t mdp3_irq_handler(int irq, void *ptr)
{
int i = 0;
@@ -1350,7 +1359,11 @@
client == MDP3_CLIENT_DMA_P)
mdss_smmu_unmap_dma_buf(data->tab_clone,
dom, dir, data->srcp_dma_buf);
- else
+ else if (client == MDP3_CLIENT_SPI) {
+ ion_unmap_kernel(iclient, data->srcp_ihdl);
+ ion_free(iclient, data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
+ } else
mdss_smmu_unmap_dma_buf(data->srcp_table,
dom, dir, data->srcp_dma_buf);
data->mapped = false;
@@ -1416,7 +1429,13 @@
data->srcp_dma_buf = NULL;
return ret;
}
-
+ data->srcp_ihdl = ion_import_dma_buf(iclient,
+ data->srcp_dma_buf);
+ if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
+ pr_err("error on ion_import_fd\n");
+ data->srcp_ihdl = NULL;
+ return -EIO;
+ }
data->srcp_attachment =
mdss_smmu_dma_buf_attach(data->srcp_dma_buf,
&mdp3_res->pdev->dev, dom);
@@ -1449,6 +1468,25 @@
data->tab_clone, dom,
&data->addr, &data->len,
DMA_BIDIRECTIONAL);
+ } else if (client == MDP3_CLIENT_SPI) {
+ void *vaddr;
+
+ if (ion_handle_get_size(iclient,
+ data->srcp_ihdl,
+ (size_t *)&data->len) < 0) {
+ pr_err("get size failed\n");
+ return -EINVAL;
+ }
+ vaddr = ion_map_kernel(iclient,
+ data->srcp_ihdl);
+ if (IS_ERR_OR_NULL(vaddr)) {
+ pr_err("Mapping failed\n");
+ mdp3_put_img(data, client);
+ return -EINVAL;
+ }
+ data->addr = (dma_addr_t) vaddr;
+ data->len -= img->offset;
+ return 0;
} else {
ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf,
data->srcp_table, dom, &data->addr,
@@ -1741,6 +1779,8 @@
if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
status = MDP3_REG_READ(MDP3_REG_DSI_VIDEO_EN);
rc = status & 0x1;
+ } else if (pdata->panel_info.type == SPI_PANEL) {
+ rc = is_spi_panel_continuous_splash_on(pdata);
} else {
status = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG);
status &= 0x180000;
@@ -1777,7 +1817,11 @@
mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate,
MDP3_CLIENT_DMA_P);
- rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
+ /*DMA not used on SPI interface, remove DMA bus voting*/
+ if (panel_info->type == SPI_PANEL)
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
+ else
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
bus_handle->restore_ab[MDP3_CLIENT_DMA_P] = ab;
bus_handle->restore_ib[MDP3_CLIENT_DMA_P] = ib;
@@ -1795,6 +1839,8 @@
if (panel_info->type == MIPI_VIDEO_PANEL)
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_VIDEO].active = 1;
+ else if (panel_info->type == SPI_PANEL)
+ mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_SPI_CMD].active = 1;
else
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_CMD].active = 1;
@@ -2111,9 +2157,41 @@
static DEVICE_ATTR(smart_blit, 0664,
mdp3_show_smart_blit, mdp3_store_smart_blit);
+static ssize_t mdp3_store_twm(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ u32 data = -1;
+ ssize_t rc = 0;
+
+ rc = kstrtoint(buf, 10, &data);
+ if (rc) {
+ pr_err("kstrtoint failed. rc=%d\n", rc);
+ return rc;
+ }
+ mdp3_res->twm_en = data ? true : false;
+ pr_err("TWM : %s\n", (mdp3_res->twm_en) ?
+ "ENABLED" : "DISABLED");
+ return len;
+}
+
+static ssize_t mdp3_show_twm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ pr_err("TWM : %s\n", (mdp3_res->twm_en) ?
+ "ENABLED" : "DISABLED");
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", mdp3_res->twm_en);
+ return ret;
+}
+
+static DEVICE_ATTR(twm_enable, 0664,
+ mdp3_show_twm, mdp3_store_twm);
+
static struct attribute *mdp3_fs_attrs[] = {
&dev_attr_caps.attr,
&dev_attr_smart_blit.attr,
+ &dev_attr_twm_enable.attr,
NULL
};
@@ -2378,6 +2456,7 @@
mdp3_dynamic_clock_gating_ctrl;
mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type;
mdp3_res->mdss_util->panel_intf_status = mdp3_panel_get_intf_status;
+ mdp3_res->twm_en = false;
if (mdp3_res->mdss_util->param_check(mdss_mdp3_panel)) {
mdp3_res->mdss_util->display_disabled = true;
@@ -2452,6 +2531,9 @@
mdp3_res->mdss_util->mdp_probe_done = true;
pr_debug("%s: END\n", __func__);
+ if (mdp3_res->pan_cfg.pan_intf == MDSS_PANEL_INTF_SPI)
+ mdp3_interface.check_dsi_status = mdp3_check_spi_panel_status;
+
probe_done:
if (IS_ERR_VALUE(rc))
kfree(mdp3_res->mdp3_hw.irq_info);
diff --git a/drivers/video/fbdev/msm/mdp3.h b/drivers/video/fbdev/msm/mdp3.h
index 6fb39a7..6b56052 100644
--- a/drivers/video/fbdev/msm/mdp3.h
+++ b/drivers/video/fbdev/msm/mdp3.h
@@ -84,6 +84,7 @@
MDP3_CLIENT_DSI = 1,
MDP3_CLIENT_PPP,
MDP3_CLIENT_IOMMU,
+ MDP3_CLIENT_SPI,
MDP3_CLIENT_MAX,
};
@@ -208,7 +209,9 @@
bool solid_fill_vote_en;
struct list_head reg_bus_clist;
struct mutex reg_bus_lock;
-
+ int bklt_level;
+ int bklt_update;
+ bool twm_en;
u32 max_bw;
u8 ppp_formats[BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)];
@@ -267,6 +270,7 @@
void mdp3_enable_regulator(int enable);
void mdp3_check_dsi_ctrl_status(struct work_struct *work,
uint32_t interval);
+void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval);
int mdp3_dynamic_clock_gating_ctrl(int enable);
int mdp3_footswitch_ctrl(int enable);
int mdp3_qos_remapper_setup(struct mdss_panel_data *panel);
@@ -279,6 +283,8 @@
void mdp3_clear_irq(u32 interrupt_mask);
int mdp3_enable_panic_ctrl(void);
+void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, u32 bl_level);
+
int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit);
int mdp3_layer_atomic_validate(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index 589f2df..c976c0e 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -23,11 +23,13 @@
#include <linux/dma-buf.h>
#include <linux/pm_runtime.h>
#include <linux/iommu.h>
+#include <linux/msm_ion.h>
#include "mdp3_ctrl.h"
#include "mdp3.h"
#include "mdp3_ppp.h"
#include "mdss_smmu.h"
+#include "mdss_spi_panel.h"
#include "mdss_sync.h"
#define VSYNC_EXPIRE_TICK 4
@@ -72,7 +74,7 @@
bufq->pop_idx = 0;
}
-void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq)
+void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq, int client)
{
int count = bufq->count;
@@ -83,7 +85,7 @@
struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx];
bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
- mdp3_put_img(data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(data, client);
}
bufq->count = 0;
bufq->push_idx = 0;
@@ -122,6 +124,18 @@
return bufq->count;
}
+int mdp3_get_ion_client(struct msm_fb_data_type *mfd)
+{
+ int intf_type;
+
+ intf_type = mdp3_ctrl_get_intf_type(mfd);
+
+ if (intf_type == MDP3_DMA_OUTPUT_SEL_SPI_CMD)
+ return MDP3_CLIENT_SPI;
+ else
+ return MDP3_CLIENT_DMA_P;
+}
+
void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses,
struct notifier_block *notifier)
{
@@ -174,6 +188,7 @@
return;
mutex_lock(&session->lock);
+ MDSS_XLOG(0x111);
if (session->vsync_enabled ||
atomic_read(&session->vsync_countdown) > 0) {
mutex_unlock(&session->lock);
@@ -182,10 +197,17 @@
return;
}
+ if (!session->clk_on) {
+ mutex_unlock(&session->lock);
+ pr_debug("%s: Clk shut down is done\n", __func__);
+ MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__);
+ return;
+ }
if (session->intf->active) {
retry_dma_done:
rc = wait_for_completion_timeout(&session->dma_completion,
WAIT_DMA_TIMEOUT);
+ MDSS_XLOG(0x222);
if (rc <= 0) {
struct mdss_panel_data *panel;
@@ -196,6 +218,7 @@
if (--retry_count) {
pr_err("dmap is busy, retry %d\n",
retry_count);
+ MDSS_XLOG(__LINE__, retry_count);
goto retry_dma_done;
}
pr_err("dmap is still busy, bug_on\n");
@@ -299,8 +322,11 @@
struct mdp3_notification vsync_client;
struct mdp3_notification *arg = NULL;
bool mod_vsync_timer = false;
+ int intf_type;
pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
+
+ intf_type = mdp3_ctrl_get_intf_type(mfd);
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
!mdp3_session->intf)
@@ -338,18 +364,25 @@
}
}
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
- mdp3_clk_enable(0, 0);
+ if (intf_type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
+ mdp3_spi_vsync_enable(mdp3_session->panel, arg);
+ } else {
+ mdp3_clk_enable(1, 0);
+ mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
+ mdp3_clk_enable(0, 0);
+ }
/*
* Need to fake vsync whenever dsi interface is not
* active or when dsi clocks are currently off
*/
- if (mod_vsync_timer) {
+ if (mod_vsync_timer && (intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)) {
mod_timer(&mdp3_session->vsync_timer,
jiffies + msecs_to_jiffies(mdp3_session->vsync_period));
- } else if (!enable) {
+ } else if (enable && !mdp3_session->clk_on) {
+ mdp3_ctrl_reset_countdown(mdp3_session, mfd);
+ mdp3_ctrl_clk_enable(mfd, 1);
+ } else if (!enable && (intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)) {
del_timer(&mdp3_session->vsync_timer);
}
@@ -380,7 +413,7 @@
return -EFAULT;
p_req = p + sizeof(req_list_header);
count = req_list_header.count;
- if (count < 0 || count >= MAX_BLIT_REQ)
+ if (count < 0 || count > MAX_BLIT_REQ)
return -EINVAL;
rc = mdp3_ppp_parse_req(p_req, &req_list_header, 1);
if (!rc)
@@ -399,7 +432,7 @@
return -EFAULT;
p_req = p + sizeof(struct mdp_blit_req_list);
count = req_list_header.count;
- if (count < 0 || count >= MAX_BLIT_REQ)
+ if (count < 0 || count > MAX_BLIT_REQ)
return -EINVAL;
req_list_header.sync.acq_fen_fd_cnt = 0;
rc = mdp3_ppp_parse_req(p_req, &req_list_header, 0);
@@ -588,14 +621,32 @@
static int mdp3_ctrl_res_req_bus(struct msm_fb_data_type *mfd, int status)
{
int rc = 0;
+ u32 vtotal = 0;
+ int frame_rate = DEFAULT_FRAME_RATE;
if (status) {
+ struct mdss_panel_info *panel_info = mfd->panel_info;
u64 ab = 0;
u64 ib = 0;
+ frame_rate = mdss_panel_get_framerate(mfd->panel_info,
+ FPS_RESOLUTION_HZ);
mdp3_calc_dma_res(mfd->panel_info, NULL, &ab, &ib,
ppp_bpp(mfd->fb_imgType));
- rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
+ vtotal = panel_info->yres + panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width;
+ ab = panel_info->xres * vtotal * ppp_bpp(mfd->fb_imgType);
+ ab *= frame_rate;
+ ib = ab;
+
+ /*DMA not used on SPI interface, remove DMA bus voting*/
+ if (mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD)
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
+ else
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P,
+ ab, ib);
} else {
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
}
@@ -644,6 +695,9 @@
case LCDC_PANEL:
type = MDP3_DMA_OUTPUT_SEL_LCDC;
break;
+ case SPI_PANEL:
+ type = MDP3_DMA_OUTPUT_SEL_SPI_CMD;
+ break;
default:
type = MDP3_DMA_OUTPUT_SEL_MAX;
}
@@ -661,8 +715,11 @@
case MDP_RGB_888:
format = MDP3_DMA_IBUF_FORMAT_RGB888;
break;
+ case MDP_XRGB_8888:
case MDP_ARGB_8888:
case MDP_RGBA_8888:
+ case MDP_BGRA_8888:
+ case MDP_RGBX_8888:
format = MDP3_DMA_IBUF_FORMAT_XRGB8888;
break;
default:
@@ -705,7 +762,8 @@
cfg.type = mdp3_ctrl_get_intf_type(mfd);
if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
- cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC ||
+ cfg.type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
video->hsync_period = hsync_period;
video->hsync_pulse_width = h_pulse_width;
video->vsync_period = vsync_period;
@@ -753,7 +811,7 @@
struct fb_var_screeninfo *var;
struct mdp3_dma_output_config outputConfig;
struct mdp3_dma_source sourceConfig;
- int frame_rate = mfd->panel_info->mipi.frame_rate;
+ int frame_rate = DEFAULT_FRAME_RATE;
int vbp, vfp, vspw;
int vtotal, vporch;
struct mdp3_notification dma_done_callback;
@@ -762,6 +820,7 @@
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ);
vbp = panel_info->lcdc.v_back_porch;
vfp = panel_info->lcdc.v_front_porch;
vspw = panel_info->lcdc.v_pulse_width;
@@ -802,7 +861,7 @@
sourceConfig.stride = fix->line_length;
}
- te.frame_rate = panel_info->mipi.frame_rate;
+ te.frame_rate = frame_rate;
te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode;
te.tear_check_en = panel_info->te.tear_check_en;
te.sync_cfg_height = panel_info->te.sync_cfg_height;
@@ -983,9 +1042,15 @@
return rc;
}
+static bool mdp3_is_twm_en(void)
+{
+ return mdp3_res->twm_en;
+}
+
static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
{
int rc = 0;
+ int client = 0;
bool intf_stopped = true;
struct mdp3_session_data *mdp3_session;
struct mdss_panel_data *panel;
@@ -1009,8 +1074,10 @@
MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__, mdss_fb_is_power_on_ulp(mfd),
mfd->panel_power_state);
panel = mdp3_session->panel;
- mutex_lock(&mdp3_session->lock);
+ cancel_work_sync(&mdp3_session->clk_off_work);
+ mutex_lock(&mdp3_session->lock);
+ MDSS_XLOG(0x111);
pr_debug("Requested power state = %d\n", mfd->panel_power_state);
if (mdss_fb_is_power_on_lp(mfd)) {
/*
@@ -1025,8 +1092,10 @@
pr_debug("fb%d is off already", mfd->index);
goto off_error;
}
- if (panel && panel->set_backlight)
+ if (panel && panel->set_backlight) {
+ if (!mdp3_is_twm_en())
panel->set_backlight(panel, 0);
+ }
}
/*
@@ -1034,12 +1103,13 @@
* events need to be sent to the interface so that the
* panel can be configured in low power mode
*/
- if (panel->event_handler)
- rc = panel->event_handler(panel, MDSS_EVENT_BLANK,
- (void *) (long int)mfd->panel_power_state);
- if (rc)
- pr_err("EVENT_BLANK error (%d)\n", rc);
-
+ if (!mdp3_is_twm_en()) {
+ if (panel->event_handler)
+ rc = panel->event_handler(panel, MDSS_EVENT_BLANK,
+ (void *) (long int)mfd->panel_power_state);
+ if (rc)
+ pr_err("EVENT_BLANK error (%d)\n", rc);
+ }
if (intf_stopped) {
if (!mdp3_session->clk_on)
mdp3_ctrl_clk_enable(mfd, 1);
@@ -1065,9 +1135,27 @@
mdp3_irq_deregister();
}
- if (panel->event_handler)
- rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF,
- (void *) (long int)mfd->panel_power_state);
+ if (panel->event_handler) {
+ if (mdp3_is_twm_en()) {
+ pr_info("TWM active skip panel off, disable disp_en\n");
+ if (gpio_is_valid(panel->panel_en_gpio)) {
+ rc = gpio_direction_output(
+ panel->panel_en_gpio, 1);
+ if (rc) {
+ pr_err("%s:set dir for gpio(%d) FAIL\n",
+ __func__, panel->panel_en_gpio);
+ } else {
+ gpio_set_value((panel->panel_en_gpio), 0);
+ usleep_range(100, 110);
+ pr_debug("%s:set disp_en_gpio_%d Low\n",
+ __func__, panel->panel_en_gpio);
+ }
+ }
+ } else {
+ rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF,
+ (void *) (long int)mfd->panel_power_state);
+ }
+ }
if (rc)
pr_err("EVENT_PANEL_OFF error (%d)\n", rc);
@@ -1116,10 +1204,11 @@
pr_err("%s: pm_runtime_put failed (rc %d)\n",
__func__, rc);
}
- mdp3_bufq_deinit(&mdp3_session->bufq_out);
+ client = mdp3_get_ion_client(mfd);
+ mdp3_bufq_deinit(&mdp3_session->bufq_out, client);
if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) {
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
- mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
}
}
@@ -1149,6 +1238,10 @@
mdp3_ctrl_clk_enable(mdp3_session->mfd, 0);
}
off_error:
+ if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) {
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
+ }
MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__);
mutex_unlock(&mdp3_session->lock);
/* Release the last reference to the runtime device */
@@ -1296,14 +1389,16 @@
struct fb_info *fbi = mfd->fbi;
struct fb_fix_screeninfo *fix;
int format;
+ int client;
fix = &fbi->fix;
format = mdp3_ctrl_get_source_format(mfd->fb_imgType);
mutex_lock(&mdp3_session->lock);
+ client = mdp3_get_ion_client(mfd);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
- mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
} else {
rc = -EINVAL;
}
@@ -1322,18 +1417,20 @@
struct msmfb_data *img = &req->data;
struct mdp3_img_data data;
struct mdp3_dma *dma = mdp3_session->dma;
+ int client;
+ client = mdp3_get_ion_client(mfd);
memset(&data, 0, sizeof(struct mdp3_img_data));
- if (mfd->panel.type == MIPI_CMD_PANEL)
+ if (mfd->panel.type == MIPI_CMD_PANEL || client == MDP3_CLIENT_SPI)
is_panel_type_cmd = true;
if (is_panel_type_cmd) {
- rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
+ rc = mdp3_iommu_enable(client);
if (rc) {
pr_err("fail to enable iommu\n");
return rc;
}
}
- rc = mdp3_get_img(img, &data, MDP3_CLIENT_DMA_P);
+ rc = mdp3_get_img(img, &data, client);
if (rc) {
pr_err("fail to get overlay buffer\n");
goto err;
@@ -1343,14 +1440,14 @@
pr_err("buf size(0x%lx) is smaller than dma config(0x%x)\n",
data.len, (dma->source_config.stride *
dma->source_config.height));
- mdp3_put_img(&data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(&data, client);
rc = -EINVAL;
goto err;
}
rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data);
if (rc) {
pr_err("fail to queue the overlay buffer, buffer drop\n");
- mdp3_put_img(&data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(&data, client);
goto err;
}
rc = 0;
@@ -1409,8 +1506,11 @@
struct mdp3_img_data *data;
struct mdss_panel_info *panel_info;
int rc = 0;
+ int client;
static bool splash_done;
struct mdss_panel_data *panel;
+ int frame_rate = DEFAULT_FRAME_RATE;
+ int stride;
if (!mfd || !mfd->mdp.private1)
return -EINVAL;
@@ -1420,6 +1520,9 @@
if (!mdp3_session || !mdp3_session->dma)
return -EINVAL;
+ frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ);
+ client = mdp3_get_ion_client(mfd);
+
if (mdp3_bufq_count(&mdp3_session->bufq_in) == 0) {
pr_debug("no buffer in queue yet\n");
return -EPERM;
@@ -1456,6 +1559,7 @@
}
mutex_unlock(&mdp3_res->fs_idle_pc_lock);
+ cancel_work_sync(&mdp3_session->clk_off_work);
mutex_lock(&mdp3_session->lock);
if (!mdp3_session->status) {
@@ -1463,13 +1567,23 @@
mutex_unlock(&mdp3_session->lock);
return -EPERM;
}
-
+ MDSS_XLOG(0x111);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
data = mdp3_bufq_pop(&mdp3_session->bufq_in);
if (data) {
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
mdp3_ctrl_clk_enable(mfd, 1);
- if (mdp3_session->dma->update_src_cfg &&
+ stride = mdp3_session->dma->source_config.stride;
+ if (mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD){
+ mdp3_session->intf->active = false;
+ msm_ion_do_cache_op(mdp3_res->ion_client,
+ data->srcp_ihdl, (void *)(int)data->addr,
+ data->len, ION_IOC_INV_CACHES);
+ rc = mdss_spi_panel_kickoff(mdp3_session->panel,
+ (void *)(int)data->addr, (int)data->len,
+ stride);
+ } else if (mdp3_session->dma->update_src_cfg &&
panel_info->partial_update_enabled) {
panel->panel_info.roi.x = mdp3_session->dma->roi.x;
panel->panel_info.roi.y = mdp3_session->dma->roi.y;
@@ -1489,13 +1603,15 @@
MDP_NOTIFY_FRAME_TIMEOUT);
} else {
if (mdp3_ctrl_get_intf_type(mfd) ==
- MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
mdp3_ctrl_notify(mdp3_session,
MDP_NOTIFY_FRAME_DONE);
}
}
mdp3_session->dma_active = 1;
- init_completion(&mdp3_session->dma_completion);
+ reinit_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
@@ -1504,16 +1620,16 @@
mdp3_release_splash_memory(mfd);
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
if (data)
- mdp3_put_img(data, MDP3_CLIENT_DMA_P);
+ mdp3_put_img(data, client);
}
if (mdp3_session->first_commit) {
/*wait to ensure frame is sent to panel*/
if (panel_info->mipi.post_init_delay)
- msleep(((1000 / panel_info->mipi.frame_rate) + 1) *
+ msleep(((1000 / frame_rate) + 1) *
panel_info->mipi.post_init_delay);
else
- msleep(1000 / panel_info->mipi.frame_rate);
+ msleep((1000 / frame_rate) + 1);
mdp3_session->first_commit = false;
if (panel)
rc |= panel->event_handler(panel,
@@ -1528,6 +1644,12 @@
mdp3_session->esd_recovery = false;
}
+ /*Update backlight only if its changed*/
+ if (mdp3_res->bklt_level && mdp3_res->bklt_update) {
+ mdss_spi_panel_bl_ctrl_update(panel, mdp3_res->bklt_level);
+ mdp3_res->bklt_update = false;
+ }
+
/* start vsync tick countdown for cmd mode if vsync isn't enabled */
if (mfd->panel.type == MIPI_CMD_PANEL && !mdp3_session->vsync_enabled)
mdp3_ctrl_vsync_enable(mdp3_session->mfd, 0);
@@ -1644,7 +1766,7 @@
}
}
mdp3_session->dma_active = 1;
- init_completion(&mdp3_session->dma_completion);
+ reinit_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
@@ -1654,17 +1776,18 @@
}
panel = mdp3_session->panel;
- if (mdp3_session->first_commit) {
- /*wait to ensure frame is sent to panel*/
- if (panel_info->mipi.post_init_delay)
- msleep(((1000 / panel_info->mipi.frame_rate) + 1) *
- panel_info->mipi.post_init_delay);
- else
- msleep(1000 / panel_info->mipi.frame_rate);
- mdp3_session->first_commit = false;
- if (panel)
- panel->event_handler(panel, MDSS_EVENT_POST_PANEL_ON,
- NULL);
+ if (mdp3_ctrl_get_intf_type(mfd) != MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
+ if (mdp3_session->first_commit) {
+ if (panel_info->mipi.init_delay)
+ msleep(((1000 / panel_info->mipi.frame_rate)
+ + 1) * panel_info->mipi.post_init_delay);
+ else
+ msleep(1000 / panel_info->mipi.frame_rate);
+ mdp3_session->first_commit = false;
+ if (panel)
+ panel->event_handler(panel,
+ MDSS_EVENT_POST_PANEL_ON, NULL);
+ }
}
mdp3_session->vsync_before_commit = 0;
@@ -1675,6 +1798,11 @@
mdp3_session->esd_recovery = false;
}
+ /*Update backlight only if its changed*/
+ if (mdp3_res->bklt_level && mdp3_res->bklt_update) {
+ mdss_spi_panel_bl_ctrl_update(panel, mdp3_res->bklt_level);
+ mdp3_res->bklt_update = false;
+ }
pan_error:
mutex_unlock(&mdp3_session->lock);
@@ -1715,7 +1843,8 @@
switch (metadata->op) {
case metadata_op_frame_rate:
metadata->data.panel_frame_rate =
- mfd->panel_info->mipi.frame_rate;
+ mdss_panel_get_framerate(mfd->panel_info,
+ FPS_RESOLUTION_HZ);
break;
case metadata_op_get_caps:
metadata->data.caps.mdp_rev = 305;
@@ -2685,6 +2814,8 @@
}
mutex_unlock(&mdp3_res->fs_idle_pc_lock);
rc = mdp3_ctrl_async_blit_req(mfd, argp);
+ if (!rc)
+ cancel_work_sync(&mdp3_session->clk_off_work);
break;
case MSMFB_BLIT:
mutex_lock(&mdp3_res->fs_idle_pc_lock);
@@ -2692,6 +2823,8 @@
mdp3_ctrl_reset(mfd);
mutex_unlock(&mdp3_res->fs_idle_pc_lock);
rc = mdp3_ctrl_blit_req(mfd, argp);
+ if (!rc)
+ cancel_work_sync(&mdp3_session->clk_off_work);
break;
case MSMFB_METADATA_GET:
rc = copy_from_user(&metadata, argp, sizeof(metadata));
@@ -2848,6 +2981,7 @@
struct msm_mdp_interface *mdp3_interface = &mfd->mdp;
struct mdp3_session_data *mdp3_session = NULL;
u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
+ int frame_rate = DEFAULT_FRAME_RATE;
int rc;
int splash_mismatch = 0;
struct sched_param sched = { .sched_priority = 16 };
@@ -2857,6 +2991,8 @@
if (rc)
splash_mismatch = 1;
+ frame_rate = mdss_panel_get_framerate(mfd->panel_info,
+ FPS_RESOLUTION_HZ);
mdp3_interface->on_fnc = mdp3_ctrl_on;
mdp3_interface->off_fnc = mdp3_ctrl_off;
mdp3_interface->do_histogram = NULL;
@@ -2870,6 +3006,7 @@
mdp3_interface->configure_panel = mdp3_update_panel_info;
mdp3_interface->input_event_handler = NULL;
mdp3_interface->signal_retire_fence = NULL;
+ mdp3_interface->is_twm_en = mdp3_is_twm_en;
mdp3_session = kzalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
if (!mdp3_session)
@@ -2934,10 +3071,11 @@
init_timer(&mdp3_session->vsync_timer);
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
mdp3_session->vsync_timer.data = (u32)mdp3_session;
- mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate;
+ mdp3_session->vsync_period = 1000 / frame_rate;
mfd->mdp.private1 = mdp3_session;
init_completion(&mdp3_session->dma_completion);
- if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
+ if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)
mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.h b/drivers/video/fbdev/msm/mdp3_ctrl.h
index b7b667b..5193af1 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.h
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.h
@@ -84,12 +84,13 @@
struct work_struct retire_work;
};
-void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq);
+void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq, int client);
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
int mdp3_bufq_push(struct mdp3_buffer_queue *bufq,
struct mdp3_img_data *data);
int mdp3_ctrl_get_source_format(u32 imgType);
int mdp3_ctrl_get_pack_pattern(u32 imgType);
int mdp3_ctrl_reset(struct msm_fb_data_type *mfd);
+int mdp3_get_ion_client(struct msm_fb_data_type *mfd);
#endif /* MDP3_CTRL_H */
diff --git a/drivers/video/fbdev/msm/mdp3_dma.c b/drivers/video/fbdev/msm/mdp3_dma.c
index 089d32d..b223c87 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.c
+++ b/drivers/video/fbdev/msm/mdp3_dma.c
@@ -114,7 +114,8 @@
}
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
- dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
mdp3_irq_enable(MDP3_INTR_LCDC_START_OF_FRAME);
} else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
@@ -150,7 +151,8 @@
}
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
- dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_SPI_CMD) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
mdp3_irq_disable(MDP3_INTR_LCDC_START_OF_FRAME);
} else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
@@ -665,6 +667,7 @@
ATRACE_BEGIN(__func__);
pr_debug("mdp3_dmap_update\n");
+ MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__);
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
if (intf->active) {
@@ -757,6 +760,7 @@
unsigned long flag;
int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
+ MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__);
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
if (intf->active)
@@ -965,6 +969,8 @@
val = MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS);
pr_err("%s DMAP Status %s\n", __func__,
(val & MDP3_DMA_P_BUSY_BIT) ? "BUSY":"IDLE");
+ MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__,
+ (val & MDP3_DMA_P_BUSY_BIT) ? 1:0);
return val & MDP3_DMA_P_BUSY_BIT;
}
@@ -1056,7 +1062,7 @@
MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0);
MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, 0xfffffff);
- init_completion(&dma->dma_comp);
+ reinit_completion(&dma->dma_comp);
dma->vsync_client.handler = NULL;
return ret;
}
@@ -1264,6 +1270,22 @@
return 0;
}
+static int spi_cmd_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg)
+{
+ return 0;
+}
+
+static int spi_cmd_start(struct mdp3_intf *intf)
+{
+ intf->active = true;
+ return 0;
+}
+
+static int spi_cmd_stop(struct mdp3_intf *intf)
+{
+ intf->active = false;
+ return 0;
+}
int mdp3_intf_init(struct mdp3_intf *intf)
{
switch (intf->cfg.type) {
@@ -1282,6 +1304,11 @@
intf->start = dsi_cmd_start;
intf->stop = dsi_cmd_stop;
break;
+ case MDP3_DMA_OUTPUT_SEL_SPI_CMD:
+ intf->config = spi_cmd_config;
+ intf->start = spi_cmd_start;
+ intf->stop = spi_cmd_stop;
+ break;
default:
return -EINVAL;
diff --git a/drivers/video/fbdev/msm/mdp3_dma.h b/drivers/video/fbdev/msm/mdp3_dma.h
index 24caedb9..ec327b6 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.h
+++ b/drivers/video/fbdev/msm/mdp3_dma.h
@@ -47,6 +47,7 @@
MDP3_DMA_OUTPUT_SEL_DSI_CMD,
MDP3_DMA_OUTPUT_SEL_LCDC,
MDP3_DMA_OUTPUT_SEL_DSI_VIDEO,
+ MDP3_DMA_OUTPUT_SEL_SPI_CMD,
MDP3_DMA_OUTPUT_SEL_MAX
};
diff --git a/drivers/video/fbdev/msm/mdp3_layer.c b/drivers/video/fbdev/msm/mdp3_layer.c
index 0078466..98b5c19 100644
--- a/drivers/video/fbdev/msm/mdp3_layer.c
+++ b/drivers/video/fbdev/msm/mdp3_layer.c
@@ -197,6 +197,7 @@
struct msmfb_data img;
bool is_panel_type_cmd = false;
struct mdp3_img_data data;
+ int intf_type;
int rc = 0;
layer = &input_layer[0];
@@ -208,23 +209,24 @@
goto err;
}
+ intf_type = mdp3_get_ion_client(mfd);
memset(&img, 0, sizeof(img));
img.memory_id = buffer->planes[0].fd;
img.offset = buffer->planes[0].offset;
memset(&data, 0, sizeof(struct mdp3_img_data));
- if (mfd->panel.type == MIPI_CMD_PANEL)
+ if (mfd->panel.type == MIPI_CMD_PANEL || intf_type == MDP3_CLIENT_SPI)
is_panel_type_cmd = true;
if (is_panel_type_cmd) {
- rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
+ rc = mdp3_iommu_enable(intf_type);
if (rc) {
pr_err("fail to enable iommu\n");
return rc;
}
}
- rc = mdp3_get_img(&img, &data, MDP3_CLIENT_DMA_P);
+ rc = mdp3_get_img(&img, &data, intf_type);
if (rc) {
pr_err("fail to get overlay buffer\n");
goto err;
@@ -260,7 +262,7 @@
struct mdp3_session_data *mdp3_session;
struct mdp3_dma *dma;
int layer_count = commit->input_layer_cnt;
- int stride, format;
+ int stride, format, client;
/* Handle NULL commit */
if (!layer_count) {
@@ -273,7 +275,8 @@
mutex_lock(&mdp3_session->lock);
- mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ client = mdp3_get_ion_client(mfd);
+ mdp3_bufq_deinit(&mdp3_session->bufq_in, client);
layer_list = commit->input_layers;
layer = &layer_list[0];
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c
index 612cccc..5459510 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp.c
@@ -22,6 +22,7 @@
#include <linux/mutex.h>
#include "linux/proc_fs.h"
#include <linux/delay.h>
+#include <linux/fence.h>
#include "mdss_fb.h"
#include "mdp3_ppp.h"
@@ -195,12 +196,20 @@
if (((req->src_rect.x + req->src_rect.w) > req->src.width) ||
((req->src_rect.y + req->src_rect.h) > req->src.height)) {
+ pr_err("%s: src roi (x=%d,y=%d,w=%d, h=%d) WxH(%dx%d)\n",
+ __func__, req->src_rect.x, req->src_rect.y,
+ req->src_rect.w, req->src_rect.h, req->src.width,
+ req->src.height);
pr_err("%s: src roi larger than boundary\n", __func__);
return -EINVAL;
}
if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) ||
((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) {
+ pr_err("%s: dst roi (x=%d,y=%d,w=%d, h=%d) WxH(%dx%d)\n",
+ __func__, req->dst_rect.x, req->dst_rect.y,
+ req->dst_rect.w, req->dst_rect.h, req->dst.width,
+ req->dst.height);
pr_err("%s: dst roi larger than boundary\n", __func__);
return -EINVAL;
}
@@ -533,6 +542,7 @@
{
struct mdss_panel_info *panel_info = mfd->panel_info;
int i, lcount = 0;
+ int frame_rate = DEFAULT_FRAME_RATE;
struct mdp_blit_req *req;
struct bpp_info bpp;
u64 old_solid_fill_pixel = 0;
@@ -547,6 +557,7 @@
ATRACE_BEGIN(__func__);
lcount = lreq->count;
+ frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ);
if (lcount == 0) {
pr_err("Blit with request count 0, continue to recover!!!\n");
ATRACE_END(__func__);
@@ -574,11 +585,11 @@
is_blit_optimization_possible(lreq, i);
req = &(lreq->req_list[i]);
- if (req->fps > 0 && req->fps <= panel_info->mipi.frame_rate) {
+ if (req->fps > 0 && req->fps <= frame_rate) {
if (fps == 0)
fps = req->fps;
else
- fps = panel_info->mipi.frame_rate;
+ fps = frame_rate;
}
mdp3_get_bpp_info(req->src.format, &bpp);
@@ -636,7 +647,7 @@
}
if (fps == 0)
- fps = panel_info->mipi.frame_rate;
+ fps = frame_rate;
if (lreq->req_list[0].flags & MDP_SOLID_FILL) {
honest_ppp_ab = ppp_res.solid_fill_byte * 4;
@@ -1453,6 +1464,18 @@
(!(check_if_rgb(bg_req.src.format))) &&
(!(hw_woraround_active))) {
/*
+ * Disable SMART blit for BG(YUV) layer when
+ * Scaling on BG layer
+ * Rotation on BG layer
+ * UD flip on BG layer
+ */
+ if ((is_scaling_needed(bg_req)) && (
+ bg_req.flags & MDP_ROT_90) &&
+ (bg_req.flags & MDP_FLIP_UD)) {
+ pr_debug("YUV layer with ROT+UD_FLIP+Scaling Not supported\n");
+ return false;
+ }
+ /*
* swap blit requests at index 0 and 1. YUV layer at
* index 0 is replaced with UI layer request present
* at index 1. Since UI layer will be in background
@@ -1645,6 +1668,7 @@
}
} else {
fence = req->cur_rel_fence;
+ fence_get((struct fence *) fence);
}
mdp3_ppp_req_push(req_q, req);
diff --git a/drivers/video/fbdev/msm/mdp3_ppp_data.c b/drivers/video/fbdev/msm/mdp3_ppp_data.c
index ac88d9b..dd2cce0 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp_data.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp_data.c
@@ -89,8 +89,8 @@
};
const uint32_t swapped_pack_patt_lut[MDP_IMGTYPE_LIMIT] = {
- [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
- [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
[MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
[MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
[MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 12a3171..e6d55b5 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -42,6 +42,7 @@
MDSS_CLK_MDP_CORE,
MDSS_CLK_MDP_LUT,
MDSS_CLK_MDP_VSYNC,
+ MDSS_CLK_MNOC_AHB,
MDSS_MAX_CLK
};
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index f38d40c..7d07040 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -46,6 +46,42 @@
#define INVALID_XIN_ID 0xFF
+static u32 dsi_dbg_bus_sdm660[] = {
+ 0x0001, 0x1001, 0x0001, 0x0011,
+ 0x1021, 0x0021, 0x0031, 0x0041,
+ 0x0051, 0x0061, 0x3061, 0x0061,
+ 0x2061, 0x2061, 0x1061, 0x1061,
+ 0x1061, 0x0071, 0x0071, 0x0071,
+ 0x0081, 0x0081, 0x00A1, 0x00A1,
+ 0x10A1, 0x20A1, 0x30A1, 0x10A1,
+ 0x10A1, 0x30A1, 0x20A1, 0x00B1,
+ 0x00C1, 0x00C1, 0x10C1, 0x20C1,
+ 0x30C1, 0x00D1, 0x00D1, 0x20D1,
+ 0x30D1, 0x00E1, 0x00E1, 0x00E1,
+ 0x00F1, 0x00F1, 0x0101, 0x0101,
+ 0x1101, 0x2101, 0x3101, 0x0111,
+ 0x0141, 0x1141, 0x0141, 0x1141,
+ 0x1141, 0x0151, 0x0151, 0x1151,
+ 0x2151, 0x3151, 0x0161, 0x0161,
+ 0x1161, 0x0171, 0x0171, 0x0181,
+ 0x0181, 0x0191, 0x0191, 0x01A1,
+ 0x01A1, 0x01B1, 0x01B1, 0x11B1,
+ 0x21B1, 0x01C1, 0x01C1, 0x11C1,
+ 0x21C1, 0x31C1, 0x01D1, 0x01D1,
+ 0x01D1, 0x01D1, 0x11D1, 0x21D1,
+ 0x21D1, 0x01E1, 0x01E1, 0x01F1,
+ 0x01F1, 0x0201, 0x0201, 0x0211,
+ 0x0221, 0x0231, 0x0241, 0x0251,
+ 0x0281, 0x0291, 0x0281, 0x0291,
+ 0x02A1, 0x02B1, 0x02C1, 0x0321,
+ 0x0321, 0x1321, 0x2321, 0x3321,
+ 0x0331, 0x0331, 0x1331, 0x0341,
+ 0x0341, 0x1341, 0x2341, 0x3341,
+ 0x0351, 0x0361, 0x0361, 0x1361,
+ 0x2361, 0x0371, 0x0381, 0x0391,
+ 0x03C1, 0x03D1, 0x03E1, 0x03F1,
+};
+
static DEFINE_MUTEX(mdss_debug_lock);
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
@@ -1377,6 +1413,38 @@
}
} else {
if (block_id <= DISPLAY_MISR_HDMI) {
+ /*
+ * In Dual LM single display configuration,
+ * the interface number (i.e. block_id)
+ * might not be the one given from ISR.
+ * We should always check with the actual
+ * intf_num from ctl.
+ */
+ struct msm_fb_data_type *mfd = NULL;
+
+ /*
+ * ISR pass in NULL ctl, so we need to get it
+ * from the mdata.
+ */
+ if (!ctl && mdata->mixer_intf)
+ ctl = mdata->mixer_intf->ctl;
+ if (ctl)
+ mfd = ctl->mfd;
+ if (mfd && is_dual_lm_single_display(mfd)) {
+ switch (ctl->intf_num) {
+ case MDSS_MDP_INTF1:
+ block_id = DISPLAY_MISR_DSI0;
+ break;
+ case MDSS_MDP_INTF2:
+ block_id = DISPLAY_MISR_DSI1;
+ break;
+ default:
+ pr_err("Unmatch INTF for Dual LM single display configuration, INTF:%d\n",
+ ctl->intf_num);
+ return NULL;
+ }
+ }
+
intf_base = (char *)mdss_mdp_get_intf_base_addr(
mdata, block_id);
@@ -1390,11 +1458,15 @@
/*
* extra offset required for
- * cmd misr in 8996
+ * cmd misr in 8996 and mdss3.x
*/
if (IS_MDSS_MAJOR_MINOR_SAME(
mdata->mdp_rev,
- MDSS_MDP_HW_REV_107)) {
+ MDSS_MDP_HW_REV_107) ||
+ (mdata->mdp_rev ==
+ MDSS_MDP_HW_REV_300) ||
+ (mdata->mdp_rev ==
+ MDSS_MDP_HW_REV_301)) {
ctrl_reg += 0x8;
value_reg += 0x8;
}
@@ -1786,6 +1858,24 @@
}
+void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata)
+{
+ if (!sdata)
+ return;
+
+ sdata->dbg_bus = NULL;
+ sdata->dbg_bus_size = 0;
+
+ switch (sdata->shared_data->hw_rev) {
+ case MDSS_DSI_HW_REV_201:
+ sdata->dbg_bus = dsi_dbg_bus_sdm660;
+ sdata->dbg_bus_size = ARRAY_SIZE(dsi_dbg_bus_sdm660);
+ break;
+ default:
+ break;
+ }
+}
+
int mdss_dump_misr_data(char **buf, u32 size)
{
struct mdss_mdp_misr_map *dsi0_map;
diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index 0d482c0..64df339 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -78,8 +78,8 @@
#define MDSS_XLOG_IOMMU(...) mdss_xlog(__func__, __LINE__, MDSS_XLOG_IOMMU, \
##__VA_ARGS__, DATA_LIMITER)
-#define ATRACE_END(name) trace_mdss_mark_write(current->tgid, name, 0)
-#define ATRACE_BEGIN(name) trace_mdss_mark_write(current->tgid, name, 1)
+#define ATRACE_END(name) trace_tracing_mark_write(current->tgid, name, 0)
+#define ATRACE_BEGIN(name) trace_tracing_mark_write(current->tgid, name, 1)
#define ATRACE_FUNC() ATRACE_BEGIN(__func__)
#define ATRACE_INT(name, value) \
@@ -173,6 +173,7 @@
void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
int len, u32 **dump_mem, bool from_isr);
void mdss_mdp_debug_mid(u32 mid);
+void mdss_dump_dsi_debug_bus(u32 bus_dump_flag, u32 **dump_mem);
#else
struct mdss_debug_base;
struct dump_offset;
diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c
index a651b55..7bf4f11 100644
--- a/drivers/video/fbdev/msm/mdss_debug_xlog.c
+++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c
@@ -32,6 +32,7 @@
#define XLOG_DEFAULT_REGDUMP 0x2 /* dump in RAM */
#define XLOG_DEFAULT_DBGBUSDUMP 0x2 /* dump in RAM */
#define XLOG_DEFAULT_VBIF_DBGBUSDUMP 0x2 /* dump in RAM */
+#define XLOG_DEFAULT_DSI_DBGBUSDUMP 0x2 /* dump in RAM */
/*
* xlog will print this number of entries when it is called through
@@ -73,14 +74,17 @@
u32 enable_reg_dump;
u32 enable_dbgbus_dump;
u32 enable_vbif_dbgbus_dump;
+ u32 enable_dsi_dbgbus_dump;
struct work_struct xlog_dump_work;
struct mdss_debug_base *blk_arr[MDSS_DEBUG_BASE_MAX];
bool work_panic;
bool work_dbgbus;
bool work_vbif_dbgbus;
+ bool work_dsi_dbgbus;
u32 *dbgbus_dump; /* address for the debug bus dump */
u32 *vbif_dbgbus_dump; /* address for the vbif debug bus dump */
u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
+ u32 *dsi_dbgbus_dump; /* address for the dsi debug bus dump */
} mdss_dbg_xlog;
static inline bool mdss_xlog_is_enabled(u32 flag)
@@ -572,7 +576,7 @@
static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[],
u32 len, bool dead, const char *name, bool dump_dbgbus,
- bool dump_vbif_dbgbus)
+ bool dump_vbif_dbgbus, bool dump_dsi_dbgbus)
{
int i;
@@ -596,6 +600,10 @@
&mdss_dbg_xlog.nrt_vbif_dbgbus_dump, false);
}
+ if (dump_dsi_dbgbus)
+ mdss_dump_dsi_debug_bus(mdss_dbg_xlog.enable_dsi_dbgbus_dump,
+ &mdss_dbg_xlog.dsi_dbgbus_dump);
+
if (dead && mdss_dbg_xlog.panic_on_err)
panic(name);
}
@@ -607,7 +615,8 @@
ARRAY_SIZE(mdss_dbg_xlog.blk_arr),
mdss_dbg_xlog.work_panic, "xlog_workitem",
mdss_dbg_xlog.work_dbgbus,
- mdss_dbg_xlog.work_vbif_dbgbus);
+ mdss_dbg_xlog.work_vbif_dbgbus,
+ mdss_dbg_xlog.work_dsi_dbgbus);
}
void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
@@ -615,6 +624,7 @@
int i, index = 0;
bool dead = false;
bool dump_dbgbus = false, dump_vbif_dbgbus = false;
+ bool dump_dsi_dbgbus = false;
va_list args;
char *blk_name = NULL;
struct mdss_debug_base *blk_base = NULL;
@@ -650,6 +660,9 @@
if (!strcmp(blk_name, "vbif_dbg_bus"))
dump_vbif_dbgbus = true;
+ if (!strcmp(blk_name, "dsi_dbg_bus"))
+ dump_dsi_dbgbus = true;
+
if (!strcmp(blk_name, "panic"))
dead = true;
}
@@ -660,10 +673,11 @@
mdss_dbg_xlog.work_panic = dead;
mdss_dbg_xlog.work_dbgbus = dump_dbgbus;
mdss_dbg_xlog.work_vbif_dbgbus = dump_vbif_dbgbus;
+ mdss_dbg_xlog.work_dsi_dbgbus = dump_dsi_dbgbus;
schedule_work(&mdss_dbg_xlog.xlog_dump_work);
} else {
mdss_xlog_dump_array(blk_arr, blk_len, dead, name, dump_dbgbus,
- dump_vbif_dbgbus);
+ dump_vbif_dbgbus, dump_dsi_dbgbus);
}
}
@@ -752,6 +766,7 @@
mdss_dbg_xlog.enable_reg_dump = XLOG_DEFAULT_REGDUMP;
mdss_dbg_xlog.enable_dbgbus_dump = XLOG_DEFAULT_DBGBUSDUMP;
mdss_dbg_xlog.enable_vbif_dbgbus_dump = XLOG_DEFAULT_VBIF_DBGBUSDUMP;
+ mdss_dbg_xlog.enable_dsi_dbgbus_dump = XLOG_DEFAULT_DSI_DBGBUSDUMP;
pr_info("xlog_status: enable:%d, panic:%d, dump:%d\n",
mdss_dbg_xlog.xlog_enable, mdss_dbg_xlog.panic_on_err,
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index cad1387..b4e4bdd 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -27,6 +27,7 @@
#include <linux/msm-bus.h>
#include <linux/pm_qos.h>
#include <linux/mdss_io_util.h>
+#include <linux/dma-buf.h>
#include "mdss.h"
#include "mdss_panel.h"
@@ -46,6 +47,97 @@
static struct pm_qos_request mdss_dsi_pm_qos_request;
+void mdss_dump_dsi_debug_bus(u32 bus_dump_flag,
+ u32 **dump_mem)
+{
+ struct mdss_dsi_data *sdata = mdss_dsi_res;
+ struct mdss_dsi_ctrl_pdata *m_ctrl, *s_ctrl;
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ u32 status0 = 0, status1 = 0;
+ phys_addr_t phys = 0;
+ int list_size = 0;
+ int i;
+ bool dsi0_active = false, dsi1_active = false;
+
+ if (!sdata || !sdata->dbg_bus || !sdata->dbg_bus_size)
+ return;
+
+ m_ctrl = sdata->ctrl_pdata[0];
+ s_ctrl = sdata->ctrl_pdata[1];
+
+ if (!m_ctrl)
+ return;
+
+ if (m_ctrl && m_ctrl->shared_data->dsi0_active)
+ dsi0_active = true;
+ if (s_ctrl && s_ctrl->shared_data->dsi1_active)
+ dsi1_active = true;
+
+ list_size = (sdata->dbg_bus_size * sizeof(sdata->dbg_bus[0]) * 4);
+
+ in_log = (bus_dump_flag & MDSS_DBG_DUMP_IN_LOG);
+ in_mem = (bus_dump_flag & MDSS_DBG_DUMP_IN_MEM);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&sdata->pdev->dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr, dump_addr + list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ pr_info("========= Start DSI Debug Bus =========\n");
+
+ mdss_dsi_clk_ctrl(m_ctrl, m_ctrl->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
+
+ for (i = 0; i < sdata->dbg_bus_size; i++) {
+ if (dsi0_active) {
+ writel_relaxed(sdata->dbg_bus[i],
+ m_ctrl->ctrl_base + 0x124);
+ wmb(); /* ensure regsiter is committed */
+ }
+ if (dsi1_active) {
+ writel_relaxed(sdata->dbg_bus[i],
+ s_ctrl->ctrl_base + 0x124);
+ wmb(); /* ensure register is committed */
+ }
+
+ if (dsi0_active) {
+ status0 = readl_relaxed(m_ctrl->ctrl_base + 0x128);
+ if (in_log)
+ pr_err("CTRL:0 bus_ctrl: 0x%x status: 0x%x\n",
+ sdata->dbg_bus[i], status0);
+ }
+ if (dsi1_active) {
+ status1 = readl_relaxed(s_ctrl->ctrl_base + 0x128);
+ if (in_log)
+ pr_err("CTRL:1 bus_ctrl: 0x%x status: 0x%x\n",
+ sdata->dbg_bus[i], status1);
+ }
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = sdata->dbg_bus[i];
+ dump_addr[i*4 + 1] = status0;
+ dump_addr[i*4 + 2] = status1;
+ dump_addr[i*4 + 3] = 0x0;
+ }
+ }
+
+ mdss_dsi_clk_ctrl(m_ctrl, m_ctrl->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
+
+ pr_info("========End DSI Debug Bus=========\n");
+}
+
static void mdss_dsi_pm_qos_add_request(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
struct irq_info *irq_info;
@@ -291,6 +383,14 @@
ret = 0;
}
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio)) {
+ ret = gpio_direction_output(
+ ctrl_pdata->vdd_ext_gpio, 0);
+ if (ret)
+ pr_err("%s: unable to set dir for vdd gpio\n",
+ __func__);
+ }
+
if (mdss_dsi_pinctrl_set_state(ctrl_pdata, false))
pr_debug("reset disable: pinctrl not enabled\n");
@@ -318,6 +418,15 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio)) {
+ ret = gpio_direction_output(
+ ctrl_pdata->vdd_ext_gpio, 1);
+ usleep_range(3000, 4000); /* h/w recommended delay */
+ if (ret)
+ pr_err("%s: unable to set dir for vdd gpio\n",
+ __func__);
+ }
+
ret = msm_mdss_enable_vreg(
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, 1);
@@ -1704,7 +1813,8 @@
ATRACE_BEGIN("dsi_panel_off");
ret = ctrl_pdata->off(pdata);
if (ret) {
- pr_err("%s: Panel OFF failed\n", __func__);
+ pr_err("%s: Panel OFF failed\n",
+ __func__);
goto error;
}
ATRACE_END("dsi_panel_off");
@@ -2658,7 +2768,6 @@
break;
case MDSS_EVENT_PANEL_OFF:
power_state = (int) (unsigned long) arg;
- disable_esd_thread();
ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
rc = mdss_dsi_blank(pdata, power_state);
@@ -3171,6 +3280,7 @@
struct mdss_util_intf *util;
static int te_irq_registered;
struct mdss_panel_data *pdata;
+ struct mdss_panel_cfg *pan_cfg = NULL;
if (!pdev || !pdev->dev.of_node) {
pr_err("%s: pdev not found for DSI controller\n", __func__);
@@ -3203,6 +3313,14 @@
return -ENODEV;
}
+ pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_SPI);
+ if (IS_ERR(pan_cfg)) {
+ return PTR_ERR(pan_cfg);
+ } else if (pan_cfg) {
+ pr_debug("%s: SPI is primary\n", __func__);
+ return -ENODEV;
+ }
+
ctrl_pdata->mdss_util = util;
atomic_set(&ctrl_pdata->te_irq_ready, 0);
@@ -3340,6 +3458,8 @@
else
ctrl_pdata->shared_data->dsi1_active = true;
+ mdss_dsi_debug_bus_init(mdss_dsi_res);
+
return 0;
error_shadow_clk_deinit:
@@ -4069,6 +4189,7 @@
if (!gpio_is_valid(ctrl_pdata->disp_en_gpio))
pr_debug("%s:%d, Disp_en gpio not specified\n",
__func__, __LINE__);
+ pdata->panel_en_gpio = ctrl_pdata->disp_en_gpio;
}
ctrl_pdata->disp_te_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
@@ -4084,6 +4205,15 @@
if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio))
pr_info("%s: bklt_en gpio not specified\n", __func__);
+ ctrl_pdata->bklt_en_gpio_invert =
+ of_property_read_bool(ctrl_pdev->dev.of_node,
+ "qcom,platform-bklight-en-gpio-invert");
+
+ ctrl_pdata->vdd_ext_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,ext-vdd-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->vdd_ext_gpio))
+ pr_info("%s: ext vdd gpio not specified\n", __func__);
+
ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
"qcom,platform-reset-gpio", 0);
if (!gpio_is_valid(ctrl_pdata->rst_gpio))
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 058c27a..5d2d677 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -57,7 +57,7 @@
#define MDSS_DSI_HW_REV_104 0x10040000 /* 8996 */
#define MDSS_DSI_HW_REV_104_1 0x10040001 /* 8996 */
#define MDSS_DSI_HW_REV_104_2 0x10040002 /* 8937 */
-
+#define MDSS_DSI_HW_REV_201 20010000 /* 660 */
#define MDSS_DSI_HW_REV_STEP_0 0x0
#define MDSS_DSI_HW_REV_STEP_1 0x1
#define MDSS_DSI_HW_REV_STEP_2 0x2
@@ -297,6 +297,8 @@
* mutex, clocks, regulator information, setup information
*/
struct dsi_shared_data *shared_data;
+ u32 *dbg_bus;
+ int dbg_bus_size;
};
/*
@@ -407,7 +409,7 @@
int (*cmdlist_commit)(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
void (*switch_mode)(struct mdss_panel_data *pdata, int mode);
struct mdss_panel_data panel_data;
- unsigned char *ctrl_base;
+ unsigned char __iomem *ctrl_base;
struct mdss_io_data ctrl_io;
struct mdss_io_data mmss_misc_io;
struct mdss_io_data phy_io;
@@ -433,9 +435,13 @@
int rst_gpio;
int disp_en_gpio;
int bklt_en_gpio;
+ int vdd_ext_gpio;
int mode_gpio;
int intf_mux_gpio;
+ bool bklt_en_gpio_invert;
+ int lcd_mode_sel_gpio;
int bklt_ctrl; /* backlight ctrl */
+ enum dsi_ctrl_op_mode bklt_dcs_op_mode; /* backlight dcs ctrl mode */
bool pwm_pmi;
int pwm_period;
int pwm_pmic_gpio;
@@ -680,6 +686,9 @@
u32 mask, u32 val);
int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, int power_state);
+void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl);
+
+void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata);
static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module)
{
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index d76be70..e221c1c 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -82,6 +82,8 @@
static struct mdss_dsi_event dsi_event;
static int dsi_event_thread(void *data);
+static void dsi_send_events(struct mdss_dsi_ctrl_pdata *ctrl,
+ u32 events, u32 arg);
void mdss_dsi_ctrl_init(struct device *ctrl_dev,
struct mdss_dsi_ctrl_pdata *ctrl)
@@ -161,7 +163,14 @@
MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid,
client);
- if (enable == 0) {
+ /*
+ * ensure that before going into ecg or turning
+ * off the clocks, cmd_mdp_busy is not true. During a
+ * race condition, clocks are turned off and so the
+ * isr for cmd_mdp_busy does not get cleared in hw.
+ */
+ if (enable == MDSS_DSI_CLK_OFF ||
+ enable == MDSS_DSI_CLK_EARLY_GATE) {
/* need wait before disable */
mutex_lock(&ctrl->cmd_mutex);
mdss_dsi_cmd_mdp_busy(ctrl);
@@ -813,8 +822,10 @@
* Disable PHY contention detection and receive.
* Configure the strength ctrl 1 register.
*/
- MIPI_OUTP((ctrl0->phy_io.base) + 0x0188, 0);
- MIPI_OUTP((ctrl1->phy_io.base) + 0x0188, 0);
+ if (ctrl0->shared_data->phy_rev != DSI_PHY_REV_12NM) {
+ MIPI_OUTP((ctrl0->phy_io.base) + 0x0188, 0);
+ MIPI_OUTP((ctrl1->phy_io.base) + 0x0188, 0);
+ }
data0 = MIPI_INP(ctrl0->ctrl_base + 0x0004);
data1 = MIPI_INP(ctrl1->ctrl_base + 0x0004);
@@ -869,6 +880,18 @@
udelay(u_dly);
}
if (i == loop) {
+ if ((ctrl0->shared_data->phy_rev == DSI_PHY_REV_12NM) &&
+ (event == DSI_EV_LP_RX_TIMEOUT)) {
+ struct mdss_panel_info *pinfo =
+ &ctrl0->panel_data.panel_info;
+ /* If ESD is not enabled, report panel dead */
+ if (!pinfo->esd_check_enabled &&
+ ctrl0->recovery)
+ ctrl0->recovery->fxn(
+ ctrl0->recovery->data,
+ MDP_INTF_DSI_PANEL_DEAD);
+ return;
+ }
MDSS_XLOG(ctrl0->ndx, ln0, 0x1f1f);
MDSS_XLOG(ctrl1->ndx, ln1, 0x1f1f);
pr_err("%s: Clock lane still in stop state\n",
@@ -889,13 +912,20 @@
* Enable PHY contention detection and receive.
* Configure the strength ctrl 1 register.
*/
- MIPI_OUTP((ctrl0->phy_io.base) + 0x0188, 0x6);
- MIPI_OUTP((ctrl1->phy_io.base) + 0x0188, 0x6);
+ if (ctrl0->shared_data->phy_rev != DSI_PHY_REV_12NM) {
+ MIPI_OUTP((ctrl0->phy_io.base) + 0x0188, 0x6);
+ MIPI_OUTP((ctrl1->phy_io.base) + 0x0188, 0x6);
+ }
/*
* Add sufficient delay to make sure
* pixel transmission as started
*/
udelay(200);
+ /* Un-mask LP_RX_TIMEOUT error if recovery successful */
+ if (event == DSI_EV_LP_RX_TIMEOUT) {
+ mdss_dsi_set_reg(ctrl0, 0x10c, BIT(5), 0);
+ mdss_dsi_set_reg(ctrl1, 0x10c, BIT(5), 0);
+ }
} else {
if (ctrl->recovery) {
rc = ctrl->recovery->fxn(ctrl->recovery->data,
@@ -907,7 +937,8 @@
}
}
/* Disable PHY contention detection and receive */
- MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0);
+ if (ctrl->shared_data->phy_rev != DSI_PHY_REV_12NM)
+ MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0);
data0 = MIPI_INP(ctrl->ctrl_base + 0x0004);
/* Disable DSI video mode */
@@ -948,6 +979,17 @@
udelay(u_dly);
}
if (i == loop) {
+ if ((ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) &&
+ (event == DSI_EV_LP_RX_TIMEOUT)) {
+ struct mdss_panel_info *pinfo =
+ &ctrl->panel_data.panel_info;
+ /* If ESD is not enabled, report panel dead */
+ if (!pinfo->esd_check_enabled && ctrl->recovery)
+ ctrl->recovery->fxn(
+ ctrl->recovery->data,
+ MDP_INTF_DSI_PANEL_DEAD);
+ return;
+ }
MDSS_XLOG(ctrl->ndx, ln0, 0x1f1f);
pr_err("%s: Clock lane still in stop state\n",
__func__);
@@ -961,12 +1003,16 @@
/* Enable Video mode for DSI controller */
MIPI_OUTP(ctrl->ctrl_base + 0x004, data0);
/* Enable PHY contention detection and receiver */
- MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0x6);
+ if (ctrl->shared_data->phy_rev != DSI_PHY_REV_12NM)
+ MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0x6);
/*
* Add sufficient delay to make sure
* pixel transmission as started
*/
udelay(200);
+ /* Un-mask LP_RX_TIMEOUT error if recovery successful */
+ if (event == DSI_EV_LP_RX_TIMEOUT)
+ mdss_dsi_set_reg(ctrl, 0x10c, BIT(5), 0);
}
pr_debug("Recovery done\n");
}
@@ -1134,6 +1180,11 @@
rc = 1;
lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen;
+ if (!lenp || !ctrl->status_cmds_rlen) {
+ pr_err("invalid dsi read params!\n");
+ return 0;
+ }
+
for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) {
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = ctrl->status_cmds.cmds + i;
@@ -1150,6 +1201,8 @@
rc = mdss_dsi_cmdlist_put(ctrl, &cmdreq);
if (rc <= 0) {
+ if (!mdss_dsi_sync_wait_enable(ctrl) ||
+ mdss_dsi_sync_wait_trigger(ctrl))
pr_err("%s: get status: fail\n", __func__);
return rc;
}
@@ -1237,6 +1290,15 @@
{
u32 data, offset;
+ if (!dsc) {
+ if (ctrl->panel_mode == DSI_VIDEO_MODE)
+ offset = MDSS_DSI_VIDEO_COMPRESSION_MODE_CTRL;
+ else
+ offset = MDSS_DSI_COMMAND_COMPRESSION_MODE_CTRL;
+ MIPI_OUTP((ctrl->ctrl_base) + offset, 0);
+ return;
+ }
+
if (dsc->pkt_per_line <= 0) {
pr_err("%s: Error: pkt_per_line cannot be negative or 0\n",
__func__);
@@ -1425,8 +1487,7 @@
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x5C, stream_total);
}
- if (dsc) /* compressed */
- mdss_dsi_dsc_config(ctrl_pdata, dsc);
+ mdss_dsi_dsc_config(ctrl_pdata, dsc);
}
void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl)
@@ -1483,21 +1544,53 @@
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
+
+ MDSS_XLOG(ctrl_pdata->ndx, ctrl_pdata->mdp_busy);
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x098, 0x01); /* trigger */
wmb(); /* ensure write is finished before progressing */
ret = wait_for_completion_killable_timeout(&ctrl_pdata->bta_comp,
DSI_BTA_EVENT_TIMEOUT);
if (ret <= 0) {
- mdss_dsi_disable_irq(ctrl_pdata, DSI_BTA_TERM);
- pr_err("%s: DSI BTA error: %i\n", __func__, ret);
+ u32 reg_val, status;
+
+ reg_val = MIPI_INP(ctrl_pdata->ctrl_base + 0x0110);
+ status = reg_val & DSI_INTR_BTA_DONE;
+ if (status) {
+ reg_val &= DSI_INTR_MASK_ALL;
+ /* clear BTA_DONE isr only */
+ reg_val |= DSI_INTR_BTA_DONE;
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0110, reg_val);
+ mdss_dsi_disable_irq(ctrl_pdata, DSI_BTA_TERM);
+ complete(&ctrl_pdata->bta_comp);
+ ret = 1;
+ pr_warn("%s: bta done but irq not triggered\n",
+ __func__);
+ } else {
+ pr_err("%s: DSI BTA error: %i\n", __func__, ret);
+ /*
+ * For 12nm DSI PHY, BTA_TO interrupt may not trigger.
+ * Treat software timer timeout as BTA_TO.
+ */
+ if (ctrl_pdata->shared_data->phy_rev ==
+ DSI_PHY_REV_12NM) {
+ /* Mask BTA_TIMEOUT/LP_RX_TIMEOUT error */
+ mdss_dsi_set_reg(ctrl_pdata, 0x10c,
+ (BIT(5) | BIT(7)), (BIT(5) | BIT(7)));
+ dsi_send_events(ctrl_pdata,
+ DSI_EV_LP_RX_TIMEOUT, 0);
+ }
+ ret = -ETIMEDOUT;
+ }
}
if (ignore_underflow) {
+ u32 data = MIPI_INP((ctrl_pdata->ctrl_base) + 0x10c);
/* clear pending overflow status */
mdss_dsi_set_reg(ctrl_pdata, 0xc, 0xffffffff, 0x44440000);
- /* restore overflow isr */
- mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0);
+ /* restore overflow isr if LP_RX_TO not masked*/
+ if (!(data & BIT(5)))
+ mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0);
}
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
@@ -1684,8 +1777,8 @@
__func__, cm->payload[0], len);
if (!wait || dchdr->wait > VSYNC_PERIOD)
- usleep_range(dchdr->wait * 1000,
- dchdr->wait * 1000);
+ usleep_range((dchdr->wait * 1000),
+ (dchdr->wait * 1000) + 10);
mdss_dsi_buf_init(tp);
len = 0;
@@ -2103,6 +2196,7 @@
MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01);
wmb(); /* ensure write is finished before progressing */
+ MDSS_XLOG(ctrl->dma_addr, len);
if (ctrl->do_unicast) {
/* let cmd_trigger to kickoff later */
@@ -2139,10 +2233,12 @@
if (mctrl && mctrl->dma_addr) {
if (ignored) {
+ u32 data = MIPI_INP((mctrl->ctrl_base) + 0x10c);
/* clear pending overflow status */
mdss_dsi_set_reg(mctrl, 0xc, 0xffffffff, 0x44440000);
- /* restore overflow isr */
- mdss_dsi_set_reg(mctrl, 0x10c, 0x0f0000, 0);
+ /* restore overflow isr if LP_RX_TO not masked*/
+ if (!(data & BIT(5)))
+ mdss_dsi_set_reg(mctrl, 0x10c, 0x0f0000, 0);
}
if (mctrl->dmap_iommu_map) {
mdss_smmu_dsi_unmap_buffer(mctrl->dma_addr, domain,
@@ -2160,10 +2256,12 @@
}
if (ignored) {
+ u32 data = MIPI_INP((ctrl->ctrl_base) + 0x10c);
/* clear pending overflow status */
mdss_dsi_set_reg(ctrl, 0xc, 0xffffffff, 0x44440000);
- /* restore overflow isr */
- mdss_dsi_set_reg(ctrl, 0x10c, 0x0f0000, 0);
+ /* restore overflow isr if LP_RX_TO/BTA_TO not masked*/
+ if (!(data & BIT(5)))
+ mdss_dsi_set_reg(ctrl, 0x10c, 0x0f0000, 0);
}
ctrl->dma_addr = 0;
ctrl->dma_size = 0;
@@ -2181,6 +2279,7 @@
bool ack_error = false;
char reg[16];
int repeated_bytes = 0;
+ struct mdss_dsi_ctrl_pdata *mctrl = mdss_dsi_get_other_ctrl(ctrl);
lp = (u32 *)rp->data;
temp = (u32 *)reg;
@@ -2241,7 +2340,11 @@
off += ((cnt - 1) * 4);
for (i = 0; i < cnt; i++) {
- data = (u32)MIPI_INP((ctrl->ctrl_base) + off);
+ if (mdss_dsi_sync_wait_trigger(ctrl))
+ data = (u32)MIPI_INP((mctrl->ctrl_base) + off);
+ else
+ data = (u32)MIPI_INP((ctrl->ctrl_base) + off);
+
/* to network byte order */
if (!repeated_bytes)
*lp++ = ntohl(data);
@@ -2969,7 +3072,10 @@
* warning message is ignored.
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
- (ctrl->status_mode == ESD_BTA) && (status & 0x1008000))
+ ((ctrl->status_mode == ESD_BTA) ||
+ (ctrl->status_mode == ESD_REG) ||
+ (ctrl->status_mode == ESD_REG_NT35596)) &&
+ (status & 0x1008000))
return false;
pr_err("%s: status=%x\n", __func__, status);
@@ -2991,8 +3097,12 @@
if (status & 0x0111) {
MIPI_OUTP(base + 0x00c0, status);
- if (status & 0x0110)
+ if (status & 0x0110) {
+ /* Mask BTA_TIMEOUT/LP_RX_TIMEOUT error */
+ mdss_dsi_set_reg(ctrl, 0x10c,
+ (BIT(5) | BIT(7)), (BIT(5) | BIT(7)));
dsi_send_events(ctrl, DSI_EV_LP_RX_TIMEOUT, 0);
+ }
pr_err("%s: status=%x\n", __func__, status);
ret = true;
}
@@ -3122,7 +3232,7 @@
pr_err("%s: panic in WQ as dsi error intrs within:%dms\n",
__func__, err_container->err_time_delta);
MDSS_XLOG_TOUT_HANDLER_WQ("mdp", "dsi0_ctrl", "dsi0_phy",
- "dsi1_ctrl", "dsi1_phy");
+ "dsi1_ctrl", "dsi1_phy", "dsi_dbg_bus", "panic");
}
}
@@ -3200,8 +3310,10 @@
* cleared.
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
- (ctrl->status_mode == ESD_BTA) &&
- (ctrl->panel_mode == DSI_VIDEO_MODE)) {
+ ((ctrl->status_mode == ESD_BTA) ||
+ (ctrl->status_mode == ESD_REG) ||
+ (ctrl->status_mode == ESD_REG_NT35596)) &&
+ (ctrl->panel_mode == DSI_VIDEO_MODE)) {
isr &= ~DSI_INTR_ERROR;
/* clear only overflow */
mdss_dsi_set_reg(ctrl, 0x0c, 0x44440000, 0x44440000);
@@ -3210,6 +3322,7 @@
}
if (isr & DSI_INTR_VIDEO_DONE) {
+ MDSS_XLOG(ctrl->ndx, isr, 0x111);
spin_lock(&ctrl->mdp_lock);
mdss_dsi_disable_irq_nosync(ctrl, DSI_VIDEO_TERM);
complete(&ctrl->video_comp);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 2ef1695..b94280d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -24,11 +24,13 @@
#include <linux/string.h>
#include "mdss_dsi.h"
+#include "mdss_debug.h"
#ifdef TARGET_HW_MDSS_HDMI
#include "mdss_dba_utils.h"
#endif
+#include "mdss_debug.h"
+
#define DT_CMD_HDR 6
-#define MIN_REFRESH_RATE 48
#define DEFAULT_MDP_TRANSFER_TIME 14000
#define VSYNC_DELAY msecs_to_jiffies(17)
@@ -238,6 +240,11 @@
cmdreq.rlen = 0;
cmdreq.cb = NULL;
+ if (ctrl->bklt_dcs_op_mode == DSI_HS_MODE)
+ cmdreq.flags |= CMD_REQ_HS_MODE;
+ else
+ cmdreq.flags |= CMD_REQ_LP_MODE;
+
mdss_dsi_cmdlist_put(ctrl, &cmdreq);
}
@@ -254,11 +261,12 @@
ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
- pr_debug("%s: Idle (%d->%d)\n", __func__, ctrl->idle, enable);
+ pr_info("%s: Idle (%d->%d)\n", __func__, ctrl->idle, enable);
if (ctrl->idle == enable)
return;
+ MDSS_XLOG(ctrl->idle, enable);
if (enable) {
if (ctrl->idle_on_cmds.cmd_cnt) {
mdss_dsi_panel_cmds_send(ctrl, &ctrl->idle_on_cmds,
@@ -318,6 +326,15 @@
goto bklt_en_gpio_err;
}
}
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio)) {
+ rc = gpio_request(ctrl_pdata->vdd_ext_gpio,
+ "vdd_enable");
+ if (rc) {
+ pr_err("request vdd enable gpio failed, rc=%d\n",
+ rc);
+ goto vdd_en_gpio_err;
+ }
+ }
if (gpio_is_valid(ctrl_pdata->mode_gpio)) {
rc = gpio_request(ctrl_pdata->mode_gpio, "panel_mode");
if (rc) {
@@ -329,6 +346,9 @@
return rc;
mode_gpio_err:
+ if (gpio_is_valid(ctrl_pdata->vdd_ext_gpio))
+ gpio_free(ctrl_pdata->vdd_ext_gpio);
+vdd_en_gpio_err:
if (gpio_is_valid(ctrl_pdata->bklt_en_gpio))
gpio_free(ctrl_pdata->bklt_en_gpio);
bklt_en_gpio_err:
@@ -421,6 +441,8 @@
__func__);
goto exit;
}
+ gpio_set_value((ctrl_pdata->disp_en_gpio), 1);
+ usleep_range(100, 110);
}
if (pdata->panel_info.rst_seq_len) {
@@ -437,13 +459,19 @@
gpio_set_value((ctrl_pdata->rst_gpio),
pdata->panel_info.rst_seq[i]);
if (pdata->panel_info.rst_seq[++i])
- usleep_range(pinfo->rst_seq[i] * 1000,
- pinfo->rst_seq[i] * 1000);
+ usleep_range((pinfo->rst_seq[i] * 1000),
+ (pinfo->rst_seq[i] * 1000) + 10);
}
if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
- rc = gpio_direction_output(
- ctrl_pdata->bklt_en_gpio, 1);
+
+ if (ctrl_pdata->bklt_en_gpio_invert)
+ rc = gpio_direction_output(
+ ctrl_pdata->bklt_en_gpio, 0);
+ else
+ rc = gpio_direction_output(
+ ctrl_pdata->bklt_en_gpio, 1);
+
if (rc) {
pr_err("%s: unable to set dir for bklt gpio\n",
__func__);
@@ -475,11 +503,17 @@
}
} else {
if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
- gpio_set_value((ctrl_pdata->bklt_en_gpio), 0);
+
+ if (ctrl_pdata->bklt_en_gpio_invert)
+ gpio_set_value((ctrl_pdata->bklt_en_gpio), 1);
+ else
+ gpio_set_value((ctrl_pdata->bklt_en_gpio), 0);
+
gpio_free(ctrl_pdata->bklt_en_gpio);
}
if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) {
gpio_set_value((ctrl_pdata->disp_en_gpio), 0);
+ usleep_range(100, 110);
gpio_free(ctrl_pdata->disp_en_gpio);
}
gpio_set_value((ctrl_pdata->rst_gpio), 0);
@@ -1570,8 +1604,9 @@
goto end;
}
}
- rc = of_property_read_string(cfg_np, "qcom,split-mode", &data);
- if (!rc && !strcmp(data, "pingpong-split"))
+
+ if (!of_property_read_string(cfg_np, "qcom,split-mode",
+ &data) && !strcmp(data, "pingpong-split"))
pinfo->use_pingpong_split = true;
if (((timing->lm_widths[0]) || (timing->lm_widths[1])) &&
@@ -1683,7 +1718,7 @@
static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl)
{
- int i, j;
+ int i, j = 0;
int len = 0, *lenp;
int group = 0;
@@ -1692,6 +1727,15 @@
for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++)
len += lenp[i];
+ for (i = 0; i < len; i++) {
+ pr_debug("[%i] return:0x%x status:0x%x\n",
+ i, (unsigned int)ctrl->return_buf[i],
+ (unsigned int)ctrl->status_value[j + i]);
+ MDSS_XLOG(ctrl->ndx, ctrl->return_buf[i],
+ ctrl->status_value[j + i]);
+ j += len;
+ }
+
for (j = 0; j < ctrl->groups; ++j) {
for (i = 0; i < len; ++i) {
if (ctrl->return_buf[i] !=
@@ -1823,10 +1867,12 @@
pr_debug("%s: default dms suspend/resume\n", __func__);
mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd,
- "qcom,video-to-cmd-mode-switch-commands", NULL);
+ "qcom,video-to-cmd-mode-switch-commands",
+ "qcom,mode-switch-commands-state");
mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video,
- "qcom,cmd-to-video-mode-switch-commands", NULL);
+ "qcom,cmd-to-video-mode-switch-commands",
+ "qcom,mode-switch-commands-state");
mdss_dsi_parse_dcs_cmds(np, &ctrl->post_dms_on_cmds,
"qcom,mdss-dsi-post-mode-switch-on-command",
@@ -2146,10 +2192,10 @@
__func__, __LINE__);
/*
- * Since min refresh rate is not specified when dynamic
- * fps is enabled, using minimum as 30
+ * If min refresh rate is not specified, set it to the
+ * default panel refresh rate.
*/
- pinfo->min_fps = MIN_REFRESH_RATE;
+ pinfo->min_fps = pinfo->mipi.frame_rate;
rc = 0;
}
@@ -2271,6 +2317,13 @@
}
} else if (!strcmp(data, "bl_ctrl_dcs")) {
ctrl_pdata->bklt_ctrl = BL_DCS_CMD;
+ data = of_get_property(np,
+ "qcom,mdss-dsi-bl-dcs-command-state", NULL);
+ if (data && !strcmp(data, "dsi_hs_mode"))
+ ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE;
+ else
+ ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE;
+
pr_debug("%s: Configured DCS_CMD bklt ctrl\n",
__func__);
}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
index 9bd2c4d..ec179e8 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
@@ -107,6 +107,7 @@
{
DSI_PHY_W32(ctrl->phy_io.base, SYS_CTRL, BIT(0) | BIT(3));
wmb(); /* make sure DSI PHY is disabled */
+ mdss_dsi_ctrl_phy_reset(ctrl);
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index 992d687..326768d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -133,26 +133,31 @@
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_panel_info *pinfo;
struct msm_fb_data_type *mfd;
+ char fb_id[7] = {'\0'};
if (!evdata) {
pr_err("%s: event data not available\n", __func__);
return NOTIFY_BAD;
}
+ strlcpy(fb_id, evdata->info->fix.id, 7);
/* handle only mdss fb device */
- if (strcmp("mdssfb", evdata->info->fix.id))
+ if (strcmp("mdssfb", fb_id))
return NOTIFY_DONE;
mfd = evdata->info->par;
- ctrl_pdata = container_of(dev_get_platdata(&mfd->pdev->dev),
+ if (mfd->panel_info->type == SPI_PANEL) {
+ pinfo = mfd->panel_info;
+ } else {
+ ctrl_pdata = container_of(dev_get_platdata(&mfd->pdev->dev),
struct mdss_dsi_ctrl_pdata, panel_data);
- if (!ctrl_pdata) {
- pr_err("%s: DSI ctrl not available\n", __func__);
- return NOTIFY_BAD;
+ if (!ctrl_pdata) {
+ pr_err("%s: DSI ctrl not available\n", __func__);
+ return NOTIFY_BAD;
+ }
+
+ pinfo = &ctrl_pdata->panel_data.panel_info;
}
-
- pinfo = &ctrl_pdata->panel_data.panel_info;
-
if ((!(pinfo->esd_check_enabled) &&
dsi_status_disable) ||
(dsi_status_disable == DSI_STATUS_CHECK_DISABLE)) {
@@ -189,7 +194,8 @@
return 0;
}
-static int param_dsi_status_disable(const char *val, struct kernel_param *kp)
+static int param_dsi_status_disable(const char *val,
+ const struct kernel_param *kp)
{
int ret = 0;
int int_val;
@@ -204,7 +210,7 @@
return ret;
}
-static int param_set_interval(const char *val, struct kernel_param *kp)
+static int param_set_interval(const char *val, const struct kernel_param *kp)
{
int ret = 0;
int int_val;
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 27299d10..edeecb4 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -345,6 +345,9 @@
case EDP_PANEL:
ret = snprintf(buf, PAGE_SIZE, "edp panel\n");
break;
+ case SPI_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "spi panel\n");
+ break;
default:
ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
break;
@@ -1161,11 +1164,24 @@
if (pdata->next) {
spt = mdss_panel_get_timing_by_name(pdata->next,
modedb[i].name);
- if (!IS_ERR_OR_NULL(spt))
+ /* for split config, recalculate xres and pixel clock */
+ if (!IS_ERR_OR_NULL(spt)) {
+ unsigned long pclk, h_total, v_total;
modedb[i].xres += spt->xres;
- else
+ h_total = modedb[i].xres +
+ modedb[i].left_margin +
+ modedb[i].right_margin +
+ modedb[i].hsync_len;
+ v_total = modedb[i].yres +
+ modedb[i].lower_margin +
+ modedb[i].upper_margin +
+ modedb[i].vsync_len;
+ pclk = h_total * v_total * modedb[i].refresh;
+ modedb[i].pixclock = KHZ2PICOS(pclk / 1000);
+ } else {
pr_debug("no matching split config for %s\n",
modedb[i].name);
+ }
/*
* if no panel timing found for current, need to
@@ -1201,6 +1217,7 @@
struct mdss_panel_data *pdata;
struct fb_info *fbi;
int rc;
+ const char *data;
if (fbi_list_index >= MAX_FBI_LIST)
return -ENOMEM;
@@ -1249,6 +1266,21 @@
mfd->pdev = pdev;
+ if (mfd->panel.type == SPI_PANEL)
+ mfd->fb_imgType = MDP_RGB_565;
+ if (mfd->panel.type == MIPI_VIDEO_PANEL || mfd->panel.type ==
+ MIPI_CMD_PANEL || mfd->panel.type == SPI_PANEL){
+ rc = of_property_read_string(pdev->dev.of_node,
+ "qcom,mdss-fb-format", &data);
+ if (!rc) {
+ if (!strcmp(data, "rgb888"))
+ mfd->fb_imgType = MDP_RGB_888;
+ else if (!strcmp(data, "rgb565"))
+ mfd->fb_imgType = MDP_RGB_565;
+ else
+ mfd->fb_imgType = MDP_RGBA_8888;
+ }
+ }
mfd->split_fb_left = mfd->split_fb_right = 0;
mdss_fb_set_split_mode(mfd, pdata);
@@ -1361,6 +1393,7 @@
mfd->mdp_sync_pt_data.threshold = 1;
mfd->mdp_sync_pt_data.retire_threshold = 0;
break;
+ case SPI_PANEL:
case MIPI_CMD_PANEL:
mfd->mdp_sync_pt_data.threshold = 1;
mfd->mdp_sync_pt_data.retire_threshold = 1;
@@ -1653,6 +1686,7 @@
u32 temp = bkl_lvl;
bool ad_bl_notify_needed = false;
bool bl_notify_needed = false;
+ bool twm_en = false;
if ((((mdss_fb_is_power_off(mfd) && mfd->dcm_state != DCM_ENTER)
|| !mfd->allow_bl_update) && !IS_CALIB_MODE_BL(mfd)) ||
@@ -1687,9 +1721,17 @@
if (mfd->bl_level != bkl_lvl)
bl_notify_needed = true;
pr_debug("backlight sent to panel :%d\n", temp);
- pdata->set_backlight(pdata, temp);
- mfd->bl_level = bkl_lvl;
- mfd->bl_level_scaled = temp;
+
+ if (mfd->mdp.is_twm_en)
+ twm_en = mfd->mdp.is_twm_en();
+
+ if (twm_en) {
+ pr_info("TWM Enabled skip backlight update\n");
+ } else {
+ pdata->set_backlight(pdata, temp);
+ mfd->bl_level = bkl_lvl;
+ mfd->bl_level_scaled = temp;
+ }
}
if (ad_bl_notify_needed)
mdss_fb_bl_update_notify(mfd,
@@ -4730,6 +4772,9 @@
if (!mfd || !mfd->panel_info)
return -EINVAL;
+ /* make sure that we are idle while switching */
+ mdss_fb_wait_for_kickoff(mfd);
+
pinfo = mfd->panel_info;
if (pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_SUSPEND_RESUME) {
ret = mdss_fb_blanking_mode_switch(mfd, mode);
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index c85f033..5f2baef 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -236,6 +236,7 @@
int (*pp_release_fnc)(struct msm_fb_data_type *mfd);
void (*signal_retire_fence)(struct msm_fb_data_type *mfd,
int retire_cnt);
+ bool (*is_twm_en)(void);
void *private1;
};
diff --git a/drivers/video/fbdev/msm/mdss_io_util.c b/drivers/video/fbdev/msm/mdss_io_util.c
index 3117793..2f70ad3 100644
--- a/drivers/video/fbdev/msm/mdss_io_util.c
+++ b/drivers/video/fbdev/msm/mdss_io_util.c
@@ -275,8 +275,8 @@
}
need_sleep = !regulator_is_enabled(in_vreg[i].vreg);
if (in_vreg[i].pre_on_sleep && need_sleep)
- usleep_range(in_vreg[i].pre_on_sleep * 1000,
- in_vreg[i].pre_on_sleep * 1000);
+ usleep_range((in_vreg[i].pre_on_sleep * 1000),
+ (in_vreg[i].pre_on_sleep * 1000) + 10);
rc = regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_ENABLE]);
if (rc < 0) {
@@ -287,8 +287,8 @@
}
rc = regulator_enable(in_vreg[i].vreg);
if (in_vreg[i].post_on_sleep && need_sleep)
- usleep_range(in_vreg[i].post_on_sleep * 1000,
- in_vreg[i].post_on_sleep * 1000);
+ usleep_range((in_vreg[i].post_on_sleep * 1000),
+ (in_vreg[i].post_on_sleep * 1000) + 10);
if (rc < 0) {
DEV_ERR("%pS->%s: %s enable failed\n",
__builtin_return_address(0), __func__,
@@ -299,8 +299,8 @@
} else {
for (i = num_vreg-1; i >= 0; i--) {
if (in_vreg[i].pre_off_sleep)
- usleep_range(in_vreg[i].pre_off_sleep * 1000,
- in_vreg[i].pre_off_sleep * 1000);
+ usleep_range((in_vreg[i].pre_off_sleep * 1000),
+ (in_vreg[i].pre_off_sleep * 1000) + 10);
regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_DISABLE]);
@@ -308,8 +308,8 @@
regulator_disable(in_vreg[i].vreg);
if (in_vreg[i].post_off_sleep)
- usleep_range(in_vreg[i].post_off_sleep * 1000,
- in_vreg[i].post_off_sleep * 1000);
+ usleep_range((in_vreg[i].post_off_sleep * 1000),
+ (in_vreg[i].post_off_sleep * 1000) + 10);
}
}
return rc;
@@ -321,14 +321,14 @@
vreg_set_opt_mode_fail:
for (i--; i >= 0; i--) {
if (in_vreg[i].pre_off_sleep)
- usleep_range(in_vreg[i].pre_off_sleep * 1000,
- in_vreg[i].pre_off_sleep * 1000);
+ usleep_range((in_vreg[i].pre_off_sleep * 1000),
+ (in_vreg[i].pre_off_sleep * 1000) + 10);
regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_DISABLE]);
regulator_disable(in_vreg[i].vreg);
if (in_vreg[i].post_off_sleep)
- usleep_range(in_vreg[i].post_off_sleep * 1000,
- in_vreg[i].post_off_sleep * 1000);
+ usleep_range((in_vreg[i].post_off_sleep * 1000),
+ (in_vreg[i].post_off_sleep * 1000) + 10);
}
return rc;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index d07650b..a6d43a6 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1270,6 +1270,7 @@
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
VOTE_INDEX_LOW);
mdss_bus_rt_bw_vote(true);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 1);
mdss_mdp_clk_update(MDSS_CLK_AHB, 1);
mdss_mdp_clk_update(MDSS_CLK_AXI, 1);
mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1);
@@ -1278,6 +1279,7 @@
mdss_mdp_clk_update(MDSS_CLK_AXI, 0);
mdss_mdp_clk_update(MDSS_CLK_AHB, 0);
mdss_bus_rt_bw_vote(false);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 0);
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
VOTE_INDEX_DISABLE);
}
@@ -1454,6 +1456,35 @@
}
/**
+ * mdss_mdp_retention_init() - initialize retention setting
+ * @mdata: pointer to the global mdss data structure.
+ */
+static int mdss_mdp_retention_init(struct mdss_data_type *mdata)
+{
+ struct clk *mdss_axi_clk = mdss_mdp_get_clk(MDSS_CLK_AXI);
+ int rc;
+
+ if (!mdss_axi_clk) {
+ pr_err("failed to get AXI clock\n");
+ return -EINVAL;
+ }
+
+ rc = clk_set_flags(mdss_axi_clk, CLKFLAG_NORETAIN_MEM);
+ if (rc) {
+ pr_err("failed to set AXI no memory retention %d\n", rc);
+ return rc;
+ }
+
+ rc = clk_set_flags(mdss_axi_clk, CLKFLAG_NORETAIN_PERIPH);
+ if (rc) {
+ pr_err("failed to set AXI no periphery retention %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+/**
* mdss_bus_bandwidth_ctrl() -- place bus bandwidth request
* @enable: value of enable or disable
*
@@ -1558,6 +1589,7 @@
mdata->clk_ena = enable;
spin_unlock_irqrestore(&mdp_lock, flags);
+ mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, enable);
mdss_mdp_clk_update(MDSS_CLK_AHB, enable);
mdss_mdp_clk_update(MDSS_CLK_AXI, enable);
mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable);
@@ -1721,6 +1753,9 @@
/* vsync_clk is optional for non-smart panels */
mdss_mdp_irq_clk_register(mdata, "vsync_clk", MDSS_CLK_MDP_VSYNC);
+ /* this clk is not present on all MDSS revisions */
+ mdss_mdp_irq_clk_register(mdata, "mnoc_clk", MDSS_CLK_MNOC_AHB);
+
/* Setting the default clock rate to the max supported.*/
mdss_mdp_set_clk_rate(mdata->max_mdp_clk_rate);
pr_debug("mdp clk rate=%ld\n",
@@ -2689,6 +2724,12 @@
goto probe_done;
}
+ rc = mdss_mdp_retention_init(mdata);
+ if (rc) {
+ pr_err("unable to initialize mdss mdp retention\n");
+ goto probe_done;
+ }
+
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT_MS);
if (mdata->idle_pc_enabled)
pm_runtime_use_autosuspend(&pdev->dev);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 344d6ef..9de02d9 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -571,11 +571,12 @@
u32 fps, u32 v_total)
{
u32 active_line_cycle, backfill_cycle, total_cycle;
- u32 ver_dwnscale;
+ u64 ver_dwnscale;
u32 active_line;
u32 backfill_line;
- ver_dwnscale = (src_h << PHASE_STEP_SHIFT) / dst.h;
+ ver_dwnscale = src_h << PHASE_STEP_SHIFT;
+ do_div(ver_dwnscale, dst.h);
if (ver_dwnscale > (MDSS_MDP_QSEED3_VER_DOWNSCALE_LIM
<< PHASE_STEP_SHIFT)) {
@@ -599,7 +600,7 @@
total_cycle = active_line_cycle + backfill_cycle;
pr_debug("line: active=%d backfill=%d vds=%d\n",
- active_line, backfill_line, ver_dwnscale);
+ active_line, backfill_line, (u32)ver_dwnscale);
pr_debug("cycle: total=%d active=%d backfill=%d\n",
total_cycle, active_line_cycle, backfill_cycle);
@@ -4340,11 +4341,13 @@
return;
pr_debug("hw ctl reset is set for ctl:%d\n", ctl->num);
- status = mdss_mdp_poll_ctl_reset_status(ctl, 5);
+ /* poll for at least ~1 frame */
+ status = mdss_mdp_poll_ctl_reset_status(ctl, 320);
if (status) {
- pr_err("hw recovery is not complete for ctl:%d\n", ctl->num);
+ pr_err("hw recovery is not complete for ctl:%d status:0x%x\n",
+ ctl->num, status);
MDSS_XLOG_TOUT_HANDLER("mdp", "vbif", "vbif_nrt", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 6024ea1..9332f49 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -955,6 +955,11 @@
mdata->dbg_bus_size = 0;
switch (mdata->mdp_rev) {
+ case MDSS_MDP_HW_REV_114:
+ case MDSS_MDP_HW_REV_116:
+ mdata->dbg_bus = dbg_bus_8996;
+ mdata->dbg_bus_size = ARRAY_SIZE(dbg_bus_8996);
+ break;
case MDSS_MDP_HW_REV_107:
case MDSS_MDP_HW_REV_107_1:
case MDSS_MDP_HW_REV_107_2:
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 4442f19..c002ba4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -575,7 +575,9 @@
}
/* Get both controllers in the correct order for dual displays */
- mdss_mdp_get_split_display_ctls(&ctl, &sctl);
+ rc = mdss_mdp_get_split_display_ctls(&ctl, &sctl);
+ if (rc)
+ goto exit;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX];
if (!ctx) {
@@ -1133,16 +1135,21 @@
return -EINVAL;
/*
- * Currently, only intf_fifo_underflow is
+ * Currently, intf_fifo_overflow is not
* supported for recovery sequence for command
* mode DSI interface
*/
- if (event != MDP_INTF_DSI_CMD_FIFO_UNDERFLOW) {
+ if (event == MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
return -EPERM;
}
+ if (event == MDP_INTF_DSI_PANEL_DEAD) {
+ mdss_fb_report_panel_dead(ctx->ctl->mfd);
+ return 0;
+ }
+
if (atomic_read(&ctx->koff_cnt)) {
mdss_mdp_ctl_reset(ctx->ctl, true);
reset_done = true;
@@ -1973,12 +1980,14 @@
MDSS_XLOG(0xbad);
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt",
- "dbg_bus", "vbif_dbg_bus", "panic");
+ "dbg_bus", "vbif_dbg_bus",
+ "dsi_dbg_bus", "panic");
} else if (ctx->pp_timeout_report_cnt == MAX_RECOVERY_TRIALS) {
MDSS_XLOG(0xbad2);
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt",
- "dbg_bus", "vbif_dbg_bus", "panic");
+ "dbg_bus", "vbif_dbg_bus",
+ "dsi_dbg_bus", "panic");
mdss_fb_report_panel_dead(ctl->mfd);
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 7b69aa3..591fa38 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -327,11 +327,11 @@
}
/*
- * Currently, only intf_fifo_overflow is
+ * Currently, intf_fifo_underflow is not
* supported for recovery sequence for video
* mode DSI interface
*/
- if (event != MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW) {
+ if (event == MDP_INTF_DSI_CMD_FIFO_UNDERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
return -EPERM;
@@ -341,6 +341,11 @@
pr_debug("%s: ctl num = %d, event = %d\n",
__func__, ctl->num, event);
+ if (event == MDP_INTF_DSI_PANEL_DEAD) {
+ mdss_fb_report_panel_dead(ctx->ctl->mfd);
+ return 0;
+ }
+
pinfo = &ctl->panel_data->panel_info;
clk_rate = ((ctl->intf_type == MDSS_INTF_DSI) ?
pinfo->mipi.dsi_pclk_rate :
@@ -1252,7 +1257,7 @@
pr_err("error polling for vsync\n");
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy",
"dsi1_ctrl", "dsi1_phy", "vbif", "dbg_bus",
- "vbif_dbg_bus", "panic");
+ "vbif_dbg_bus", "dsi_dbg_bus", "panic");
}
} else {
rc = 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 80708aa..672b634 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -6603,6 +6603,7 @@
mdp5_interface->configure_panel = mdss_mdp_update_panel_info;
mdp5_interface->input_event_handler = mdss_mdp_input_event_handler;
mdp5_interface->signal_retire_fence = mdss_mdp_signal_retire_fence;
+ mdp5_interface->is_twm_en = NULL;
if (mfd->panel_info->type == WRITEBACK_PANEL) {
mdp5_interface->atomic_validate =
diff --git a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
index 44817c3..cf19501 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
@@ -149,7 +149,7 @@
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- int rc, ret;
+ int ret;
/*
* iommu dynamic attach for following conditions.
@@ -166,26 +166,41 @@
return -EPERM;
}
- rc = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE,
+ /*
+ * Putting handoff pending to false to ensure smmu attach happens
+ * with early flag attribute
+ */
+ mdata->handoff_pending = false;
+
+ ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 1);
+ if (ret) {
+ pr_debug("mdss set attribute failed for early map\n");
+ goto end;
+ }
+
+ ret = mdss_iommu_ctrl(1);
+ if (IS_ERR_VALUE((unsigned long)ret)) {
+ pr_err("mdss iommu attach failed\n");
+ goto end;
+ }
+
+ ret = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE,
mdp5_data->splash_mem_addr,
mdp5_data->splash_mem_addr,
mdp5_data->splash_mem_size,
IOMMU_READ | IOMMU_NOEXEC);
- if (rc) {
- pr_debug("iommu memory mapping failed rc=%d\n", rc);
+ if (ret) {
+ pr_err("iommu memory mapping failed ret=%d\n", ret);
} else {
- ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE((unsigned long)ret)) {
- pr_err("mdss iommu attach failed\n");
- mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
- mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_size);
- } else {
- mfd->splash_info.iommu_dynamic_attached = true;
- }
+ pr_debug("iommu map passed for PA=VA\n");
+ mfd->splash_info.iommu_dynamic_attached = true;
}
- return rc;
+ ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 0);
+end:
+ mdata->handoff_pending = true;
+
+ return ret;
}
static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd)
@@ -193,12 +208,10 @@
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
if (mfd->splash_info.iommu_dynamic_attached) {
-
mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
mdp5_data->splash_mem_addr,
mdp5_data->splash_mem_size);
mdss_iommu_ctrl(0);
-
mfd->splash_info.iommu_dynamic_attached = false;
}
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_trace.h b/drivers/video/fbdev/msm/mdss_mdp_trace.h
index f8a6baf..c100e9c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_trace.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_trace.h
@@ -376,7 +376,7 @@
__entry->kickoff_cnt)
);
-TRACE_EVENT(mdss_mark_write,
+TRACE_EVENT(tracing_mark_write,
TP_PROTO(int pid, const char *name, bool trace_begin),
TP_ARGS(pid, name, trace_begin),
TP_STRUCT__entry(
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index 29f6c1a..c030109 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -31,6 +31,7 @@
#include "mdss_panel.h"
#define PHY_ADDR_4G (1ULL<<32)
+#define ALIGN_UP(x, align) ((DIV_ROUND_UP((x), (align))) * (align))
void mdss_mdp_format_flag_removal(u32 *table, u32 num, u32 remove_bits)
{
@@ -451,13 +452,13 @@
}
/* Y bitstream stride and plane size */
- ps->ystride[0] = ALIGN(width, y_stride_alignment);
+ ps->ystride[0] = ALIGN_UP(width, y_stride_alignment);
ps->ystride[0] = (ps->ystride[0] * y_bpp_numer) / y_bpp_denom;
ps->plane_size[0] = ALIGN(ps->ystride[0] *
ALIGN(height, y_height_alignment), 4096);
/* CbCr bitstream stride and plane size */
- ps->ystride[1] = ALIGN(width / 2, uv_stride_alignment);
+ ps->ystride[1] = ALIGN_UP(width / 2, uv_stride_alignment);
ps->ystride[1] = (ps->ystride[1] * uv_bpp_numer) / uv_bpp_denom;
ps->plane_size[1] = ALIGN(ps->ystride[1] *
ALIGN(height / 2, uv_height_alignment), 4096);
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index a3f9349..314dc0e 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -60,6 +60,7 @@
#define WRITEBACK_PANEL 10 /* Wifi display */
#define LVDS_PANEL 11 /* LVDS */
#define EDP_PANEL 12 /* LVDS */
+#define SPI_PANEL 13
#define DSC_PPS_LEN 128
@@ -108,6 +109,7 @@
MDSS_PANEL_INTF_DSI,
MDSS_PANEL_INTF_EDP,
MDSS_PANEL_INTF_HDMI,
+ MDSS_PANEL_INTF_SPI,
};
enum {
@@ -118,6 +120,12 @@
};
enum {
+ MDSS_PANEL_BLANK_BLANK = 0,
+ MDSS_PANEL_BLANK_UNBLANK,
+ MDSS_PANEL_BLANK_LOW_POWER,
+};
+
+enum {
MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0,
MDSS_PANEL_LOW_PERSIST_MODE_ON,
};
@@ -167,6 +175,7 @@
#define MDP_INTF_DSI_CMD_FIFO_UNDERFLOW 0x0001
#define MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW 0x0002
+#define MDP_INTF_DSI_PANEL_DEAD 0x0003
enum {
@@ -429,6 +438,10 @@
char frame_rate; /* fps */
};
+struct spi_panel_info {
+ char frame_rate;
+};
+
/**
* struct dynamic_fps_data - defines dynamic fps related data
* @hfp: horizontal front porch
@@ -676,6 +689,7 @@
u32 partial_update_roi_merge;
struct ion_handle *splash_ihdl;
int panel_power_state;
+ int blank_state;
int compression_mode;
uint32_t panel_dead;
@@ -735,6 +749,7 @@
struct lcd_panel_info lcdc;
struct fbc_panel_info fbc;
struct mipi_panel_info mipi;
+ struct spi_panel_info spi;
struct lvds_panel_info lvds;
struct edp_panel_info edp;
@@ -821,6 +836,7 @@
struct mdss_panel_data *next;
int panel_te_gpio;
+ int panel_en_gpio;
struct completion te_done;
};
@@ -873,6 +889,9 @@
frame_rate = panel_info->lcdc.frame_rate;
break;
}
+ case SPI_PANEL:
+ frame_rate = panel_info->spi.frame_rate;
+ break;
default:
pixel_total = (panel_info->lcdc.h_back_porch +
panel_info->lcdc.h_front_porch +
diff --git a/drivers/video/fbdev/msm/mdss_qpic.c b/drivers/video/fbdev/msm/mdss_qpic.c
index aa8d783..2f170cc 100644
--- a/drivers/video/fbdev/msm/mdss_qpic.c
+++ b/drivers/video/fbdev/msm/mdss_qpic.c
@@ -231,6 +231,7 @@
qpic_interface->dma_fnc = mdss_qpic_pan_display;
qpic_interface->ioctl_handler = NULL;
qpic_interface->kickoff_fnc = NULL;
+ qpic_interface->is_twm_en = NULL;
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index f51a1b8..4952e7e 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -23,6 +23,7 @@
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include <linux/regulator/consumer.h>
+#include <linux/sync_file.h>
#include "mdss_rotator_internal.h"
#include "mdss_mdp.h"
@@ -374,6 +375,24 @@
return false;
}
+static int mdss_rotator_install_fence_fd(struct mdss_rot_entry_container *req)
+{
+ int i;
+ int ret = 0;
+ struct sync_file *sync_file;
+
+ for (i = 0; i < req->count; i++) {
+ sync_file = sync_file_create((struct fence *)
+ (req->entries[i].output_fence));
+ if (!sync_file) {
+ ret = -ENOMEM;
+ break;
+ }
+ fd_install(req->entries[i].output_fence_fd, sync_file->file);
+ }
+ return ret;
+}
+
static int mdss_rotator_create_fence(struct mdss_rot_entry *entry)
{
int ret = 0, fd;
@@ -395,9 +414,10 @@
pr_err("cannot create sync point\n");
goto sync_pt_create_err;
}
- fd = mdss_get_sync_fence_fd(fence);
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
- pr_err("get_unused_fd_flags failed error:0x%x\n", fd);
+ pr_err("fail to get unused fd\n");
ret = fd;
goto get_fd_err;
}
@@ -2245,6 +2265,13 @@
goto handle_request_err1;
}
+ ret = mdss_rotator_install_fence_fd(req);
+ if (ret) {
+ pr_err("get_unused_fd_flags failed error:0x%x\n", ret);
+ mdss_rotator_remove_request(mgr, private, req);
+ goto handle_request_err1;
+ }
+
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
@@ -2404,6 +2431,13 @@
goto handle_request32_err1;
}
+ ret = mdss_rotator_install_fence_fd(req);
+ if (ret) {
+ pr_err("get_unused_fd_flags failed error:0x%x\n", ret);
+ mdss_rotator_remove_request(mgr, private, req);
+ goto handle_request32_err1;
+ }
+
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 2d8b5d5..c02823b 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -180,6 +180,27 @@
return rc;
}
+int mdss_smmu_set_attribute(int domain, int flag, int val)
+{
+ int rc = 0, domain_attr = 0;
+ struct mdss_smmu_client *mdss_smmu = mdss_smmu_get_cb(domain);
+
+ if (!mdss_smmu) {
+ pr_err("not able to get smmu context\n");
+ return -EINVAL;
+ }
+
+ if (flag == EARLY_MAP)
+ domain_attr = DOMAIN_ATTR_EARLY_MAP;
+ else
+ goto end;
+
+ rc = iommu_domain_set_attr(mdss_smmu->mmu_mapping->domain,
+ domain_attr, &val);
+end:
+ return rc;
+}
+
/*
* mdss_smmu_v2_attach()
*
@@ -693,13 +714,13 @@
}
static struct mdss_smmu_domain mdss_mdp_unsec = {
- "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_1M, (SZ_4G - SZ_1M)};
+ "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128M, (SZ_4G - SZ_128M)};
static struct mdss_smmu_domain mdss_rot_unsec = {
- NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_1M, (SZ_4G - SZ_1M)};
+ NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128M, (SZ_4G - SZ_128M)};
static struct mdss_smmu_domain mdss_mdp_sec = {
- "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_1M, (SZ_4G - SZ_1M)};
+ "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128M, (SZ_4G - SZ_128M)};
static struct mdss_smmu_domain mdss_rot_sec = {
- NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_1M, (SZ_4G - SZ_1M)};
+ NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128M, (SZ_4G - SZ_128M)};
static const struct of_device_id mdss_smmu_dt_match[] = {
{ .compatible = "qcom,smmu_mdp_unsec", .data = &mdss_mdp_unsec},
diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h
index 091af3b..97f0933 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -42,6 +42,11 @@
void mdss_smmu_register(struct device *dev);
int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev);
+int mdss_smmu_set_attribute(int domain, int flag, int val);
+
+enum smmu_attributes {
+ EARLY_MAP
+};
static inline int mdss_smmu_dma_data_direction(int dir)
{
diff --git a/drivers/video/fbdev/msm/mdss_spi_client.c b/drivers/video/fbdev/msm/mdss_spi_client.c
new file mode 100644
index 0000000..541e382
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_client.c
@@ -0,0 +1,198 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/qpnp/pin.h>
+#include <linux/delay.h>
+
+#include "mdss_spi_client.h"
+
+#define MAX_READ_SPEED_HZ 9600000
+#define SPI_PANEL_COMMAND_LEN 1
+static struct spi_device *mdss_spi_client;
+
+int mdss_spi_read_data(u8 reg_addr, u8 *data, u8 len)
+{
+ int rc = 0;
+ u32 max_speed_hz;
+ u8 memory_write_reg = 0x2c;
+ u8 empty_pack[] = {0x29, 0x29, 0x29};
+ struct spi_transfer t[4] = {
+ [0] = {
+ .tx_buf = ®_addr,
+ .len = 1,
+ },
+ [1] = {
+ .rx_buf = data,
+ .len = len,
+ },
+ [2] = {
+ .tx_buf = &empty_pack,
+ .len = 3,
+ },
+ [3] = {
+ .tx_buf = &memory_write_reg,
+ .len = 1,
+ }
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 8;
+ max_speed_hz = mdss_spi_client->max_speed_hz;
+ mdss_spi_client->max_speed_hz = MAX_READ_SPEED_HZ;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[2], &m);
+ rc = spi_sync(mdss_spi_client, &m);
+ spi_message_init(&m);
+ spi_message_add_tail(&t[3], &m);
+ rc = spi_sync(mdss_spi_client, &m);
+ mdss_spi_client->max_speed_hz = max_speed_hz;
+
+ return rc;
+}
+
+int mdss_spi_tx_command(const void *buf)
+{
+ int rc = 0;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = SPI_PANEL_COMMAND_LEN,
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 8;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ return rc;
+}
+
+int mdss_spi_tx_parameter(const void *buf, size_t len)
+{
+ int rc = 0;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 8;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ return rc;
+}
+
+int mdss_spi_tx_pixel(const void *buf, size_t len)
+{
+ int rc = 0;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ if (!mdss_spi_client) {
+ pr_err("%s: spi client not available\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_spi_client->bits_per_word = 16;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ rc = spi_sync(mdss_spi_client, &m);
+
+ return rc;
+}
+
+static int mdss_spi_client_probe(struct spi_device *spidev)
+{
+ int irq;
+ int cs;
+ int cpha, cpol, cs_high;
+ u32 max_speed;
+ struct device_node *np;
+
+ irq = spidev->irq;
+ cs = spidev->chip_select;
+ cpha = (spidev->mode & SPI_CPHA) ? 1:0;
+ cpol = (spidev->mode & SPI_CPOL) ? 1:0;
+ cs_high = (spidev->mode & SPI_CS_HIGH) ? 1:0;
+ max_speed = spidev->max_speed_hz;
+ np = spidev->dev.of_node;
+ pr_debug("cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x] Max_speed[%d]\n",
+ cs, cpha, cpol, cs_high, max_speed);
+ mdss_spi_client = spidev;
+
+ return 0;
+}
+
+
+static const struct of_device_id mdss_spi_dt_match[] = {
+ { .compatible = "qcom,mdss-spi-client" },
+ {},
+};
+
+static struct spi_driver mdss_spi_client_driver = {
+ .probe = mdss_spi_client_probe,
+ .driver = {
+ .name = "mdss-spi-client",
+ .owner = THIS_MODULE,
+ .of_match_table = mdss_spi_dt_match,
+ },
+};
+
+static int __init mdss_spi_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&mdss_spi_client_driver);
+
+ return 0;
+}
+module_init(mdss_spi_init);
+
+static void __exit mdss_spi_exit(void)
+{
+ spi_unregister_driver(&mdss_spi_client_driver);
+}
+module_exit(mdss_spi_exit);
+
diff --git a/drivers/video/fbdev/msm/mdss_spi_client.h b/drivers/video/fbdev/msm/mdss_spi_client.h
new file mode 100644
index 0000000..2d15625
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_client.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MDSS_SPI_CLINET_H__
+#define __MDSS_SPI_CLINET_H__
+
+int mdss_spi_tx_command(const void *buf);
+int mdss_spi_tx_parameter(const void *buf, size_t len);
+int mdss_spi_tx_pixel(const void *buf, size_t len);
+int mdss_spi_read_data(u8 reg_addr, u8 *data, u8 len);
+#endif /* End of __MDSS_SPI_CLINET_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_spi_panel.c b/drivers/video/fbdev/msm/mdss_spi_panel.c
new file mode 100644
index 0000000..86a1c1c
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_panel.c
@@ -0,0 +1,1713 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/qpnp/pin.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/leds.h>
+#include <linux/qpnp/pwm.h>
+#include <linux/of_device.h>
+
+#include "mdss.h"
+#include "mdss_panel.h"
+#include "mdss_spi_panel.h"
+#include "mdss_spi_client.h"
+#include "mdp3.h"
+
+DEFINE_LED_TRIGGER(bl_led_trigger);
+static int mdss_spi_panel_reset(struct mdss_panel_data *pdata, int enable)
+{
+ struct spi_panel_data *ctrl_pdata = NULL;
+ struct mdss_panel_info *pinfo = NULL;
+ int i, rc = 0;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (!gpio_is_valid(ctrl_pdata->rst_gpio)) {
+ pr_debug("%s:%d, reset line not configured\n",
+ __func__, __LINE__);
+ return rc;
+ }
+
+ if (!gpio_is_valid(ctrl_pdata->disp_dc_gpio)) {
+ pr_debug("%s:%d, dc line not configured\n",
+ __func__, __LINE__);
+ return rc;
+ }
+
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ pinfo = &(ctrl_pdata->panel_data.panel_info);
+
+ if (enable) {
+ rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n");
+ if (rc) {
+ pr_err("display reset gpio request failed\n");
+ return rc;
+ }
+
+ rc = gpio_request(ctrl_pdata->disp_dc_gpio, "disp_dc");
+ if (rc) {
+ pr_err("display dc gpio request failed\n");
+ return rc;
+ }
+
+ if (!pinfo->cont_splash_enabled) {
+ for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) {
+ gpio_direction_output((ctrl_pdata->rst_gpio),
+ pdata->panel_info.rst_seq[i]);
+ if (pdata->panel_info.rst_seq[++i])
+ usleep_range(pinfo->rst_seq[i] * 1000,
+ pinfo->rst_seq[i] * 1000);
+ }
+ }
+
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
+ pr_debug("%s: Panel Not properly turned OFF\n",
+ __func__);
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ pr_err("%s: Reset panel done\n", __func__);
+ }
+ } else {
+ gpio_direction_output((ctrl_pdata->rst_gpio), 0);
+ gpio_free(ctrl_pdata->rst_gpio);
+
+ gpio_direction_output(ctrl_pdata->disp_dc_gpio, 0);
+ gpio_free(ctrl_pdata->disp_dc_gpio);
+ }
+ return rc;
+}
+
+
+static int mdss_spi_panel_pinctrl_set_state(
+ struct spi_panel_data *ctrl_pdata,
+ bool active)
+{
+ struct pinctrl_state *pin_state;
+ int rc = -EFAULT;
+
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl))
+ return PTR_ERR(ctrl_pdata->pin_res.pinctrl);
+
+ pin_state = active ? ctrl_pdata->pin_res.gpio_state_active
+ : ctrl_pdata->pin_res.gpio_state_suspend;
+ if (!IS_ERR_OR_NULL(pin_state)) {
+ rc = pinctrl_select_state(ctrl_pdata->pin_res.pinctrl,
+ pin_state);
+ if (rc)
+ pr_err("%s: can not set %s pins\n", __func__,
+ active ? MDSS_PINCTRL_STATE_DEFAULT
+ : MDSS_PINCTRL_STATE_SLEEP);
+ } else {
+ pr_err("%s: invalid '%s' pinstate\n", __func__,
+ active ? MDSS_PINCTRL_STATE_DEFAULT
+ : MDSS_PINCTRL_STATE_SLEEP);
+ }
+ return rc;
+}
+
+
+static int mdss_spi_panel_pinctrl_init(struct platform_device *pdev)
+{
+ struct spi_panel_data *ctrl_pdata;
+
+ ctrl_pdata = platform_get_drvdata(pdev);
+ ctrl_pdata->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl)) {
+ pr_err("%s: failed to get pinctrl\n", __func__);
+ return PTR_ERR(ctrl_pdata->pin_res.pinctrl);
+ }
+
+ ctrl_pdata->pin_res.gpio_state_active
+ = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl,
+ MDSS_PINCTRL_STATE_DEFAULT);
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_active))
+ pr_warn("%s: can not get default pinstate\n", __func__);
+
+ ctrl_pdata->pin_res.gpio_state_suspend
+ = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl,
+ MDSS_PINCTRL_STATE_SLEEP);
+ if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_suspend))
+ pr_warn("%s: can not get sleep pinstate\n", __func__);
+
+ return 0;
+}
+
+
+static int mdss_spi_panel_power_on(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+ ret = msm_mdss_enable_vreg(
+ ctrl_pdata->panel_power_data.vreg_config,
+ ctrl_pdata->panel_power_data.num_vreg, 1);
+ if (ret) {
+ pr_err("%s: failed to enable vregs for %s\n",
+ __func__, "PANEL_PM");
+ }
+
+ /*
+ * If continuous splash screen feature is enabled, then we need to
+ * request all the GPIOs that have already been configured in the
+ * bootloader. This needs to be done irresepective of whether
+ * the lp11_init flag is set or not.
+ */
+ if (pdata->panel_info.cont_splash_enabled) {
+ if (mdss_spi_panel_pinctrl_set_state(ctrl_pdata, true))
+ pr_debug("reset enable: pinctrl not enabled\n");
+
+ ret = mdss_spi_panel_reset(pdata, 1);
+ if (ret)
+ pr_err("%s: Panel reset failed. rc=%d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+
+static int mdss_spi_panel_power_off(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ ret = -EINVAL;
+ goto end;
+ }
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ ret = mdss_spi_panel_reset(pdata, 0);
+ if (ret) {
+ pr_warn("%s: Panel reset failed. rc=%d\n", __func__, ret);
+ ret = 0;
+ }
+
+ if (mdss_spi_panel_pinctrl_set_state(ctrl_pdata, false))
+ pr_warn("reset disable: pinctrl not enabled\n");
+
+ ret = msm_mdss_enable_vreg(
+ ctrl_pdata->panel_power_data.vreg_config,
+ ctrl_pdata->panel_power_data.num_vreg, 0);
+ if (ret)
+ pr_err("%s: failed to disable vregs for %s\n",
+ __func__, "PANEL_PM");
+
+end:
+ return ret;
+}
+
+
+static int mdss_spi_panel_power_ctrl(struct mdss_panel_data *pdata,
+ int power_state)
+{
+ int ret;
+ struct mdss_panel_info *pinfo;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ pinfo = &pdata->panel_info;
+ pr_debug("%s: cur_power_state=%d req_power_state=%d\n", __func__,
+ pinfo->panel_power_state, power_state);
+
+ if (pinfo->panel_power_state == power_state) {
+ pr_debug("%s: no change needed\n", __func__);
+ return 0;
+ }
+
+ switch (power_state) {
+ case MDSS_PANEL_POWER_OFF:
+ ret = mdss_spi_panel_power_off(pdata);
+ break;
+ case MDSS_PANEL_POWER_ON:
+ ret = mdss_spi_panel_power_on(pdata);
+ break;
+ default:
+ pr_err("%s: unknown panel power state requested (%d)\n",
+ __func__, power_state);
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ pinfo->panel_power_state = power_state;
+
+ return ret;
+}
+
+static int mdss_spi_panel_unblank(struct mdss_panel_data *pdata)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (!(ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)) {
+ ret = ctrl_pdata->on(pdata);
+ if (ret) {
+ pr_err("%s: unable to initialize the panel\n",
+ __func__);
+ return ret;
+ }
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
+ }
+
+ return ret;
+}
+
+static int mdss_spi_panel_blank(struct mdss_panel_data *pdata, int power_state)
+{
+ int ret = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
+ ret = ctrl_pdata->off(pdata);
+ if (ret) {
+ pr_err("%s: Panel OFF failed\n", __func__);
+ return ret;
+ }
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ }
+
+ return ret;
+}
+
+
+static int mdss_spi_panel_event_handler(struct mdss_panel_data *pdata,
+ int event, void *arg)
+{
+ int rc = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+ int power_state;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ switch (event) {
+ case MDSS_EVENT_LINK_READY:
+ rc = mdss_spi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_ON);
+ if (rc) {
+ pr_err("%s:Panel power on failed. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ mdss_spi_panel_pinctrl_set_state(ctrl_pdata, true);
+ mdss_spi_panel_reset(pdata, 1);
+ break;
+ case MDSS_EVENT_UNBLANK:
+ rc = mdss_spi_panel_unblank(pdata);
+ break;
+ case MDSS_EVENT_PANEL_ON:
+ ctrl_pdata->ctrl_state |= CTRL_STATE_MDP_ACTIVE;
+ break;
+ case MDSS_EVENT_BLANK:
+ power_state = (int) (unsigned long) arg;
+ break;
+ case MDSS_EVENT_PANEL_OFF:
+ power_state = (int) (unsigned long) arg;
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
+ rc = mdss_spi_panel_blank(pdata, power_state);
+ rc = mdss_spi_panel_power_ctrl(pdata, power_state);
+ break;
+ default:
+ pr_debug("%s: unhandled event=%d\n", __func__, event);
+ break;
+ }
+ pr_debug("%s-:event=%d, rc=%d\n", __func__, event, rc);
+ return rc;
+}
+
+int is_spi_panel_continuous_splash_on(struct mdss_panel_data *pdata)
+{
+ int i = 0, voltage = 0;
+ struct mdss_vreg *vreg;
+ int num_vreg;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+ vreg = ctrl_pdata->panel_power_data.vreg_config;
+ num_vreg = ctrl_pdata->panel_power_data.num_vreg;
+
+ for (i = 0; i < num_vreg; i++) {
+ if (regulator_is_enabled(vreg[i].vreg) <= 0)
+ return false;
+ voltage = regulator_get_voltage(vreg[i].vreg);
+ if (!(voltage >= vreg[i].min_voltage &&
+ voltage <= vreg[i].max_voltage))
+ return false;
+ }
+
+ return true;
+}
+
+static void enable_spi_panel_te_irq(struct spi_panel_data *ctrl_pdata,
+ bool enable)
+{
+ static bool is_enabled = true;
+
+ if (is_enabled == enable)
+ return;
+
+ if (!gpio_is_valid(ctrl_pdata->disp_te_gpio)) {
+ pr_err("%s:%d,SPI panel TE GPIO not configured\n",
+ __func__, __LINE__);
+ return;
+ }
+
+ if (enable)
+ enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
+ else
+ disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
+
+ is_enabled = enable;
+}
+
+int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata,
+ char *buf, int len, int dma_stride)
+{
+ struct spi_panel_data *ctrl_pdata = NULL;
+ char *tx_buf;
+ int rc = 0;
+ int panel_yres;
+ int panel_xres;
+ int padding_length = 0;
+ int actual_stride = 0;
+ int byte_per_pixel = 0;
+ int scan_count = 0;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ tx_buf = ctrl_pdata->tx_buf;
+ panel_xres = ctrl_pdata->panel_data.panel_info.xres;
+ panel_yres = ctrl_pdata->panel_data.panel_info.yres;
+
+ byte_per_pixel = ctrl_pdata->panel_data.panel_info.bpp / 8;
+ actual_stride = panel_xres * byte_per_pixel;
+ padding_length = dma_stride - actual_stride;
+
+ /* remove the padding and copy to continuous buffer */
+ while (scan_count < panel_yres) {
+ memcpy((tx_buf + scan_count * actual_stride),
+ (buf + scan_count * (actual_stride + padding_length)),
+ actual_stride);
+ scan_count++;
+ }
+
+ enable_spi_panel_te_irq(ctrl_pdata, true);
+
+ mutex_lock(&ctrl_pdata->spi_tx_mutex);
+ reinit_completion(&ctrl_pdata->spi_panel_te);
+
+ rc = wait_for_completion_timeout(&ctrl_pdata->spi_panel_te,
+ msecs_to_jiffies(SPI_PANEL_TE_TIMEOUT));
+
+ if (rc == 0)
+ pr_err("wait panel TE time out\n");
+
+ rc = mdss_spi_tx_pixel(tx_buf, ctrl_pdata->byte_pre_frame);
+ mutex_unlock(&ctrl_pdata->spi_tx_mutex);
+
+ return rc;
+}
+
+static int mdss_spi_read_panel_data(struct mdss_panel_data *pdata,
+ u8 reg_addr, u8 *data, u8 len)
+{
+ int rc = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ mutex_lock(&ctrl_pdata->spi_tx_mutex);
+ gpio_direction_output(ctrl_pdata->disp_dc_gpio, 0);
+ rc = mdss_spi_read_data(reg_addr, data, len);
+ gpio_direction_output(ctrl_pdata->disp_dc_gpio, 1);
+ mutex_unlock(&ctrl_pdata->spi_tx_mutex);
+
+ return rc;
+}
+
+static int mdss_spi_panel_on(struct mdss_panel_data *pdata)
+{
+ struct spi_panel_data *ctrl = NULL;
+ struct mdss_panel_info *pinfo;
+ int i;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+ pinfo = &pdata->panel_info;
+ ctrl = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ for (i = 0; i < ctrl->on_cmds.cmd_cnt; i++) {
+ /* pull down dc gpio indicate this is command */
+ gpio_direction_output(ctrl->disp_dc_gpio, 0);
+ mdss_spi_tx_command(ctrl->on_cmds.cmds[i].command);
+ gpio_direction_output((ctrl->disp_dc_gpio), 1);
+
+ if (ctrl->on_cmds.cmds[i].dchdr.dlen > 1) {
+ mdss_spi_tx_parameter(ctrl->on_cmds.cmds[i].parameter,
+ ctrl->on_cmds.cmds[i].dchdr.dlen-1);
+ }
+ if (ctrl->on_cmds.cmds[i].dchdr.wait != 0)
+ msleep(ctrl->on_cmds.cmds[i].dchdr.wait);
+ }
+
+ pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
+
+ pr_debug("%s:-\n", __func__);
+
+ return 0;
+}
+
+
+static int mdss_spi_panel_off(struct mdss_panel_data *pdata)
+{
+ struct spi_panel_data *ctrl = NULL;
+ struct mdss_panel_info *pinfo;
+ int i;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ pinfo = &pdata->panel_info;
+ ctrl = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ for (i = 0; i < ctrl->off_cmds.cmd_cnt; i++) {
+ /* pull down dc gpio indicate this is command */
+ gpio_direction_output(ctrl->disp_dc_gpio, 0);
+ mdss_spi_tx_command(ctrl->off_cmds.cmds[i].command);
+ gpio_direction_output((ctrl->disp_dc_gpio), 1);
+
+ if (ctrl->off_cmds.cmds[i].dchdr.dlen > 1) {
+ mdss_spi_tx_parameter(ctrl->off_cmds.cmds[i].parameter,
+ ctrl->off_cmds.cmds[i].dchdr.dlen-1);
+ }
+
+ if (ctrl->off_cmds.cmds[i].dchdr.wait != 0)
+ msleep(ctrl->off_cmds.cmds[i].dchdr.wait);
+ }
+
+ pinfo->blank_state = MDSS_PANEL_BLANK_BLANK;
+
+ pr_debug("%s:-\n", __func__);
+ return 0;
+}
+
+static void mdss_spi_put_dt_vreg_data(struct device *dev,
+ struct mdss_module_power *module_power)
+{
+ if (!module_power) {
+ pr_err("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (module_power->vreg_config) {
+ devm_kfree(dev, module_power->vreg_config);
+ module_power->vreg_config = NULL;
+ }
+ module_power->num_vreg = 0;
+}
+
+
+static int mdss_spi_get_panel_vreg_data(struct device *dev,
+ struct mdss_module_power *mp)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *of_node = NULL, *supply_node = NULL;
+ struct device_node *supply_root_node = NULL;
+
+ if (!dev || !mp) {
+ pr_err("%s: invalid input\n", __func__);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ of_node = dev->of_node;
+
+ mp->num_vreg = 0;
+
+ supply_root_node = of_get_child_by_name(of_node,
+ "qcom,panel-supply-entries");
+
+ for_each_available_child_of_node(supply_root_node, supply_node) {
+ mp->num_vreg++;
+ }
+ if (mp->num_vreg == 0) {
+ pr_debug("%s: no vreg\n", __func__);
+ goto novreg;
+ } else {
+ pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg);
+ }
+
+ mp->vreg_config = kcalloc(mp->num_vreg, sizeof(struct mdss_vreg),
+ GFP_KERNEL);
+
+ if (mp->vreg_config != NULL) {
+ for_each_available_child_of_node(supply_root_node,
+ supply_node) {
+ const char *st = NULL;
+ /* vreg-name */
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("%s: error reading name. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ snprintf(mp->vreg_config[i].vreg_name,
+ ARRAY_SIZE((mp->vreg_config[i].vreg_name)),
+ "%s", st);
+ /* vreg-min-voltage */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err("%s: error reading min volt. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].min_voltage = tmp;
+
+ /* vreg-max-voltage */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err("%s: error reading max volt. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].max_voltage = tmp;
+
+ /* enable-load */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-enable-load", &tmp);
+ if (rc) {
+ pr_err("%s: error read enable load. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp;
+
+ /* disable-load */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-disable-load", &tmp);
+ if (rc) {
+ pr_err("%s: error read disable load. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp;
+
+ /* pre-sleep */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-on-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read pre on value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].pre_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-off-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read pre off value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].pre_off_sleep = tmp;
+ }
+
+ /* post-sleep */
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-on-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read post on value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].post_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-off-sleep", &tmp);
+ if (rc) {
+ pr_debug("%s: error read post off value\n",
+ __func__);
+ rc = 0;
+ } else {
+ mp->vreg_config[i].post_off_sleep = tmp;
+ }
+
+ ++i;
+ }
+ }
+ return rc;
+error:
+ kfree(mp->vreg_config);
+ mp->vreg_config = NULL;
+
+novreg:
+ mp->num_vreg = 0;
+
+ return rc;
+
+}
+
+static int mdss_spi_panel_parse_cmds(struct device_node *np,
+ struct spi_panel_cmds *pcmds, char *cmd_key)
+{
+ const char *data;
+ int blen = 0, len;
+ char *buf, *bp;
+ struct spi_ctrl_hdr *dchdr;
+ int i, cnt;
+
+ data = of_get_property(np, cmd_key, &blen);
+ if (!data) {
+ pr_err("%s: failed, key=%s\n", __func__, cmd_key);
+ return -ENOMEM;
+ }
+
+ buf = kcalloc(blen, sizeof(char), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, data, blen);
+
+ /* scan dcs commands */
+ bp = buf;
+ len = blen;
+ cnt = 0;
+ while (len >= sizeof(*dchdr)) {
+ dchdr = (struct spi_ctrl_hdr *)bp;
+ if (dchdr->dlen > len) {
+ pr_err("%s: dtsi parse error, len=%d",
+ __func__, dchdr->dlen);
+ goto exit_free;
+ }
+ bp += sizeof(*dchdr);
+ len -= sizeof(*dchdr);
+ bp += dchdr->dlen;
+ len -= dchdr->dlen;
+ cnt++;
+ }
+
+ if (len != 0) {
+ pr_err("%s: dcs_cmd=%x len=%d error",
+ __func__, buf[0], len);
+ goto exit_free;
+ }
+
+ pcmds->cmds = kcalloc(cnt, sizeof(struct spi_cmd_desc),
+ GFP_KERNEL);
+ if (!pcmds->cmds)
+ goto exit_free;
+
+ pcmds->cmd_cnt = cnt;
+ pcmds->buf = buf;
+ pcmds->blen = blen;
+
+ bp = buf;
+ len = blen;
+ for (i = 0; i < cnt; i++) {
+ dchdr = (struct spi_ctrl_hdr *)bp;
+ len -= sizeof(*dchdr);
+ bp += sizeof(*dchdr);
+ pcmds->cmds[i].dchdr = *dchdr;
+ pcmds->cmds[i].command = bp;
+ pcmds->cmds[i].parameter = bp + sizeof(char);
+ bp += dchdr->dlen;
+ len -= dchdr->dlen;
+ }
+
+ pr_debug("%s: dcs_cmd=%x, len=%d, cmd_cnt=%d\n", __func__,
+ pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt);
+
+ return 0;
+
+exit_free:
+ kfree(buf);
+ return -ENOMEM;
+}
+static int mdss_spi_panel_parse_reset_seq(struct device_node *np,
+ u32 rst_seq[MDSS_SPI_RST_SEQ_LEN], u32 *rst_len,
+ const char *name)
+{
+ int num = 0, i;
+ int rc;
+ struct property *data;
+ u32 tmp[MDSS_SPI_RST_SEQ_LEN];
+
+ *rst_len = 0;
+ data = of_find_property(np, name, &num);
+ num /= sizeof(u32);
+ if (!data || !num || num > MDSS_SPI_RST_SEQ_LEN || num % 2) {
+ pr_err("%s:%d, error reading %s, length found = %d\n",
+ __func__, __LINE__, name, num);
+ } else {
+ rc = of_property_read_u32_array(np, name, tmp, num);
+ if (rc)
+ pr_err("%s:%d, error reading %s, rc = %d\n",
+ __func__, __LINE__, name, rc);
+ else {
+ for (i = 0; i < num; ++i)
+ rst_seq[i] = tmp[i];
+ *rst_len = num;
+ }
+ }
+ return 0;
+}
+
+static bool mdss_send_panel_cmd_for_esd(struct spi_panel_data *ctrl_pdata)
+{
+
+ if (ctrl_pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return false;
+ }
+
+ mutex_lock(&ctrl_pdata->spi_tx_mutex);
+ mdss_spi_panel_on(&ctrl_pdata->panel_data);
+ mutex_unlock(&ctrl_pdata->spi_tx_mutex);
+
+ return true;
+}
+
+static bool mdss_spi_reg_status_check(struct spi_panel_data *ctrl_pdata)
+{
+ int ret = 0;
+ int i = 0;
+
+ if (ctrl_pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return false;
+ }
+
+ pr_debug("%s: Checking Register status\n", __func__);
+
+ ret = mdss_spi_read_panel_data(&ctrl_pdata->panel_data,
+ ctrl_pdata->panel_status_reg,
+ ctrl_pdata->act_status_value,
+ ctrl_pdata->status_cmds_rlen);
+ if (ret < 0) {
+ pr_err("%s: Read status register returned error\n", __func__);
+ } else {
+ for (i = 0; i < ctrl_pdata->status_cmds_rlen; i++) {
+ pr_debug("act_value[%d] = %x, exp_value[%d] = %x\n",
+ i, ctrl_pdata->act_status_value[i],
+ i, ctrl_pdata->exp_status_value[i]);
+ if (ctrl_pdata->act_status_value[i] !=
+ ctrl_pdata->exp_status_value[i])
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void mdss_spi_parse_esd_params(struct device_node *np,
+ struct spi_panel_data *ctrl)
+{
+ u32 tmp;
+ int rc;
+ struct property *data;
+ const char *string;
+ struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info;
+
+ pinfo->esd_check_enabled = of_property_read_bool(np,
+ "qcom,esd-check-enabled");
+
+ if (!pinfo->esd_check_enabled)
+ return;
+
+ ctrl->status_mode = SPI_ESD_MAX;
+
+ rc = of_property_read_string(np,
+ "qcom,mdss-spi-panel-status-check-mode", &string);
+ if (!rc) {
+ if (!strcmp(string, "reg_read")) {
+ ctrl->status_mode = SPI_ESD_REG;
+ ctrl->check_status =
+ mdss_spi_reg_status_check;
+ } else if (!strcmp(string, "send_init_command")) {
+ ctrl->status_mode = SPI_SEND_PANEL_COMMAND;
+ ctrl->check_status =
+ mdss_send_panel_cmd_for_esd;
+ return;
+ } else {
+ pr_err("No valid panel-status-check-mode string\n");
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+ }
+
+ rc = of_property_read_u8(np, "qcom,mdss-spi-panel-status-reg",
+ &ctrl->panel_status_reg);
+ if (rc) {
+ pr_warn("%s:%d, Read status reg failed, disable ESD check\n",
+ __func__, __LINE__);
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-status-read-length",
+ &tmp);
+ if (rc) {
+ pr_warn("%s:%d, Read reg length failed, disable ESD check\n",
+ __func__, __LINE__);
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+
+ ctrl->status_cmds_rlen = (!rc ? tmp : 1);
+
+ ctrl->exp_status_value = kzalloc(sizeof(u8) *
+ (ctrl->status_cmds_rlen + 1), GFP_KERNEL);
+ ctrl->act_status_value = kzalloc(sizeof(u8) *
+ (ctrl->status_cmds_rlen + 1), GFP_KERNEL);
+
+ if (!ctrl->exp_status_value || !ctrl->act_status_value) {
+ pr_err("%s: Error allocating memory for status buffer\n",
+ __func__);
+ pinfo->esd_check_enabled = false;
+ return;
+ }
+
+ data = of_find_property(np, "qcom,mdss-spi-panel-status-value", &tmp);
+ tmp /= sizeof(u8);
+ if (!data || (tmp != ctrl->status_cmds_rlen)) {
+ pr_err("%s: Panel status values not found\n", __func__);
+ pinfo->esd_check_enabled = false;
+ memset(ctrl->exp_status_value, 0, ctrl->status_cmds_rlen);
+ } else {
+ rc = of_property_read_u8_array(np,
+ "qcom,mdss-spi-panel-status-value",
+ ctrl->exp_status_value, tmp);
+ if (rc) {
+ pr_err("%s: Error reading panel status values\n",
+ __func__);
+ pinfo->esd_check_enabled = false;
+ memset(ctrl->exp_status_value, 0,
+ ctrl->status_cmds_rlen);
+ }
+ }
+}
+
+static int mdss_spi_panel_parse_dt(struct device_node *np,
+ struct spi_panel_data *ctrl_pdata)
+{
+ u32 tmp;
+ int rc;
+ const char *data;
+ struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
+
+ pinfo->cont_splash_enabled = of_property_read_bool(np,
+ "qcom,cont-splash-enabled");
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-width", &tmp);
+ if (rc) {
+ pr_err("%s: panel width not specified\n", __func__);
+ return -EINVAL;
+ }
+ pinfo->xres = (!rc ? tmp : 240);
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-height", &tmp);
+ if (rc) {
+ pr_err("%s:panel height not specified\n", __func__);
+ return -EINVAL;
+ }
+ pinfo->yres = (!rc ? tmp : 320);
+
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-physical-width-dimension", &tmp);
+ pinfo->physical_width = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np,
+ "qcom,mdss-pan-physical-height-dimension", &tmp);
+ pinfo->physical_height = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-panel-framerate", &tmp);
+ pinfo->spi.frame_rate = (!rc ? tmp : 30);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-front-porch", &tmp);
+ pinfo->lcdc.h_front_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-back-porch", &tmp);
+ pinfo->lcdc.h_back_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-pulse-width", &tmp);
+ pinfo->lcdc.h_pulse_width = (!rc ? tmp : 2);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-h-sync-skew", &tmp);
+ pinfo->lcdc.hsync_skew = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-v-back-porch", &tmp);
+ pinfo->lcdc.v_back_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-v-front-porch", &tmp);
+ pinfo->lcdc.v_front_porch = (!rc ? tmp : 6);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-v-pulse-width", &tmp);
+ pinfo->lcdc.v_pulse_width = (!rc ? tmp : 2);
+
+
+ rc = of_property_read_u32(np, "qcom,mdss-spi-bpp", &tmp);
+ if (rc) {
+ pr_err("%s: bpp not specified\n", __func__);
+ return -EINVAL;
+ }
+ pinfo->bpp = (!rc ? tmp : 16);
+
+ pinfo->pdest = DISPLAY_1;
+
+ ctrl_pdata->bklt_ctrl = SPI_UNKNOWN_CTRL;
+ data = of_get_property(np, "qcom,mdss-spi-bl-pmic-control-type", NULL);
+ if (data) {
+ if (!strcmp(data, "bl_ctrl_wled")) {
+ led_trigger_register_simple("bkl-trigger",
+ &bl_led_trigger);
+ pr_debug("%s: SUCCESS-> WLED TRIGGER register\n",
+ __func__);
+ ctrl_pdata->bklt_ctrl = SPI_BL_WLED;
+ } else if (!strcmp(data, "bl_gpio_pulse")) {
+ led_trigger_register_simple("gpio-bklt-trigger",
+ &bl_led_trigger);
+ pr_debug("%s: SUCCESS-> GPIO PULSE TRIGGER register\n",
+ __func__);
+ ctrl_pdata->bklt_ctrl = SPI_BL_WLED;
+ } else if (!strcmp(data, "bl_ctrl_pwm")) {
+ ctrl_pdata->bklt_ctrl = SPI_BL_PWM;
+ ctrl_pdata->pwm_pmi = of_property_read_bool(np,
+ "qcom,mdss-spi-bl-pwm-pmi");
+ rc = of_property_read_u32(np,
+ "qcom,mdss-spi-bl-pmic-pwm-frequency", &tmp);
+ if (rc) {
+ pr_err("%s: Error, panel pwm_period\n",
+ __func__);
+ return -EINVAL;
+ }
+ ctrl_pdata->pwm_period = tmp;
+ if (ctrl_pdata->pwm_pmi) {
+ ctrl_pdata->pwm_bl = of_pwm_get(np, NULL);
+ if (IS_ERR(ctrl_pdata->pwm_bl)) {
+ pr_err("%s: Error, pwm device\n",
+ __func__);
+ ctrl_pdata->pwm_bl = NULL;
+ return -EINVAL;
+ }
+ } else {
+ rc = of_property_read_u32(np,
+ "qcom,mdss-spi-bl-pmic-bank-select",
+ &tmp);
+ if (rc) {
+ pr_err("%s: Error, lpg channel\n",
+ __func__);
+ return -EINVAL;
+ }
+ ctrl_pdata->pwm_lpg_chan = tmp;
+ tmp = of_get_named_gpio(np,
+ "qcom,mdss-spi-pwm-gpio", 0);
+ ctrl_pdata->pwm_pmic_gpio = tmp;
+ pr_debug("%s: Configured PWM bklt ctrl\n",
+ __func__);
+ }
+ }
+ }
+ rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp);
+ pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-bl-min-level", &tmp);
+ pinfo->bl_min = (!rc ? tmp : 0);
+ rc = of_property_read_u32(np, "qcom,mdss-spi-bl-max-level", &tmp);
+ pinfo->bl_max = (!rc ? tmp : 255);
+ ctrl_pdata->bklt_max = pinfo->bl_max;
+
+
+ mdss_spi_panel_parse_reset_seq(np, pinfo->rst_seq,
+ &(pinfo->rst_seq_len),
+ "qcom,mdss-spi-reset-sequence");
+
+ mdss_spi_panel_parse_cmds(np, &ctrl_pdata->on_cmds,
+ "qcom,mdss-spi-on-command");
+
+ mdss_spi_panel_parse_cmds(np, &ctrl_pdata->off_cmds,
+ "qcom,mdss-spi-off-command");
+
+ mdss_spi_parse_esd_params(np, ctrl_pdata);
+
+
+ return 0;
+}
+
+static void mdss_spi_panel_pwm_cfg(struct spi_panel_data *ctrl)
+{
+ if (ctrl->pwm_pmi)
+ return;
+
+ ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt");
+ if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) {
+ pr_err("%s: Error: lpg_chan=%d pwm request failed",
+ __func__, ctrl->pwm_lpg_chan);
+ }
+ ctrl->pwm_enabled = 0;
+}
+
+static void mdss_spi_panel_bklt_pwm(struct spi_panel_data *ctrl, int level)
+{
+ int ret;
+ u32 duty;
+ u32 period_ns;
+
+ if (ctrl->pwm_bl == NULL) {
+ pr_err("%s: no PWM\n", __func__);
+ return;
+ }
+
+ if (level == 0) {
+ if (ctrl->pwm_enabled) {
+ ret = pwm_config_us(ctrl->pwm_bl, level,
+ ctrl->pwm_period);
+ if (ret)
+ pr_err("%s: pwm_config_us() failed err=%d.\n",
+ __func__, ret);
+ pwm_disable(ctrl->pwm_bl);
+ }
+ ctrl->pwm_enabled = 0;
+ return;
+ }
+
+ duty = level * ctrl->pwm_period;
+ duty /= ctrl->bklt_max;
+
+ pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n",
+ __func__, ctrl->bklt_ctrl, ctrl->pwm_period,
+ ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan);
+
+ if (ctrl->pwm_period >= USEC_PER_SEC) {
+ ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period);
+ if (ret) {
+ pr_err("%s: pwm_config_us() failed err=%d\n",
+ __func__, ret);
+ return;
+ }
+ } else {
+ period_ns = ctrl->pwm_period * NSEC_PER_USEC;
+ ret = pwm_config(ctrl->pwm_bl,
+ level * period_ns / ctrl->bklt_max,
+ period_ns);
+ if (ret) {
+ pr_err("%s: pwm_config() failed err=%d\n",
+ __func__, ret);
+ return;
+ }
+ }
+
+ if (!ctrl->pwm_enabled) {
+ ret = pwm_enable(ctrl->pwm_bl);
+ if (ret)
+ pr_err("%s: pwm_enable() failed err=%d\n", __func__,
+ ret);
+ ctrl->pwm_enabled = 1;
+ }
+}
+
+static void mdss_spi_panel_bl_ctrl(struct mdss_panel_data *pdata,
+ u32 bl_level)
+{
+ if (bl_level) {
+ mdp3_res->bklt_level = bl_level;
+ mdp3_res->bklt_update = true;
+ } else {
+ mdss_spi_panel_bl_ctrl_update(pdata, bl_level);
+ }
+}
+
+#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) && defined(CONFIG_SPI_QUP)
+void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata,
+ u32 bl_level)
+{
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0))
+ bl_level = pdata->panel_info.bl_min;
+
+ switch (ctrl_pdata->bklt_ctrl) {
+ case SPI_BL_WLED:
+ led_trigger_event(bl_led_trigger, bl_level);
+ break;
+ case SPI_BL_PWM:
+ mdss_spi_panel_bklt_pwm(ctrl_pdata, bl_level);
+ break;
+ default:
+ pr_err("%s: Unknown bl_ctrl configuration %d\n",
+ __func__, ctrl_pdata->bklt_ctrl);
+ break;
+ }
+}
+#endif
+
+static int mdss_spi_panel_init(struct device_node *node,
+ struct spi_panel_data *ctrl_pdata,
+ bool cmd_cfg_cont_splash)
+{
+ int rc = 0;
+ static const char *panel_name;
+ struct mdss_panel_info *pinfo;
+
+ if (!node || !ctrl_pdata) {
+ pr_err("%s: Invalid arguments\n", __func__);
+ return -ENODEV;
+ }
+
+ pinfo = &ctrl_pdata->panel_data.panel_info;
+
+ pr_debug("%s:%d\n", __func__, __LINE__);
+ pinfo->panel_name[0] = '\0';
+ panel_name = of_get_property(node, "qcom,mdss-spi-panel-name", NULL);
+ if (!panel_name) {
+ pr_info("%s:%d, Panel name not specified\n",
+ __func__, __LINE__);
+ } else {
+ pr_debug("%s: Panel Name = %s\n", __func__, panel_name);
+ strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN);
+ }
+ rc = mdss_spi_panel_parse_dt(node, ctrl_pdata);
+ if (rc) {
+ pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__);
+ return rc;
+ }
+
+ ctrl_pdata->byte_pre_frame = pinfo->xres * pinfo->yres * pinfo->bpp/8;
+
+ ctrl_pdata->tx_buf = kmalloc(ctrl_pdata->byte_pre_frame, GFP_KERNEL);
+
+ if (!cmd_cfg_cont_splash)
+ pinfo->cont_splash_enabled = false;
+
+ pr_info("%s: Continuous splash %s\n", __func__,
+ pinfo->cont_splash_enabled ? "enabled" : "disabled");
+
+ pinfo->dynamic_switch_pending = false;
+ pinfo->is_lpm_mode = false;
+ pinfo->esd_rdy = false;
+
+ ctrl_pdata->on = mdss_spi_panel_on;
+ ctrl_pdata->off = mdss_spi_panel_off;
+ ctrl_pdata->panel_data.set_backlight = mdss_spi_panel_bl_ctrl;
+
+ return 0;
+}
+
+static int mdss_spi_get_panel_cfg(char *panel_cfg,
+ struct spi_panel_data *ctrl_pdata)
+{
+ int rc;
+ struct mdss_panel_cfg *pan_cfg = NULL;
+
+ if (!ctrl_pdata)
+ return MDSS_PANEL_INTF_INVALID;
+
+ pan_cfg = ctrl_pdata->mdss_util->panel_intf_type(MDSS_PANEL_INTF_SPI);
+ if (IS_ERR(pan_cfg)) {
+ return PTR_ERR(pan_cfg);
+ } else if (!pan_cfg) {
+ panel_cfg[0] = 0;
+ return 0;
+ }
+
+ pr_debug("%s:%d: cfg:[%s]\n", __func__, __LINE__,
+ pan_cfg->arg_cfg);
+ ctrl_pdata->panel_data.panel_info.is_prim_panel = true;
+ rc = strlcpy(panel_cfg, pan_cfg->arg_cfg,
+ sizeof(pan_cfg->arg_cfg));
+ return rc;
+}
+
+static int mdss_spi_panel_regulator_init(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (!pdev) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl_pdata = platform_get_drvdata(pdev);
+ if (!ctrl_pdata) {
+ pr_err("%s: invalid driver data\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_mdss_config_vreg(&pdev->dev,
+ ctrl_pdata->panel_power_data.vreg_config,
+ ctrl_pdata->panel_power_data.num_vreg, 1);
+ if (rc)
+ pr_err("%s: failed to init vregs for %s\n",
+ __func__, "PANEL_PM");
+
+ return rc;
+
+}
+
+static irqreturn_t spi_panel_te_handler(int irq, void *data)
+{
+ struct spi_panel_data *ctrl_pdata = (struct spi_panel_data *)data;
+ static int count = 2;
+
+ if (!ctrl_pdata) {
+ pr_err("%s: SPI display not available\n", __func__);
+ return IRQ_HANDLED;
+ }
+ complete(&ctrl_pdata->spi_panel_te);
+
+ if (ctrl_pdata->vsync_client.handler && !(--count)) {
+ ctrl_pdata->vsync_client.handler(ctrl_pdata->vsync_client.arg);
+ count = 2;
+ }
+
+ return IRQ_HANDLED;
+}
+
+void mdp3_spi_vsync_enable(struct mdss_panel_data *pdata,
+ struct mdp3_notification *vsync_client)
+{
+ int updated = 0;
+ struct spi_panel_data *ctrl_pdata = NULL;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct spi_panel_data,
+ panel_data);
+
+ if (vsync_client) {
+ if (ctrl_pdata->vsync_client.handler != vsync_client->handler) {
+ ctrl_pdata->vsync_client = *vsync_client;
+ updated = 1;
+ }
+ } else {
+ if (ctrl_pdata->vsync_client.handler) {
+ ctrl_pdata->vsync_client.handler = NULL;
+ ctrl_pdata->vsync_client.arg = NULL;
+ updated = 1;
+ }
+ }
+
+ if (updated) {
+ if (vsync_client && vsync_client->handler)
+ enable_spi_panel_te_irq(ctrl_pdata, true);
+ else
+ enable_spi_panel_te_irq(ctrl_pdata, false);
+ }
+}
+
+static struct device_node *mdss_spi_pref_prim_panel(
+ struct platform_device *pdev)
+{
+ struct device_node *spi_pan_node = NULL;
+
+ pr_debug("%s:%d: Select primary panel from dt\n",
+ __func__, __LINE__);
+ spi_pan_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,spi-pref-prim-pan", 0);
+ if (!spi_pan_node)
+ pr_err("%s:can't find panel phandle\n", __func__);
+
+ return spi_pan_node;
+}
+
+static int spi_panel_device_register(struct device_node *pan_node,
+ struct spi_panel_data *ctrl_pdata)
+{
+ int rc;
+ struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
+ struct device_node *spi_ctrl_np = NULL;
+ struct platform_device *ctrl_pdev = NULL;
+
+ pinfo->type = SPI_PANEL;
+
+ spi_ctrl_np = of_parse_phandle(pan_node,
+ "qcom,mdss-spi-panel-controller", 0);
+ if (!spi_ctrl_np) {
+ pr_err("%s: SPI controller node not initialized\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ ctrl_pdev = of_find_device_by_node(spi_ctrl_np);
+ if (!ctrl_pdev) {
+ of_node_put(spi_ctrl_np);
+ pr_err("%s: SPI controller node not find\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ rc = mdss_spi_panel_regulator_init(ctrl_pdev);
+ if (rc) {
+ pr_err("%s: failed to init regulator, rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo,
+ FPS_RESOLUTION_HZ);
+ pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo);
+
+ ctrl_pdata->disp_te_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-te-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->disp_te_gpio))
+ pr_err("%s:%d, TE gpio not specified\n",
+ __func__, __LINE__);
+
+ ctrl_pdata->disp_dc_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-spi-dc-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->disp_dc_gpio))
+ pr_err("%s:%d, SPI DC gpio not specified\n",
+ __func__, __LINE__);
+
+ ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
+ "qcom,platform-reset-gpio", 0);
+ if (!gpio_is_valid(ctrl_pdata->rst_gpio))
+ pr_err("%s:%d, reset gpio not specified\n",
+ __func__, __LINE__);
+
+ ctrl_pdata->panel_data.event_handler = mdss_spi_panel_event_handler;
+
+ if (ctrl_pdata->bklt_ctrl == SPI_BL_PWM)
+ mdss_spi_panel_pwm_cfg(ctrl_pdata);
+
+ ctrl_pdata->ctrl_state = CTRL_STATE_UNKNOWN;
+
+ if (pinfo->cont_splash_enabled) {
+ rc = mdss_spi_panel_power_ctrl(&(ctrl_pdata->panel_data),
+ MDSS_PANEL_POWER_ON);
+ if (rc) {
+ pr_err("%s: Panel power on failed\n", __func__);
+ return rc;
+ }
+ if (ctrl_pdata->bklt_ctrl == SPI_BL_PWM)
+ ctrl_pdata->pwm_enabled = 1;
+ pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
+ ctrl_pdata->ctrl_state |=
+ (CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
+ } else {
+ pinfo->panel_power_state = MDSS_PANEL_POWER_OFF;
+ }
+
+ rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data));
+ if (rc) {
+ pr_err("%s: unable to register SPI panel\n", __func__);
+ return rc;
+ }
+
+ pr_debug("%s: Panel data initialized\n", __func__);
+ return 0;
+}
+
+
+/**
+ * mdss_spi_find_panel_of_node(): find device node of spi panel
+ * @pdev: platform_device of the spi ctrl node
+ * @panel_cfg: string containing intf specific config data
+ *
+ * Function finds the panel device node using the interface
+ * specific configuration data. This configuration data is
+ * could be derived from the result of bootloader's GCDB
+ * panel detection mechanism. If such config data doesn't
+ * exist then this panel returns the default panel configured
+ * in the device tree.
+ *
+ * returns pointer to panel node on success, NULL on error.
+ */
+static struct device_node *mdss_spi_find_panel_of_node(
+ struct platform_device *pdev, char *panel_cfg)
+{
+ int len, i;
+ int ctrl_id = pdev->id - 1;
+ char panel_name[MDSS_MAX_PANEL_LEN] = "";
+ char ctrl_id_stream[3] = "0:";
+ char *stream = NULL, *pan = NULL;
+ struct device_node *spi_pan_node = NULL, *mdss_node = NULL;
+
+ len = strlen(panel_cfg);
+ if (!len) {
+ /* no panel cfg chg, parse dt */
+ pr_err("%s:%d: no cmd line cfg present\n",
+ __func__, __LINE__);
+ goto end;
+ } else {
+ if (ctrl_id == 1)
+ strlcpy(ctrl_id_stream, "1:", 3);
+
+ stream = strnstr(panel_cfg, ctrl_id_stream, len);
+ if (!stream) {
+ pr_err("controller config is not present\n");
+ goto end;
+ }
+ stream += 2;
+
+ pan = strnchr(stream, strlen(stream), ':');
+ if (!pan) {
+ strlcpy(panel_name, stream, MDSS_MAX_PANEL_LEN);
+ } else {
+ for (i = 0; (stream + i) < pan; i++)
+ panel_name[i] = *(stream + i);
+ panel_name[i] = 0;
+ }
+
+ pr_debug("%s:%d:%s:%s\n", __func__, __LINE__,
+ panel_cfg, panel_name);
+
+ mdss_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,mdss-mdp", 0);
+ if (!mdss_node) {
+ pr_err("%s: %d: mdss_node null\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ spi_pan_node = of_find_node_by_name(mdss_node,
+ panel_name);
+ if (!spi_pan_node) {
+ pr_err("%s: invalid pan node, selecting prim panel\n",
+ __func__);
+ goto end;
+ }
+ return spi_pan_node;
+ }
+end:
+ if (strcmp(panel_name, NONE_PANEL))
+ spi_pan_node = mdss_spi_pref_prim_panel(pdev);
+ of_node_put(mdss_node);
+ return spi_pan_node;
+}
+
+
+static int mdss_spi_panel_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct spi_panel_data *ctrl_pdata;
+ struct mdss_panel_cfg *pan_cfg = NULL;
+ struct device_node *spi_pan_node = NULL;
+ bool cmd_cfg_cont_splash = true;
+ char panel_cfg[MDSS_MAX_PANEL_LEN];
+ struct mdss_util_intf *util;
+ const char *ctrl_name;
+
+ util = mdss_get_util_intf();
+ if (util == NULL) {
+ pr_err("Failed to get mdss utility functions\n");
+ return -ENODEV;
+ }
+
+ if (!util->mdp_probe_done) {
+ pr_err("%s: MDP not probed yet\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ if (!pdev->dev.of_node) {
+ pr_err("SPI driver only supports device tree probe\n");
+ return -ENOTSUPP;
+ }
+
+ pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_DSI);
+ if (IS_ERR(pan_cfg)) {
+ pr_err("%s: return MDSS_PANEL_INTF_DSI\n", __func__);
+ return PTR_ERR(pan_cfg);
+ } else if (pan_cfg) {
+ pr_err("%s: DSI is primary\n", __func__);
+ return -ENODEV;
+ }
+
+ ctrl_pdata = platform_get_drvdata(pdev);
+ if (!ctrl_pdata) {
+ ctrl_pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct spi_panel_data),
+ GFP_KERNEL);
+ if (!ctrl_pdata) {
+ pr_err("%s: FAILED: cannot alloc spi panel\n",
+ __func__);
+ rc = -ENOMEM;
+ goto error_no_mem;
+ }
+ platform_set_drvdata(pdev, ctrl_pdata);
+ }
+
+ ctrl_pdata->mdss_util = util;
+
+ ctrl_name = of_get_property(pdev->dev.of_node, "label", NULL);
+ if (!ctrl_name)
+ pr_info("%s:%d, Ctrl name not specified\n",
+ __func__, __LINE__);
+ else
+ pr_debug("%s: Ctrl name = %s\n",
+ __func__, ctrl_name);
+
+
+ rc = of_platform_populate(pdev->dev.of_node,
+ NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ goto error_no_mem;
+ }
+
+ rc = mdss_spi_panel_pinctrl_init(pdev);
+ if (rc)
+ pr_warn("%s: failed to get pin resources\n", __func__);
+
+ rc = mdss_spi_get_panel_vreg_data(&pdev->dev,
+ &ctrl_pdata->panel_power_data);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: failed to get panel vreg data, rc=%d\n",
+ __func__, rc);
+ goto error_vreg;
+ }
+
+ /* SPI panels can be different between controllers */
+ rc = mdss_spi_get_panel_cfg(panel_cfg, ctrl_pdata);
+ if (!rc)
+ /* spi panel cfg not present */
+ pr_warn("%s:%d:spi specific cfg not present\n",
+ __func__, __LINE__);
+
+ /* find panel device node */
+ spi_pan_node = mdss_spi_find_panel_of_node(pdev, panel_cfg);
+ if (!spi_pan_node) {
+ pr_err("%s: can't find panel node %s\n", __func__, panel_cfg);
+ goto error_pan_node;
+ }
+
+ cmd_cfg_cont_splash = true;
+
+ rc = mdss_spi_panel_init(spi_pan_node, ctrl_pdata, cmd_cfg_cont_splash);
+ if (rc) {
+ pr_err("%s: spi panel init failed\n", __func__);
+ goto error_pan_node;
+ }
+
+ rc = spi_panel_device_register(spi_pan_node, ctrl_pdata);
+ if (rc) {
+ pr_err("%s: spi panel dev reg failed\n", __func__);
+ goto error_pan_node;
+ }
+
+ ctrl_pdata->panel_data.event_handler = mdss_spi_panel_event_handler;
+
+
+ init_completion(&ctrl_pdata->spi_panel_te);
+ mutex_init(&ctrl_pdata->spi_tx_mutex);
+
+ rc = devm_request_irq(&pdev->dev,
+ gpio_to_irq(ctrl_pdata->disp_te_gpio),
+ spi_panel_te_handler, IRQF_TRIGGER_RISING,
+ "TE_GPIO", ctrl_pdata);
+ if (rc) {
+ pr_err("TE request_irq failed.\n");
+ return rc;
+ }
+
+ pr_debug("%s: spi panel initialized\n", __func__);
+ return 0;
+
+error_pan_node:
+ of_node_put(spi_pan_node);
+error_vreg:
+ mdss_spi_put_dt_vreg_data(&pdev->dev,
+ &ctrl_pdata->panel_power_data);
+error_no_mem:
+ devm_kfree(&pdev->dev, ctrl_pdata);
+ return rc;
+}
+
+
+static const struct of_device_id mdss_spi_panel_match[] = {
+ { .compatible = "qcom,mdss-spi-display" },
+ {},
+};
+
+static struct platform_driver this_driver = {
+ .probe = mdss_spi_panel_probe,
+ .driver = {
+ .name = "spi_panel",
+ .owner = THIS_MODULE,
+ .of_match_table = mdss_spi_panel_match,
+ },
+};
+
+static int __init mdss_spi_display_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&this_driver);
+ return 0;
+}
+module_init(mdss_spi_display_init);
+
+MODULE_DEVICE_TABLE(of, mdss_spi_panel_match);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/fbdev/msm/mdss_spi_panel.h b/drivers/video/fbdev/msm/mdss_spi_panel.h
new file mode 100644
index 0000000..80b7ea8
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_spi_panel.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MDSS_SPI_PANEL_H__
+#define __MDSS_SPI_PANEL_H__
+
+#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) && defined(CONFIG_SPI_QUP)
+#include <linux/list.h>
+#include <linux/mdss_io_util.h>
+#include <linux/irqreturn.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/gpio.h>
+
+#include "mdss_panel.h"
+#include "mdp3_dma.h"
+
+#define MDSS_MAX_BL_BRIGHTNESS 255
+
+#define MDSS_SPI_RST_SEQ_LEN 10
+
+#define NONE_PANEL "none"
+
+#define CTRL_STATE_UNKNOWN 0x00
+#define CTRL_STATE_PANEL_INIT BIT(0)
+#define CTRL_STATE_MDP_ACTIVE BIT(1)
+
+#define MDSS_PINCTRL_STATE_DEFAULT "mdss_default"
+#define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep"
+#define SPI_PANEL_TE_TIMEOUT 400
+
+enum spi_panel_data_type {
+ panel_cmd,
+ panel_parameter,
+ panel_pixel,
+ UNKNOWN_FORMAT,
+};
+
+enum spi_panel_bl_ctrl {
+ SPI_BL_PWM,
+ SPI_BL_WLED,
+ SPI_BL_DCS_CMD,
+ SPI_UNKNOWN_CTRL,
+};
+
+struct spi_pinctrl_res {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
+};
+#define SPI_PANEL_DST_FORMAT_RGB565 0
+
+struct spi_ctrl_hdr {
+ char wait; /* ms */
+ char dlen; /* 8 bits */
+};
+
+struct spi_cmd_desc {
+ struct spi_ctrl_hdr dchdr;
+ char *command;
+ char *parameter;
+};
+
+struct spi_panel_cmds {
+ char *buf;
+ int blen;
+ struct spi_cmd_desc *cmds;
+ int cmd_cnt;
+};
+
+enum spi_panel_status_mode {
+ SPI_ESD_REG,
+ SPI_SEND_PANEL_COMMAND,
+ SPI_ESD_MAX,
+};
+
+
+struct spi_panel_data {
+ struct mdss_panel_data panel_data;
+ struct mdss_util_intf *mdss_util;
+ struct spi_pinctrl_res pin_res;
+ struct mdss_module_power panel_power_data;
+ struct completion spi_panel_te;
+ struct mdp3_notification vsync_client;
+ unsigned int vsync_status;
+ int byte_pre_frame;
+ char *tx_buf;
+ u8 ctrl_state;
+ int disp_te_gpio;
+ int rst_gpio;
+ int disp_dc_gpio; /* command or data */
+ struct spi_panel_cmds on_cmds;
+ struct spi_panel_cmds off_cmds;
+ bool (*check_status)(struct spi_panel_data *pdata);
+ int (*on)(struct mdss_panel_data *pdata);
+ int (*off)(struct mdss_panel_data *pdata);
+ struct mutex spi_tx_mutex;
+ struct pwm_device *pwm_bl;
+ int bklt_ctrl; /* backlight ctrl */
+ bool pwm_pmi;
+ int pwm_period;
+ int pwm_pmic_gpio;
+ int pwm_lpg_chan;
+ int pwm_enabled;
+ int bklt_max;
+ int status_mode;
+ u32 status_cmds_rlen;
+ u8 panel_status_reg;
+ u8 *exp_status_value;
+ u8 *act_status_value;
+ unsigned char *return_buf;
+};
+
+int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata,
+ char *buf, int len, int stride);
+int is_spi_panel_continuous_splash_on(struct mdss_panel_data *pdata);
+void mdp3_spi_vsync_enable(struct mdss_panel_data *pdata,
+ struct mdp3_notification *vsync_client);
+void mdp3_check_spi_panel_status(struct work_struct *work,
+ uint32_t interval);
+
+#else
+static inline int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata,
+ char *buf, int len, int stride){
+ return 0;
+}
+static inline int is_spi_panel_continuous_splash_on(
+ struct mdss_panel_data *pdata)
+{
+ return 0;
+}
+static inline int mdp3_spi_vsync_enable(struct mdss_panel_data *pdata,
+ struct mdp3_notification *vsync_client){
+ return 0;
+}
+
+#endif/* End of CONFIG_FB_MSM_MDSS_SPI_PANEL && ONFIG_SPI_QUP */
+
+#endif /* End of __MDSS_SPI_PANEL_H__ */
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index f0e46f7..9ccd428 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -417,7 +417,7 @@
wmb(); /* make sure phy timings are updated*/
}
-static void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl)
+void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl)
{
/* start phy sw reset */
MIPI_OUTP(ctrl->ctrl_base + 0x12c, 0x0001);
@@ -519,8 +519,7 @@
* is only done from the clock master. This will ensure that the PLL is
* off when PHY reset is called.
*/
- if (mdss_dsi_is_ctrl_clk_slave(ctrl) ||
- (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM))
+ if (mdss_dsi_is_ctrl_clk_slave(ctrl))
return;
mdss_dsi_phy_sw_reset_sub(ctrl);
@@ -2280,7 +2279,8 @@
if (ctrl->phy_power_off || mmss_clamp)
mdss_dsi_phy_power_on(ctrl, mmss_clamp);
}
- if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK)) {
+
+ if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_LP_CLK)) {
if (ctrl->ulps) {
/*
* ULPS Entry Request. This is needed if the lanes were
@@ -2311,9 +2311,12 @@
goto error;
}
}
- /* Enable HS TX driver in DSI PHY if applicable */
- mdss_dsi_phy_hstx_drv_ctrl(ctrl, true);
}
+
+ /* Enable HS TX driver in DSI PHY if applicable */
+ if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK))
+ mdss_dsi_phy_hstx_drv_ctrl(ctrl, true);
+
error:
return rc;
}
@@ -2429,6 +2432,26 @@
if (ctrl->mdss_util->dyn_clk_gating_ctrl)
ctrl->mdss_util->dyn_clk_gating_ctrl(0);
+ if ((clk_type & MDSS_DSI_LINK_CLK) &&
+ (l_type == MDSS_DSI_LINK_HS_CLK)) {
+ u32 data = 0;
+
+ data = MIPI_INP((ctrl->ctrl_io.base) + 0x0120);
+ /*
+ * For 12nm PHY, the PLL unlock bit in DSI_CLK_STATUS gets set
+ * when PLL is turned off. When device comes out of static
+ * screen without the DSI controller getting power collapsed,
+ * the bit might not clear sometimes. Clear the bit before
+ * turning ON the PLL. This avoids false error interrupt due to
+ * PLL unlocked bit after PLL is turned ON.
+ */
+ if (data & BIT(16)) {
+ pr_debug("pll unlocked: 0x%x\n", data);
+ MIPI_OUTP((ctrl->ctrl_io.base) + 0x120, BIT(16));
+ }
+
+ }
+
return rc;
}
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c
index b529a8c..3984716 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td028ttec1.c
@@ -455,6 +455,8 @@
}
static const struct of_device_id td028ttec1_of_match[] = {
+ { .compatible = "omapdss,tpo,td028ttec1", },
+ /* keep to not break older DTB */
{ .compatible = "omapdss,toppoly,td028ttec1", },
{},
};
@@ -474,6 +476,7 @@
module_spi_driver(td028ttec1_spi_driver);
+MODULE_ALIAS("spi:tpo,td028ttec1");
MODULE_ALIAS("spi:toppoly,td028ttec1");
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.c b/drivers/video/fbdev/omap2/omapfb/dss/dss.c
index 47d7f69..48c6500 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/dss.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.c
@@ -941,11 +941,13 @@
return 0;
}
+static void dss_uninit_ports(struct platform_device *pdev);
+
static int dss_init_ports(struct platform_device *pdev)
{
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
- int r;
+ int r, ret = 0;
if (parent == NULL)
return 0;
@@ -972,17 +974,21 @@
switch (port_type) {
case OMAP_DISPLAY_TYPE_DPI:
- dpi_init_port(pdev, port);
+ ret = dpi_init_port(pdev, port);
break;
case OMAP_DISPLAY_TYPE_SDI:
- sdi_init_port(pdev, port);
+ ret = sdi_init_port(pdev, port);
break;
default:
break;
}
- } while ((port = omapdss_of_get_next_port(parent, port)) != NULL);
+ } while (!ret &&
+ (port = omapdss_of_get_next_port(parent, port)) != NULL);
- return 0;
+ if (ret)
+ dss_uninit_ports(pdev);
+
+ return ret;
}
static void dss_uninit_ports(struct platform_device *pdev)
diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c
index d0a4e2f..d215faa 100644
--- a/drivers/video/fbdev/sm501fb.c
+++ b/drivers/video/fbdev/sm501fb.c
@@ -1600,6 +1600,7 @@
info->fbmem = ioremap(res->start, resource_size(res));
if (info->fbmem == NULL) {
dev_err(dev, "cannot remap framebuffer\n");
+ ret = -ENXIO;
goto err_mem_res;
}
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index 53326ba..2add8de 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -1487,15 +1487,25 @@
static int dlfb_select_std_channel(struct dlfb_data *dev)
{
int ret;
- u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7,
+ void *buf;
+ static const u8 set_def_chn[] = {
+ 0x57, 0xCD, 0xDC, 0xA7,
0x1C, 0x88, 0x5E, 0x15,
0x60, 0xFE, 0xC6, 0x97,
0x16, 0x3D, 0x47, 0xF2 };
+ buf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
NR_USB_REQUEST_CHANNEL,
(USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0,
- set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT);
+ buf, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT);
+
+ kfree(buf);
+
return ret;
}
diff --git a/drivers/video/fbdev/vfb.c b/drivers/video/fbdev/vfb.c
index a042329..026df9a 100644
--- a/drivers/video/fbdev/vfb.c
+++ b/drivers/video/fbdev/vfb.c
@@ -241,8 +241,23 @@
*/
static int vfb_set_par(struct fb_info *info)
{
+ switch (info->var.bits_per_pixel) {
+ case 1:
+ info->fix.visual = FB_VISUAL_MONO01;
+ break;
+ case 8:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ case 16:
+ case 24:
+ case 32:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ }
+
info->fix.line_length = get_line_length(info->var.xres_virtual,
info->var.bits_per_pixel);
+
return 0;
}
@@ -454,6 +469,8 @@
goto err2;
platform_set_drvdata(dev, info);
+ vfb_set_par(info);
+
fb_info(info, "Virtual frame buffer device, using %ldK of video memory\n",
videomemorysize >> 10);
return 0;
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1cf907e..111a0ab 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -321,6 +321,17 @@
}
EXPORT_SYMBOL(hdmi_vendor_infoframe_init);
+static int hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *frame)
+{
+ /* for side by side (half) we also need to provide 3D_Ext_Data */
+ if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
+ return 6;
+ else if (frame->vic != 0 || frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
+ return 5;
+ else
+ return 4;
+}
+
/**
* hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer
* @frame: HDMI infoframe
@@ -341,19 +352,11 @@
u8 *ptr = buffer;
size_t length;
- /* empty info frame */
- if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID)
- return -EINVAL;
-
/* only one of those can be supplied */
if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
return -EINVAL;
- /* for side by side (half) we also need to provide 3D_Ext_Data */
- if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
- frame->length = 6;
- else
- frame->length = 5;
+ frame->length = hdmi_vendor_infoframe_length(frame);
length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
@@ -372,14 +375,16 @@
ptr[5] = 0x0c;
ptr[6] = 0x00;
- if (frame->vic) {
- ptr[7] = 0x1 << 5; /* video format */
- ptr[8] = frame->vic;
- } else {
+ if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
ptr[7] = 0x2 << 5; /* video format */
ptr[8] = (frame->s3d_struct & 0xf) << 4;
if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
ptr[9] = (frame->s3d_ext_data & 0xf) << 4;
+ } else if (frame->vic) {
+ ptr[7] = 0x1 << 5; /* video format */
+ ptr[8] = frame->vic;
+ } else {
+ ptr[7] = 0x0 << 5; /* video format */
}
hdmi_infoframe_set_checksum(buffer, length);
@@ -1165,7 +1170,7 @@
if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR ||
ptr[1] != 1 ||
- (ptr[2] != 5 && ptr[2] != 6))
+ (ptr[2] != 4 && ptr[2] != 5 && ptr[2] != 6))
return -EINVAL;
length = ptr[2];
@@ -1193,16 +1198,22 @@
hvf->length = length;
- if (hdmi_video_format == 0x1) {
- hvf->vic = ptr[4];
- } else if (hdmi_video_format == 0x2) {
+ if (hdmi_video_format == 0x2) {
+ if (length != 5 && length != 6)
+ return -EINVAL;
hvf->s3d_struct = ptr[4] >> 4;
if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
- if (length == 6)
- hvf->s3d_ext_data = ptr[5] >> 4;
- else
+ if (length != 6)
return -EINVAL;
+ hvf->s3d_ext_data = ptr[5] >> 4;
}
+ } else if (hdmi_video_format == 0x1) {
+ if (length != 5)
+ return -EINVAL;
+ hvf->vic = ptr[4];
+ } else {
+ if (length != 4)
+ return -EINVAL;
}
return 0;
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 489bfc6..8977f40 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -423,8 +423,6 @@
i = vq->vring.desc[i].next;
}
- vq->vq.num_free += total_sg;
-
if (indirect)
kfree(desc);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 3eb58cb..8f8909a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -799,11 +799,12 @@
the timeout module parameter.
config F71808E_WDT
- tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
+ tristate "Fintek F718xx, F818xx Super I/O Watchdog"
depends on X86
help
- This is the driver for the hardware watchdog on the Fintek
- F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers.
+ This is the driver for the hardware watchdog on the Fintek F71808E,
+ F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
+ Super I/O controllers.
You can compile this driver directly into the kernel, or use
it as a module. The module will be called f71808e_wdt.
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index 1b7e916..e682bf0 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -57,6 +57,7 @@
#define SIO_F71808_ID 0x0901 /* Chipset ID */
#define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */
+#define SIO_F71868_ID 0x1106 /* Chipset ID */
#define SIO_F71869_ID 0x0814 /* Chipset ID */
#define SIO_F71869A_ID 0x1007 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
@@ -101,7 +102,7 @@
static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
module_param(pulse_width, uint, 0);
MODULE_PARM_DESC(pulse_width,
- "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
+ "Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms"
" (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
@@ -119,13 +120,14 @@
MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
" given initial timeout. Zero (default) disables this feature.");
-enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865,
- f81866};
+enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
+ f81865, f81866};
static const char *f71808e_names[] = {
"f71808fg",
"f71858fg",
"f71862fg",
+ "f71868",
"f71869",
"f71882fg",
"f71889fg",
@@ -252,16 +254,23 @@
static int watchdog_set_pulse_width(unsigned int pw)
{
int err = 0;
+ unsigned int t1 = 25, t2 = 125, t3 = 5000;
+
+ if (watchdog.type == f71868) {
+ t1 = 30;
+ t2 = 150;
+ t3 = 6000;
+ }
mutex_lock(&watchdog.lock);
- if (pw <= 1) {
+ if (pw <= 1) {
watchdog.pulse_val = 0;
- } else if (pw <= 25) {
+ } else if (pw <= t1) {
watchdog.pulse_val = 1;
- } else if (pw <= 125) {
+ } else if (pw <= t2) {
watchdog.pulse_val = 2;
- } else if (pw <= 5000) {
+ } else if (pw <= t3) {
watchdog.pulse_val = 3;
} else {
pr_err("pulse width out of range\n");
@@ -354,6 +363,7 @@
goto exit_superio;
break;
+ case f71868:
case f71869:
/* GPIO14 --> WDTRST# */
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
@@ -486,7 +496,7 @@
is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0))
&& (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF)
- & F71808FG_FLAG_WD_EN);
+ & BIT(F71808FG_FLAG_WD_EN));
superio_exit(watchdog.sioaddr);
@@ -792,6 +802,9 @@
watchdog.type = f71862fg;
err = f71862fg_pin_configure(0); /* validate module parameter */
break;
+ case SIO_F71868_ID:
+ watchdog.type = f71868;
+ break;
case SIO_F71869_ID:
case SIO_F71869A_ID:
watchdog.type = f71869;
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 70c7194..b0a1580 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -28,16 +28,7 @@
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
-#ifdef CONFIG_HPWDT_NMI_DECODING
-#include <linux/dmi.h>
-#include <linux/spinlock.h>
-#include <linux/nmi.h>
-#include <linux/kdebug.h>
-#include <linux/notifier.h>
-#include <asm/cacheflush.h>
-#endif /* CONFIG_HPWDT_NMI_DECODING */
#include <asm/nmi.h>
-#include <asm/frame.h>
#define HPWDT_VERSION "1.4.0"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
@@ -48,10 +39,14 @@
static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
static unsigned int reload; /* the computed soft_margin */
static bool nowayout = WATCHDOG_NOWAYOUT;
+#ifdef CONFIG_HPWDT_NMI_DECODING
+static unsigned int allow_kdump = 1;
+#endif
static char expect_release;
static unsigned long hpwdt_is_open;
static void __iomem *pci_mem_addr; /* the PCI-memory address */
+static unsigned long __iomem *hpwdt_nmistat;
static unsigned long __iomem *hpwdt_timer_reg;
static unsigned long __iomem *hpwdt_timer_con;
@@ -62,373 +57,6 @@
};
MODULE_DEVICE_TABLE(pci, hpwdt_devices);
-#ifdef CONFIG_HPWDT_NMI_DECODING
-#define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */
-#define CRU_BIOS_SIGNATURE_VALUE 0x55524324
-#define PCI_BIOS32_PARAGRAPH_LEN 16
-#define PCI_ROM_BASE1 0x000F0000
-#define ROM_SIZE 0x10000
-
-struct bios32_service_dir {
- u32 signature;
- u32 entry_point;
- u8 revision;
- u8 length;
- u8 checksum;
- u8 reserved[5];
-};
-
-/* type 212 */
-struct smbios_cru64_info {
- u8 type;
- u8 byte_length;
- u16 handle;
- u32 signature;
- u64 physical_address;
- u32 double_length;
- u32 double_offset;
-};
-#define SMBIOS_CRU64_INFORMATION 212
-
-/* type 219 */
-struct smbios_proliant_info {
- u8 type;
- u8 byte_length;
- u16 handle;
- u32 power_features;
- u32 omega_features;
- u32 reserved;
- u32 misc_features;
-};
-#define SMBIOS_ICRU_INFORMATION 219
-
-
-struct cmn_registers {
- union {
- struct {
- u8 ral;
- u8 rah;
- u16 rea2;
- };
- u32 reax;
- } u1;
- union {
- struct {
- u8 rbl;
- u8 rbh;
- u8 reb2l;
- u8 reb2h;
- };
- u32 rebx;
- } u2;
- union {
- struct {
- u8 rcl;
- u8 rch;
- u16 rec2;
- };
- u32 recx;
- } u3;
- union {
- struct {
- u8 rdl;
- u8 rdh;
- u16 red2;
- };
- u32 redx;
- } u4;
-
- u32 resi;
- u32 redi;
- u16 rds;
- u16 res;
- u32 reflags;
-} __attribute__((packed));
-
-static unsigned int hpwdt_nmi_decoding;
-static unsigned int allow_kdump = 1;
-static unsigned int is_icru;
-static unsigned int is_uefi;
-static DEFINE_SPINLOCK(rom_lock);
-static void *cru_rom_addr;
-static struct cmn_registers cmn_regs;
-
-extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs,
- unsigned long *pRomEntry);
-
-#ifdef CONFIG_X86_32
-/* --32 Bit Bios------------------------------------------------------------ */
-
-#define HPWDT_ARCH 32
-
-asm(".text \n\t"
- ".align 4 \n\t"
- ".globl asminline_call \n"
- "asminline_call: \n\t"
- "pushl %ebp \n\t"
- "movl %esp, %ebp \n\t"
- "pusha \n\t"
- "pushf \n\t"
- "push %es \n\t"
- "push %ds \n\t"
- "pop %es \n\t"
- "movl 8(%ebp),%eax \n\t"
- "movl 4(%eax),%ebx \n\t"
- "movl 8(%eax),%ecx \n\t"
- "movl 12(%eax),%edx \n\t"
- "movl 16(%eax),%esi \n\t"
- "movl 20(%eax),%edi \n\t"
- "movl (%eax),%eax \n\t"
- "push %cs \n\t"
- "call *12(%ebp) \n\t"
- "pushf \n\t"
- "pushl %eax \n\t"
- "movl 8(%ebp),%eax \n\t"
- "movl %ebx,4(%eax) \n\t"
- "movl %ecx,8(%eax) \n\t"
- "movl %edx,12(%eax) \n\t"
- "movl %esi,16(%eax) \n\t"
- "movl %edi,20(%eax) \n\t"
- "movw %ds,24(%eax) \n\t"
- "movw %es,26(%eax) \n\t"
- "popl %ebx \n\t"
- "movl %ebx,(%eax) \n\t"
- "popl %ebx \n\t"
- "movl %ebx,28(%eax) \n\t"
- "pop %es \n\t"
- "popf \n\t"
- "popa \n\t"
- "leave \n\t"
- "ret \n\t"
- ".previous");
-
-
-/*
- * cru_detect
- *
- * Routine Description:
- * This function uses the 32-bit BIOS Service Directory record to
- * search for a $CRU record.
- *
- * Return Value:
- * 0 : SUCCESS
- * <0 : FAILURE
- */
-static int cru_detect(unsigned long map_entry,
- unsigned long map_offset)
-{
- void *bios32_map;
- unsigned long *bios32_entrypoint;
- unsigned long cru_physical_address;
- unsigned long cru_length;
- unsigned long physical_bios_base = 0;
- unsigned long physical_bios_offset = 0;
- int retval = -ENODEV;
-
- bios32_map = ioremap(map_entry, (2 * PAGE_SIZE));
-
- if (bios32_map == NULL)
- return -ENODEV;
-
- bios32_entrypoint = bios32_map + map_offset;
-
- cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE;
-
- set_memory_x((unsigned long)bios32_map, 2);
- asminline_call(&cmn_regs, bios32_entrypoint);
-
- if (cmn_regs.u1.ral != 0) {
- pr_warn("Call succeeded but with an error: 0x%x\n",
- cmn_regs.u1.ral);
- } else {
- physical_bios_base = cmn_regs.u2.rebx;
- physical_bios_offset = cmn_regs.u4.redx;
- cru_length = cmn_regs.u3.recx;
- cru_physical_address =
- physical_bios_base + physical_bios_offset;
-
- /* If the values look OK, then map it in. */
- if ((physical_bios_base + physical_bios_offset)) {
- cru_rom_addr =
- ioremap(cru_physical_address, cru_length);
- if (cru_rom_addr) {
- set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
- (cru_length + PAGE_SIZE - 1) >> PAGE_SHIFT);
- retval = 0;
- }
- }
-
- pr_debug("CRU Base Address: 0x%lx\n", physical_bios_base);
- pr_debug("CRU Offset Address: 0x%lx\n", physical_bios_offset);
- pr_debug("CRU Length: 0x%lx\n", cru_length);
- pr_debug("CRU Mapped Address: %p\n", &cru_rom_addr);
- }
- iounmap(bios32_map);
- return retval;
-}
-
-/*
- * bios_checksum
- */
-static int bios_checksum(const char __iomem *ptr, int len)
-{
- char sum = 0;
- int i;
-
- /*
- * calculate checksum of size bytes. This should add up
- * to zero if we have a valid header.
- */
- for (i = 0; i < len; i++)
- sum += ptr[i];
-
- return ((sum == 0) && (len > 0));
-}
-
-/*
- * bios32_present
- *
- * Routine Description:
- * This function finds the 32-bit BIOS Service Directory
- *
- * Return Value:
- * 0 : SUCCESS
- * <0 : FAILURE
- */
-static int bios32_present(const char __iomem *p)
-{
- struct bios32_service_dir *bios_32_ptr;
- int length;
- unsigned long map_entry, map_offset;
-
- bios_32_ptr = (struct bios32_service_dir *) p;
-
- /*
- * Search for signature by checking equal to the swizzled value
- * instead of calling another routine to perform a strcmp.
- */
- if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) {
- length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN;
- if (bios_checksum(p, length)) {
- /*
- * According to the spec, we're looking for the
- * first 4KB-aligned address below the entrypoint
- * listed in the header. The Service Directory code
- * is guaranteed to occupy no more than 2 4KB pages.
- */
- map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1);
- map_offset = bios_32_ptr->entry_point - map_entry;
-
- return cru_detect(map_entry, map_offset);
- }
- }
- return -ENODEV;
-}
-
-static int detect_cru_service(void)
-{
- char __iomem *p, *q;
- int rc = -1;
-
- /*
- * Search from 0x0f0000 through 0x0fffff, inclusive.
- */
- p = ioremap(PCI_ROM_BASE1, ROM_SIZE);
- if (p == NULL)
- return -ENOMEM;
-
- for (q = p; q < p + ROM_SIZE; q += 16) {
- rc = bios32_present(q);
- if (!rc)
- break;
- }
- iounmap(p);
- return rc;
-}
-/* ------------------------------------------------------------------------- */
-#endif /* CONFIG_X86_32 */
-#ifdef CONFIG_X86_64
-/* --64 Bit Bios------------------------------------------------------------ */
-
-#define HPWDT_ARCH 64
-
-asm(".text \n\t"
- ".align 4 \n\t"
- ".globl asminline_call \n\t"
- ".type asminline_call, @function \n\t"
- "asminline_call: \n\t"
- FRAME_BEGIN
- "pushq %rax \n\t"
- "pushq %rbx \n\t"
- "pushq %rdx \n\t"
- "pushq %r12 \n\t"
- "pushq %r9 \n\t"
- "movq %rsi, %r12 \n\t"
- "movq %rdi, %r9 \n\t"
- "movl 4(%r9),%ebx \n\t"
- "movl 8(%r9),%ecx \n\t"
- "movl 12(%r9),%edx \n\t"
- "movl 16(%r9),%esi \n\t"
- "movl 20(%r9),%edi \n\t"
- "movl (%r9),%eax \n\t"
- "call *%r12 \n\t"
- "pushfq \n\t"
- "popq %r12 \n\t"
- "movl %eax, (%r9) \n\t"
- "movl %ebx, 4(%r9) \n\t"
- "movl %ecx, 8(%r9) \n\t"
- "movl %edx, 12(%r9) \n\t"
- "movl %esi, 16(%r9) \n\t"
- "movl %edi, 20(%r9) \n\t"
- "movq %r12, %rax \n\t"
- "movl %eax, 28(%r9) \n\t"
- "popq %r9 \n\t"
- "popq %r12 \n\t"
- "popq %rdx \n\t"
- "popq %rbx \n\t"
- "popq %rax \n\t"
- FRAME_END
- "ret \n\t"
- ".previous");
-
-/*
- * dmi_find_cru
- *
- * Routine Description:
- * This function checks whether or not a SMBIOS/DMI record is
- * the 64bit CRU info or not
- */
-static void dmi_find_cru(const struct dmi_header *dm, void *dummy)
-{
- struct smbios_cru64_info *smbios_cru64_ptr;
- unsigned long cru_physical_address;
-
- if (dm->type == SMBIOS_CRU64_INFORMATION) {
- smbios_cru64_ptr = (struct smbios_cru64_info *) dm;
- if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) {
- cru_physical_address =
- smbios_cru64_ptr->physical_address +
- smbios_cru64_ptr->double_offset;
- cru_rom_addr = ioremap(cru_physical_address,
- smbios_cru64_ptr->double_length);
- set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK,
- smbios_cru64_ptr->double_length >> PAGE_SHIFT);
- }
- }
-}
-
-static int detect_cru_service(void)
-{
- cru_rom_addr = NULL;
-
- dmi_walk(dmi_find_cru, NULL);
-
- /* if cru_rom_addr has been set then we found a CRU service */
- return ((cru_rom_addr != NULL) ? 0 : -ENODEV);
-}
-/* ------------------------------------------------------------------------- */
-#endif /* CONFIG_X86_64 */
-#endif /* CONFIG_HPWDT_NMI_DECODING */
/*
* Watchdog operations
@@ -475,32 +103,22 @@
}
#ifdef CONFIG_HPWDT_NMI_DECODING
+static int hpwdt_my_nmi(void)
+{
+ return ioread8(hpwdt_nmistat) & 0x6;
+}
+
/*
* NMI Handler
*/
static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
{
- unsigned long rom_pl;
- static int die_nmi_called;
-
- if (!hpwdt_nmi_decoding)
+ if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi())
return NMI_DONE;
- spin_lock_irqsave(&rom_lock, rom_pl);
- if (!die_nmi_called && !is_icru && !is_uefi)
- asminline_call(&cmn_regs, cru_rom_addr);
- die_nmi_called = 1;
- spin_unlock_irqrestore(&rom_lock, rom_pl);
-
if (allow_kdump)
hpwdt_stop();
- if (!is_icru && !is_uefi) {
- if (cmn_regs.u1.ral == 0) {
- nmi_panic(regs, "An NMI occurred, but unable to determine source.\n");
- return NMI_HANDLED;
- }
- }
nmi_panic(regs, "An NMI occurred. Depending on your system the reason "
"for the NMI is logged in any one of the following "
"resources:\n"
@@ -666,84 +284,11 @@
* Init & Exit
*/
-#ifdef CONFIG_HPWDT_NMI_DECODING
-#ifdef CONFIG_X86_LOCAL_APIC
-static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
-{
- /*
- * If nmi_watchdog is turned off then we can turn on
- * our nmi decoding capability.
- */
- hpwdt_nmi_decoding = 1;
-}
-#else
-static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
-{
- dev_warn(&dev->dev, "NMI decoding is disabled. "
- "Your kernel does not support a NMI Watchdog.\n");
-}
-#endif /* CONFIG_X86_LOCAL_APIC */
-
-/*
- * dmi_find_icru
- *
- * Routine Description:
- * This function checks whether or not we are on an iCRU-based server.
- * This check is independent of architecture and needs to be made for
- * any ProLiant system.
- */
-static void dmi_find_icru(const struct dmi_header *dm, void *dummy)
-{
- struct smbios_proliant_info *smbios_proliant_ptr;
-
- if (dm->type == SMBIOS_ICRU_INFORMATION) {
- smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
- if (smbios_proliant_ptr->misc_features & 0x01)
- is_icru = 1;
- if (smbios_proliant_ptr->misc_features & 0x408)
- is_uefi = 1;
- }
-}
static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
{
+#ifdef CONFIG_HPWDT_NMI_DECODING
int retval;
-
- /*
- * On typical CRU-based systems we need to map that service in
- * the BIOS. For 32 bit Operating Systems we need to go through
- * the 32 Bit BIOS Service Directory. For 64 bit Operating
- * Systems we get that service through SMBIOS.
- *
- * On systems that support the new iCRU service all we need to
- * do is call dmi_walk to get the supported flag value and skip
- * the old cru detect code.
- */
- dmi_walk(dmi_find_icru, NULL);
- if (!is_icru && !is_uefi) {
-
- /*
- * We need to map the ROM to get the CRU service.
- * For 32 bit Operating Systems we need to go through the 32 Bit
- * BIOS Service Directory
- * For 64 bit Operating Systems we get that service through SMBIOS.
- */
- retval = detect_cru_service();
- if (retval < 0) {
- dev_warn(&dev->dev,
- "Unable to detect the %d Bit CRU Service.\n",
- HPWDT_ARCH);
- return retval;
- }
-
- /*
- * We know this is the only CRU call we need to make so lets keep as
- * few instructions as possible once the NMI comes in.
- */
- cmn_regs.u1.rah = 0x0D;
- cmn_regs.u1.ral = 0x02;
- }
-
/*
* Only one function can register for NMI_UNKNOWN
*/
@@ -771,33 +316,19 @@
dev_warn(&dev->dev,
"Unable to register a die notifier (err=%d).\n",
retval);
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
return retval;
-}
-
-static void hpwdt_exit_nmi_decoding(void)
-{
- unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
- unregister_nmi_handler(NMI_SERR, "hpwdt");
- unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
- if (cru_rom_addr)
- iounmap(cru_rom_addr);
-}
-#else /* !CONFIG_HPWDT_NMI_DECODING */
-static void hpwdt_check_nmi_decoding(struct pci_dev *dev)
-{
-}
-
-static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
-{
+#endif /* CONFIG_HPWDT_NMI_DECODING */
return 0;
}
static void hpwdt_exit_nmi_decoding(void)
{
+#ifdef CONFIG_HPWDT_NMI_DECODING
+ unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
+ unregister_nmi_handler(NMI_SERR, "hpwdt");
+ unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
+#endif
}
-#endif /* CONFIG_HPWDT_NMI_DECODING */
static int hpwdt_init_one(struct pci_dev *dev,
const struct pci_device_id *ent)
@@ -805,11 +336,6 @@
int retval;
/*
- * Check if we can do NMI decoding or not
- */
- hpwdt_check_nmi_decoding(dev);
-
- /*
* First let's find out if we are on an iLO2+ server. We will
* not run on a legacy ASM box.
* So we only support the G5 ProLiant servers and higher.
@@ -842,6 +368,7 @@
retval = -ENOMEM;
goto error_pci_iomap;
}
+ hpwdt_nmistat = pci_mem_addr + 0x6e;
hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72;
@@ -912,6 +439,6 @@
#ifdef CONFIG_HPWDT_NMI_DECODING
module_param(allow_kdump, int, 0);
MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
-#endif /* !CONFIG_HPWDT_NMI_DECODING */
+#endif /* CONFIG_HPWDT_NMI_DECODING */
module_pci_driver(hpwdt_driver);
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 32930a0..977fe74 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -760,6 +760,7 @@
{
struct watchdog_core_data *wd_data;
struct watchdog_device *wdd;
+ bool hw_running;
int err;
/* Get the corresponding watchdog device */
@@ -779,7 +780,8 @@
* If the /dev/watchdog device is open, we don't want the module
* to be unloaded.
*/
- if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) {
+ hw_running = watchdog_hw_running(wdd);
+ if (!hw_running && !try_module_get(wdd->ops->owner)) {
err = -EBUSY;
goto out_clear;
}
@@ -790,7 +792,7 @@
file->private_data = wd_data;
- if (!watchdog_hw_running(wdd))
+ if (!hw_running)
kref_get(&wd_data->kref);
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
index 6d1fbda..0da9943 100644
--- a/drivers/watchdog/wdat_wdt.c
+++ b/drivers/watchdog/wdat_wdt.c
@@ -392,7 +392,7 @@
memset(&r, 0, sizeof(r));
r.start = gas->address;
- r.end = r.start + gas->access_width;
+ r.end = r.start + gas->access_width - 1;
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
r.flags = IORESOURCE_MEM;
} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 8feab810..7bd882d 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -7,8 +7,10 @@
nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_features.o := $(nostackp)
+ifndef CONFIG_LTO_CLANG
CFLAGS_efi.o += -fshort-wchar
LDFLAGS += $(call ld-option, --no-wchar-size-warning)
+endif
dom0-$(CONFIG_ARM64) += arm-device.o
dom0-$(CONFIG_PCI) += pci.o
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 79b8ab4..910b5d4 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -378,10 +378,8 @@
}
range = 0;
while (range < pages) {
- if (map->unmap_ops[offset+range].handle == -1) {
- range--;
+ if (map->unmap_ops[offset+range].handle == -1)
break;
- }
range++;
}
err = __unmap_grant_pages(map, offset, range);
@@ -1079,8 +1077,10 @@
out_unlock_put:
mutex_unlock(&priv->lock);
out_put_map:
- if (use_ptemod)
+ if (use_ptemod) {
map->vma = NULL;
+ unmap_grant_pages(map, 0, map->count);
+ }
gntdev_put_map(priv, map);
return err;
}
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 7237297..37f97ba 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -123,11 +123,10 @@
/*
* read page from file, directory or symlink, given a key to use
*/
-int afs_page_filler(void *data, struct page *page)
+static int __afs_page_filler(struct key *key, struct page *page)
{
struct inode *inode = page->mapping->host;
struct afs_vnode *vnode = AFS_FS_I(inode);
- struct key *key = data;
size_t len;
off_t offset;
int ret;
@@ -209,6 +208,13 @@
return ret;
}
+int afs_page_filler(struct file *data, struct page *page)
+{
+ struct key *key = (struct key *)data;
+
+ return __afs_page_filler(key, page);
+}
+
/*
* read page from file, directory or symlink, given a file to nominate the key
* to be used
@@ -221,14 +227,14 @@
if (file) {
key = file->private_data;
ASSERT(key != NULL);
- ret = afs_page_filler(key, page);
+ ret = __afs_page_filler(key, page);
} else {
struct inode *inode = page->mapping->host;
key = afs_request_key(AFS_FS_S(inode->i_sb)->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
} else {
- ret = afs_page_filler(key, page);
+ ret = __afs_page_filler(key, page);
key_put(key);
}
}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index dd98dcd..b4165cc 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -497,7 +497,7 @@
extern int afs_open(struct inode *, struct file *);
extern int afs_release(struct inode *, struct file *);
-extern int afs_page_filler(void *, struct page *);
+extern int afs_page_filler(struct file *, struct page *);
/*
* flock.c
diff --git a/fs/aio.c b/fs/aio.c
index 0fcb49a..0606f03 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -68,9 +68,9 @@
#define AIO_RING_PAGES 8
struct kioctx_table {
- struct rcu_head rcu;
- unsigned nr;
- struct kioctx *table[];
+ struct rcu_head rcu;
+ unsigned nr;
+ struct kioctx __rcu *table[];
};
struct kioctx_cpu {
@@ -115,7 +115,8 @@
struct page **ring_pages;
long nr_pages;
- struct work_struct free_work;
+ struct rcu_head free_rcu;
+ struct work_struct free_work; /* see free_ioctx() */
/*
* signals when all in-flight requests are done
@@ -329,7 +330,7 @@
for (i = 0; i < table->nr; i++) {
struct kioctx *ctx;
- ctx = table->table[i];
+ ctx = rcu_dereference(table->table[i]);
if (ctx && ctx->aio_ring_file == file) {
if (!atomic_read(&ctx->dead)) {
ctx->user_id = ctx->mmap_base = vma->vm_start;
@@ -581,6 +582,12 @@
return cancel(&kiocb->common);
}
+/*
+ * free_ioctx() should be RCU delayed to synchronize against the RCU
+ * protected lookup_ioctx() and also needs process context to call
+ * aio_free_ring(), so the double bouncing through kioctx->free_rcu and
+ * ->free_work.
+ */
static void free_ioctx(struct work_struct *work)
{
struct kioctx *ctx = container_of(work, struct kioctx, free_work);
@@ -594,6 +601,14 @@
kmem_cache_free(kioctx_cachep, ctx);
}
+static void free_ioctx_rcufn(struct rcu_head *head)
+{
+ struct kioctx *ctx = container_of(head, struct kioctx, free_rcu);
+
+ INIT_WORK(&ctx->free_work, free_ioctx);
+ schedule_work(&ctx->free_work);
+}
+
static void free_ioctx_reqs(struct percpu_ref *ref)
{
struct kioctx *ctx = container_of(ref, struct kioctx, reqs);
@@ -602,8 +617,8 @@
if (ctx->rq_wait && atomic_dec_and_test(&ctx->rq_wait->count))
complete(&ctx->rq_wait->comp);
- INIT_WORK(&ctx->free_work, free_ioctx);
- schedule_work(&ctx->free_work);
+ /* Synchronize against RCU protected table->table[] dereferences */
+ call_rcu(&ctx->free_rcu, free_ioctx_rcufn);
}
/*
@@ -644,9 +659,9 @@
while (1) {
if (table)
for (i = 0; i < table->nr; i++)
- if (!table->table[i]) {
+ if (!rcu_access_pointer(table->table[i])) {
ctx->id = i;
- table->table[i] = ctx;
+ rcu_assign_pointer(table->table[i], ctx);
spin_unlock(&mm->ioctx_lock);
/* While kioctx setup is in progress,
@@ -821,11 +836,11 @@
}
table = rcu_dereference_raw(mm->ioctx_table);
- WARN_ON(ctx != table->table[ctx->id]);
- table->table[ctx->id] = NULL;
+ WARN_ON(ctx != rcu_access_pointer(table->table[ctx->id]));
+ RCU_INIT_POINTER(table->table[ctx->id], NULL);
spin_unlock(&mm->ioctx_lock);
- /* percpu_ref_kill() will do the necessary call_rcu() */
+ /* free_ioctx_reqs() will do the necessary RCU synchronization */
wake_up_all(&ctx->wait);
/*
@@ -867,7 +882,8 @@
skipped = 0;
for (i = 0; i < table->nr; ++i) {
- struct kioctx *ctx = table->table[i];
+ struct kioctx *ctx =
+ rcu_dereference_protected(table->table[i], true);
if (!ctx) {
skipped++;
@@ -1056,7 +1072,7 @@
if (!table || id >= table->nr)
goto out;
- ctx = table->table[id];
+ ctx = rcu_dereference(table->table[id]);
if (ctx && ctx->user_id == ctx_id) {
percpu_ref_get(&ctx->users);
ret = ctx;
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index a11f731..6182d69 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -746,7 +746,7 @@
autofs4_del_active(dentry);
- inode = autofs4_get_inode(dir->i_sb, S_IFDIR | 0555);
+ inode = autofs4_get_inode(dir->i_sb, S_IFDIR | mode);
if (!inode)
return -ENOMEM;
d_add(dentry, inode);
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 8d8370d..1ba49eb 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -114,13 +114,17 @@
int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
int ret;
+ umode_t old_mode = inode->i_mode;
if (type == ACL_TYPE_ACCESS && acl) {
ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
if (ret)
return ret;
}
- return __btrfs_set_acl(NULL, inode, acl, type);
+ ret = __btrfs_set_acl(NULL, inode, acl, type);
+ if (ret)
+ inode->i_mode = old_mode;
+ return ret;
}
/*
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index f6ba165..409b123 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -2486,10 +2486,8 @@
if (p->reada != READA_NONE)
reada_for_search(root, p, level, slot, key->objectid);
- btrfs_release_path(p);
-
ret = -EAGAIN;
- tmp = read_tree_block(root, blocknr, 0);
+ tmp = read_tree_block(root, blocknr, gen);
if (!IS_ERR(tmp)) {
/*
* If the read above didn't mark this buffer up to date,
@@ -2503,6 +2501,8 @@
} else {
ret = PTR_ERR(tmp);
}
+
+ btrfs_release_path(p);
return ret;
}
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 8ed05d9..03ac3ab 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2453,7 +2453,7 @@
if (!uptodate) {
ClearPageUptodate(page);
SetPageError(page);
- ret = ret < 0 ? ret : -EIO;
+ ret = err < 0 ? err : -EIO;
mapping_set_error(page->mapping, ret);
}
}
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 3286a6e..c95ff09 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2817,8 +2817,10 @@
}
ret = btrfs_qgroup_reserve_data(inode, cur_offset,
last_byte - cur_offset);
- if (ret < 0)
+ if (ret < 0) {
+ free_extent_map(em);
break;
+ }
} else {
/*
* Do not need to reserve unwritten extent for this
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index d196ce4..ffd5831 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -567,8 +567,10 @@
PAGE_SET_WRITEBACK |
page_error_op |
PAGE_END_WRITEBACK);
- btrfs_free_reserved_data_space_noquota(inode, start,
- end - start + 1);
+ if (ret == 0)
+ btrfs_free_reserved_data_space_noquota(inode,
+ start,
+ end - start + 1);
goto free_pages_out;
}
}
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 9a47b55..d040afc 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -5156,13 +5156,19 @@
while (key.offset < ekey->offset + left_len) {
ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
right_type = btrfs_file_extent_type(eb, ei);
- if (right_type != BTRFS_FILE_EXTENT_REG) {
+ if (right_type != BTRFS_FILE_EXTENT_REG &&
+ right_type != BTRFS_FILE_EXTENT_INLINE) {
ret = 0;
goto out;
}
right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
- right_len = btrfs_file_extent_num_bytes(eb, ei);
+ if (right_type == BTRFS_FILE_EXTENT_INLINE) {
+ right_len = btrfs_file_extent_inline_len(eb, slot, ei);
+ right_len = PAGE_ALIGN(right_len);
+ } else {
+ right_len = btrfs_file_extent_num_bytes(eb, ei);
+ }
right_offset = btrfs_file_extent_offset(eb, ei);
right_gen = btrfs_file_extent_generation(eb, ei);
@@ -5176,6 +5182,19 @@
goto out;
}
+ /*
+ * We just wanted to see if when we have an inline extent, what
+ * follows it is a regular extent (wanted to check the above
+ * condition for inline extents too). This should normally not
+ * happen but it's possible for example when we have an inline
+ * compressed extent representing data with a size matching
+ * the page size (currently the same as sector size).
+ */
+ if (right_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = 0;
+ goto out;
+ }
+
left_offset_fixed = left_offset;
if (key.offset < ekey->offset) {
/* Fix the right offset for 2a and 7. */
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 5539f0b..c65350e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -3664,7 +3664,7 @@
src_offset = btrfs_item_ptr_offset(src, start_slot + i);
- if ((i == (nr - 1)))
+ if (i == nr - 1)
last_key = ins_keys[i];
if (ins_keys[i].type == BTRFS_INODE_ITEM_KEY) {
@@ -4614,6 +4614,7 @@
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
u64 logged_isize = 0;
bool need_log_inode_item = true;
+ bool xattrs_logged = false;
path = btrfs_alloc_path();
if (!path)
@@ -4918,6 +4919,7 @@
err = btrfs_log_all_xattrs(trans, root, inode, path, dst_path);
if (err)
goto out_unlock;
+ xattrs_logged = true;
if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) {
btrfs_release_path(path);
btrfs_release_path(dst_path);
@@ -4930,6 +4932,11 @@
btrfs_release_path(dst_path);
if (need_log_inode_item) {
err = log_inode_item(trans, log, dst_path, inode);
+ if (!err && !xattrs_logged) {
+ err = btrfs_log_all_xattrs(trans, root, inode, path,
+ dst_path);
+ btrfs_release_path(path);
+ }
if (err)
goto out_unlock;
}
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index fad7b37..d95eddc 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -583,6 +583,7 @@
btrfs_sysfs_remove_fsid(fs_devs);
list_del(&fs_devs->list);
free_fs_devices(fs_devs);
+ break;
} else {
fs_devs->num_devices--;
list_del(&dev->dev_list);
@@ -3764,6 +3765,7 @@
struct btrfs_ioctl_balance_args *bargs)
{
struct btrfs_fs_info *fs_info = bctl->fs_info;
+ u64 meta_target, data_target;
u64 allowed;
int mixed = 0;
int ret;
@@ -3860,11 +3862,16 @@
}
} while (read_seqretry(&fs_info->profiles_lock, seq));
- if (btrfs_get_num_tolerated_disk_barrier_failures(bctl->meta.target) <
- btrfs_get_num_tolerated_disk_barrier_failures(bctl->data.target)) {
+ /* if we're not converting, the target field is uninitialized */
+ meta_target = (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
+ bctl->meta.target : fs_info->avail_metadata_alloc_bits;
+ data_target = (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
+ bctl->data.target : fs_info->avail_data_alloc_bits;
+ if (btrfs_get_num_tolerated_disk_barrier_failures(meta_target) <
+ btrfs_get_num_tolerated_disk_barrier_failures(data_target)) {
btrfs_warn(fs_info,
"metadata profile 0x%llx has lower redundancy than data profile 0x%llx",
- bctl->meta.target, bctl->data.target);
+ meta_target, data_target);
}
if (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) {
@@ -3959,6 +3966,15 @@
return 0;
}
+ /*
+ * A ro->rw remount sequence should continue with the paused balance
+ * regardless of who pauses it, system or the user as of now, so set
+ * the resume flag.
+ */
+ spin_lock(&fs_info->balance_lock);
+ fs_info->balance_ctl->flags |= BTRFS_BALANCE_RESUME;
+ spin_unlock(&fs_info->balance_lock);
+
tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance");
return PTR_ERR_OR_ZERO(tsk);
}
@@ -4748,10 +4764,13 @@
if (devs_max && ndevs > devs_max)
ndevs = devs_max;
/*
- * the primary goal is to maximize the number of stripes, so use as many
- * devices as possible, even if the stripes are not maximum sized.
+ * The primary goal is to maximize the number of stripes, so use as
+ * many devices as possible, even if the stripes are not maximum sized.
+ *
+ * The DUP profile stores more than one stripe per device, the
+ * max_avail is the total size so we have to adjust.
*/
- stripe_size = devices_info[ndevs-1].max_avail;
+ stripe_size = div_u64(devices_info[ndevs - 1].max_avail, dev_stripes);
num_stripes = ndevs * dev_stripes;
/*
@@ -4791,8 +4810,6 @@
stripe_size = devices_info[ndevs-1].max_avail;
}
- stripe_size = div_u64(stripe_size, dev_stripes);
-
/* align to BTRFS_STRIPE_LEN */
stripe_size = div_u64(stripe_size, raid_stripe_len);
stripe_size *= raid_stripe_len;
diff --git a/fs/buffer.c b/fs/buffer.c
index 5d8f496..3a8064d 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1455,12 +1455,48 @@
return 0;
}
+static void __evict_bh_lru(void *arg)
+{
+ struct bh_lru *b = &get_cpu_var(bh_lrus);
+ struct buffer_head *bh = arg;
+ int i;
+
+ for (i = 0; i < BH_LRU_SIZE; i++) {
+ if (b->bhs[i] == bh) {
+ brelse(b->bhs[i]);
+ b->bhs[i] = NULL;
+ goto out;
+ }
+ }
+out:
+ put_cpu_var(bh_lrus);
+}
+
+static bool bh_exists_in_lru(int cpu, void *arg)
+{
+ struct bh_lru *b = per_cpu_ptr(&bh_lrus, cpu);
+ struct buffer_head *bh = arg;
+ int i;
+
+ for (i = 0; i < BH_LRU_SIZE; i++) {
+ if (b->bhs[i] == bh)
+ return 1;
+ }
+
+ return 0;
+
+}
void invalidate_bh_lrus(void)
{
on_each_cpu_cond(has_bh_in_lru, invalidate_bh_lru, NULL, 1, GFP_KERNEL);
}
EXPORT_SYMBOL_GPL(invalidate_bh_lrus);
+static void evict_bh_lrus(struct buffer_head *bh)
+{
+ on_each_cpu_cond(bh_exists_in_lru, __evict_bh_lru, bh, 1, GFP_ATOMIC);
+}
+
void set_bh_page(struct buffer_head *bh,
struct page *page, unsigned long offset)
{
@@ -3250,8 +3286,15 @@
do {
if (buffer_write_io_error(bh) && page->mapping)
mapping_set_error(page->mapping, -EIO);
- if (buffer_busy(bh))
- goto failed;
+ if (buffer_busy(bh)) {
+ /*
+ * Check if the busy failure was due to an
+ * outstanding LRU reference
+ */
+ evict_bh_lrus(bh);
+ if (buffer_busy(bh))
+ goto failed;
+ }
bh = bh->b_this_page;
} while (bh != head);
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index ca3f630..e7ddb23 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -598,7 +598,8 @@
struct ceph_aio_request {
struct kiocb *iocb;
size_t total_len;
- int write;
+ bool write;
+ bool should_dirty;
int error;
struct list_head osd_reqs;
unsigned num_reqs;
@@ -708,7 +709,7 @@
}
}
- ceph_put_page_vector(osd_data->pages, num_pages, !aio_req->write);
+ ceph_put_page_vector(osd_data->pages, num_pages, aio_req->should_dirty);
ceph_osdc_put_request(req);
if (rc < 0)
@@ -890,6 +891,7 @@
size_t count = iov_iter_count(iter);
loff_t pos = iocb->ki_pos;
bool write = iov_iter_rw(iter) == WRITE;
+ bool should_dirty = !write && iter_is_iovec(iter);
if (write && ceph_snap(file_inode(file)) != CEPH_NOSNAP)
return -EROFS;
@@ -954,6 +956,7 @@
if (aio_req) {
aio_req->iocb = iocb;
aio_req->write = write;
+ aio_req->should_dirty = should_dirty;
INIT_LIST_HEAD(&aio_req->osd_reqs);
if (write) {
aio_req->mtime = mtime;
@@ -1012,7 +1015,7 @@
len = ret;
}
- ceph_put_page_vector(pages, num_pages, !write);
+ ceph_put_page_vector(pages, num_pages, should_dirty);
ceph_osdc_put_request(req);
if (ret < 0)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 7b496a4..4ed4736 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1412,6 +1412,7 @@
#define CIFS_FATTR_NEED_REVAL 0x4
#define CIFS_FATTR_INO_COLLISION 0x8
#define CIFS_FATTR_UNKNOWN_NLINK 0x10
+#define CIFS_FATTR_FAKE_ROOT_INO 0x20
struct cifs_fattr {
u32 cf_flags;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index d9cbda2..331ddd0 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -673,6 +673,9 @@
goto mknod_out;
}
+ if (!S_ISCHR(mode) && !S_ISBLK(mode))
+ goto mknod_out;
+
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
goto mknod_out;
@@ -681,10 +684,8 @@
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
- kfree(full_path);
rc = -ENOMEM;
- free_xid(xid);
- return rc;
+ goto mknod_out;
}
if (backup_cred(cifs_sb))
@@ -731,7 +732,7 @@
pdev->minor = cpu_to_le64(MINOR(device_number));
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
&bytes_written, iov, 1);
- } /* else if (S_ISFIFO) */
+ }
tcon->ses->server->ops->close(xid, tcon, &fid);
d_drop(direntry);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 02e403a..49eeed2 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -589,7 +589,7 @@
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
- down_read(&cinode->lock_sem);
+ down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING);
if (cinode->can_cache_brlcks) {
/* can cache locks - no need to relock */
up_read(&cinode->lock_sem);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 7ab5be7..24c19eb 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -701,6 +701,18 @@
return rc;
}
+/* Simple function to return a 64 bit hash of string. Rarely called */
+static __u64 simple_hashstr(const char *str)
+{
+ const __u64 hash_mult = 1125899906842597L; /* a big enough prime */
+ __u64 hash = 0;
+
+ while (*str)
+ hash = (hash + (__u64) *str++) * hash_mult;
+
+ return hash;
+}
+
int
cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb, int xid,
@@ -810,6 +822,14 @@
tmprc);
fattr.cf_uniqueid = iunique(sb, ROOT_I);
cifs_autodisable_serverino(cifs_sb);
+ } else if ((fattr.cf_uniqueid == 0) &&
+ strlen(full_path) == 0) {
+ /* some servers ret bad root ino ie 0 */
+ cifs_dbg(FYI, "Invalid (0) inodenum\n");
+ fattr.cf_flags |=
+ CIFS_FATTR_FAKE_ROOT_INO;
+ fattr.cf_uniqueid =
+ simple_hashstr(tcon->treeName);
}
}
} else
@@ -826,6 +846,16 @@
&fattr.cf_uniqueid, data);
if (tmprc)
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ else if ((fattr.cf_uniqueid == 0) &&
+ strlen(full_path) == 0) {
+ /*
+ * Reuse existing root inode num since
+ * inum zero for root causes ls of . and .. to
+ * not be returned
+ */
+ cifs_dbg(FYI, "Srv ret 0 inode num for root\n");
+ fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ }
} else
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
}
@@ -887,6 +917,9 @@
}
cgii_exit:
+ if ((*inode) && ((*inode)->i_ino == 0))
+ cifs_dbg(FYI, "inode number of zero returned\n");
+
kfree(buf);
cifs_put_tlink(tlink);
return rc;
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index abae6dd..cc88f4f 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -980,10 +980,10 @@
cifs_dbg(VFS, "illegal hours %d\n", st->Hours);
days = sd->Day;
month = sd->Month;
- if ((days > 31) || (month > 12)) {
+ if (days < 1 || days > 31 || month < 1 || month > 12) {
cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
- if (month > 12)
- month = 12;
+ days = clamp(days, 1, 31);
+ month = clamp(month, 1, 12);
}
month -= 1;
days += total_days_of_prev_months[month];
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 538d9b5..c3db2a8 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -344,13 +344,12 @@
/* BB is NTLMV2 session security format easier to use here? */
flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
- if (ses->server->sign) {
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
+ NTLMSSP_NEGOTIATE_SEAL;
+ if (ses->server->sign)
flags |= NTLMSSP_NEGOTIATE_SIGN;
- if (!ses->server->session_estab ||
- ses->ntlmssp->sesskey_per_smbsess)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
- }
+ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
sec_blob->NegotiateFlags = cpu_to_le32(flags);
@@ -407,13 +406,12 @@
flags = NTLMSSP_NEGOTIATE_56 |
NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
- if (ses->server->sign) {
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
+ NTLMSSP_NEGOTIATE_SEAL;
+ if (ses->server->sign)
flags |= NTLMSSP_NEGOTIATE_SIGN;
- if (!ses->server->session_estab ||
- ses->ntlmssp->sesskey_per_smbsess)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
- }
+ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 94c4c19..44b7ccb 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -707,15 +707,13 @@
struct cifs_ses *ses = sess_data->ses;
mutex_lock(&ses->server->srv_mutex);
- if (ses->server->sign && ses->server->ops->generate_signingkey) {
+ if (ses->server->ops->generate_signingkey) {
rc = ses->server->ops->generate_signingkey(ses);
- kfree(ses->auth_key.response);
- ses->auth_key.response = NULL;
if (rc) {
cifs_dbg(FYI,
"SMB3 session key generation failed\n");
mutex_unlock(&ses->server->srv_mutex);
- goto keygen_exit;
+ return rc;
}
}
if (!ses->server->session_estab) {
@@ -729,12 +727,6 @@
ses->status = CifsGood;
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock);
-
-keygen_exit:
- if (!ses->server->sign) {
- kfree(ses->auth_key.response);
- ses->auth_key.response = NULL;
- }
return rc;
}
@@ -1159,15 +1151,19 @@
goto tcon_exit;
}
- if (rsp->ShareType & SMB2_SHARE_TYPE_DISK)
+ switch (rsp->ShareType) {
+ case SMB2_SHARE_TYPE_DISK:
cifs_dbg(FYI, "connection to disk share\n");
- else if (rsp->ShareType & SMB2_SHARE_TYPE_PIPE) {
+ break;
+ case SMB2_SHARE_TYPE_PIPE:
tcon->ipc = true;
cifs_dbg(FYI, "connection to pipe share\n");
- } else if (rsp->ShareType & SMB2_SHARE_TYPE_PRINT) {
- tcon->print = true;
+ break;
+ case SMB2_SHARE_TYPE_PRINT:
+ tcon->ipc = true;
cifs_dbg(FYI, "connection to printer\n");
- } else {
+ break;
+ default:
cifs_dbg(VFS, "unknown share type %d\n", rsp->ShareType);
rc = -EOPNOTSUPP;
goto tcon_error_exit;
@@ -1712,6 +1708,9 @@
} else
iov[0].iov_len = get_rfc1002_length(req) + 4;
+ /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
+ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
+ req->hdr.Flags |= SMB2_FLAGS_SIGNED;
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index f2d7402..93c8e4a 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -833,7 +833,7 @@
*/
#define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff)
-#define COMPATIBLE_IOCTL(cmd) XFORM(cmd),
+#define COMPATIBLE_IOCTL(cmd) XFORM((u32)cmd),
/* ioctl should not be warned about even if it's not implemented.
Valid reasons to use this:
- It is implemented with ->compat_ioctl on some device, but programs
diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile
index cb49698..cc42e5e 100644
--- a/fs/crypto/Makefile
+++ b/fs/crypto/Makefile
@@ -1,4 +1,7 @@
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
-fscrypto-y := crypto.o fname.o hooks.o keyinfo.o policy.o
+ccflags-y += -Ifs/ext4
+ccflags-y += -Ifs/f2fs
+
+fscrypto-y := crypto.o fname.o hooks.o keyinfo.o policy.o fscrypt_ice.o
fscrypto-$(CONFIG_BLOCK) += bio.o
diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c
index 2596c9a..c629e97 100644
--- a/fs/crypto/bio.c
+++ b/fs/crypto/bio.c
@@ -25,42 +25,55 @@
#include <linux/namei.h>
#include "fscrypt_private.h"
-/*
- * Call fscrypt_decrypt_page on every single page, reusing the encryption
- * context.
- */
-static void completion_pages(struct work_struct *work)
+static void __fscrypt_decrypt_bio(struct bio *bio, bool done)
{
- struct fscrypt_ctx *ctx =
- container_of(work, struct fscrypt_ctx, r.work);
- struct bio *bio = ctx->r.bio;
struct bio_vec *bv;
int i;
bio_for_each_segment_all(bv, bio, i) {
struct page *page = bv->bv_page;
- int ret = fscrypt_decrypt_page(page->mapping->host, page,
- PAGE_SIZE, 0, page->index);
- if (ret) {
- WARN_ON_ONCE(1);
- SetPageError(page);
- } else {
+ if (fscrypt_using_hardware_encryption(page->mapping->host)) {
SetPageUptodate(page);
+ } else {
+ int ret = fscrypt_decrypt_page(page->mapping->host,
+ page, PAGE_SIZE, 0, page->index);
+ if (ret) {
+ WARN_ON_ONCE(1);
+ SetPageError(page);
+ } else if (done) {
+ SetPageUptodate(page);
+ }
}
- unlock_page(page);
+ if (done)
+ unlock_page(page);
}
+}
+
+void fscrypt_decrypt_bio(struct bio *bio)
+{
+ __fscrypt_decrypt_bio(bio, false);
+}
+EXPORT_SYMBOL(fscrypt_decrypt_bio);
+
+static void completion_pages(struct work_struct *work)
+{
+ struct fscrypt_ctx *ctx =
+ container_of(work, struct fscrypt_ctx, r.work);
+ struct bio *bio = ctx->r.bio;
+
+ __fscrypt_decrypt_bio(bio, true);
fscrypt_release_ctx(ctx);
bio_put(bio);
}
-void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio)
+void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, struct bio *bio)
{
INIT_WORK(&ctx->r.work, completion_pages);
ctx->r.bio = bio;
- queue_work(fscrypt_read_workqueue, &ctx->r.work);
+ fscrypt_enqueue_decrypt_work(&ctx->r.work);
}
-EXPORT_SYMBOL(fscrypt_decrypt_bio_pages);
+EXPORT_SYMBOL(fscrypt_enqueue_decrypt_bio);
void fscrypt_pullback_bio_page(struct page **page, bool restore)
{
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 732a786..0758d32 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -27,6 +27,7 @@
#include <linux/dcache.h>
#include <linux/namei.h>
#include <crypto/aes.h>
+#include <crypto/skcipher.h>
#include "fscrypt_private.h"
static unsigned int num_prealloc_crypto_pages = 32;
@@ -44,12 +45,18 @@
static LIST_HEAD(fscrypt_free_ctxs);
static DEFINE_SPINLOCK(fscrypt_ctx_lock);
-struct workqueue_struct *fscrypt_read_workqueue;
+static struct workqueue_struct *fscrypt_read_workqueue;
static DEFINE_MUTEX(fscrypt_init_mutex);
static struct kmem_cache *fscrypt_ctx_cachep;
struct kmem_cache *fscrypt_info_cachep;
+void fscrypt_enqueue_decrypt_work(struct work_struct *work)
+{
+ queue_work(fscrypt_read_workqueue, work);
+}
+EXPORT_SYMBOL(fscrypt_enqueue_decrypt_work);
+
/**
* fscrypt_release_ctx() - Releases an encryption context
* @ctx: The encryption context to release.
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 6eb4343..b18fa32 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -12,42 +12,46 @@
#include <linux/scatterlist.h>
#include <linux/ratelimit.h>
+#include <crypto/skcipher.h>
#include "fscrypt_private.h"
+static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
+{
+ if (str->len == 1 && str->name[0] == '.')
+ return true;
+
+ if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
+ return true;
+
+ return false;
+}
+
/**
* fname_encrypt() - encrypt a filename
*
- * The caller must have allocated sufficient memory for the @oname string.
+ * The output buffer must be at least as large as the input buffer.
+ * Any extra space is filled with NUL padding before encryption.
*
* Return: 0 on success, -errno on failure
*/
-static int fname_encrypt(struct inode *inode,
- const struct qstr *iname, struct fscrypt_str *oname)
+int fname_encrypt(struct inode *inode, const struct qstr *iname,
+ u8 *out, unsigned int olen)
{
struct skcipher_request *req = NULL;
DECLARE_CRYPTO_WAIT(wait);
- struct fscrypt_info *ci = inode->i_crypt_info;
- struct crypto_skcipher *tfm = ci->ci_ctfm;
+ struct crypto_skcipher *tfm = inode->i_crypt_info->ci_ctfm;
int res = 0;
char iv[FS_CRYPTO_BLOCK_SIZE];
struct scatterlist sg;
- int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
- unsigned int lim;
- unsigned int cryptlen;
-
- lim = inode->i_sb->s_cop->max_namelen(inode);
- if (iname->len <= 0 || iname->len > lim)
- return -EIO;
/*
* Copy the filename to the output buffer for encrypting in-place and
* pad it with the needed number of NUL bytes.
*/
- cryptlen = max_t(unsigned int, iname->len, FS_CRYPTO_BLOCK_SIZE);
- cryptlen = round_up(cryptlen, padding);
- cryptlen = min(cryptlen, lim);
- memcpy(oname->name, iname->name, iname->len);
- memset(oname->name + iname->len, 0, cryptlen - iname->len);
+ if (WARN_ON(olen < iname->len))
+ return -ENOBUFS;
+ memcpy(out, iname->name, iname->len);
+ memset(out + iname->len, 0, olen - iname->len);
/* Initialize the IV */
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
@@ -62,8 +66,8 @@
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
crypto_req_done, &wait);
- sg_init_one(&sg, oname->name, cryptlen);
- skcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv);
+ sg_init_one(&sg, out, olen);
+ skcipher_request_set_crypt(req, &sg, &sg, olen, iv);
/* Do the encryption */
res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
@@ -74,7 +78,6 @@
return res;
}
- oname->len = cryptlen;
return 0;
}
@@ -187,50 +190,52 @@
return cp - dst;
}
-u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen)
+bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
+ u32 max_len, u32 *encrypted_len_ret)
{
- int padding = 32;
- struct fscrypt_info *ci = inode->i_crypt_info;
+ int padding = 4 << (inode->i_crypt_info->ci_flags &
+ FS_POLICY_FLAGS_PAD_MASK);
+ u32 encrypted_len;
- if (ci)
- padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
- ilen = max(ilen, (u32)FS_CRYPTO_BLOCK_SIZE);
- return round_up(ilen, padding);
+ if (orig_len > max_len)
+ return false;
+ encrypted_len = max(orig_len, (u32)FS_CRYPTO_BLOCK_SIZE);
+ encrypted_len = round_up(encrypted_len, padding);
+ *encrypted_len_ret = min(encrypted_len, max_len);
+ return true;
}
-EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
/**
- * fscrypt_fname_crypto_alloc_obuff() -
+ * fscrypt_fname_alloc_buffer - allocate a buffer for presented filenames
*
- * Allocates an output buffer that is sufficient for the crypto operation
- * specified by the context and the direction.
+ * Allocate a buffer that is large enough to hold any decrypted or encoded
+ * filename (null-terminated), for the given maximum encrypted filename length.
+ *
+ * Return: 0 on success, -errno on failure
*/
int fscrypt_fname_alloc_buffer(const struct inode *inode,
- u32 ilen, struct fscrypt_str *crypto_str)
+ u32 max_encrypted_len,
+ struct fscrypt_str *crypto_str)
{
- u32 olen = fscrypt_fname_encrypted_size(inode, ilen);
const u32 max_encoded_len =
max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
+ u32 max_presented_len;
- crypto_str->len = olen;
- olen = max(olen, max_encoded_len);
+ max_presented_len = max(max_encoded_len, max_encrypted_len);
- /*
- * Allocated buffer can hold one more character to null-terminate the
- * string
- */
- crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
- if (!(crypto_str->name))
+ crypto_str->name = kmalloc(max_presented_len + 1, GFP_NOFS);
+ if (!crypto_str->name)
return -ENOMEM;
+ crypto_str->len = max_presented_len;
return 0;
}
EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
/**
- * fscrypt_fname_crypto_free_buffer() -
+ * fscrypt_fname_free_buffer - free the buffer for presented filenames
*
- * Frees the buffer allocated for crypto operation.
+ * Free the buffer allocated by fscrypt_fname_alloc_buffer().
*/
void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
{
@@ -297,35 +302,6 @@
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
/**
- * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk
- * space
- *
- * The caller must have allocated sufficient memory for the @oname string.
- *
- * Return: 0 on success, -errno on failure
- */
-int fscrypt_fname_usr_to_disk(struct inode *inode,
- const struct qstr *iname,
- struct fscrypt_str *oname)
-{
- if (fscrypt_is_dot_dotdot(iname)) {
- oname->name[0] = '.';
- oname->name[iname->len - 1] = '.';
- oname->len = iname->len;
- return 0;
- }
- if (inode->i_crypt_info)
- return fname_encrypt(inode, iname, oname);
- /*
- * Without a proper key, a user is not allowed to modify the filenames
- * in a directory. Consequently, a user space name cannot be mapped to
- * a disk-space name
- */
- return -ENOKEY;
-}
-EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
-
-/**
* fscrypt_setup_filename() - prepare to search a possibly encrypted directory
* @dir: the directory that will be searched
* @iname: the user-provided filename being searched for
@@ -368,11 +344,17 @@
return ret;
if (dir->i_crypt_info) {
- ret = fscrypt_fname_alloc_buffer(dir, iname->len,
- &fname->crypto_buf);
- if (ret)
- return ret;
- ret = fname_encrypt(dir, iname, &fname->crypto_buf);
+ if (!fscrypt_fname_encrypted_size(dir, iname->len,
+ dir->i_sb->s_cop->max_namelen(dir),
+ &fname->crypto_buf.len))
+ return -ENAMETOOLONG;
+ fname->crypto_buf.name = kmalloc(fname->crypto_buf.len,
+ GFP_NOFS);
+ if (!fname->crypto_buf.name)
+ return -ENOMEM;
+
+ ret = fname_encrypt(dir, iname, fname->crypto_buf.name,
+ fname->crypto_buf.len);
if (ret)
goto errout;
fname->disk_name.name = fname->crypto_buf.name;
@@ -424,7 +406,7 @@
return 0;
errout:
- fscrypt_fname_free_buffer(&fname->crypto_buf);
+ kfree(fname->crypto_buf.name);
return ret;
}
EXPORT_SYMBOL(fscrypt_setup_filename);
diff --git a/fs/crypto/fscrypt_ice.c b/fs/crypto/fscrypt_ice.c
new file mode 100644
index 0000000..62dae83
--- /dev/null
+++ b/fs/crypto/fscrypt_ice.c
@@ -0,0 +1,146 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "fscrypt_ice.h"
+
+int fscrypt_using_hardware_encryption(const struct inode *inode)
+{
+ struct fscrypt_info *ci = inode->i_crypt_info;
+
+ return S_ISREG(inode->i_mode) && ci &&
+ ci->ci_data_mode == FS_ENCRYPTION_MODE_PRIVATE;
+}
+EXPORT_SYMBOL(fscrypt_using_hardware_encryption);
+
+/*
+ * Retrieves encryption key from the inode
+ */
+char *fscrypt_get_ice_encryption_key(const struct inode *inode)
+{
+ struct fscrypt_info *ci = NULL;
+
+ if (!inode)
+ return NULL;
+
+ ci = inode->i_crypt_info;
+ if (!ci)
+ return NULL;
+
+ return &(ci->ci_raw_key[0]);
+}
+
+/*
+ * Retrieves encryption salt from the inode
+ */
+char *fscrypt_get_ice_encryption_salt(const struct inode *inode)
+{
+ struct fscrypt_info *ci = NULL;
+
+ if (!inode)
+ return NULL;
+
+ ci = inode->i_crypt_info;
+ if (!ci)
+ return NULL;
+
+ return &(ci->ci_raw_key[fscrypt_get_ice_encryption_key_size(inode)]);
+}
+
+/*
+ * returns true if the cipher mode in inode is AES XTS
+ */
+int fscrypt_is_aes_xts_cipher(const struct inode *inode)
+{
+ struct fscrypt_info *ci = inode->i_crypt_info;
+
+ if (!ci)
+ return 0;
+
+ return (ci->ci_data_mode == FS_ENCRYPTION_MODE_PRIVATE);
+}
+
+/*
+ * returns true if encryption info in both inodes is equal
+ */
+bool fscrypt_is_ice_encryption_info_equal(const struct inode *inode1,
+ const struct inode *inode2)
+{
+ char *key1 = NULL;
+ char *key2 = NULL;
+ char *salt1 = NULL;
+ char *salt2 = NULL;
+
+ if (!inode1 || !inode2)
+ return false;
+
+ if (inode1 == inode2)
+ return true;
+
+ /* both do not belong to ice, so we don't care, they are equal
+ *for us
+ */
+ if (!fscrypt_should_be_processed_by_ice(inode1) &&
+ !fscrypt_should_be_processed_by_ice(inode2))
+ return true;
+
+ /* one belongs to ice, the other does not -> not equal */
+ if (fscrypt_should_be_processed_by_ice(inode1) ^
+ fscrypt_should_be_processed_by_ice(inode2))
+ return false;
+
+ key1 = fscrypt_get_ice_encryption_key(inode1);
+ key2 = fscrypt_get_ice_encryption_key(inode2);
+ salt1 = fscrypt_get_ice_encryption_salt(inode1);
+ salt2 = fscrypt_get_ice_encryption_salt(inode2);
+
+ /* key and salt should not be null by this point */
+ if (!key1 || !key2 || !salt1 || !salt2 ||
+ (fscrypt_get_ice_encryption_key_size(inode1) !=
+ fscrypt_get_ice_encryption_key_size(inode2)) ||
+ (fscrypt_get_ice_encryption_salt_size(inode1) !=
+ fscrypt_get_ice_encryption_salt_size(inode2)))
+ return false;
+
+ if ((memcmp(key1, key2,
+ fscrypt_get_ice_encryption_key_size(inode1)) == 0) &&
+ (memcmp(salt1, salt2,
+ fscrypt_get_ice_encryption_salt_size(inode1)) == 0))
+ return true;
+
+ return false;
+}
+
+void fscrypt_set_ice_dun(const struct inode *inode, struct bio *bio, u64 dun)
+{
+ if (fscrypt_should_be_processed_by_ice(inode))
+ bio->bi_iter.bi_dun = dun;
+}
+EXPORT_SYMBOL(fscrypt_set_ice_dun);
+
+/*
+ * This function will be used for filesystem when deciding to merge bios.
+ * Basic assumption is, if inline_encryption is set, single bio has to
+ * guarantee consecutive LBAs as well as ino|pg->index.
+ */
+bool fscrypt_mergeable_bio(struct bio *bio, u64 dun, bool bio_encrypted)
+{
+ if (!bio)
+ return true;
+
+ /* if both of them are not encrypted, no further check is needed */
+ if (!bio_dun(bio) && !bio_encrypted)
+ return true;
+
+ /* ICE allows only consecutive iv_key stream. */
+ return bio_end_dun(bio) == dun;
+}
+EXPORT_SYMBOL(fscrypt_mergeable_bio);
diff --git a/fs/crypto/fscrypt_ice.h b/fs/crypto/fscrypt_ice.h
new file mode 100644
index 0000000..c540506
--- /dev/null
+++ b/fs/crypto/fscrypt_ice.h
@@ -0,0 +1,106 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _FSCRYPT_ICE_H
+#define _FSCRYPT_ICE_H
+
+#include <linux/blkdev.h>
+#include "fscrypt_private.h"
+
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+static inline bool fscrypt_should_be_processed_by_ice(const struct inode *inode)
+{
+ if (!inode->i_sb->s_cop)
+ return 0;
+ if (!inode->i_sb->s_cop->is_encrypted((struct inode *)inode))
+ return 0;
+
+ return fscrypt_using_hardware_encryption(inode);
+}
+
+static inline int fscrypt_is_ice_capable(const struct super_block *sb)
+{
+ return blk_queue_inlinecrypt(bdev_get_queue(sb->s_bdev));
+}
+
+int fscrypt_is_aes_xts_cipher(const struct inode *inode);
+
+char *fscrypt_get_ice_encryption_key(const struct inode *inode);
+char *fscrypt_get_ice_encryption_salt(const struct inode *inode);
+
+bool fscrypt_is_ice_encryption_info_equal(const struct inode *inode1,
+ const struct inode *inode2);
+
+static inline size_t fscrypt_get_ice_encryption_key_size(
+ const struct inode *inode)
+{
+ return FS_AES_256_XTS_KEY_SIZE / 2;
+}
+
+static inline size_t fscrypt_get_ice_encryption_salt_size(
+ const struct inode *inode)
+{
+ return FS_AES_256_XTS_KEY_SIZE / 2;
+}
+#else
+static inline bool fscrypt_should_be_processed_by_ice(const struct inode *inode)
+{
+ return 0;
+}
+
+static inline int fscrypt_is_ice_capable(const struct super_block *sb)
+{
+ return 0;
+}
+
+static inline char *fscrypt_get_ice_encryption_key(const struct inode *inode)
+{
+ return NULL;
+}
+
+static inline char *fscrypt_get_ice_encryption_salt(const struct inode *inode)
+{
+ return NULL;
+}
+
+static inline size_t fscrypt_get_ice_encryption_key_size(
+ const struct inode *inode)
+{
+ return 0;
+}
+
+static inline size_t fscrypt_get_ice_encryption_salt_size(
+ const struct inode *inode)
+{
+ return 0;
+}
+
+static inline int fscrypt_is_xts_cipher(const struct inode *inode)
+{
+ return 0;
+}
+
+static inline bool fscrypt_is_ice_encryption_info_equal(
+ const struct inode *inode1,
+ const struct inode *inode2)
+{
+ return 0;
+}
+
+static inline int fscrypt_is_aes_xts_cipher(const struct inode *inode)
+{
+ return 0;
+}
+
+#endif
+
+#endif /* _FSCRYPT_ICE_H */
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 4688a64..8f73c1d 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -11,9 +11,12 @@
#ifndef _FSCRYPT_PRIVATE_H
#define _FSCRYPT_PRIVATE_H
+#ifndef __FS_HAS_ENCRYPTION
#define __FS_HAS_ENCRYPTION 1
+#endif
#include <linux/fscrypt.h>
#include <crypto/hash.h>
+#include <linux/pfk.h>
/* Encryption parameters */
#define FS_IV_SIZE 16
@@ -49,17 +52,34 @@
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
+/**
+ * For encrypted symlinks, the ciphertext length is stored at the beginning
+ * of the string in little-endian format.
+ */
+struct fscrypt_symlink_data {
+ __le16 len;
+ char encrypted_path[1];
+} __packed;
+
+enum ci_mode_info {
+ CI_NONE_MODE = 0,
+ CI_DATA_MODE,
+ CI_FNAME_MODE,
+};
+
/*
* A pointer to this structure is stored in the file system's in-core
* representation of an inode.
*/
struct fscrypt_info {
+ u8 ci_mode;
u8 ci_data_mode;
u8 ci_filename_mode;
u8 ci_flags;
struct crypto_skcipher *ci_ctfm;
struct crypto_cipher *ci_essiv_tfm;
u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
+ u8 ci_raw_key[FS_MAX_KEY_SIZE];
};
typedef enum {
@@ -70,9 +90,32 @@
#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
#define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002
+static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
+ u32 filenames_mode)
+{
+ if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC &&
+ filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS)
+ return true;
+
+ if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
+ filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
+ return true;
+
+ if (contents_mode == FS_ENCRYPTION_MODE_PRIVATE)
+ return true;
+
+ return false;
+}
+
+static inline bool is_private_data_mode(struct fscrypt_info *ci)
+{
+ return ci->ci_mode == CI_DATA_MODE &&
+ ci->ci_data_mode == FS_ENCRYPTION_MODE_PRIVATE;
+}
+
/* crypto.c */
+extern struct kmem_cache *fscrypt_info_cachep;
extern int fscrypt_initialize(unsigned int cop_flags);
-extern struct workqueue_struct *fscrypt_read_workqueue;
extern int fscrypt_do_page_crypto(const struct inode *inode,
fscrypt_direction_t rw, u64 lblk_num,
struct page *src_page,
@@ -82,6 +125,13 @@
extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
gfp_t gfp_flags);
+/* fname.c */
+extern int fname_encrypt(struct inode *inode, const struct qstr *iname,
+ u8 *out, unsigned int olen);
+extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
+ u32 orig_len, u32 max_len,
+ u32 *encrypted_len_ret);
+
/* keyinfo.c */
extern void __exit fscrypt_essiv_cleanup(void);
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c
index 9f5fb2e..bec0649 100644
--- a/fs/crypto/hooks.c
+++ b/fs/crypto/hooks.c
@@ -110,3 +110,161 @@
return 0;
}
EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
+
+int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
+ unsigned int max_len,
+ struct fscrypt_str *disk_link)
+{
+ int err;
+
+ /*
+ * To calculate the size of the encrypted symlink target we need to know
+ * the amount of NUL padding, which is determined by the flags set in
+ * the encryption policy which will be inherited from the directory.
+ * The easiest way to get access to this is to just load the directory's
+ * fscrypt_info, since we'll need it to create the dir_entry anyway.
+ *
+ * Note: in test_dummy_encryption mode, @dir may be unencrypted.
+ */
+ err = fscrypt_get_encryption_info(dir);
+ if (err)
+ return err;
+ if (!fscrypt_has_encryption_key(dir))
+ return -ENOKEY;
+
+ /*
+ * Calculate the size of the encrypted symlink and verify it won't
+ * exceed max_len. Note that for historical reasons, encrypted symlink
+ * targets are prefixed with the ciphertext length, despite this
+ * actually being redundant with i_size. This decreases by 2 bytes the
+ * longest symlink target we can accept.
+ *
+ * We could recover 1 byte by not counting a null terminator, but
+ * counting it (even though it is meaningless for ciphertext) is simpler
+ * for now since filesystems will assume it is there and subtract it.
+ */
+ if (!fscrypt_fname_encrypted_size(dir, len,
+ max_len - sizeof(struct fscrypt_symlink_data),
+ &disk_link->len))
+ return -ENAMETOOLONG;
+ disk_link->len += sizeof(struct fscrypt_symlink_data);
+
+ disk_link->name = NULL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fscrypt_prepare_symlink);
+
+int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
+ unsigned int len, struct fscrypt_str *disk_link)
+{
+ int err;
+ struct qstr iname = QSTR_INIT(target, len);
+ struct fscrypt_symlink_data *sd;
+ unsigned int ciphertext_len;
+
+ err = fscrypt_require_key(inode);
+ if (err)
+ return err;
+
+ if (disk_link->name) {
+ /* filesystem-provided buffer */
+ sd = (struct fscrypt_symlink_data *)disk_link->name;
+ } else {
+ sd = kmalloc(disk_link->len, GFP_NOFS);
+ if (!sd)
+ return -ENOMEM;
+ }
+ ciphertext_len = disk_link->len - sizeof(*sd);
+ sd->len = cpu_to_le16(ciphertext_len);
+
+ err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len);
+ if (err) {
+ if (!disk_link->name)
+ kfree(sd);
+ return err;
+ }
+ /*
+ * Null-terminating the ciphertext doesn't make sense, but we still
+ * count the null terminator in the length, so we might as well
+ * initialize it just in case the filesystem writes it out.
+ */
+ sd->encrypted_path[ciphertext_len] = '\0';
+
+ if (!disk_link->name)
+ disk_link->name = (unsigned char *)sd;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
+
+/**
+ * fscrypt_get_symlink - get the target of an encrypted symlink
+ * @inode: the symlink inode
+ * @caddr: the on-disk contents of the symlink
+ * @max_size: size of @caddr buffer
+ * @done: if successful, will be set up to free the returned target
+ *
+ * If the symlink's encryption key is available, we decrypt its target.
+ * Otherwise, we encode its target for presentation.
+ *
+ * This may sleep, so the filesystem must have dropped out of RCU mode already.
+ *
+ * Return: the presentable symlink target or an ERR_PTR()
+ */
+const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
+ unsigned int max_size,
+ struct delayed_call *done)
+{
+ const struct fscrypt_symlink_data *sd;
+ struct fscrypt_str cstr, pstr;
+ int err;
+
+ /* This is for encrypted symlinks only */
+ if (WARN_ON(!IS_ENCRYPTED(inode)))
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * Try to set up the symlink's encryption key, but we can continue
+ * regardless of whether the key is available or not.
+ */
+ err = fscrypt_get_encryption_info(inode);
+ if (err)
+ return ERR_PTR(err);
+
+ /*
+ * For historical reasons, encrypted symlink targets are prefixed with
+ * the ciphertext length, even though this is redundant with i_size.
+ */
+
+ if (max_size < sizeof(*sd))
+ return ERR_PTR(-EUCLEAN);
+ sd = caddr;
+ cstr.name = (unsigned char *)sd->encrypted_path;
+ cstr.len = le16_to_cpu(sd->len);
+
+ if (cstr.len == 0)
+ return ERR_PTR(-EUCLEAN);
+
+ if (cstr.len + sizeof(*sd) - 1 > max_size)
+ return ERR_PTR(-EUCLEAN);
+
+ err = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
+ if (err)
+ return ERR_PTR(err);
+
+ err = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
+ if (err)
+ goto err_kfree;
+
+ err = -EUCLEAN;
+ if (pstr.name[0] == '\0')
+ goto err_kfree;
+
+ pstr.name[pstr.len] = '\0';
+ set_delayed_call(done, kfree_link, pstr.name);
+ return pstr.name;
+
+err_kfree:
+ kfree(pstr.name);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(fscrypt_get_symlink);
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index c891d2e..d8478e7 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -13,7 +13,9 @@
#include <linux/ratelimit.h>
#include <crypto/aes.h>
#include <crypto/sha.h>
+#include <crypto/skcipher.h>
#include "fscrypt_private.h"
+#include "fscrypt_ice.h"
static struct crypto_shash *essiv_hash_tfm;
@@ -66,7 +68,7 @@
}
static int validate_user_key(struct fscrypt_info *crypt_info,
- struct fscrypt_context *ctx, u8 *raw_key,
+ struct fscrypt_context *ctx,
const char *prefix, int min_keysize)
{
char *description;
@@ -114,7 +116,24 @@
res = -ENOKEY;
goto out;
}
- res = derive_key_aes(ctx->nonce, master_key, raw_key);
+ res = derive_key_aes(ctx->nonce, master_key, crypt_info->ci_raw_key);
+ /* If we don't need to derive, we still want to do everything
+ * up until now to validate the key. It's cleaner to fail now
+ * than to fail in block I/O.
+ if (!is_private_data_mode(crypt_info)) {
+ res = derive_key_aes(ctx->nonce, master_key,
+ crypt_info->ci_raw_key);
+ } else {
+ * Inline encryption: no key derivation required because IVs are
+ * assigned based on iv_sector.
+
+ BUILD_BUG_ON(sizeof(crypt_info->ci_raw_key) !=
+ sizeof(master_key->raw));
+ memcpy(crypt_info->ci_raw_key,
+ master_key->raw, sizeof(crypt_info->ci_raw_key));
+ res = 0;
+ }
+ */
out:
up_read(&keyring_key->sem);
key_put(keyring_key);
@@ -133,10 +152,12 @@
FS_AES_128_CBC_KEY_SIZE },
[FS_ENCRYPTION_MODE_AES_128_CTS] = { "cts(cbc(aes))",
FS_AES_128_CTS_KEY_SIZE },
+ [FS_ENCRYPTION_MODE_PRIVATE] = { "bugon",
+ FS_AES_256_XTS_KEY_SIZE },
};
static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode,
- const char **cipher_str_ret, int *keysize_ret)
+ const char **cipher_str_ret, int *keysize_ret, int *fname)
{
u32 mode;
@@ -148,9 +169,12 @@
}
if (S_ISREG(inode->i_mode)) {
+ ci->ci_mode = CI_DATA_MODE;
mode = ci->ci_data_mode;
} else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+ ci->ci_mode = CI_FNAME_MODE;
mode = ci->ci_filename_mode;
+ *fname = 1;
} else {
WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n",
inode->i_ino, (inode->i_mode & S_IFMT));
@@ -169,6 +193,7 @@
crypto_free_skcipher(ci->ci_ctfm);
crypto_free_cipher(ci->ci_essiv_tfm);
+ memset(ci, 0, sizeof(*ci)); /* sanitizes ->ci_raw_key */
kmem_cache_free(fscrypt_info_cachep, ci);
}
@@ -238,6 +263,12 @@
crypto_free_shash(essiv_hash_tfm);
}
+static int fscrypt_data_encryption_mode(struct inode *inode)
+{
+ return fscrypt_should_be_processed_by_ice(inode) ?
+ FS_ENCRYPTION_MODE_PRIVATE : FS_ENCRYPTION_MODE_AES_256_XTS;
+}
+
int fscrypt_get_encryption_info(struct inode *inode)
{
struct fscrypt_info *crypt_info;
@@ -245,8 +276,8 @@
struct crypto_skcipher *ctfm;
const char *cipher_str;
int keysize;
- u8 *raw_key = NULL;
int res;
+ int fname = 0;
if (inode->i_crypt_info)
return 0;
@@ -263,7 +294,8 @@
/* Fake up a context for an unencrypted directory */
memset(&ctx, 0, sizeof(ctx));
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
- ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
+ ctx.contents_encryption_mode =
+ fscrypt_data_encryption_mode(inode);
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE);
} else if (res != sizeof(ctx)) {
@@ -288,7 +320,8 @@
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
- res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
+ res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize,
+ &fname);
if (res)
goto out;
@@ -297,14 +330,11 @@
* crypto API as part of key derivation.
*/
res = -ENOMEM;
- raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
- if (!raw_key)
- goto out;
- res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX,
+ res = validate_user_key(crypt_info, &ctx, FS_KEY_DESC_PREFIX,
keysize);
if (res && inode->i_sb->s_cop->key_prefix) {
- int res2 = validate_user_key(crypt_info, &ctx, raw_key,
+ int res2 = validate_user_key(crypt_info, &ctx,
inode->i_sb->s_cop->key_prefix,
keysize);
if (res2) {
@@ -312,9 +342,23 @@
res = -ENOKEY;
goto out;
}
+ res = 0;
} else if (res) {
goto out;
}
+
+ if (is_private_data_mode(crypt_info)) {
+ if (!fscrypt_is_ice_capable(inode->i_sb)) {
+ pr_warn("%s: ICE support not available\n",
+ __func__);
+ res = -EINVAL;
+ goto out;
+ }
+ /* Let's encrypt/decrypt by ICE */
+ goto do_ice;
+ }
+
+
ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
if (!ctfm || IS_ERR(ctfm)) {
res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
@@ -329,43 +373,36 @@
* if the provided key is longer than keysize, we use the first
* keysize bytes of the derived key only
*/
- res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
+ res = crypto_skcipher_setkey(ctfm, crypt_info->ci_raw_key, keysize);
if (res)
goto out;
if (S_ISREG(inode->i_mode) &&
crypt_info->ci_data_mode == FS_ENCRYPTION_MODE_AES_128_CBC) {
- res = init_essiv_generator(crypt_info, raw_key, keysize);
+ res = init_essiv_generator(crypt_info, crypt_info->ci_raw_key,
+ keysize);
if (res) {
pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n",
__func__, res, inode->i_ino);
goto out;
}
}
+ memzero_explicit(crypt_info->ci_raw_key,
+ sizeof(crypt_info->ci_raw_key));
+do_ice:
if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
crypt_info = NULL;
out:
if (res == -ENOKEY)
res = 0;
put_crypt_info(crypt_info);
- kzfree(raw_key);
return res;
}
EXPORT_SYMBOL(fscrypt_get_encryption_info);
-void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)
+void fscrypt_put_encryption_info(struct inode *inode)
{
- struct fscrypt_info *prev;
-
- if (ci == NULL)
- ci = ACCESS_ONCE(inode->i_crypt_info);
- if (ci == NULL)
- return;
-
- prev = cmpxchg(&inode->i_crypt_info, ci, NULL);
- if (prev != ci)
- return;
-
- put_crypt_info(ci);
+ put_crypt_info(inode->i_crypt_info);
+ inode->i_crypt_info = NULL;
}
EXPORT_SYMBOL(fscrypt_put_encryption_info);
diff --git a/fs/dax.c b/fs/dax.c
index 800748f..71f87d7 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -785,6 +785,7 @@
if (ret < 0)
return ret;
}
+ start_index = indices[pvec.nr - 1] + 1;
}
return 0;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 227a4f9..885f74e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -461,9 +461,11 @@
* d_drop() is used mainly for stuff that wants to invalidate a dentry for some
* reason (NFS timeouts or autofs deletes).
*
- * __d_drop requires dentry->d_lock.
+ * __d_drop requires dentry->d_lock
+ * ___d_drop doesn't mark dentry as "unhashed"
+ * (dentry->d_hash.pprev will be LIST_POISON2, not NULL).
*/
-void __d_drop(struct dentry *dentry)
+static void ___d_drop(struct dentry *dentry)
{
if (!d_unhashed(dentry)) {
struct hlist_bl_head *b;
@@ -479,12 +481,17 @@
hlist_bl_lock(b);
__hlist_bl_del(&dentry->d_hash);
- dentry->d_hash.pprev = NULL;
hlist_bl_unlock(b);
/* After this call, in-progress rcu-walk path lookup will fail. */
write_seqcount_invalidate(&dentry->d_seq);
}
}
+
+void __d_drop(struct dentry *dentry)
+{
+ ___d_drop(dentry);
+ dentry->d_hash.pprev = NULL;
+}
EXPORT_SYMBOL(__d_drop);
void d_drop(struct dentry *dentry)
@@ -637,11 +644,16 @@
spin_unlock(&parent->d_lock);
goto again;
}
- rcu_read_unlock();
- if (parent != dentry)
+ if (parent != dentry) {
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
- else
+ if (unlikely(dentry->d_lockref.count < 0)) {
+ spin_unlock(&parent->d_lock);
+ parent = NULL;
+ }
+ } else {
parent = NULL;
+ }
+ rcu_read_unlock();
return parent;
}
@@ -2373,7 +2385,7 @@
static void __d_rehash(struct dentry *entry)
{
struct hlist_bl_head *b = d_hash(entry->d_name.hash);
- BUG_ON(!d_unhashed(entry));
+
hlist_bl_lock(b);
hlist_bl_add_head_rcu(&entry->d_hash, b);
hlist_bl_unlock(b);
@@ -2810,9 +2822,9 @@
write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED);
/* unhash both */
- /* __d_drop does write_seqcount_barrier, but they're OK to nest. */
- __d_drop(dentry);
- __d_drop(target);
+ /* ___d_drop does write_seqcount_barrier, but they're OK to nest. */
+ ___d_drop(dentry);
+ ___d_drop(target);
/* Switch the names.. */
if (exchange)
@@ -2824,6 +2836,8 @@
__d_rehash(dentry);
if (exchange)
__d_rehash(target);
+ else
+ target->d_hash.pprev = NULL;
/* ... and switch them in the tree */
if (IS_ROOT(dentry)) {
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 354e2ab..0536072 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -22,7 +22,6 @@
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/device.h>
-#include <linux/srcu.h>
#include <asm/poll.h>
#include "internal.h"
@@ -48,66 +47,108 @@
.llseek = noop_llseek,
};
+#define F_DENTRY(filp) ((filp)->f_path.dentry)
+
+const struct file_operations *debugfs_real_fops(const struct file *filp)
+{
+ struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;
+
+ if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
+ /*
+ * Urgh, we've been called w/o a protecting
+ * debugfs_file_get().
+ */
+ WARN_ON(1);
+ return NULL;
+ }
+
+ return fsd->real_fops;
+}
+EXPORT_SYMBOL_GPL(debugfs_real_fops);
+
/**
- * debugfs_use_file_start - mark the beginning of file data access
+ * debugfs_file_get - mark the beginning of file data access
* @dentry: the dentry object whose data is being accessed.
- * @srcu_idx: a pointer to some memory to store a SRCU index in.
*
- * Up to a matching call to debugfs_use_file_finish(), any
- * successive call into the file removing functions debugfs_remove()
- * and debugfs_remove_recursive() will block. Since associated private
+ * Up to a matching call to debugfs_file_put(), any successive call
+ * into the file removing functions debugfs_remove() and
+ * debugfs_remove_recursive() will block. Since associated private
* file data may only get freed after a successful return of any of
* the removal functions, you may safely access it after a successful
- * call to debugfs_use_file_start() without worrying about
- * lifetime issues.
+ * call to debugfs_file_get() without worrying about lifetime issues.
*
* If -%EIO is returned, the file has already been removed and thus,
* it is not safe to access any of its data. If, on the other hand,
* it is allowed to access the file data, zero is returned.
- *
- * Regardless of the return code, any call to
- * debugfs_use_file_start() must be followed by a matching call
- * to debugfs_use_file_finish().
*/
-int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
- __acquires(&debugfs_srcu)
+int debugfs_file_get(struct dentry *dentry)
{
- *srcu_idx = srcu_read_lock(&debugfs_srcu);
- barrier();
+ struct debugfs_fsdata *fsd;
+ void *d_fsd;
+
+ d_fsd = READ_ONCE(dentry->d_fsdata);
+ if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
+ fsd = d_fsd;
+ } else {
+ fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
+ if (!fsd)
+ return -ENOMEM;
+
+ fsd->real_fops = (void *)((unsigned long)d_fsd &
+ ~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
+ refcount_set(&fsd->active_users, 1);
+ init_completion(&fsd->active_users_drained);
+ if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
+ kfree(fsd);
+ fsd = READ_ONCE(dentry->d_fsdata);
+ }
+ }
+
+ /*
+ * In case of a successful cmpxchg() above, this check is
+ * strictly necessary and must follow it, see the comment in
+ * __debugfs_remove_file().
+ * OTOH, if the cmpxchg() hasn't been executed or wasn't
+ * successful, this serves the purpose of not starving
+ * removers.
+ */
if (d_unlinked(dentry))
return -EIO;
+
+ if (!refcount_inc_not_zero(&fsd->active_users))
+ return -EIO;
+
return 0;
}
-EXPORT_SYMBOL_GPL(debugfs_use_file_start);
+EXPORT_SYMBOL_GPL(debugfs_file_get);
/**
- * debugfs_use_file_finish - mark the end of file data access
- * @srcu_idx: the SRCU index "created" by a former call to
- * debugfs_use_file_start().
+ * debugfs_file_put - mark the end of file data access
+ * @dentry: the dentry object formerly passed to
+ * debugfs_file_get().
*
* Allow any ongoing concurrent call into debugfs_remove() or
* debugfs_remove_recursive() blocked by a former call to
- * debugfs_use_file_start() to proceed and return to its caller.
+ * debugfs_file_get() to proceed and return to its caller.
*/
-void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
+void debugfs_file_put(struct dentry *dentry)
{
- srcu_read_unlock(&debugfs_srcu, srcu_idx);
-}
-EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
+ struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
-#define F_DENTRY(filp) ((filp)->f_path.dentry)
+ if (refcount_dec_and_test(&fsd->active_users))
+ complete(&fsd->active_users_drained);
+}
+EXPORT_SYMBOL_GPL(debugfs_file_put);
static int open_proxy_open(struct inode *inode, struct file *filp)
{
- const struct dentry *dentry = F_DENTRY(filp);
+ struct dentry *dentry = F_DENTRY(filp);
const struct file_operations *real_fops = NULL;
- int srcu_idx, r;
+ int r;
- r = debugfs_use_file_start(dentry, &srcu_idx);
- if (r) {
- r = -ENOENT;
- goto out;
- }
+ r = debugfs_file_get(dentry);
+ if (r)
+ return r == -EIO ? -ENOENT : r;
real_fops = debugfs_real_fops(filp);
real_fops = fops_get(real_fops);
@@ -124,7 +165,7 @@
r = real_fops->open(inode, filp);
out:
- debugfs_use_file_finish(srcu_idx);
+ debugfs_file_put(dentry);
return r;
}
@@ -138,16 +179,16 @@
#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \
static ret_type full_proxy_ ## name(proto) \
{ \
- const struct dentry *dentry = F_DENTRY(filp); \
- const struct file_operations *real_fops = \
- debugfs_real_fops(filp); \
- int srcu_idx; \
+ struct dentry *dentry = F_DENTRY(filp); \
+ const struct file_operations *real_fops; \
ret_type r; \
\
- r = debugfs_use_file_start(dentry, &srcu_idx); \
- if (likely(!r)) \
- r = real_fops->name(args); \
- debugfs_use_file_finish(srcu_idx); \
+ r = debugfs_file_get(dentry); \
+ if (unlikely(r)) \
+ return r; \
+ real_fops = debugfs_real_fops(filp); \
+ r = real_fops->name(args); \
+ debugfs_file_put(dentry); \
return r; \
}
@@ -172,18 +213,16 @@
static unsigned int full_proxy_poll(struct file *filp,
struct poll_table_struct *wait)
{
- const struct dentry *dentry = F_DENTRY(filp);
- const struct file_operations *real_fops = debugfs_real_fops(filp);
- int srcu_idx;
+ struct dentry *dentry = F_DENTRY(filp);
unsigned int r = 0;
+ const struct file_operations *real_fops;
- if (debugfs_use_file_start(dentry, &srcu_idx)) {
- debugfs_use_file_finish(srcu_idx);
+ if (debugfs_file_get(dentry))
return POLLHUP;
- }
+ real_fops = debugfs_real_fops(filp);
r = real_fops->poll(filp, wait);
- debugfs_use_file_finish(srcu_idx);
+ debugfs_file_put(dentry);
return r;
}
@@ -227,16 +266,14 @@
static int full_proxy_open(struct inode *inode, struct file *filp)
{
- const struct dentry *dentry = F_DENTRY(filp);
+ struct dentry *dentry = F_DENTRY(filp);
const struct file_operations *real_fops = NULL;
struct file_operations *proxy_fops = NULL;
- int srcu_idx, r;
+ int r;
- r = debugfs_use_file_start(dentry, &srcu_idx);
- if (r) {
- r = -ENOENT;
- goto out;
- }
+ r = debugfs_file_get(dentry);
+ if (r)
+ return r == -EIO ? -ENOENT : r;
real_fops = debugfs_real_fops(filp);
real_fops = fops_get(real_fops);
@@ -274,7 +311,7 @@
kfree(proxy_fops);
fops_put(real_fops);
out:
- debugfs_use_file_finish(srcu_idx);
+ debugfs_file_put(dentry);
return r;
}
@@ -285,13 +322,14 @@
ssize_t debugfs_attr_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
+ struct dentry *dentry = F_DENTRY(file);
ssize_t ret;
- int srcu_idx;
- ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
- if (likely(!ret))
- ret = simple_attr_read(file, buf, len, ppos);
- debugfs_use_file_finish(srcu_idx);
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+ ret = simple_attr_read(file, buf, len, ppos);
+ debugfs_file_put(dentry);
return ret;
}
EXPORT_SYMBOL_GPL(debugfs_attr_read);
@@ -299,13 +337,14 @@
ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
+ struct dentry *dentry = F_DENTRY(file);
ssize_t ret;
- int srcu_idx;
- ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
- if (likely(!ret))
- ret = simple_attr_write(file, buf, len, ppos);
- debugfs_use_file_finish(srcu_idx);
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+ ret = simple_attr_write(file, buf, len, ppos);
+ debugfs_file_put(dentry);
return ret;
}
EXPORT_SYMBOL_GPL(debugfs_attr_write);
@@ -739,14 +778,14 @@
{
char buf[3];
bool val;
- int r, srcu_idx;
+ int r;
+ struct dentry *dentry = F_DENTRY(file);
- r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
- if (likely(!r))
- val = *(bool *)file->private_data;
- debugfs_use_file_finish(srcu_idx);
- if (r)
+ r = debugfs_file_get(dentry);
+ if (unlikely(r))
return r;
+ val = *(bool *)file->private_data;
+ debugfs_file_put(dentry);
if (val)
buf[0] = 'Y';
@@ -764,8 +803,9 @@
char buf[32];
size_t buf_size;
bool bv;
- int r, srcu_idx;
+ int r;
bool *val = file->private_data;
+ struct dentry *dentry = F_DENTRY(file);
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
@@ -773,12 +813,11 @@
buf[buf_size] = '\0';
if (strtobool(buf, &bv) == 0) {
- r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
- if (likely(!r))
- *val = bv;
- debugfs_use_file_finish(srcu_idx);
- if (r)
+ r = debugfs_file_get(dentry);
+ if (unlikely(r))
return r;
+ *val = bv;
+ debugfs_file_put(dentry);
}
return count;
@@ -840,14 +879,15 @@
size_t count, loff_t *ppos)
{
struct debugfs_blob_wrapper *blob = file->private_data;
+ struct dentry *dentry = F_DENTRY(file);
ssize_t r;
- int srcu_idx;
- r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
- if (likely(!r))
- r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
- blob->size);
- debugfs_use_file_finish(srcu_idx);
+ r = debugfs_file_get(dentry);
+ if (unlikely(r))
+ return r;
+ r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
+ blob->size);
+ debugfs_file_put(dentry);
return r;
}
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 3d7de9f..f7f672d 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -27,14 +27,11 @@
#include <linux/parser.h>
#include <linux/magic.h>
#include <linux/slab.h>
-#include <linux/srcu.h>
#include "internal.h"
#define DEBUGFS_DEFAULT_MODE 0700
-DEFINE_SRCU(debugfs_srcu);
-
static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
static bool debugfs_registered;
@@ -185,6 +182,14 @@
.evict_inode = debugfs_evict_inode,
};
+static void debugfs_release_dentry(struct dentry *dentry)
+{
+ void *fsd = dentry->d_fsdata;
+
+ if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
+ kfree(dentry->d_fsdata);
+}
+
static struct vfsmount *debugfs_automount(struct path *path)
{
debugfs_automount_t f;
@@ -194,6 +199,7 @@
static const struct dentry_operations debugfs_dops = {
.d_delete = always_delete_dentry,
+ .d_release = debugfs_release_dentry,
.d_automount = debugfs_automount,
};
@@ -324,7 +330,8 @@
inode->i_private = data;
inode->i_fop = proxy_fops;
- dentry->d_fsdata = (void *)real_fops;
+ dentry->d_fsdata = (void *)((unsigned long)real_fops |
+ DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
d_instantiate(dentry, inode);
fsnotify_create(d_inode(dentry->d_parent), dentry);
@@ -581,18 +588,43 @@
}
EXPORT_SYMBOL_GPL(debugfs_create_symlink);
+static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent)
+{
+ struct debugfs_fsdata *fsd;
+
+ simple_unlink(d_inode(parent), dentry);
+ d_delete(dentry);
+
+ /*
+ * Paired with the closing smp_mb() implied by a successful
+ * cmpxchg() in debugfs_file_get(): either
+ * debugfs_file_get() must see a dead dentry or we must see a
+ * debugfs_fsdata instance at ->d_fsdata here (or both).
+ */
+ smp_mb();
+ fsd = READ_ONCE(dentry->d_fsdata);
+ if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
+ return;
+ if (!refcount_dec_and_test(&fsd->active_users))
+ wait_for_completion(&fsd->active_users_drained);
+}
+
static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
{
int ret = 0;
if (simple_positive(dentry)) {
dget(dentry);
- if (d_is_dir(dentry))
- ret = simple_rmdir(d_inode(parent), dentry);
- else
- simple_unlink(d_inode(parent), dentry);
- if (!ret)
- d_delete(dentry);
+ if (!d_is_reg(dentry)) {
+ if (d_is_dir(dentry))
+ ret = simple_rmdir(d_inode(parent), dentry);
+ else
+ simple_unlink(d_inode(parent), dentry);
+ if (!ret)
+ d_delete(dentry);
+ } else {
+ __debugfs_remove_file(dentry, parent);
+ }
dput(dentry);
}
return ret;
@@ -626,8 +658,6 @@
inode_unlock(d_inode(parent));
if (!ret)
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
-
- synchronize_srcu(&debugfs_srcu);
}
EXPORT_SYMBOL_GPL(debugfs_remove);
@@ -701,8 +731,6 @@
if (!__debugfs_remove(child, parent))
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
inode_unlock(d_inode(parent));
-
- synchronize_srcu(&debugfs_srcu);
}
EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h
index b3e8443..49ba8dd 100644
--- a/fs/debugfs/internal.h
+++ b/fs/debugfs/internal.h
@@ -12,6 +12,8 @@
#ifndef _DEBUGFS_INTERNAL_H_
#define _DEBUGFS_INTERNAL_H_
+#include <linux/refcount.h>
+
struct file_operations;
/* declared over in file.c */
@@ -19,4 +21,18 @@
extern const struct file_operations debugfs_open_proxy_file_operations;
extern const struct file_operations debugfs_full_proxy_file_operations;
+struct debugfs_fsdata {
+ const struct file_operations *real_fops;
+ refcount_t active_users;
+ struct completion active_users_drained;
+};
+
+/*
+ * A dentry's ->d_fsdata either points to the real fops or to a
+ * dynamically allocated debugfs_fsdata instance.
+ * In order to distinguish between these two cases, a real fops
+ * pointer gets its lowest bit set.
+ */
+#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)
+
#endif /* _DEBUGFS_INTERNAL_H_ */
diff --git a/fs/direct-io.c b/fs/direct-io.c
index c6220a2..bf03a92 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -411,6 +411,7 @@
if (dio->is_async && dio->op == REQ_OP_READ && dio->should_dirty)
bio_set_pages_dirty(bio);
+ bio->bi_dio_inode = dio->inode;
dio->bio_bdev = bio->bi_bdev;
if (sdio->submit_io) {
@@ -424,6 +425,18 @@
sdio->logical_offset_in_bio = 0;
}
+struct inode *dio_bio_get_inode(struct bio *bio)
+{
+ struct inode *inode = NULL;
+
+ if (bio == NULL)
+ return NULL;
+
+ inode = bio->bi_dio_inode;
+
+ return inode;
+}
+EXPORT_SYMBOL(dio_bio_get_inode);
/*
* Release any resources in case of a failure
*/
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index d8072bc..7f5b735 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -377,9 +377,8 @@
* and will start a new collection. Eventually caller must submit the last
* segment if present.
*/
-static int readpage_strip(void *data, struct page *page)
+static int __readpage_strip(struct page_collect *pcol, struct page *page)
{
- struct page_collect *pcol = data;
struct inode *inode = pcol->inode;
struct exofs_i_info *oi = exofs_i(inode);
loff_t i_size = i_size_read(inode);
@@ -470,6 +469,13 @@
return ret;
}
+static int readpage_strip(struct file *data, struct page *page)
+{
+ struct page_collect *pcol = (struct page_collect *)data;
+
+ return __readpage_strip(pcol, page);
+}
+
static int exofs_readpages(struct file *file, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
{
@@ -499,7 +505,7 @@
_pcol_init(&pcol, 1, page->mapping->host);
pcol.read_4_write = read_4_write;
- ret = readpage_strip(&pcol, page);
+ ret = __readpage_strip(&pcol, page);
if (ret) {
EXOFS_ERR("_readpage => %d\n", ret);
return ret;
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 41b8b44..85449a6 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -1258,21 +1258,11 @@
static void ext2_truncate_blocks(struct inode *inode, loff_t offset)
{
- /*
- * XXX: it seems like a bug here that we don't allow
- * IS_APPEND inode to have blocks-past-i_size trimmed off.
- * review and fix this.
- *
- * Also would be nice to be able to handle IO errors and such,
- * but that's probably too much to ask.
- */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return;
if (ext2_inode_is_fast_symlink(inode))
return;
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- return;
dax_sem_down_write(EXT2_I(inode));
__ext2_truncate_blocks(inode, offset);
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index e38039f..e9232a0 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -109,10 +109,16 @@
decrypted pages in the page cache.
config EXT4_FS_ENCRYPTION
- bool
- default y
+ bool "Ext4 FS Encryption"
+ default n
depends on EXT4_ENCRYPTION
+config EXT4_FS_ICE_ENCRYPTION
+ bool "Ext4 Encryption with ICE support"
+ default n
+ depends on EXT4_FS_ENCRYPTION
+ depends on PFK
+
config EXT4_DEBUG
bool "EXT4 debugging support"
depends on EXT4_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 354103f..b9dfa0d 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -1,6 +1,7 @@
#
# Makefile for the linux ext4-filesystem routines.
#
+ccflags-y += -Ifs/crypto
obj-$(CONFIG_EXT4_FS) += ext4.o
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index e04ec86..6776f4a 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -242,8 +242,6 @@
*/
ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group),
sb->s_blocksize * 8, bh->b_data);
- ext4_block_bitmap_csum_set(sb, block_group, gdp, bh);
- ext4_group_desc_csum_set(sb, block_group, gdp);
return 0;
}
@@ -322,6 +320,7 @@
struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_grpblk_t offset;
ext4_grpblk_t next_zero_bit;
+ ext4_grpblk_t max_bit = EXT4_CLUSTERS_PER_GROUP(sb);
ext4_fsblk_t blk;
ext4_fsblk_t group_first_block;
@@ -339,20 +338,25 @@
/* check whether block bitmap block number is set */
blk = ext4_block_bitmap(sb, desc);
offset = blk - group_first_block;
- if (!ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
+ if (offset < 0 || EXT4_B2C(sbi, offset) >= max_bit ||
+ !ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
/* bad block bitmap */
return blk;
/* check whether the inode bitmap block number is set */
blk = ext4_inode_bitmap(sb, desc);
offset = blk - group_first_block;
- if (!ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
+ if (offset < 0 || EXT4_B2C(sbi, offset) >= max_bit ||
+ !ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
/* bad block bitmap */
return blk;
/* check whether the inode table block number is set */
blk = ext4_inode_table(sb, desc);
offset = blk - group_first_block;
+ if (offset < 0 || EXT4_B2C(sbi, offset) >= max_bit ||
+ EXT4_B2C(sbi, offset + sbi->s_itb_per_group) >= max_bit)
+ return blk;
next_zero_bit = ext4_find_next_zero_bit(bh->b_data,
EXT4_B2C(sbi, offset + EXT4_SB(sb)->s_itb_per_group),
EXT4_B2C(sbi, offset));
@@ -418,6 +422,7 @@
ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
{
struct ext4_group_desc *desc;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
struct buffer_head *bh;
ext4_fsblk_t bitmap_blk;
int err;
@@ -426,6 +431,12 @@
if (!desc)
return ERR_PTR(-EFSCORRUPTED);
bitmap_blk = ext4_block_bitmap(sb, desc);
+ if ((bitmap_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
+ (bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
+ ext4_error(sb, "Invalid block bitmap block %llu in "
+ "block_group %u", bitmap_blk, block_group);
+ return ERR_PTR(-EFSCORRUPTED);
+ }
bh = sb_getblk(sb, bitmap_blk);
if (unlikely(!bh)) {
ext4_error(sb, "Cannot get buffer for block bitmap - "
@@ -447,6 +458,7 @@
err = ext4_init_block_bitmap(sb, bh, block_group, desc);
set_bitmap_uptodate(bh);
set_buffer_uptodate(bh);
+ set_buffer_verified(bh);
ext4_unlock_group(sb, block_group);
unlock_buffer(bh);
if (err) {
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index a8573fa..fb9ae76 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -5358,8 +5358,9 @@
stop = le32_to_cpu(extent->ee_block);
/*
- * In case of left shift, Don't start shifting extents until we make
- * sure the hole is big enough to accommodate the shift.
+ * For left shifts, make sure the hole on the left is big enough to
+ * accommodate the shift. For right shifts, make sure the last extent
+ * won't be shifted beyond EXT_MAX_BLOCKS.
*/
if (SHIFT == SHIFT_LEFT) {
path = ext4_find_extent(inode, start - 1, &path,
@@ -5379,9 +5380,14 @@
if ((start == ex_start && shift > ex_start) ||
(shift > start - ex_end)) {
- ext4_ext_drop_refs(path);
- kfree(path);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
+ }
+ } else {
+ if (shift > EXT_MAX_BLOCKS -
+ (stop + ext4_ext_get_actual_len(extent))) {
+ ret = -EINVAL;
+ goto out;
}
}
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 510e664..08fca4a 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -429,7 +429,7 @@
int i, num;
unsigned long nr_pages;
- num = min_t(pgoff_t, end - index, PAGEVEC_SIZE);
+ num = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1;
nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
(pgoff_t)num);
if (nr_pages == 0)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 2cec605..1ee26da 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -63,44 +63,6 @@
memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
}
-/* Initializes an uninitialized inode bitmap */
-static int ext4_init_inode_bitmap(struct super_block *sb,
- struct buffer_head *bh,
- ext4_group_t block_group,
- struct ext4_group_desc *gdp)
-{
- struct ext4_group_info *grp;
- struct ext4_sb_info *sbi = EXT4_SB(sb);
- J_ASSERT_BH(bh, buffer_locked(bh));
-
- /* If checksum is bad mark all blocks and inodes use to prevent
- * allocation, essentially implementing a per-group read-only flag. */
- if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
- grp = ext4_get_group_info(sb, block_group);
- if (!EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
- percpu_counter_sub(&sbi->s_freeclusters_counter,
- grp->bb_free);
- set_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &grp->bb_state);
- if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
- int count;
- count = ext4_free_inodes_count(sb, gdp);
- percpu_counter_sub(&sbi->s_freeinodes_counter,
- count);
- }
- set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state);
- return -EFSBADCRC;
- }
-
- memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);
- ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8,
- bh->b_data);
- ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh,
- EXT4_INODES_PER_GROUP(sb) / 8);
- ext4_group_desc_csum_set(sb, block_group, gdp);
-
- return 0;
-}
-
void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate)
{
if (uptodate) {
@@ -157,6 +119,7 @@
ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
{
struct ext4_group_desc *desc;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
struct buffer_head *bh = NULL;
ext4_fsblk_t bitmap_blk;
int err;
@@ -166,6 +129,12 @@
return ERR_PTR(-EFSCORRUPTED);
bitmap_blk = ext4_inode_bitmap(sb, desc);
+ if ((bitmap_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
+ (bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
+ ext4_error(sb, "Invalid inode bitmap blk %llu in "
+ "block_group %u", bitmap_blk, block_group);
+ return ERR_PTR(-EFSCORRUPTED);
+ }
bh = sb_getblk(sb, bitmap_blk);
if (unlikely(!bh)) {
ext4_error(sb, "Cannot read inode bitmap - "
@@ -184,17 +153,14 @@
ext4_lock_group(sb, block_group);
if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
- err = ext4_init_inode_bitmap(sb, bh, block_group, desc);
+ memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);
+ ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb),
+ sb->s_blocksize * 8, bh->b_data);
set_bitmap_uptodate(bh);
set_buffer_uptodate(bh);
set_buffer_verified(bh);
ext4_unlock_group(sb, block_group);
unlock_buffer(bh);
- if (err) {
- ext4_error(sb, "Failed to init inode bitmap for group "
- "%u: %d", block_group, err);
- goto out;
- }
return bh;
}
ext4_unlock_group(sb, block_group);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 21c0670..ecee29a 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@
#include "xattr.h"
#include "acl.h"
#include "truncate.h"
+//#include "ext4_ice.h"
#include <trace/events/ext4.h>
#include <trace/events/android_fs.h>
@@ -1152,7 +1153,8 @@
ll_rw_block(REQ_OP_READ, 0, 1, &bh);
*wait_bh++ = bh;
decrypt = ext4_encrypted_inode(inode) &&
- S_ISREG(inode->i_mode);
+ S_ISREG(inode->i_mode) &&
+ !fscrypt_using_hardware_encryption(inode);
}
}
/*
@@ -3421,7 +3423,6 @@
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
- struct ext4_inode_info *ei = EXT4_I(inode);
ssize_t ret;
loff_t offset = iocb->ki_pos;
size_t count = iov_iter_count(iter);
@@ -3445,7 +3446,7 @@
goto out;
}
orphan = 1;
- ei->i_disksize = inode->i_size;
+ ext4_update_i_disksize(inode, inode->i_size);
ext4_journal_stop(handle);
}
@@ -3510,8 +3511,9 @@
get_block_func = ext4_dio_get_block_unwritten_async;
dio_flags = DIO_LOCKING;
}
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
- BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode));
+#if defined(CONFIG_EXT4_FS_ENCRYPTION)
+ WARN_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)
+ && !fscrypt_using_hardware_encryption(inode));
#endif
if (IS_DAX(inode)) {
ret = dax_do_io(iocb, inode, iter, get_block_func,
@@ -3573,7 +3575,7 @@
if (ret > 0) {
loff_t end = offset + ret;
if (end > inode->i_size) {
- ei->i_disksize = end;
+ ext4_update_i_disksize(inode, end);
i_size_write(inode, end);
/*
* We're going to return a positive `ret'
@@ -3632,8 +3634,9 @@
ssize_t ret;
int rw = iov_iter_rw(iter);
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
- if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+#if defined(CONFIG_EXT4_FS_ENCRYPTION)
+ if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)
+ && !fscrypt_using_hardware_encryption(inode))
return 0;
#endif
@@ -3829,7 +3832,8 @@
if (!buffer_uptodate(bh))
goto unlock;
if (S_ISREG(inode->i_mode) &&
- ext4_encrypted_inode(inode)) {
+ ext4_encrypted_inode(inode) &&
+ !fscrypt_using_hardware_encryption(inode)) {
/* We expect the key to be set. */
BUG_ON(!fscrypt_has_encryption_key(inode));
BUG_ON(blocksize != PAGE_SIZE);
@@ -4561,6 +4565,12 @@
goto bad_inode;
raw_inode = ext4_raw_inode(&iloc);
+ if ((ino == EXT4_ROOT_INO) && (raw_inode->i_links_count == 0)) {
+ EXT4_ERROR_INODE(inode, "root inode unallocated");
+ ret = -EFSCORRUPTED;
+ goto bad_inode;
+ }
+
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 15ca15c..3585e26 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -774,8 +774,8 @@
return ext4_ext_precache(inode);
case EXT4_IOC_SET_ENCRYPTION_POLICY:
- if (!ext4_has_feature_encrypt(sb))
- return -EOPNOTSUPP;
+// if (!ext4_has_feature_encrypt(sb))
+// return -EOPNOTSUPP;
return fscrypt_ioctl_set_policy(filp, (const void __user *)arg);
case EXT4_IOC_GET_ENCRYPTION_PWSALT: {
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index e5e99a7..4beca06 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -3878,7 +3878,8 @@
err = ext4_mb_load_buddy(sb, group, &e4b);
if (err) {
- ext4_error(sb, "Error loading buddy information for %u", group);
+ ext4_warning(sb, "Error %d loading buddy information for %u",
+ err, group);
put_bh(bitmap_bh);
return 0;
}
@@ -4035,10 +4036,11 @@
BUG_ON(pa->pa_type != MB_INODE_PA);
group = ext4_get_group_number(sb, pa->pa_pstart);
- err = ext4_mb_load_buddy(sb, group, &e4b);
+ err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
+ GFP_NOFS|__GFP_NOFAIL);
if (err) {
- ext4_error(sb, "Error loading buddy information for %u",
- group);
+ ext4_error(sb, "Error %d loading buddy information for %u",
+ err, group);
continue;
}
@@ -4294,11 +4296,14 @@
spin_unlock(&lg->lg_prealloc_lock);
list_for_each_entry_safe(pa, tmp, &discard_list, u.pa_tmp_list) {
+ int err;
group = ext4_get_group_number(sb, pa->pa_pstart);
- if (ext4_mb_load_buddy(sb, group, &e4b)) {
- ext4_error(sb, "Error loading buddy information for %u",
- group);
+ err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
+ GFP_NOFS|__GFP_NOFAIL);
+ if (err) {
+ ext4_error(sb, "Error %d loading buddy information for %u",
+ err, group);
continue;
}
ext4_lock_group(sb, group);
@@ -5122,8 +5127,8 @@
ret = ext4_mb_load_buddy(sb, group, &e4b);
if (ret) {
- ext4_error(sb, "Error in loading buddy "
- "information for %u", group);
+ ext4_warning(sb, "Error %d loading buddy information for %u",
+ ret, group);
return ret;
}
bitmap = e4b.bd_bitmap;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 8338cd4..e3183e8 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3027,36 +3027,16 @@
struct inode *inode;
int err, len = strlen(symname);
int credits;
- bool encryption_required;
struct fscrypt_str disk_link;
- struct fscrypt_symlink_data *sd = NULL;
- disk_link.len = len + 1;
- disk_link.name = (char *) symname;
-
- encryption_required = (ext4_encrypted_inode(dir) ||
- DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)));
- if (encryption_required) {
- err = fscrypt_get_encryption_info(dir);
- if (err)
- return err;
- if (!fscrypt_has_encryption_key(dir))
- return -ENOKEY;
- disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
- sizeof(struct fscrypt_symlink_data));
- sd = kzalloc(disk_link.len, GFP_KERNEL);
- if (!sd)
- return -ENOMEM;
- }
-
- if (disk_link.len > dir->i_sb->s_blocksize) {
- err = -ENAMETOOLONG;
- goto err_free_sd;
- }
+ err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
+ &disk_link);
+ if (err)
+ return err;
err = dquot_initialize(dir);
if (err)
- goto err_free_sd;
+ return err;
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
/*
@@ -3085,27 +3065,18 @@
if (IS_ERR(inode)) {
if (handle)
ext4_journal_stop(handle);
- err = PTR_ERR(inode);
- goto err_free_sd;
+ return PTR_ERR(inode);
}
- if (encryption_required) {
- struct qstr istr;
- struct fscrypt_str ostr =
- FSTR_INIT(sd->encrypted_path, disk_link.len);
-
- istr.name = (const unsigned char *) symname;
- istr.len = len;
- err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
+ if (IS_ENCRYPTED(inode)) {
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
if (err)
goto err_drop_inode;
- sd->len = cpu_to_le16(ostr.len);
- disk_link.name = (char *) sd;
inode->i_op = &ext4_encrypted_symlink_inode_operations;
}
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
- if (!encryption_required)
+ if (!IS_ENCRYPTED(inode))
inode->i_op = &ext4_symlink_inode_operations;
inode_nohighmem(inode);
ext4_set_aops(inode);
@@ -3147,7 +3118,7 @@
} else {
/* clear the extent format for fast symlink */
ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
- if (!encryption_required) {
+ if (!IS_ENCRYPTED(inode)) {
inode->i_op = &ext4_fast_symlink_inode_operations;
inode->i_link = (char *)&EXT4_I(inode)->i_data;
}
@@ -3162,16 +3133,17 @@
if (handle)
ext4_journal_stop(handle);
- kfree(sd);
- return err;
+ goto out_free_encrypted_link;
+
err_drop_inode:
if (handle)
ext4_journal_stop(handle);
clear_nlink(inode);
unlock_new_inode(inode);
iput(inode);
-err_free_sd:
- kfree(sd);
+out_free_encrypted_link:
+ if (disk_link.name != (unsigned char *)symname)
+ kfree(disk_link.name);
return err;
}
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 0718a86..8d4ec1a 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -28,6 +28,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+//#include "ext4_ice.h"
static struct kmem_cache *io_end_cachep;
@@ -469,6 +470,7 @@
gfp_t gfp_flags = GFP_NOFS;
retry_encrypt:
+ if (!fscrypt_using_hardware_encryption(inode))
data_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0,
page->index, gfp_flags);
if (IS_ERR(data_page)) {
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index 2531cc1..c39a12d 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -91,7 +91,7 @@
if (bio->bi_error) {
fscrypt_release_ctx(bio->bi_private);
} else {
- fscrypt_decrypt_bio_pages(bio->bi_private, bio);
+ fscrypt_enqueue_decrypt_bio(bio->bi_private, bio);
return;
}
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 43ce5fc..23c5716 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1028,9 +1028,7 @@
jbd2_free_inode(EXT4_I(inode)->jinode);
EXT4_I(inode)->jinode = NULL;
}
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
- fscrypt_put_encryption_info(inode, NULL);
-#endif
+ fscrypt_put_encryption_info(inode);
}
static struct inode *ext4_nfs_get_inode(struct super_block *sb,
@@ -1182,6 +1180,11 @@
EXT4_NAME_LEN;
}
+static inline bool ext4_is_encrypted(struct inode *inode)
+{
+ return ext4_encrypted_inode(inode);
+}
+
static const struct fscrypt_operations ext4_cryptops = {
.key_prefix = "ext4:",
.get_context = ext4_get_context,
@@ -1189,6 +1192,7 @@
.dummy_context = ext4_dummy_context,
.empty_dir = ext4_empty_dir,
.max_namelen = ext4_max_namelen,
+ .is_encrypted = ext4_is_encrypted,
};
#endif
@@ -2272,6 +2276,8 @@
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
"Block bitmap for group %u overlaps "
"superblock", i);
+ if (!(sb->s_flags & MS_RDONLY))
+ return 0;
}
if (block_bitmap < first_block || block_bitmap > last_block) {
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
@@ -2284,6 +2290,8 @@
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
"Inode bitmap for group %u overlaps "
"superblock", i);
+ if (!(sb->s_flags & MS_RDONLY))
+ return 0;
}
if (inode_bitmap < first_block || inode_bitmap > last_block) {
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
@@ -2296,6 +2304,8 @@
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
"Inode table for group %u overlaps "
"superblock", i);
+ if (!(sb->s_flags & MS_RDONLY))
+ return 0;
}
if (inode_table < first_block ||
inode_table + sbi->s_itb_per_group - 1 > last_block) {
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index 557b3b0..d4ce3af 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -27,59 +27,28 @@
struct delayed_call *done)
{
struct page *cpage = NULL;
- char *caddr, *paddr = NULL;
- struct fscrypt_str cstr, pstr;
- struct fscrypt_symlink_data *sd;
- int res;
- u32 max_size = inode->i_sb->s_blocksize;
+ const void *caddr;
+ unsigned int max_size;
+ const char *paddr;
if (!dentry)
return ERR_PTR(-ECHILD);
- res = fscrypt_get_encryption_info(inode);
- if (res)
- return ERR_PTR(res);
-
if (ext4_inode_is_fast_symlink(inode)) {
- caddr = (char *) EXT4_I(inode)->i_data;
+ caddr = EXT4_I(inode)->i_data;
max_size = sizeof(EXT4_I(inode)->i_data);
} else {
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
if (IS_ERR(cpage))
return ERR_CAST(cpage);
caddr = page_address(cpage);
+ max_size = inode->i_sb->s_blocksize;
}
- /* Symlink is encrypted */
- sd = (struct fscrypt_symlink_data *)caddr;
- cstr.name = sd->encrypted_path;
- cstr.len = le16_to_cpu(sd->len);
- if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
- /* Symlink data on the disk is corrupted */
- res = -EFSCORRUPTED;
- goto errout;
- }
-
- res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
- if (res)
- goto errout;
- paddr = pstr.name;
-
- res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
- if (res)
- goto errout;
-
- /* Null-terminate the name */
- paddr[pstr.len] = '\0';
+ paddr = fscrypt_get_symlink(inode, caddr, max_size, done);
if (cpage)
put_page(cpage);
- set_delayed_call(done, kfree_link, paddr);
return paddr;
-errout:
- if (cpage)
- put_page(cpage);
- kfree(paddr);
- return ERR_PTR(res);
}
const struct inode_operations ext4_encrypted_symlink_inode_operations = {
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 3eeed8f..3fadfab 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -837,8 +837,6 @@
if (!IS_LAST_ENTRY(s->first))
ext4_xattr_rehash(header(s->base),
s->here);
- ext4_xattr_cache_insert(ext4_mb_cache,
- bs->bh);
}
ext4_xattr_block_csum_set(inode, bs->bh);
unlock_buffer(bs->bh);
@@ -959,6 +957,7 @@
} else if (bs->bh && s->base == bs->bh->b_data) {
/* We were modifying this block in-place. */
ea_bdebug(bs->bh, "keeping this block");
+ ext4_xattr_cache_insert(ext4_mb_cache, bs->bh);
new_bh = bs->bh;
get_bh(new_bh);
} else {
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 701781a..91b2d00 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -68,6 +68,7 @@
.old_blkaddr = index,
.new_blkaddr = index,
.encrypted_page = NULL,
+ .is_meta = is_meta,
};
if (unlikely(!is_meta))
@@ -162,6 +163,7 @@
.op_flags = sync ? (REQ_META | REQ_PRIO) : REQ_RAHEAD,
.encrypted_page = NULL,
.in_list = false,
+ .is_meta = (type != META_POR),
};
struct blk_plug plug;
@@ -383,7 +385,7 @@
if (!PageUptodate(page))
SetPageUptodate(page);
if (!PageDirty(page)) {
- f2fs_set_page_dirty_nobuffers(page);
+ __set_page_dirty_nobuffers(page);
inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META);
SetPagePrivate(page);
f2fs_trace_pid(page);
@@ -572,13 +574,8 @@
struct node_info ni;
int err = acquire_orphan_inode(sbi);
- if (err) {
- set_sbi_flag(sbi, SBI_NEED_FSCK);
- f2fs_msg(sbi->sb, KERN_WARNING,
- "%s: orphan failed (ino=%x), run fsck to fix.",
- __func__, ino);
- return err;
- }
+ if (err)
+ goto err_out;
__add_ino_entry(sbi, ino, 0, ORPHAN_INO);
@@ -592,6 +589,11 @@
return PTR_ERR(inode);
}
+ err = dquot_initialize(inode);
+ if (err)
+ goto err_out;
+
+ dquot_initialize(inode);
clear_nlink(inode);
/* truncate all the data during iput */
@@ -601,14 +603,18 @@
/* ENOMEM was fully retried in f2fs_evict_inode. */
if (ni.blk_addr != NULL_ADDR) {
- set_sbi_flag(sbi, SBI_NEED_FSCK);
- f2fs_msg(sbi->sb, KERN_WARNING,
- "%s: orphan failed (ino=%x) by kernel, retry mount.",
- __func__, ino);
- return -EIO;
+ err = -EIO;
+ goto err_out;
}
__remove_ino_entry(sbi, ino, ORPHAN_INO);
return 0;
+
+err_out:
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ f2fs_msg(sbi->sb, KERN_WARNING,
+ "%s: orphan failed (ino=%x), run fsck to fix.",
+ __func__, ino);
+ return err;
}
int recover_orphan_inodes(struct f2fs_sb_info *sbi)
@@ -1139,6 +1145,8 @@
if (cpc->reason & CP_TRIMMED)
__set_ckpt_flags(ckpt, CP_TRIMMED_FLAG);
+ else
+ __clear_ckpt_flags(ckpt, CP_TRIMMED_FLAG);
if (cpc->reason & CP_UMOUNT)
__set_ckpt_flags(ckpt, CP_UMOUNT_FLAG);
@@ -1165,6 +1173,39 @@
spin_unlock_irqrestore(&sbi->cp_lock, flags);
}
+static void commit_checkpoint(struct f2fs_sb_info *sbi,
+ void *src, block_t blk_addr)
+{
+ struct writeback_control wbc = {
+ .for_reclaim = 0,
+ };
+
+ /*
+ * pagevec_lookup_tag and lock_page again will take
+ * some extra time. Therefore, update_meta_pages and
+ * sync_meta_pages are combined in this function.
+ */
+ struct page *page = grab_meta_page(sbi, blk_addr);
+ int err;
+
+ memcpy(page_address(page), src, PAGE_SIZE);
+ set_page_dirty(page);
+
+ f2fs_wait_on_page_writeback(page, META, true);
+ f2fs_bug_on(sbi, PageWriteback(page));
+ if (unlikely(!clear_page_dirty_for_io(page)))
+ f2fs_bug_on(sbi, 1);
+
+ /* writeout cp pack 2 page */
+ err = __f2fs_write_meta_page(page, &wbc, FS_CP_META_IO);
+ f2fs_bug_on(sbi, err);
+
+ f2fs_put_page(page, 0);
+
+ /* submit checkpoint (with barrier if NOBARRIER is not set) */
+ f2fs_submit_merged_write(sbi, META_FLUSH);
+}
+
static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -1267,16 +1308,6 @@
}
}
- /* need to wait for end_io results */
- wait_on_all_pages_writeback(sbi);
- if (unlikely(f2fs_cp_error(sbi)))
- return -EIO;
-
- /* flush all device cache */
- err = f2fs_flush_device_cache(sbi);
- if (err)
- return err;
-
/* write out checkpoint buffer at block 0 */
update_meta_page(sbi, ckpt, start_blk++);
@@ -1304,26 +1335,26 @@
start_blk += NR_CURSEG_NODE_TYPE;
}
- /* writeout checkpoint block */
- update_meta_page(sbi, ckpt, start_blk);
+ /* update user_block_counts */
+ sbi->last_valid_block_count = sbi->total_valid_block_count;
+ percpu_counter_set(&sbi->alloc_valid_block_count, 0);
- /* wait for previous submitted node/meta pages writeback */
+ /* Here, we have one bio having CP pack except cp pack 2 page */
+ sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO);
+
+ /* wait for previous submitted meta pages writeback */
wait_on_all_pages_writeback(sbi);
if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
- filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LLONG_MAX);
- filemap_fdatawait_range(META_MAPPING(sbi), 0, LLONG_MAX);
+ /* flush all device cache */
+ err = f2fs_flush_device_cache(sbi);
+ if (err)
+ return err;
- /* update user_block_counts */
- sbi->last_valid_block_count = sbi->total_valid_block_count;
- percpu_counter_set(&sbi->alloc_valid_block_count, 0);
-
- /* Here, we only have one bio having CP pack */
- sync_meta_pages(sbi, META_FLUSH, LONG_MAX, FS_CP_META_IO);
-
- /* wait for previous submitted meta pages writeback */
+ /* barrier and flush checkpoint cp pack 2 page if it can */
+ commit_checkpoint(sbi, ckpt, start_blk);
wait_on_all_pages_writeback(sbi);
release_ino_entry(sbi, false);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 6c13ee3..9efe77e 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -19,8 +19,6 @@
#include <linux/bio.h>
#include <linux/prefetch.h>
#include <linux/uio.h>
-#include <linux/mm.h>
-#include <linux/memcontrol.h>
#include <linux/cleancache.h>
#include "f2fs.h"
@@ -30,6 +28,11 @@
#include <trace/events/f2fs.h>
#include <trace/events/android_fs.h>
+#define NUM_PREALLOC_POST_READ_CTXS 128
+
+static struct kmem_cache *bio_post_read_ctx_cache;
+static mempool_t *bio_post_read_ctx_pool;
+
static bool __is_cp_guaranteed(struct page *page)
{
struct address_space *mapping = page->mapping;
@@ -50,11 +53,77 @@
return false;
}
-static void f2fs_read_end_io(struct bio *bio)
+/* postprocessing steps for read bios */
+enum bio_post_read_step {
+ STEP_INITIAL = 0,
+ STEP_DECRYPT,
+};
+
+struct bio_post_read_ctx {
+ struct bio *bio;
+ struct work_struct work;
+ unsigned int cur_step;
+ unsigned int enabled_steps;
+};
+
+static void __read_end_io(struct bio *bio)
{
- struct bio_vec *bvec;
+ struct page *page;
+ struct bio_vec *bv;
int i;
+ bio_for_each_segment_all(bv, bio, i) {
+ page = bv->bv_page;
+
+ /* PG_error was set if any post_read step failed */
+ if (bio->bi_error || PageError(page)) {
+ ClearPageUptodate(page);
+ SetPageError(page);
+ } else {
+ SetPageUptodate(page);
+ }
+ unlock_page(page);
+ }
+ if (bio->bi_private)
+ mempool_free(bio->bi_private, bio_post_read_ctx_pool);
+ bio_put(bio);
+}
+
+static void bio_post_read_processing(struct bio_post_read_ctx *ctx);
+
+static void decrypt_work(struct work_struct *work)
+{
+ struct bio_post_read_ctx *ctx =
+ container_of(work, struct bio_post_read_ctx, work);
+
+ fscrypt_decrypt_bio(ctx->bio);
+
+ bio_post_read_processing(ctx);
+}
+
+static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
+{
+ switch (++ctx->cur_step) {
+ case STEP_DECRYPT:
+ if (ctx->enabled_steps & (1 << STEP_DECRYPT)) {
+ INIT_WORK(&ctx->work, decrypt_work);
+ fscrypt_enqueue_decrypt_work(&ctx->work);
+ return;
+ }
+ ctx->cur_step++;
+ /* fall-through */
+ default:
+ __read_end_io(ctx->bio);
+ }
+}
+
+static bool f2fs_bio_post_read_required(struct bio *bio)
+{
+ return bio->bi_private && !bio->bi_error;
+}
+
+static void f2fs_read_end_io(struct bio *bio)
+{
#ifdef CONFIG_F2FS_FAULT_INJECTION
if (time_to_inject(F2FS_P_SB(bio->bi_io_vec->bv_page), FAULT_IO)) {
f2fs_show_injection_info(FAULT_IO);
@@ -62,28 +131,15 @@
}
#endif
- if (f2fs_bio_encrypted(bio)) {
- if (bio->bi_error) {
- fscrypt_release_ctx(bio->bi_private);
- } else {
- fscrypt_decrypt_bio_pages(bio->bi_private, bio);
- return;
- }
+ if (f2fs_bio_post_read_required(bio)) {
+ struct bio_post_read_ctx *ctx = bio->bi_private;
+
+ ctx->cur_step = STEP_INITIAL;
+ bio_post_read_processing(ctx);
+ return;
}
- bio_for_each_segment_all(bvec, bio, i) {
- struct page *page = bvec->bv_page;
-
- if (!bio->bi_error) {
- if (!PageUptodate(page))
- SetPageUptodate(page);
- } else {
- ClearPageUptodate(page);
- SetPageError(page);
- }
- unlock_page(page);
- }
- bio_put(bio);
+ __read_end_io(bio);
}
static void f2fs_write_end_io(struct bio *bio)
@@ -174,15 +230,22 @@
*/
static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr,
struct writeback_control *wbc,
- int npages, bool is_read)
+ int npages, bool is_read,
+ enum page_type type, enum temp_type temp)
{
struct bio *bio;
bio = f2fs_bio_alloc(sbi, npages, true);
f2fs_target_device(sbi, blk_addr, bio);
- bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io;
- bio->bi_private = is_read ? NULL : sbi;
+ if (is_read) {
+ bio->bi_end_io = f2fs_read_end_io;
+ bio->bi_private = NULL;
+ } else {
+ bio->bi_end_io = f2fs_write_end_io;
+ bio->bi_private = sbi;
+ bio->bi_write_hint = io_type_to_rw_hint(sbi, type, temp);
+ }
if (wbc)
wbc_init_bio(wbc, bio);
@@ -195,13 +258,12 @@
if (!is_read_io(bio_op(bio))) {
unsigned int start;
- if (f2fs_sb_mounted_blkzoned(sbi->sb) &&
- current->plug && (type == DATA || type == NODE))
- blk_finish_plug(current->plug);
-
if (type != DATA && type != NODE)
goto submit_io;
+ if (f2fs_sb_has_blkzoned(sbi->sb) && current->plug)
+ blk_finish_plug(current->plug);
+
start = bio->bi_iter.bi_size >> F2FS_BLKSIZE_BITS;
start %= F2FS_IO_SIZE(sbi);
@@ -375,13 +437,18 @@
struct bio *bio;
struct page *page = fio->encrypted_page ?
fio->encrypted_page : fio->page;
+ struct inode *inode = fio->page->mapping->host;
+ verify_block_addr(fio, fio->new_blkaddr);
trace_f2fs_submit_page_bio(page, fio);
f2fs_trace_ios(fio, 0);
/* Allocate a new bio */
bio = __bio_alloc(fio->sbi, fio->new_blkaddr, fio->io_wbc,
- 1, is_read_io(fio->op));
+ 1, is_read_io(fio->op), fio->type, fio->temp);
+
+ if (f2fs_may_encrypt_bio(inode, fio))
+ fscrypt_set_ice_dun(inode, bio, PG_DUN(inode, fio->page));
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
bio_put(bio);
@@ -402,6 +469,9 @@
enum page_type btype = PAGE_TYPE_OF_BIO(fio->type);
struct f2fs_bio_info *io = sbi->write_io[btype] + fio->temp;
struct page *bio_page;
+ struct inode *inode;
+ bool bio_encrypted;
+ u64 dun;
int err = 0;
f2fs_bug_on(sbi, is_read_io(fio->op));
@@ -421,10 +491,13 @@
}
if (fio->old_blkaddr != NEW_ADDR)
- verify_block_addr(sbi, fio->old_blkaddr);
- verify_block_addr(sbi, fio->new_blkaddr);
+ verify_block_addr(fio, fio->old_blkaddr);
+ verify_block_addr(fio, fio->new_blkaddr);
bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page;
+ inode = fio->page->mapping->host;
+ dun = PG_DUN(inode, fio->page);
+ bio_encrypted = f2fs_may_encrypt_bio(inode, fio);
/* set submitted = true as a return value */
fio->submitted = true;
@@ -435,6 +508,11 @@
(io->fio.op != fio->op || io->fio.op_flags != fio->op_flags) ||
!__same_bdev(sbi, fio->new_blkaddr, io->bio)))
__submit_merged_bio(io);
+
+ /* ICE support */
+ if (!fscrypt_mergeable_bio(io->bio, dun, bio_encrypted))
+ __submit_merged_bio(io);
+
alloc_new:
if (io->bio == NULL) {
if ((fio->type == DATA || fio->type == NODE) &&
@@ -444,7 +522,11 @@
goto out_fail;
}
io->bio = __bio_alloc(sbi, fio->new_blkaddr, fio->io_wbc,
- BIO_MAX_PAGES, false);
+ BIO_MAX_PAGES, false,
+ fio->type, fio->temp);
+ if (bio_encrypted)
+ fscrypt_set_ice_dun(inode, io->bio, dun);
+
io->fio = *fio;
}
@@ -472,29 +554,33 @@
unsigned nr_pages)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct fscrypt_ctx *ctx = NULL;
struct bio *bio;
+ struct bio_post_read_ctx *ctx;
+ unsigned int post_read_steps = 0;
- if (f2fs_encrypted_file(inode)) {
- ctx = fscrypt_get_ctx(inode, GFP_NOFS);
- if (IS_ERR(ctx))
- return ERR_CAST(ctx);
+ bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false);
+ if (!bio)
+ return ERR_PTR(-ENOMEM);
+ f2fs_target_device(sbi, blkaddr, bio);
+ bio->bi_end_io = f2fs_read_end_io;
+ bio_set_op_attrs(bio, REQ_OP_READ, 0);
+
+ if (f2fs_encrypted_file(inode))
+ post_read_steps |= 1 << STEP_DECRYPT;
+ if (post_read_steps) {
+ ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
+ if (!ctx) {
+ bio_put(bio);
+ return ERR_PTR(-ENOMEM);
+ }
+ ctx->bio = bio;
+ ctx->enabled_steps = post_read_steps;
+ bio->bi_private = ctx;
/* wait the page to be moved by cleaning */
f2fs_wait_on_block_writeback(sbi, blkaddr);
}
- bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false);
- if (!bio) {
- if (ctx)
- fscrypt_release_ctx(ctx);
- return ERR_PTR(-ENOMEM);
- }
- f2fs_target_device(sbi, blkaddr, bio);
- bio->bi_end_io = f2fs_read_end_io;
- bio->bi_private = ctx;
- bio_set_op_attrs(bio, REQ_OP_READ, 0);
-
return bio;
}
@@ -507,6 +593,9 @@
if (IS_ERR(bio))
return PTR_ERR(bio);
+ if (f2fs_may_encrypt_bio(inode, NULL))
+ fscrypt_set_ice_dun(inode, bio, PG_DUN(inode, page));
+
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
bio_put(bio);
return -EFAULT;
@@ -831,13 +920,6 @@
return 0;
}
-static inline bool __force_buffered_io(struct inode *inode, int rw)
-{
- return (f2fs_encrypted_file(inode) ||
- (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) ||
- F2FS_I_SB(inode)->s_ndevs);
-}
-
int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from)
{
struct inode *inode = file_inode(iocb->ki_filp);
@@ -868,9 +950,8 @@
map.m_seg_type = NO_CHECK_TYPE;
if (direct_io) {
- /* map.m_seg_type = rw_hint_to_seg_type(iocb->ki_hint); */
- map.m_seg_type = rw_hint_to_seg_type(WRITE_LIFE_NOT_SET);
- flag = __force_buffered_io(inode, WRITE) ?
+ map.m_seg_type = rw_hint_to_seg_type(iocb->ki_hint);
+ flag = f2fs_force_buffered_io(inode, WRITE) ?
F2FS_GET_BLOCK_PRE_AIO :
F2FS_GET_BLOCK_PRE_DIO;
goto map_blocks;
@@ -1114,6 +1195,31 @@
return err;
}
+bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len)
+{
+ struct f2fs_map_blocks map;
+ block_t last_lblk;
+ int err;
+
+ if (pos + len > i_size_read(inode))
+ return false;
+
+ map.m_lblk = F2FS_BYTES_TO_BLK(pos);
+ map.m_next_pgofs = NULL;
+ map.m_next_extent = NULL;
+ map.m_seg_type = NO_CHECK_TYPE;
+ last_lblk = F2FS_BLK_ALIGN(pos + len);
+
+ while (map.m_lblk < last_lblk) {
+ map.m_len = last_lblk - map.m_lblk;
+ err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT);
+ if (err || map.m_len == 0)
+ return false;
+ map.m_lblk += map.m_len;
+ }
+ return true;
+}
+
static int __get_data_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh, int create, int flag,
pgoff_t *next_pgofs, int seg_type)
@@ -1151,8 +1257,7 @@
return __get_data_block(inode, iblock, bh_result, create,
F2FS_GET_BLOCK_DEFAULT, NULL,
rw_hint_to_seg_type(
- WRITE_LIFE_NOT_SET));
- /* inode->i_write_hint)); */
+ inode->i_write_hint));
}
static int get_data_block_bmap(struct inode *inode, sector_t iblock,
@@ -1350,6 +1455,8 @@
sector_t last_block_in_file;
sector_t block_nr;
struct f2fs_map_blocks map;
+ bool bio_encrypted;
+ u64 dun;
map.m_pblk = 0;
map.m_lblk = 0;
@@ -1427,6 +1534,14 @@
__submit_bio(F2FS_I_SB(inode), bio, DATA);
bio = NULL;
}
+
+ dun = PG_DUN(inode, page);
+ bio_encrypted = f2fs_may_encrypt_bio(inode, NULL);
+ if (!fscrypt_mergeable_bio(bio, dun, bio_encrypted)) {
+ __submit_bio(F2FS_I_SB(inode), bio, DATA);
+ bio = NULL;
+ }
+
if (bio == NULL) {
bio = f2fs_grab_read_bio(inode, block_nr, nr_pages);
if (IS_ERR(bio)) {
@@ -1434,7 +1549,8 @@
goto set_error_page;
}
}
-
+ if (bio_encrypted)
+ fscrypt_set_ice_dun(inode, bio, dun);
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
goto submit_and_realloc;
@@ -1500,10 +1616,13 @@
if (!f2fs_encrypted_file(inode))
return 0;
- /* wait for GCed encrypted page writeback */
+ /* wait for GCed page writeback via META_MAPPING */
f2fs_wait_on_block_writeback(fio->sbi, fio->old_blkaddr);
retry_encrypt:
+ if (fscrypt_using_hardware_encryption(inode))
+ return 0;
+
fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page,
PAGE_SIZE, 0, fio->page->index, gfp_flags);
if (!IS_ERR(fio->encrypted_page))
@@ -1650,6 +1769,7 @@
goto out_writepage;
set_page_writeback(page);
+ ClearPageError(page);
f2fs_put_dnode(&dn);
if (fio->need_lock == LOCK_REQ)
f2fs_unlock_op(fio->sbi);
@@ -1672,6 +1792,7 @@
goto out_writepage;
set_page_writeback(page);
+ ClearPageError(page);
/* LFS mode write path */
write_data_page(&dn, fio);
@@ -1817,7 +1938,13 @@
redirty_out:
redirty_page_for_writepage(wbc, page);
- if (!err)
+ /*
+ * pageout() in MM traslates EAGAIN, so calls handle_write_error()
+ * -> mapping_set_error() -> set_bit(AS_EIO, ...).
+ * file_write_and_wait_range() will see EIO error, which is critical
+ * to return value of fsync() followed by atomic_write failure to user.
+ */
+ if (!err || wbc->for_reclaim)
return AOP_WRITEPAGE_ACTIVATE;
unlock_page(page);
return err;
@@ -2212,8 +2339,8 @@
f2fs_wait_on_page_writeback(page, DATA, false);
- /* wait for GCed encrypted page writeback */
- if (f2fs_encrypted_file(inode))
+ /* wait for GCed page writeback via META_MAPPING */
+ if (f2fs_post_read_required(inode))
f2fs_wait_on_block_writeback(sbi, blkaddr);
if (len == PAGE_SIZE || PageUptodate(page))
@@ -2304,16 +2431,19 @@
{
struct address_space *mapping = iocb->ki_filp->f_mapping;
struct inode *inode = mapping->host;
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
size_t count = iov_iter_count(iter);
loff_t offset = iocb->ki_pos;
int rw = iov_iter_rw(iter);
int err;
+ enum rw_hint hint = iocb->ki_hint;
+ int whint_mode = F2FS_OPTION(sbi).whint_mode;
err = check_direct_IO(inode, iter, offset);
if (err)
return err;
- if (__force_buffered_io(inode, rw))
+ if (f2fs_force_buffered_io(inode, rw))
return 0;
trace_f2fs_direct_IO_enter(inode, offset, count, rw);
@@ -2340,12 +2470,24 @@
current->pid, path,
current->comm);
}
+ if (rw == WRITE && whint_mode == WHINT_MODE_OFF)
+ iocb->ki_hint = WRITE_LIFE_NOT_SET;
- down_read(&F2FS_I(inode)->dio_rwsem[rw]);
+ if (!down_read_trylock(&F2FS_I(inode)->dio_rwsem[rw])) {
+ if (iocb->ki_flags & IOCB_NOWAIT) {
+ iocb->ki_hint = hint;
+ err = -EAGAIN;
+ goto out;
+ }
+ down_read(&F2FS_I(inode)->dio_rwsem[rw]);
+ }
+
err = blockdev_direct_IO(iocb, inode, iter, get_data_block_dio);
up_read(&F2FS_I(inode)->dio_rwsem[rw]);
if (rw == WRITE) {
+ if (whint_mode == WHINT_MODE_OFF)
+ iocb->ki_hint = hint;
if (err > 0) {
f2fs_update_iostat(F2FS_I_SB(inode), APP_DIRECT_IO,
err);
@@ -2354,7 +2496,7 @@
f2fs_write_failed(mapping, offset + count);
}
}
-
+out:
if (trace_android_fs_dataread_start_enabled() &&
(rw == READ))
trace_android_fs_dataread_end(inode, offset, count);
@@ -2411,35 +2553,6 @@
return 1;
}
-/*
- * This was copied from __set_page_dirty_buffers which gives higher performance
- * in very high speed storages. (e.g., pmem)
- */
-void f2fs_set_page_dirty_nobuffers(struct page *page)
-{
- struct address_space *mapping = page->mapping;
- unsigned long flags;
-
- if (unlikely(!mapping))
- return;
-
- spin_lock(&mapping->private_lock);
- lock_page_memcg(page);
- SetPageDirty(page);
- spin_unlock(&mapping->private_lock);
-
- spin_lock_irqsave(&mapping->tree_lock, flags);
- WARN_ON_ONCE(!PageUptodate(page));
- account_page_dirtied(page, mapping);
- radix_tree_tag_set(&mapping->page_tree,
- page_index(page), PAGECACHE_TAG_DIRTY);
- spin_unlock_irqrestore(&mapping->tree_lock, flags);
- unlock_page_memcg(page);
-
- __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
- return;
-}
-
static int f2fs_set_data_page_dirty(struct page *page)
{
struct address_space *mapping = page->mapping;
@@ -2463,7 +2576,7 @@
}
if (!PageDirty(page)) {
- f2fs_set_page_dirty_nobuffers(page);
+ __set_page_dirty_nobuffers(page);
update_dirty_page(inode, page);
return 1;
}
@@ -2556,3 +2669,27 @@
.migratepage = f2fs_migrate_page,
#endif
};
+
+int __init f2fs_init_post_read_processing(void)
+{
+ bio_post_read_ctx_cache = KMEM_CACHE(bio_post_read_ctx, 0);
+ if (!bio_post_read_ctx_cache)
+ goto fail;
+ bio_post_read_ctx_pool =
+ mempool_create_slab_pool(NUM_PREALLOC_POST_READ_CTXS,
+ bio_post_read_ctx_cache);
+ if (!bio_post_read_ctx_pool)
+ goto fail_free_cache;
+ return 0;
+
+fail_free_cache:
+ kmem_cache_destroy(bio_post_read_ctx_cache);
+fail:
+ return -ENOMEM;
+}
+
+void __exit f2fs_destroy_post_read_processing(void)
+{
+ mempool_destroy(bio_post_read_ctx_pool);
+ kmem_cache_destroy(bio_post_read_ctx_cache);
+}
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 6ef3c83..f9a1e18 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -94,14 +94,12 @@
struct f2fs_dir_entry *de;
struct f2fs_dentry_ptr d;
- dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
+ dentry_blk = (struct f2fs_dentry_block *)page_address(dentry_page);
make_dentry_ptr_block(NULL, &d, dentry_blk);
de = find_target_dentry(fname, namehash, max_slots, &d);
if (de)
*res_page = dentry_page;
- else
- kunmap(dentry_page);
return de;
}
@@ -287,7 +285,6 @@
de = f2fs_find_entry(dir, qstr, page);
if (de) {
res = le32_to_cpu(de->ino);
- f2fs_dentry_kunmap(dir, *page);
f2fs_put_page(*page, 0);
}
@@ -302,7 +299,6 @@
f2fs_wait_on_page_writeback(page, type, true);
de->ino = cpu_to_le32(inode->i_ino);
set_de_type(de, inode->i_mode);
- f2fs_dentry_kunmap(dir, page);
set_page_dirty(page);
dir->i_mtime = dir->i_ctime = current_time(dir);
@@ -350,13 +346,11 @@
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
- dentry_blk = kmap_atomic(dentry_page);
+ dentry_blk = page_address(dentry_page);
make_dentry_ptr_block(NULL, &d, dentry_blk);
do_make_empty_dir(inode, parent, &d);
- kunmap_atomic(dentry_blk);
-
set_page_dirty(dentry_page);
f2fs_put_page(dentry_page, 1);
return 0;
@@ -367,6 +361,7 @@
struct page *dpage)
{
struct page *page;
+ int dummy_encrypt = DUMMY_ENCRYPTION_ENABLED(F2FS_I_SB(dir));
int err;
if (is_inode_flag_set(inode, FI_NEW_INODE)) {
@@ -393,7 +388,8 @@
if (err)
goto put_error;
- if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
+ if ((f2fs_encrypted_inode(dir) || dummy_encrypt) &&
+ f2fs_may_encrypt(inode)) {
err = fscrypt_inherit_context(dir, inode, page, false);
if (err)
goto put_error;
@@ -402,8 +398,6 @@
page = get_node_page(F2FS_I_SB(dir), inode->i_ino);
if (IS_ERR(page))
return page;
-
- set_cold_node(inode, page);
}
if (new_name) {
@@ -547,13 +541,12 @@
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
- dentry_blk = kmap(dentry_page);
+ dentry_blk = page_address(dentry_page);
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
slots, NR_DENTRY_IN_BLOCK);
if (bit_pos < NR_DENTRY_IN_BLOCK)
goto add_dentry;
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
}
@@ -588,7 +581,6 @@
if (inode)
up_write(&F2FS_I(inode)->i_sem);
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
return err;
@@ -642,7 +634,6 @@
F2FS_I(dir)->task = NULL;
}
if (de) {
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
err = -EEXIST;
} else if (IS_ERR(page)) {
@@ -713,7 +704,8 @@
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
- add_ino_entry(F2FS_I_SB(dir), dir->i_ino, TRANS_DIR_INO);
+ if (F2FS_OPTION(F2FS_I_SB(dir)).fsync_mode == FSYNC_MODE_STRICT)
+ add_ino_entry(F2FS_I_SB(dir), dir->i_ino, TRANS_DIR_INO);
if (f2fs_has_inline_dentry(dir))
return f2fs_delete_inline_entry(dentry, page, dir, inode);
@@ -730,7 +722,6 @@
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
NR_DENTRY_IN_BLOCK,
0);
- kunmap(page); /* kunmap - pair of f2fs_find_entry */
set_page_dirty(page);
dir->i_ctime = dir->i_mtime = current_time(dir);
@@ -775,7 +766,7 @@
return false;
}
- dentry_blk = kmap_atomic(dentry_page);
+ dentry_blk = page_address(dentry_page);
if (bidx == 0)
bit_pos = 2;
else
@@ -783,7 +774,6 @@
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
NR_DENTRY_IN_BLOCK,
bit_pos);
- kunmap_atomic(dentry_blk);
f2fs_put_page(dentry_page, 1);
@@ -901,19 +891,17 @@
}
}
- dentry_blk = kmap(dentry_page);
+ dentry_blk = page_address(dentry_page);
make_dentry_ptr_block(inode, &d, dentry_blk);
err = f2fs_fill_dentries(ctx, &d,
n * NR_DENTRY_IN_BLOCK, &fstr);
if (err) {
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
break;
}
- kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
}
out_free:
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index ff2352a..d5a861b 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -460,7 +460,7 @@
struct rb_node *insert_parent)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct rb_node **p = &et->root.rb_node;
+ struct rb_node **p;
struct rb_node *parent = NULL;
struct extent_node *en = NULL;
@@ -706,6 +706,9 @@
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct extent_tree *et = F2FS_I(inode)->extent_tree;
+ if (!f2fs_may_extent_tree(inode))
+ return;
+
set_inode_flag(inode, FI_NO_EXTENT);
write_lock(&et->lock);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a8434af..c65b697 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -99,9 +99,10 @@
#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000
#define F2FS_MOUNT_RESERVE_ROOT 0x01000000
-#define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
-#define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
-#define test_opt(sbi, option) ((sbi)->mount_opt.opt & F2FS_MOUNT_##option)
+#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
+#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
+#define set_opt(sbi, option) (F2FS_OPTION(sbi).opt |= F2FS_MOUNT_##option)
+#define test_opt(sbi, option) (F2FS_OPTION(sbi).opt & F2FS_MOUNT_##option)
#define ver_after(a, b) (typecheck(unsigned long long, a) && \
typecheck(unsigned long long, b) && \
@@ -114,7 +115,26 @@
typedef u32 nid_t;
struct f2fs_mount_info {
- unsigned int opt;
+ unsigned int opt;
+ int write_io_size_bits; /* Write IO size bits */
+ block_t root_reserved_blocks; /* root reserved blocks */
+ kuid_t s_resuid; /* reserved blocks for uid */
+ kgid_t s_resgid; /* reserved blocks for gid */
+ int active_logs; /* # of active logs */
+ int inline_xattr_size; /* inline xattr size */
+#ifdef CONFIG_F2FS_FAULT_INJECTION
+ struct f2fs_fault_info fault_info; /* For fault injection */
+#endif
+#ifdef CONFIG_QUOTA
+ /* Names of quota files with journalled quota */
+ char *s_qf_names[MAXQUOTAS];
+ int s_jquota_fmt; /* Format of quota to use */
+#endif
+ /* For which write hints are passed down to block layer */
+ int whint_mode;
+ int alloc_mode; /* segment allocation policy */
+ int fsync_mode; /* fsync policy */
+ bool test_dummy_encryption; /* test dummy encryption */
};
#define F2FS_FEATURE_ENCRYPT 0x0001
@@ -126,6 +146,8 @@
#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040
#define F2FS_FEATURE_QUOTA_INO 0x0080
#define F2FS_FEATURE_INODE_CRTIME 0x0100
+#define F2FS_FEATURE_LOST_FOUND 0x0200
+#define F2FS_FEATURE_VERITY 0x0400 /* reserved */
#define F2FS_HAS_FEATURE(sb, mask) \
((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -448,7 +470,7 @@
d->inode = inode;
d->max = NR_DENTRY_IN_BLOCK;
d->nr_bitmap = SIZE_OF_DENTRY_BITMAP;
- d->bitmap = &t->dentry_bitmap;
+ d->bitmap = t->dentry_bitmap;
d->dentry = t->dentry;
d->filename = t->filename;
}
@@ -574,6 +596,8 @@
#define FADVISE_ENCRYPT_BIT 0x04
#define FADVISE_ENC_NAME_BIT 0x08
#define FADVISE_KEEP_SIZE_BIT 0x10
+#define FADVISE_HOT_BIT 0x20
+#define FADVISE_VERITY_BIT 0x40 /* reserved */
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
@@ -588,6 +612,9 @@
#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT)
#define file_keep_isize(inode) is_file(inode, FADVISE_KEEP_SIZE_BIT)
#define file_set_keep_isize(inode) set_file(inode, FADVISE_KEEP_SIZE_BIT)
+#define file_is_hot(inode) is_file(inode, FADVISE_HOT_BIT)
+#define file_set_hot(inode) set_file(inode, FADVISE_HOT_BIT)
+#define file_clear_hot(inode) clear_file(inode, FADVISE_HOT_BIT)
#define DEF_DIR_LEVEL 0
@@ -635,6 +662,7 @@
kprojid_t i_projid; /* id for project quota */
int i_inline_xattr_size; /* inline xattr size */
struct timespec i_crtime; /* inode creation time */
+ struct timespec i_disk_time[4]; /* inode disk times */
};
static inline void get_extent_info(struct extent_info *ext,
@@ -741,7 +769,7 @@
unsigned int nid_cnt[MAX_NID_STATE]; /* the number of free node id */
spinlock_t nid_list_lock; /* protect nid lists ops */
struct mutex build_lock; /* lock for build free nids */
- unsigned char (*free_nid_bitmap)[NAT_ENTRY_BITMAP_SIZE];
+ unsigned char **free_nid_bitmap;
unsigned char *nat_block_bitmap;
unsigned short *free_nid_count; /* free nid count of NAT block */
@@ -974,6 +1002,7 @@
bool submitted; /* indicate IO submission */
int need_lock; /* indicate we need to lock cp_rwsem */
bool in_list; /* indicate fio is in io_list */
+ bool is_meta; /* indicate borrow meta inode mapping or not */
enum iostat_type io_type; /* io type */
struct writeback_control *io_wbc; /* writeback control */
};
@@ -1035,10 +1064,34 @@
MAX_TIME,
};
+enum {
+ WHINT_MODE_OFF, /* not pass down write hints */
+ WHINT_MODE_USER, /* try to pass down hints given by users */
+ WHINT_MODE_FS, /* pass down hints with F2FS policy */
+};
+
+enum {
+ ALLOC_MODE_DEFAULT, /* stay default */
+ ALLOC_MODE_REUSE, /* reuse segments as much as possible */
+};
+
+enum fsync_mode {
+ FSYNC_MODE_POSIX, /* fsync follows posix semantics */
+ FSYNC_MODE_STRICT, /* fsync behaves in line with ext4 */
+};
+
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+#define DUMMY_ENCRYPTION_ENABLED(sbi) \
+ (unlikely(F2FS_OPTION(sbi).test_dummy_encryption))
+#else
+#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
+#endif
+
struct f2fs_sb_info {
struct super_block *sb; /* pointer to VFS super block */
struct proc_dir_entry *s_proc; /* proc entry */
struct f2fs_super_block *raw_super; /* raw super block pointer */
+ struct rw_semaphore sb_lock; /* lock for raw super block */
int valid_super_block; /* valid super block no */
unsigned long s_flag; /* flags for sbi */
@@ -1058,7 +1111,6 @@
struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */
struct mutex wio_mutex[NR_PAGE_TYPE - 1][NR_TEMP_TYPE];
/* bio ordering for NODE/DATA */
- int write_io_size_bits; /* Write IO size bits */
mempool_t *write_io_dummy; /* Dummy pages */
/* for checkpoint */
@@ -1108,9 +1160,7 @@
unsigned int total_node_count; /* total node block count */
unsigned int total_valid_node_count; /* valid node block count */
loff_t max_file_blocks; /* max block index of file */
- int active_logs; /* # of active logs */
int dir_level; /* directory level */
- int inline_xattr_size; /* inline xattr size */
unsigned int trigger_ssr_threshold; /* threshold to trigger ssr */
int readdir_ra; /* readahead inode in readdir */
@@ -1120,9 +1170,6 @@
block_t last_valid_block_count; /* for recovery */
block_t reserved_blocks; /* configurable reserved blocks */
block_t current_reserved_blocks; /* current reserved blocks */
- block_t root_reserved_blocks; /* root reserved blocks */
- kuid_t s_resuid; /* reserved blocks for uid */
- kgid_t s_resgid; /* reserved blocks for gid */
unsigned int nquota_files; /* # of quota sysfile */
@@ -1207,17 +1254,6 @@
/* Precomputed FS UUID checksum for seeding other checksums */
__u32 s_chksum_seed;
-
- /* For fault injection */
-#ifdef CONFIG_F2FS_FAULT_INJECTION
- struct f2fs_fault_info fault_info;
-#endif
-
-#ifdef CONFIG_QUOTA
- /* Names of quota files with journalled quota */
- char *s_qf_names[MAXQUOTAS];
- int s_jquota_fmt; /* Format of quota to use */
-#endif
};
#ifdef CONFIG_F2FS_FAULT_INJECTION
@@ -1227,7 +1263,7 @@
__func__, __builtin_return_address(0))
static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type)
{
- struct f2fs_fault_info *ffi = &sbi->fault_info;
+ struct f2fs_fault_info *ffi = &F2FS_OPTION(sbi).fault_info;
if (!ffi->inject_rate)
return false;
@@ -1576,7 +1612,7 @@
}
static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
- struct inode *inode)
+ struct inode *inode, bool cap)
{
if (!inode)
return true;
@@ -1584,12 +1620,12 @@
return false;
if (IS_NOQUOTA(inode))
return true;
- if (capable(CAP_SYS_RESOURCE))
+ if (uid_eq(F2FS_OPTION(sbi).s_resuid, current_fsuid()))
return true;
- if (uid_eq(sbi->s_resuid, current_fsuid()))
+ if (!gid_eq(F2FS_OPTION(sbi).s_resgid, GLOBAL_ROOT_GID) &&
+ in_group_p(F2FS_OPTION(sbi).s_resgid))
return true;
- if (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) &&
- in_group_p(sbi->s_resgid))
+ if (cap && capable(CAP_SYS_RESOURCE))
return true;
return false;
}
@@ -1624,8 +1660,8 @@
avail_user_block_count = sbi->user_block_count -
sbi->current_reserved_blocks;
- if (!__allow_reserved_blocks(sbi, inode))
- avail_user_block_count -= sbi->root_reserved_blocks;
+ if (!__allow_reserved_blocks(sbi, inode, true))
+ avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
diff = sbi->total_valid_block_count - avail_user_block_count;
@@ -1760,6 +1796,12 @@
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
int offset;
+ if (is_set_ckpt_flags(sbi, CP_LARGE_NAT_BITMAP_FLAG)) {
+ offset = (flag == SIT_BITMAP) ?
+ le32_to_cpu(ckpt->nat_ver_bitmap_bytesize) : 0;
+ return &ckpt->sit_nat_version_bitmap + offset;
+ }
+
if (__cp_payload(sbi) > 0) {
if (flag == NAT_BITMAP)
return &ckpt->sit_nat_version_bitmap;
@@ -1825,8 +1867,8 @@
valid_block_count = sbi->total_valid_block_count +
sbi->current_reserved_blocks + 1;
- if (!__allow_reserved_blocks(sbi, inode))
- valid_block_count += sbi->root_reserved_blocks;
+ if (!__allow_reserved_blocks(sbi, inode, false))
+ valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
if (unlikely(valid_block_count > sbi->user_block_count)) {
spin_unlock(&sbi->stat_lock);
@@ -2397,12 +2439,6 @@
return is_inode_flag_set(inode, FI_INLINE_DENTRY);
}
-static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page)
-{
- if (!f2fs_has_inline_dentry(dir))
- kunmap(page);
-}
-
static inline int is_file(struct inode *inode, int type)
{
return F2FS_I(inode)->i_advise & type;
@@ -2434,7 +2470,17 @@
}
if (!is_inode_flag_set(inode, FI_AUTO_RECOVER) ||
file_keep_isize(inode) ||
- i_size_read(inode) & PAGE_MASK)
+ i_size_read(inode) & ~PAGE_MASK)
+ return false;
+
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time, &inode->i_atime))
+ return false;
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time + 1, &inode->i_ctime))
+ return false;
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time + 2, &inode->i_mtime))
+ return false;
+ if (!timespec_equal(F2FS_I(inode)->i_disk_time + 3,
+ &F2FS_I(inode)->i_crtime))
return false;
down_read(&F2FS_I(inode)->i_sem);
@@ -2444,8 +2490,7 @@
return ret;
}
-#define sb_rdonly f2fs_readonly
-static inline int f2fs_readonly(struct super_block *sb)
+static inline bool f2fs_readonly(struct super_block *sb)
{
return sb->s_flags & MS_RDONLY;
}
@@ -2507,15 +2552,6 @@
return ret;
}
-enum rw_hint {
- WRITE_LIFE_NOT_SET = 0,
- WRITE_LIFE_NONE = 1, /* RWH_WRITE_LIFE_NONE */
- WRITE_LIFE_SHORT = 2, /* RWH_WRITE_LIFE_SHORT */
- WRITE_LIFE_MEDIUM = 3, /* RWH_WRITE_LIFE_MEDIUM */
- WRITE_LIFE_LONG = 4, /* RWH_WRITE_LIFE_LONG */
- WRITE_LIFE_EXTREME = 5, /* RWH_WRITE_LIFE_EXTREME */
-};
-
static inline int wbc_to_write_flags(struct writeback_control *wbc)
{
if (wbc->sync_mode == WB_SYNC_ALL)
@@ -2634,6 +2670,8 @@
/*
* namei.c
*/
+int update_extension_list(struct f2fs_sb_info *sbi, const char *name,
+ bool hot, bool set);
struct dentry *f2fs_get_parent(struct dentry *child);
/*
@@ -2806,6 +2844,8 @@
int __init create_segment_manager_caches(void);
void destroy_segment_manager_caches(void);
int rw_hint_to_seg_type(enum rw_hint hint);
+enum rw_hint io_type_to_rw_hint(struct f2fs_sb_info *sbi, enum page_type type,
+ enum temp_type temp);
/*
* checkpoint.c
@@ -2846,6 +2886,8 @@
/*
* data.c
*/
+int f2fs_init_post_read_processing(void);
+void f2fs_destroy_post_read_processing(void);
void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type);
void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi,
struct inode *inode, nid_t ino, pgoff_t idx,
@@ -2877,7 +2919,6 @@
u64 start, u64 len);
bool should_update_inplace(struct inode *inode, struct f2fs_io_info *fio);
bool should_update_outplace(struct inode *inode, struct f2fs_io_info *fio);
-void f2fs_set_page_dirty_nobuffers(struct page *page);
int __f2fs_write_data_pages(struct address_space *mapping,
struct writeback_control *wbc,
enum iostat_type io_type);
@@ -2888,6 +2929,7 @@
int f2fs_migrate_page(struct address_space *mapping, struct page *newpage,
struct page *page, enum migrate_mode mode);
#endif
+bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
/*
* gc.c
@@ -3205,50 +3247,30 @@
#endif
}
-static inline bool f2fs_bio_encrypted(struct bio *bio)
+/*
+ * Returns true if the reads of the inode's data need to undergo some
+ * postprocessing step, like decryption or authenticity verification.
+ */
+static inline bool f2fs_post_read_required(struct inode *inode)
{
- return bio->bi_private != NULL;
+ return f2fs_encrypted_file(inode);
}
-static inline int f2fs_sb_has_crypto(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
+#define F2FS_FEATURE_FUNCS(name, flagname) \
+static inline int f2fs_sb_has_##name(struct super_block *sb) \
+{ \
+ return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_##flagname); \
}
-static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_BLKZONED);
-}
-
-static inline int f2fs_sb_has_extra_attr(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_EXTRA_ATTR);
-}
-
-static inline int f2fs_sb_has_project_quota(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA);
-}
-
-static inline int f2fs_sb_has_inode_chksum(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM);
-}
-
-static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR);
-}
-
-static inline int f2fs_sb_has_quota_ino(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO);
-}
-
-static inline int f2fs_sb_has_inode_crtime(struct super_block *sb)
-{
- return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CRTIME);
-}
+F2FS_FEATURE_FUNCS(encrypt, ENCRYPT);
+F2FS_FEATURE_FUNCS(blkzoned, BLKZONED);
+F2FS_FEATURE_FUNCS(extra_attr, EXTRA_ATTR);
+F2FS_FEATURE_FUNCS(project_quota, PRJQUOTA);
+F2FS_FEATURE_FUNCS(inode_chksum, INODE_CHKSUM);
+F2FS_FEATURE_FUNCS(flexible_inline_xattr, FLEXIBLE_INLINE_XATTR);
+F2FS_FEATURE_FUNCS(quota_ino, QUOTA_INO);
+F2FS_FEATURE_FUNCS(inode_crtime, INODE_CRTIME);
+F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND);
#ifdef CONFIG_BLK_DEV_ZONED
static inline int get_blkz_type(struct f2fs_sb_info *sbi,
@@ -3268,7 +3290,7 @@
{
struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev);
- return blk_queue_discard(q) || f2fs_sb_mounted_blkzoned(sbi->sb);
+ return blk_queue_discard(q) || f2fs_sb_has_blkzoned(sbi->sb);
}
static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt)
@@ -3297,4 +3319,22 @@
#endif
}
+static inline bool f2fs_force_buffered_io(struct inode *inode, int rw)
+{
+ return ((f2fs_post_read_required(inode) &&
+ !fscrypt_using_hardware_encryption(inode)) ||
+ (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) ||
+ F2FS_I_SB(inode)->s_ndevs);
+}
+
+static inline bool f2fs_may_encrypt_bio(struct inode *inode,
+ struct f2fs_io_info *fio)
+{
+ if (fio && (fio->type != DATA || fio->encrypted_page))
+ return false;
+
+ return (f2fs_encrypted_file(inode) &&
+ fscrypt_using_hardware_encryption(inode));
+}
+
#endif
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index b926df7..0e39c77 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -112,8 +112,8 @@
/* fill the page */
f2fs_wait_on_page_writeback(page, DATA, false);
- /* wait for GCed encrypted page writeback */
- if (f2fs_encrypted_file(inode))
+ /* wait for GCed page writeback via META_MAPPING */
+ if (f2fs_post_read_required(inode))
f2fs_wait_on_block_writeback(sbi, dn.data_blkaddr);
out_sem:
@@ -165,9 +165,10 @@
cp_reason = CP_NODE_NEED_CP;
else if (test_opt(sbi, FASTBOOT))
cp_reason = CP_FASTBOOT_MODE;
- else if (sbi->active_logs == 2)
+ else if (F2FS_OPTION(sbi).active_logs == 2)
cp_reason = CP_SPEC_LOG_NUM;
- else if (need_dentry_mark(sbi, inode->i_ino) &&
+ else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT &&
+ need_dentry_mark(sbi, inode->i_ino) &&
exist_written_data(sbi, F2FS_I(inode)->i_pino, TRANS_DIR_INO))
cp_reason = CP_RECOVER_DIR;
@@ -480,6 +481,9 @@
if (err)
return err;
+
+ filp->f_mode |= FMODE_NOWAIT;
+
return dquot_file_open(inode, filp);
}
@@ -570,7 +574,6 @@
int truncate_blocks(struct inode *inode, u64 from, bool lock)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- unsigned int blocksize = inode->i_sb->s_blocksize;
struct dnode_of_data dn;
pgoff_t free_from;
int count = 0, err = 0;
@@ -579,7 +582,7 @@
trace_f2fs_truncate_blocks_enter(inode, from);
- free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1);
+ free_from = (pgoff_t)F2FS_BLK_ALIGN(from);
if (free_from >= sbi->max_file_blocks)
goto free_partial;
@@ -1350,8 +1353,12 @@
}
out:
- if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size)
- f2fs_i_size_write(inode, new_size);
+ if (new_size > i_size_read(inode)) {
+ if (mode & FALLOC_FL_KEEP_SIZE)
+ file_set_keep_isize(inode);
+ else
+ f2fs_i_size_write(inode, new_size);
+ }
out_sem:
up_write(&F2FS_I(inode)->i_mmap_sem);
@@ -1705,6 +1712,8 @@
inode_lock(inode);
+ down_write(&F2FS_I(inode)->dio_rwsem[WRITE]);
+
if (f2fs_is_volatile_file(inode))
goto err_out;
@@ -1723,6 +1732,7 @@
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false);
}
err_out:
+ up_write(&F2FS_I(inode)->dio_rwsem[WRITE]);
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
@@ -1932,7 +1942,7 @@
{
struct inode *inode = file_inode(filp);
- if (!f2fs_sb_has_crypto(inode->i_sb))
+ if (!f2fs_sb_has_encrypt(inode->i_sb))
return -EOPNOTSUPP;
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
@@ -1942,7 +1952,7 @@
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
{
- if (!f2fs_sb_has_crypto(file_inode(filp)->i_sb))
+ if (!f2fs_sb_has_encrypt(file_inode(filp)->i_sb))
return -EOPNOTSUPP;
return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
}
@@ -1953,16 +1963,18 @@
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int err;
- if (!f2fs_sb_has_crypto(inode->i_sb))
+ if (!f2fs_sb_has_encrypt(inode->i_sb))
return -EOPNOTSUPP;
- if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt))
- goto got_it;
-
err = mnt_want_write_file(filp);
if (err)
return err;
+ down_write(&sbi->sb_lock);
+
+ if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt))
+ goto got_it;
+
/* update superblock with uuid */
generate_random_uuid(sbi->raw_super->encrypt_pw_salt);
@@ -1970,15 +1982,16 @@
if (err) {
/* undo new data */
memset(sbi->raw_super->encrypt_pw_salt, 0, 16);
- mnt_drop_write_file(filp);
- return err;
+ goto out_err;
}
- mnt_drop_write_file(filp);
got_it:
if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt,
16))
- return -EFAULT;
- return 0;
+ err = -EFAULT;
+out_err:
+ up_write(&sbi->sb_lock);
+ mnt_drop_write_file(filp);
+ return err;
}
static int f2fs_ioc_gc(struct file *filp, unsigned long arg)
@@ -2039,8 +2052,10 @@
return ret;
end = range.start + range.len;
- if (range.start < MAIN_BLKADDR(sbi) || end >= MAX_BLKADDR(sbi))
- return -EINVAL;
+ if (range.start < MAIN_BLKADDR(sbi) || end >= MAX_BLKADDR(sbi)) {
+ ret = -EINVAL;
+ goto out;
+ }
do_more:
if (!range.sync) {
if (!mutex_trylock(&sbi->gc_mutex)) {
@@ -2681,25 +2696,54 @@
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
return -EIO;
- inode_lock(inode);
+ if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT))
+ return -EINVAL;
+
+ if (!inode_trylock(inode)) {
+ if (iocb->ki_flags & IOCB_NOWAIT)
+ return -EAGAIN;
+ inode_lock(inode);
+ }
+
ret = generic_write_checks(iocb, from);
if (ret > 0) {
+ bool preallocated = false;
+ size_t target_size = 0;
int err;
if (iov_iter_fault_in_readable(from, iov_iter_count(from)))
set_inode_flag(inode, FI_NO_PREALLOC);
- err = f2fs_preallocate_blocks(iocb, from);
- if (err) {
- clear_inode_flag(inode, FI_NO_PREALLOC);
- inode_unlock(inode);
- return err;
+ if ((iocb->ki_flags & IOCB_NOWAIT) &&
+ (iocb->ki_flags & IOCB_DIRECT)) {
+ if (!f2fs_overwrite_io(inode, iocb->ki_pos,
+ iov_iter_count(from)) ||
+ f2fs_has_inline_data(inode) ||
+ f2fs_force_buffered_io(inode, WRITE)) {
+ inode_unlock(inode);
+ return -EAGAIN;
+ }
+
+ } else {
+ preallocated = true;
+ target_size = iocb->ki_pos + iov_iter_count(from);
+
+ err = f2fs_preallocate_blocks(iocb, from);
+ if (err) {
+ clear_inode_flag(inode, FI_NO_PREALLOC);
+ inode_unlock(inode);
+ return err;
+ }
}
blk_start_plug(&plug);
ret = __generic_file_write_iter(iocb, from);
blk_finish_plug(&plug);
clear_inode_flag(inode, FI_NO_PREALLOC);
+ /* if we couldn't write data, we should deallocate blocks. */
+ if (preallocated && i_size_read(inode) < target_size)
+ f2fs_truncate(inode);
+
if (ret > 0)
f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret);
}
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 3b26aa1..66044fa 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -76,14 +76,15 @@
* invalidated soon after by user update or deletion.
* So, I'd like to wait some time to collect dirty segments.
*/
- if (!mutex_trylock(&sbi->gc_mutex))
- goto next;
-
if (gc_th->gc_urgent) {
wait_ms = gc_th->urgent_sleep_time;
+ mutex_lock(&sbi->gc_mutex);
goto do_gc;
}
+ if (!mutex_trylock(&sbi->gc_mutex))
+ goto next;
+
if (!is_idle(sbi)) {
increase_sleep_time(gc_th, &wait_ms);
mutex_unlock(&sbi->gc_mutex);
@@ -161,12 +162,17 @@
{
int gc_mode = (gc_type == BG_GC) ? GC_CB : GC_GREEDY;
- if (gc_th && gc_th->gc_idle) {
+ if (!gc_th)
+ return gc_mode;
+
+ if (gc_th->gc_idle) {
if (gc_th->gc_idle == 1)
gc_mode = GC_CB;
else if (gc_th->gc_idle == 2)
gc_mode = GC_GREEDY;
}
+ if (gc_th->gc_urgent)
+ gc_mode = GC_GREEDY;
return gc_mode;
}
@@ -188,11 +194,14 @@
}
/* we need to check every dirty segments in the FG_GC case */
- if (gc_type != FG_GC && p->max_search > sbi->max_victim_search)
+ if (gc_type != FG_GC &&
+ (sbi->gc_thread && !sbi->gc_thread->gc_urgent) &&
+ p->max_search > sbi->max_victim_search)
p->max_search = sbi->max_victim_search;
- /* let's select beginning hot/small space first */
- if (type == CURSEG_HOT_DATA || IS_NODESEG(type))
+ /* let's select beginning hot/small space first in no_heap mode*/
+ if (test_opt(sbi, NOHEAP) &&
+ (type == CURSEG_HOT_DATA || IS_NODESEG(type)))
p->offset = 0;
else
p->offset = SIT_I(sbi)->last_victim[p->gc_mode];
@@ -684,6 +693,7 @@
dec_page_count(fio.sbi, F2FS_DIRTY_META);
set_page_writeback(fio.encrypted_page);
+ ClearPageError(page);
/* allocate block address */
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
@@ -841,8 +851,8 @@
if (IS_ERR(inode) || is_bad_inode(inode))
continue;
- /* if encrypted inode, let's go phase 3 */
- if (f2fs_encrypted_file(inode)) {
+ /* if inode uses special I/O path, let's go phase 3 */
+ if (f2fs_post_read_required(inode)) {
add_gc_inode(gc_list, inode);
continue;
}
@@ -890,7 +900,7 @@
start_bidx = start_bidx_of_node(nofs, inode)
+ ofs_in_node;
- if (f2fs_encrypted_file(inode))
+ if (f2fs_post_read_required(inode))
move_data_block(inode, start_bidx, segno, off);
else
move_data_page(inode, start_bidx, gc_type,
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 364114a..156ac4f 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -26,7 +26,7 @@
if (i_size_read(inode) > MAX_INLINE_DATA(inode))
return false;
- if (f2fs_encrypted_file(inode))
+ if (f2fs_post_read_required(inode))
return false;
return true;
@@ -157,6 +157,7 @@
/* write data page to try to make data consistent */
set_page_writeback(page);
+ ClearPageError(page);
fio.old_blkaddr = dn->data_blkaddr;
set_inode_flag(dn->inode, FI_HOT_DATA);
write_data_page(dn, &fio);
@@ -387,7 +388,7 @@
f2fs_wait_on_page_writeback(page, DATA, true);
zero_user_segment(page, MAX_INLINE_DATA(dir), PAGE_SIZE);
- dentry_blk = kmap_atomic(page);
+ dentry_blk = page_address(page);
make_dentry_ptr_inline(dir, &src, inline_dentry);
make_dentry_ptr_block(dir, &dst, dentry_blk);
@@ -404,7 +405,6 @@
memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max);
memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN);
- kunmap_atomic(dentry_blk);
if (!PageUptodate(page))
SetPageUptodate(page);
set_page_dirty(page);
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 89c838b..e0d9e8f 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -284,6 +284,10 @@
fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec);
}
+ F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
+ F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
+ F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
+ F2FS_I(inode)->i_disk_time[3] = F2FS_I(inode)->i_crtime;
f2fs_put_page(node_page, 1);
stat_inc_inline_xattr(inode);
@@ -328,7 +332,7 @@
inode->i_op = &f2fs_dir_inode_operations;
inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
- mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
+ inode_nohighmem(inode);
} else if (S_ISLNK(inode->i_mode)) {
if (f2fs_encrypted_inode(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
@@ -439,12 +443,15 @@
}
__set_inode_rdev(inode, ri);
- set_cold_node(inode, node_page);
/* deleted inode */
if (inode->i_nlink == 0)
clear_inline_node(node_page);
+ F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
+ F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
+ F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
+ F2FS_I(inode)->i_disk_time[3] = F2FS_I(inode)->i_crtime;
}
void update_inode_page(struct inode *inode)
@@ -585,7 +592,7 @@
!exist_written_data(sbi, inode->i_ino, ORPHAN_INO));
}
out_clear:
- fscrypt_put_encryption_info(inode, NULL);
+ fscrypt_put_encryption_info(inode);
clear_inode(inode);
}
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 070b3a7..392d1ed 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -78,7 +78,8 @@
set_inode_flag(inode, FI_NEW_INODE);
/* If the directory encrypted, then we should encrypt the inode. */
- if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
+ if ((f2fs_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) &&
+ f2fs_may_encrypt(inode))
f2fs_set_encrypted_inode(inode);
if (f2fs_sb_has_extra_attr(sbi->sb)) {
@@ -97,7 +98,7 @@
if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) {
f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode));
if (f2fs_has_inline_xattr(inode))
- xattr_size = sbi->inline_xattr_size;
+ xattr_size = F2FS_OPTION(sbi).inline_xattr_size;
/* Otherwise, will be 0 */
} else if (f2fs_has_inline_xattr(inode) ||
f2fs_has_inline_dentry(inode)) {
@@ -142,7 +143,7 @@
return ERR_PTR(err);
}
-static int is_multimedia_file(const unsigned char *s, const char *sub)
+static int is_extension_exist(const unsigned char *s, const char *sub)
{
size_t slen = strlen(s);
size_t sublen = strlen(sub);
@@ -168,19 +169,94 @@
/*
* Set multimedia files as cold files for hot/cold data separation
*/
-static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode,
+static inline void set_file_temperature(struct f2fs_sb_info *sbi, struct inode *inode,
const unsigned char *name)
{
- int i;
- __u8 (*extlist)[8] = sbi->raw_super->extension_list;
+ __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list;
+ int i, cold_count, hot_count;
- int count = le32_to_cpu(sbi->raw_super->extension_count);
- for (i = 0; i < count; i++) {
- if (is_multimedia_file(name, extlist[i])) {
+ down_read(&sbi->sb_lock);
+
+ cold_count = le32_to_cpu(sbi->raw_super->extension_count);
+ hot_count = sbi->raw_super->hot_ext_count;
+
+ for (i = 0; i < cold_count + hot_count; i++) {
+ if (!is_extension_exist(name, extlist[i]))
+ continue;
+ if (i < cold_count)
file_set_cold(inode);
- break;
- }
+ else
+ file_set_hot(inode);
+ break;
}
+
+ up_read(&sbi->sb_lock);
+}
+
+int update_extension_list(struct f2fs_sb_info *sbi, const char *name,
+ bool hot, bool set)
+{
+ __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list;
+ int cold_count = le32_to_cpu(sbi->raw_super->extension_count);
+ int hot_count = sbi->raw_super->hot_ext_count;
+ int total_count = cold_count + hot_count;
+ int start, count;
+ int i;
+
+ if (set) {
+ if (total_count == F2FS_MAX_EXTENSION)
+ return -EINVAL;
+ } else {
+ if (!hot && !cold_count)
+ return -EINVAL;
+ if (hot && !hot_count)
+ return -EINVAL;
+ }
+
+ if (hot) {
+ start = cold_count;
+ count = total_count;
+ } else {
+ start = 0;
+ count = cold_count;
+ }
+
+ for (i = start; i < count; i++) {
+ if (strcmp(name, extlist[i]))
+ continue;
+
+ if (set)
+ return -EINVAL;
+
+ memcpy(extlist[i], extlist[i + 1],
+ F2FS_EXTENSION_LEN * (total_count - i - 1));
+ memset(extlist[total_count - 1], 0, F2FS_EXTENSION_LEN);
+ if (hot)
+ sbi->raw_super->hot_ext_count = hot_count - 1;
+ else
+ sbi->raw_super->extension_count =
+ cpu_to_le32(cold_count - 1);
+ return 0;
+ }
+
+ if (!set)
+ return -EINVAL;
+
+ if (hot) {
+ strncpy(extlist[count], name, strlen(name));
+ sbi->raw_super->hot_ext_count = hot_count + 1;
+ } else {
+ char buf[F2FS_MAX_EXTENSION][F2FS_EXTENSION_LEN];
+
+ memcpy(buf, &extlist[cold_count],
+ F2FS_EXTENSION_LEN * hot_count);
+ memset(extlist[cold_count], 0, F2FS_EXTENSION_LEN);
+ strncpy(extlist[cold_count], name, strlen(name));
+ memcpy(&extlist[cold_count + 1], buf,
+ F2FS_EXTENSION_LEN * hot_count);
+ sbi->raw_super->extension_count = cpu_to_le32(cold_count + 1);
+ }
+ return 0;
}
static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
@@ -203,7 +279,7 @@
return PTR_ERR(inode);
if (!test_opt(sbi, DISABLE_EXT_IDENTIFY))
- set_cold_files(sbi, inode, dentry->d_name.name);
+ set_file_temperature(sbi, inode, dentry->d_name.name);
inode->i_op = &f2fs_file_inode_operations;
inode->i_fop = &f2fs_file_operations;
@@ -218,8 +294,8 @@
alloc_nid_done(sbi, ino);
- d_instantiate(dentry, inode);
unlock_new_inode(inode);
+ d_instantiate(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
@@ -317,7 +393,6 @@
de = f2fs_find_entry(dir, &dot, &page);
if (de) {
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
} else if (IS_ERR(page)) {
err = PTR_ERR(page);
@@ -329,14 +404,12 @@
}
de = f2fs_find_entry(dir, &dotdot, &page);
- if (de) {
- f2fs_dentry_kunmap(dir, page);
+ if (de)
f2fs_put_page(page, 0);
- } else if (IS_ERR(page)) {
+ else if (IS_ERR(page))
err = PTR_ERR(page);
- } else {
+ else
err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR);
- }
out:
if (!err)
clear_inode_flag(dir, FI_INLINE_DOTS);
@@ -377,7 +450,6 @@
}
ino = le32_to_cpu(de->ino);
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
inode = f2fs_iget(dir->i_sb, ino);
@@ -452,7 +524,6 @@
err = acquire_orphan_inode(sbi);
if (err) {
f2fs_unlock_op(sbi);
- f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0);
goto fail;
}
@@ -486,27 +557,16 @@
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct inode *inode;
size_t len = strlen(symname);
- struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
- struct fscrypt_symlink_data *sd = NULL;
+ struct fscrypt_str disk_link;
int err;
if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
- if (f2fs_encrypted_inode(dir)) {
- err = fscrypt_get_encryption_info(dir);
- if (err)
- return err;
-
- if (!fscrypt_has_encryption_key(dir))
- return -ENOKEY;
-
- disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
- sizeof(struct fscrypt_symlink_data));
- }
-
- if (disk_link.len > dir->i_sb->s_blocksize)
- return -ENAMETOOLONG;
+ err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
+ &disk_link);
+ if (err)
+ return err;
err = dquot_initialize(dir);
if (err)
@@ -516,7 +576,7 @@
if (IS_ERR(inode))
return PTR_ERR(inode);
- if (f2fs_encrypted_inode(inode))
+ if (IS_ENCRYPTED(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations;
@@ -526,44 +586,19 @@
f2fs_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
if (err)
- goto out;
+ goto out_handle_failed_inode;
f2fs_unlock_op(sbi);
alloc_nid_done(sbi, inode->i_ino);
- if (f2fs_encrypted_inode(inode)) {
- struct qstr istr = QSTR_INIT(symname, len);
- struct fscrypt_str ostr;
-
- sd = f2fs_kzalloc(sbi, disk_link.len, GFP_NOFS);
- if (!sd) {
- err = -ENOMEM;
- goto err_out;
- }
-
- err = fscrypt_get_encryption_info(inode);
- if (err)
- goto err_out;
-
- if (!fscrypt_has_encryption_key(inode)) {
- err = -ENOKEY;
- goto err_out;
- }
-
- ostr.name = sd->encrypted_path;
- ostr.len = disk_link.len;
- err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
- if (err)
- goto err_out;
-
- sd->len = cpu_to_le16(ostr.len);
- disk_link.name = (char *)sd;
- }
+ err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
+ if (err)
+ goto err_out;
err = page_symlink(inode, disk_link.name, disk_link.len);
err_out:
- d_instantiate(dentry, inode);
unlock_new_inode(inode);
+ d_instantiate(dentry, inode);
/*
* Let's flush symlink data in order to avoid broken symlink as much as
@@ -584,12 +619,14 @@
f2fs_unlink(dir, dentry);
}
- kfree(sd);
-
f2fs_balance_fs(sbi, true);
- return err;
-out:
+ goto out_free_encrypted_link;
+
+out_handle_failed_inode:
handle_failed_inode(inode);
+out_free_encrypted_link:
+ if (disk_link.name != (unsigned char *)symname)
+ kfree(disk_link.name);
return err;
}
@@ -613,7 +650,7 @@
inode->i_op = &f2fs_dir_inode_operations;
inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
- mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
+ inode_nohighmem(inode);
set_inode_flag(inode, FI_INC_LINK);
f2fs_lock_op(sbi);
@@ -624,8 +661,8 @@
alloc_nid_done(sbi, inode->i_ino);
- d_instantiate(dentry, inode);
unlock_new_inode(inode);
+ d_instantiate(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
@@ -676,8 +713,8 @@
alloc_nid_done(sbi, inode->i_ino);
- d_instantiate(dentry, inode);
unlock_new_inode(inode);
+ d_instantiate(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
@@ -751,10 +788,12 @@
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
{
- if (unlikely(f2fs_cp_error(F2FS_I_SB(dir))))
+ struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
+
+ if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
- if (f2fs_encrypted_inode(dir)) {
+ if (f2fs_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) {
int err = fscrypt_get_encryption_info(dir);
if (err)
return err;
@@ -927,16 +966,15 @@
}
if (old_dir_entry) {
- if (old_dir != new_dir && !whiteout) {
+ if (old_dir != new_dir && !whiteout)
f2fs_set_link(old_inode, old_dir_entry,
old_dir_page, new_dir);
- } else {
- f2fs_dentry_kunmap(old_inode, old_dir_page);
+ else
f2fs_put_page(old_dir_page, 0);
- }
f2fs_i_links_write(old_dir, false);
}
- add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
+ if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
+ add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
f2fs_unlock_op(sbi);
@@ -946,20 +984,15 @@
put_out_dir:
f2fs_unlock_op(sbi);
- if (new_page) {
- f2fs_dentry_kunmap(new_dir, new_page);
+ if (new_page)
f2fs_put_page(new_page, 0);
- }
out_whiteout:
if (whiteout)
iput(whiteout);
out_dir:
- if (old_dir_entry) {
- f2fs_dentry_kunmap(old_inode, old_dir_page);
+ if (old_dir_entry)
f2fs_put_page(old_dir_page, 0);
- }
out_old:
- f2fs_dentry_kunmap(old_dir, old_page);
f2fs_put_page(old_page, 0);
out:
return err;
@@ -1091,8 +1124,10 @@
}
f2fs_mark_inode_dirty_sync(new_dir, false);
- add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO);
- add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
+ if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) {
+ add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO);
+ add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO);
+ }
f2fs_unlock_op(sbi);
@@ -1101,19 +1136,15 @@
return 0;
out_new_dir:
if (new_dir_entry) {
- f2fs_dentry_kunmap(new_inode, new_dir_page);
f2fs_put_page(new_dir_page, 0);
}
out_old_dir:
if (old_dir_entry) {
- f2fs_dentry_kunmap(old_inode, old_dir_page);
f2fs_put_page(old_dir_page, 0);
}
out_new:
- f2fs_dentry_kunmap(new_dir, new_page);
f2fs_put_page(new_page, 0);
out_old:
- f2fs_dentry_kunmap(old_dir, old_page);
f2fs_put_page(old_page, 0);
out:
return err;
@@ -1148,68 +1179,20 @@
struct inode *inode,
struct delayed_call *done)
{
- struct page *cpage = NULL;
- char *caddr, *paddr = NULL;
- struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
- struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
- struct fscrypt_symlink_data *sd;
- u32 max_size = inode->i_sb->s_blocksize;
- int res;
+ struct page *page;
+ const char *target;
if (!dentry)
return ERR_PTR(-ECHILD);
- res = fscrypt_get_encryption_info(inode);
- if (res)
- return ERR_PTR(res);
+ page = read_mapping_page(inode->i_mapping, 0, NULL);
+ if (IS_ERR(page))
+ return ERR_CAST(page);
- cpage = read_mapping_page(inode->i_mapping, 0, NULL);
- if (IS_ERR(cpage))
- return ERR_CAST(cpage);
- caddr = page_address(cpage);
-
- /* Symlink is encrypted */
- sd = (struct fscrypt_symlink_data *)caddr;
- cstr.name = sd->encrypted_path;
- cstr.len = le16_to_cpu(sd->len);
-
- /* this is broken symlink case */
- if (unlikely(cstr.len == 0)) {
- res = -ENOENT;
- goto errout;
- }
-
- if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
- /* Symlink data on the disk is corrupted */
- res = -EIO;
- goto errout;
- }
- res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
- if (res)
- goto errout;
-
- res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
- if (res)
- goto errout;
-
- /* this is broken symlink case */
- if (unlikely(pstr.name[0] == 0)) {
- res = -ENOENT;
- goto errout;
- }
-
- paddr = pstr.name;
-
- /* Null-terminate the name */
- paddr[pstr.len] = '\0';
-
- put_page(cpage);
- set_delayed_call(done, kfree_link, paddr);
- return paddr;
-errout:
- fscrypt_fname_free_buffer(&pstr);
- put_page(cpage);
- return ERR_PTR(res);
+ target = fscrypt_get_symlink(inode, page_address(page),
+ inode->i_sb->s_blocksize, done);
+ put_page(page);
+ return target;
}
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 7cded84..803a010 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -193,8 +193,8 @@
__free_nat_entry(e);
}
-static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,
- struct nat_entry *ne)
+static struct nat_entry_set *__grab_nat_entry_set(struct f2fs_nm_info *nm_i,
+ struct nat_entry *ne)
{
nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid);
struct nat_entry_set *head;
@@ -209,15 +209,36 @@
head->entry_cnt = 0;
f2fs_radix_tree_insert(&nm_i->nat_set_root, set, head);
}
+ return head;
+}
+
+static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,
+ struct nat_entry *ne)
+{
+ struct nat_entry_set *head;
+ bool new_ne = nat_get_blkaddr(ne) == NEW_ADDR;
+
+ if (!new_ne)
+ head = __grab_nat_entry_set(nm_i, ne);
+
+ /*
+ * update entry_cnt in below condition:
+ * 1. update NEW_ADDR to valid block address;
+ * 2. update old block address to new one;
+ */
+ if (!new_ne && (get_nat_flag(ne, IS_PREALLOC) ||
+ !get_nat_flag(ne, IS_DIRTY)))
+ head->entry_cnt++;
+
+ set_nat_flag(ne, IS_PREALLOC, new_ne);
if (get_nat_flag(ne, IS_DIRTY))
goto refresh_list;
nm_i->dirty_nat_cnt++;
- head->entry_cnt++;
set_nat_flag(ne, IS_DIRTY, true);
refresh_list:
- if (nat_get_blkaddr(ne) == NEW_ADDR)
+ if (new_ne)
list_del_init(&ne->list);
else
list_move_tail(&ne->list, &head->entry_list);
@@ -1076,7 +1097,7 @@
f2fs_wait_on_page_writeback(page, NODE, true);
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
- set_cold_node(dn->inode, page);
+ set_cold_node(page, S_ISDIR(dn->inode->i_mode));
if (!PageUptodate(page))
SetPageUptodate(page);
if (set_page_dirty(page))
@@ -1377,6 +1398,7 @@
fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
set_page_writeback(page);
+ ClearPageError(page);
fio.old_blkaddr = ni.blk_addr;
write_node_page(nid, &fio);
set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page));
@@ -1751,7 +1773,7 @@
if (!PageUptodate(page))
SetPageUptodate(page);
if (!PageDirty(page)) {
- f2fs_set_page_dirty_nobuffers(page);
+ __set_page_dirty_nobuffers(page);
inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES);
SetPagePrivate(page);
f2fs_trace_pid(page);
@@ -2310,6 +2332,7 @@
if (!PageUptodate(ipage))
SetPageUptodate(ipage);
fill_node_footer(ipage, ino, ino, 0, true);
+ set_cold_node(page, false);
src = F2FS_INODE(page);
dst = F2FS_INODE(ipage);
@@ -2599,8 +2622,7 @@
if (!enabled_nat_bits(sbi, NULL))
return 0;
- nm_i->nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
- F2FS_BLKSIZE - 1);
+ nm_i->nat_bits_blocks = F2FS_BLK_ALIGN((nat_bits_bytes << 1) + 8);
nm_i->nat_bits = f2fs_kzalloc(sbi,
nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, GFP_KERNEL);
if (!nm_i->nat_bits)
@@ -2726,12 +2748,20 @@
static int init_free_nid_cache(struct f2fs_sb_info *sbi)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
+ int i;
- nm_i->free_nid_bitmap = f2fs_kvzalloc(sbi, nm_i->nat_blocks *
- NAT_ENTRY_BITMAP_SIZE, GFP_KERNEL);
+ nm_i->free_nid_bitmap = f2fs_kzalloc(sbi, nm_i->nat_blocks *
+ sizeof(unsigned char *), GFP_KERNEL);
if (!nm_i->free_nid_bitmap)
return -ENOMEM;
+ for (i = 0; i < nm_i->nat_blocks; i++) {
+ nm_i->free_nid_bitmap[i] = f2fs_kvzalloc(sbi,
+ NAT_ENTRY_BITMAP_SIZE_ALIGNED, GFP_KERNEL);
+ if (!nm_i->free_nid_bitmap)
+ return -ENOMEM;
+ }
+
nm_i->nat_block_bitmap = f2fs_kvzalloc(sbi, nm_i->nat_blocks / 8,
GFP_KERNEL);
if (!nm_i->nat_block_bitmap)
@@ -2822,7 +2852,13 @@
up_write(&nm_i->nat_tree_lock);
kvfree(nm_i->nat_block_bitmap);
- kvfree(nm_i->free_nid_bitmap);
+ if (nm_i->free_nid_bitmap) {
+ int i;
+
+ for (i = 0; i < nm_i->nat_blocks; i++)
+ kvfree(nm_i->free_nid_bitmap[i]);
+ kfree(nm_i->free_nid_bitmap);
+ }
kvfree(nm_i->free_nid_count);
kfree(nm_i->nat_bitmap);
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
index 081ef0d..b95e49e 100644
--- a/fs/f2fs/node.h
+++ b/fs/f2fs/node.h
@@ -44,6 +44,7 @@
HAS_FSYNCED_INODE, /* is the inode fsynced before? */
HAS_LAST_FSYNC, /* has the latest node fsync mark? */
IS_DIRTY, /* this nat entry is dirty? */
+ IS_PREALLOC, /* nat entry is preallocated */
};
/*
@@ -422,12 +423,12 @@
ClearPageChecked(page);
}
-static inline void set_cold_node(struct inode *inode, struct page *page)
+static inline void set_cold_node(struct page *page, bool is_dir)
{
struct f2fs_node *rn = F2FS_NODE(page);
unsigned int flag = le32_to_cpu(rn->footer.flag);
- if (S_ISDIR(inode->i_mode))
+ if (is_dir)
flag &= ~(0x1 << COLD_BIT_SHIFT);
else
flag |= (0x1 << COLD_BIT_SHIFT);
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index b6d1ec6..4ddc226 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -144,7 +144,7 @@
retry:
de = __f2fs_find_entry(dir, &fname, &page);
if (de && inode->i_ino == le32_to_cpu(de->ino))
- goto out_unmap_put;
+ goto out_put;
if (de) {
einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino));
@@ -153,19 +153,19 @@
err = PTR_ERR(einode);
if (err == -ENOENT)
err = -EEXIST;
- goto out_unmap_put;
+ goto out_put;
}
err = dquot_initialize(einode);
if (err) {
iput(einode);
- goto out_unmap_put;
+ goto out_put;
}
err = acquire_orphan_inode(F2FS_I_SB(inode));
if (err) {
iput(einode);
- goto out_unmap_put;
+ goto out_put;
}
f2fs_delete_entry(de, page, dir, einode);
iput(einode);
@@ -180,8 +180,7 @@
goto retry;
goto out;
-out_unmap_put:
- f2fs_dentry_kunmap(dir, page);
+out_put:
f2fs_put_page(page, 0);
out:
if (file_enc_name(inode))
@@ -243,6 +242,9 @@
struct curseg_info *curseg;
struct page *page = NULL;
block_t blkaddr;
+ unsigned int loop_cnt = 0;
+ unsigned int free_blocks = sbi->user_block_count -
+ valid_user_blocks(sbi);
int err = 0;
/* get node pages in the current segment */
@@ -295,6 +297,17 @@
if (IS_INODE(page) && is_dent_dnode(page))
entry->last_dentry = blkaddr;
next:
+ /* sanity check in order to detect looped node chain */
+ if (++loop_cnt >= free_blocks ||
+ blkaddr == next_blkaddr_of_node(page)) {
+ f2fs_msg(sbi->sb, KERN_NOTICE,
+ "%s: detect looped node chain, "
+ "blkaddr:%u, next:%u",
+ __func__, blkaddr, next_blkaddr_of_node(page));
+ err = -EINVAL;
+ break;
+ }
+
/* check next segment */
blkaddr = next_blkaddr_of_node(page);
f2fs_put_page(page, 1);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index cc34d88..bdf567a 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -1411,12 +1411,11 @@
if (kthread_should_stop())
return 0;
- if (dcc->discard_wake) {
+ if (dcc->discard_wake)
dcc->discard_wake = 0;
- if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
- init_discard_policy(&dpolicy,
- DPOLICY_FORCE, 1);
- }
+
+ if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
+ init_discard_policy(&dpolicy, DPOLICY_FORCE, 1);
sb_start_intwrite(sbi->sb);
@@ -1485,7 +1484,7 @@
struct block_device *bdev, block_t blkstart, block_t blklen)
{
#ifdef CONFIG_BLK_DEV_ZONED
- if (f2fs_sb_mounted_blkzoned(sbi->sb) &&
+ if (f2fs_sb_has_blkzoned(sbi->sb) &&
bdev_zoned_model(bdev) != BLK_ZONED_NONE)
return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen);
#endif
@@ -1683,7 +1682,7 @@
sbi->blocks_per_seg, cur_pos);
len = next_pos - cur_pos;
- if (f2fs_sb_mounted_blkzoned(sbi->sb) ||
+ if (f2fs_sb_has_blkzoned(sbi->sb) ||
(force && len < cpc->trim_minlen))
goto skip;
@@ -1727,7 +1726,7 @@
} else if (discard_type == DPOLICY_FORCE) {
dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
- dpolicy->io_aware = true;
+ dpolicy->io_aware = false;
} else if (discard_type == DPOLICY_FSTRIM) {
dpolicy->io_aware = false;
} else if (discard_type == DPOLICY_UMOUNT) {
@@ -1863,7 +1862,7 @@
sbi->discard_blks--;
/* don't overwrite by SSR to keep node chain */
- if (se->type == CURSEG_WARM_NODE) {
+ if (IS_NODESEG(se->type)) {
if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
se->ckpt_valid_blocks++;
}
@@ -2164,11 +2163,17 @@
if (sbi->segs_per_sec != 1)
return CURSEG_I(sbi, type)->segno;
- if (type == CURSEG_HOT_DATA || IS_NODESEG(type))
+ if (test_opt(sbi, NOHEAP) &&
+ (type == CURSEG_HOT_DATA || IS_NODESEG(type)))
return 0;
if (SIT_I(sbi)->last_victim[ALLOC_NEXT])
return SIT_I(sbi)->last_victim[ALLOC_NEXT];
+
+ /* find segments from 0 to reuse freed segments */
+ if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
+ return 0;
+
return CURSEG_I(sbi, type)->segno;
}
@@ -2455,6 +2460,101 @@
}
}
+/* This returns write hints for each segment type. This hints will be
+ * passed down to block layer. There are mapping tables which depend on
+ * the mount option 'whint_mode'.
+ *
+ * 1) whint_mode=off. F2FS only passes down WRITE_LIFE_NOT_SET.
+ *
+ * 2) whint_mode=user-based. F2FS tries to pass down hints given by users.
+ *
+ * User F2FS Block
+ * ---- ---- -----
+ * META WRITE_LIFE_NOT_SET
+ * HOT_NODE "
+ * WARM_NODE "
+ * COLD_NODE "
+ * ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
+ * extension list " "
+ *
+ * -- buffered io
+ * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME
+ * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT
+ * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET
+ * WRITE_LIFE_NONE " "
+ * WRITE_LIFE_MEDIUM " "
+ * WRITE_LIFE_LONG " "
+ *
+ * -- direct io
+ * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME
+ * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT
+ * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET
+ * WRITE_LIFE_NONE " WRITE_LIFE_NONE
+ * WRITE_LIFE_MEDIUM " WRITE_LIFE_MEDIUM
+ * WRITE_LIFE_LONG " WRITE_LIFE_LONG
+ *
+ * 3) whint_mode=fs-based. F2FS passes down hints with its policy.
+ *
+ * User F2FS Block
+ * ---- ---- -----
+ * META WRITE_LIFE_MEDIUM;
+ * HOT_NODE WRITE_LIFE_NOT_SET
+ * WARM_NODE "
+ * COLD_NODE WRITE_LIFE_NONE
+ * ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
+ * extension list " "
+ *
+ * -- buffered io
+ * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME
+ * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT
+ * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_LONG
+ * WRITE_LIFE_NONE " "
+ * WRITE_LIFE_MEDIUM " "
+ * WRITE_LIFE_LONG " "
+ *
+ * -- direct io
+ * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME
+ * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT
+ * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET
+ * WRITE_LIFE_NONE " WRITE_LIFE_NONE
+ * WRITE_LIFE_MEDIUM " WRITE_LIFE_MEDIUM
+ * WRITE_LIFE_LONG " WRITE_LIFE_LONG
+ */
+
+enum rw_hint io_type_to_rw_hint(struct f2fs_sb_info *sbi,
+ enum page_type type, enum temp_type temp)
+{
+ if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_USER) {
+ if (type == DATA) {
+ if (temp == WARM)
+ return WRITE_LIFE_NOT_SET;
+ else if (temp == HOT)
+ return WRITE_LIFE_SHORT;
+ else if (temp == COLD)
+ return WRITE_LIFE_EXTREME;
+ } else {
+ return WRITE_LIFE_NOT_SET;
+ }
+ } else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS) {
+ if (type == DATA) {
+ if (temp == WARM)
+ return WRITE_LIFE_LONG;
+ else if (temp == HOT)
+ return WRITE_LIFE_SHORT;
+ else if (temp == COLD)
+ return WRITE_LIFE_EXTREME;
+ } else if (type == NODE) {
+ if (temp == WARM || temp == HOT)
+ return WRITE_LIFE_NOT_SET;
+ else if (temp == COLD)
+ return WRITE_LIFE_NONE;
+ } else if (type == META) {
+ return WRITE_LIFE_MEDIUM;
+ }
+ }
+ return WRITE_LIFE_NOT_SET;
+}
+
static int __get_segment_type_2(struct f2fs_io_info *fio)
{
if (fio->type == DATA)
@@ -2487,7 +2587,8 @@
if (is_cold_data(fio->page) || file_is_cold(inode))
return CURSEG_COLD_DATA;
- if (is_inode_flag_set(inode, FI_HOT_DATA))
+ if (file_is_hot(inode) ||
+ is_inode_flag_set(inode, FI_HOT_DATA))
return CURSEG_HOT_DATA;
/* rw_hint_to_seg_type(inode->i_write_hint); */
return CURSEG_WARM_DATA;
@@ -2503,7 +2604,7 @@
{
int type = 0;
- switch (fio->sbi->active_logs) {
+ switch (F2FS_OPTION(fio->sbi).active_logs) {
case 2:
type = __get_segment_type_2(fio);
break;
@@ -2643,6 +2744,7 @@
struct f2fs_io_info fio = {
.sbi = sbi,
.type = META,
+ .temp = HOT,
.op = REQ_OP_WRITE,
.op_flags = REQ_SYNC | REQ_META | REQ_PRIO,
.old_blkaddr = page->index,
@@ -2656,6 +2758,7 @@
fio.op_flags &= ~REQ_META;
set_page_writeback(page);
+ ClearPageError(page);
f2fs_submit_page_write(&fio);
f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE);
@@ -2689,8 +2792,15 @@
int rewrite_data_page(struct f2fs_io_info *fio)
{
int err;
+ struct f2fs_sb_info *sbi = fio->sbi;
fio->new_blkaddr = fio->old_blkaddr;
+ /* i/o temperature is needed for passing down write hints */
+ __get_segment_type(fio);
+
+ f2fs_bug_on(sbi, !IS_DATASEG(get_seg_entry(sbi,
+ GET_SEGNO(sbi, fio->new_blkaddr))->type));
+
stat_inc_inplace_blocks(fio->sbi);
err = f2fs_submit_page_bio(fio);
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index f11c4bc..3325d07 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -53,13 +53,19 @@
((secno) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \
(sbi)->segs_per_sec)) \
-#define MAIN_BLKADDR(sbi) (SM_I(sbi)->main_blkaddr)
-#define SEG0_BLKADDR(sbi) (SM_I(sbi)->seg0_blkaddr)
+#define MAIN_BLKADDR(sbi) \
+ (SM_I(sbi) ? SM_I(sbi)->main_blkaddr : \
+ le32_to_cpu(F2FS_RAW_SUPER(sbi)->main_blkaddr))
+#define SEG0_BLKADDR(sbi) \
+ (SM_I(sbi) ? SM_I(sbi)->seg0_blkaddr : \
+ le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment0_blkaddr))
#define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments)
#define MAIN_SECS(sbi) ((sbi)->total_sections)
-#define TOTAL_SEGS(sbi) (SM_I(sbi)->segment_count)
+#define TOTAL_SEGS(sbi) \
+ (SM_I(sbi) ? SM_I(sbi)->segment_count : \
+ le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count))
#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << (sbi)->log_blocks_per_seg)
#define MAX_BLKADDR(sbi) (SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi))
@@ -596,6 +602,8 @@
#define DEF_MIN_FSYNC_BLOCKS 8
#define DEF_MIN_HOT_BLOCKS 16
+#define SMALL_VOLUME_SEGMENTS (16 * 512) /* 16GB */
+
enum {
F2FS_IPU_FORCE,
F2FS_IPU_SSR,
@@ -630,10 +638,17 @@
f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1);
}
-static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
+static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr)
{
- BUG_ON(blk_addr < SEG0_BLKADDR(sbi)
- || blk_addr >= MAX_BLKADDR(sbi));
+ struct f2fs_sb_info *sbi = fio->sbi;
+
+ if (PAGE_TYPE_OF_BIO(fio->type) == META &&
+ (!is_read_io(fio->op) || fio->is_meta))
+ BUG_ON(blk_addr < SEG0_BLKADDR(sbi) ||
+ blk_addr >= MAIN_BLKADDR(sbi));
+ else
+ BUG_ON(blk_addr < MAIN_BLKADDR(sbi) ||
+ blk_addr >= MAX_BLKADDR(sbi));
}
/*
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 78b763c..771b039 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -60,7 +60,7 @@
static void f2fs_build_fault_attr(struct f2fs_sb_info *sbi,
unsigned int rate)
{
- struct f2fs_fault_info *ffi = &sbi->fault_info;
+ struct f2fs_fault_info *ffi = &F2FS_OPTION(sbi).fault_info;
if (rate) {
atomic_set(&ffi->inject_ops, 0);
@@ -129,6 +129,10 @@
Opt_jqfmt_vfsold,
Opt_jqfmt_vfsv0,
Opt_jqfmt_vfsv1,
+ Opt_whint,
+ Opt_alloc,
+ Opt_fsync,
+ Opt_test_dummy_encryption,
Opt_err,
};
@@ -182,6 +186,10 @@
{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
+ {Opt_whint, "whint_mode=%s"},
+ {Opt_alloc, "alloc_mode=%s"},
+ {Opt_fsync, "fsync_mode=%s"},
+ {Opt_test_dummy_encryption, "test_dummy_encryption"},
{Opt_err, NULL},
};
@@ -202,21 +210,24 @@
block_t limit = (sbi->user_block_count << 1) / 1000;
/* limit is 0.2% */
- if (test_opt(sbi, RESERVE_ROOT) && sbi->root_reserved_blocks > limit) {
- sbi->root_reserved_blocks = limit;
+ if (test_opt(sbi, RESERVE_ROOT) &&
+ F2FS_OPTION(sbi).root_reserved_blocks > limit) {
+ F2FS_OPTION(sbi).root_reserved_blocks = limit;
f2fs_msg(sbi->sb, KERN_INFO,
"Reduce reserved blocks for root = %u",
- sbi->root_reserved_blocks);
+ F2FS_OPTION(sbi).root_reserved_blocks);
}
if (!test_opt(sbi, RESERVE_ROOT) &&
- (!uid_eq(sbi->s_resuid,
+ (!uid_eq(F2FS_OPTION(sbi).s_resuid,
make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
- !gid_eq(sbi->s_resgid,
+ !gid_eq(F2FS_OPTION(sbi).s_resgid,
make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
f2fs_msg(sbi->sb, KERN_INFO,
"Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
- from_kuid_munged(&init_user_ns, sbi->s_resuid),
- from_kgid_munged(&init_user_ns, sbi->s_resgid));
+ from_kuid_munged(&init_user_ns,
+ F2FS_OPTION(sbi).s_resuid),
+ from_kgid_munged(&init_user_ns,
+ F2FS_OPTION(sbi).s_resgid));
}
static void init_once(void *foo)
@@ -236,7 +247,7 @@
char *qname;
int ret = -EINVAL;
- if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) {
+ if (sb_any_quota_loaded(sb) && !F2FS_OPTION(sbi).s_qf_names[qtype]) {
f2fs_msg(sb, KERN_ERR,
"Cannot change journaled "
"quota options when quota turned on");
@@ -254,8 +265,8 @@
"Not enough memory for storing quotafile name");
return -EINVAL;
}
- if (sbi->s_qf_names[qtype]) {
- if (strcmp(sbi->s_qf_names[qtype], qname) == 0)
+ if (F2FS_OPTION(sbi).s_qf_names[qtype]) {
+ if (strcmp(F2FS_OPTION(sbi).s_qf_names[qtype], qname) == 0)
ret = 0;
else
f2fs_msg(sb, KERN_ERR,
@@ -268,7 +279,7 @@
"quotafile must be on filesystem root");
goto errout;
}
- sbi->s_qf_names[qtype] = qname;
+ F2FS_OPTION(sbi).s_qf_names[qtype] = qname;
set_opt(sbi, QUOTA);
return 0;
errout:
@@ -280,13 +291,13 @@
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
- if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) {
+ if (sb_any_quota_loaded(sb) && F2FS_OPTION(sbi).s_qf_names[qtype]) {
f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options"
" when quota turned on");
return -EINVAL;
}
- kfree(sbi->s_qf_names[qtype]);
- sbi->s_qf_names[qtype] = NULL;
+ kfree(F2FS_OPTION(sbi).s_qf_names[qtype]);
+ F2FS_OPTION(sbi).s_qf_names[qtype] = NULL;
return 0;
}
@@ -302,15 +313,19 @@
"Cannot enable project quota enforcement.");
return -1;
}
- if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
- sbi->s_qf_names[PRJQUOTA]) {
- if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
+ if (F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
+ F2FS_OPTION(sbi).s_qf_names[GRPQUOTA] ||
+ F2FS_OPTION(sbi).s_qf_names[PRJQUOTA]) {
+ if (test_opt(sbi, USRQUOTA) &&
+ F2FS_OPTION(sbi).s_qf_names[USRQUOTA])
clear_opt(sbi, USRQUOTA);
- if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
+ if (test_opt(sbi, GRPQUOTA) &&
+ F2FS_OPTION(sbi).s_qf_names[GRPQUOTA])
clear_opt(sbi, GRPQUOTA);
- if (test_opt(sbi, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
+ if (test_opt(sbi, PRJQUOTA) &&
+ F2FS_OPTION(sbi).s_qf_names[PRJQUOTA])
clear_opt(sbi, PRJQUOTA);
if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
@@ -320,19 +335,19 @@
return -1;
}
- if (!sbi->s_jquota_fmt) {
+ if (!F2FS_OPTION(sbi).s_jquota_fmt) {
f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format "
"not specified");
return -1;
}
}
- if (f2fs_sb_has_quota_ino(sbi->sb) && sbi->s_jquota_fmt) {
+ if (f2fs_sb_has_quota_ino(sbi->sb) && F2FS_OPTION(sbi).s_jquota_fmt) {
f2fs_msg(sbi->sb, KERN_INFO,
"QUOTA feature is enabled, so ignore jquota_fmt");
- sbi->s_jquota_fmt = 0;
+ F2FS_OPTION(sbi).s_jquota_fmt = 0;
}
- if (f2fs_sb_has_quota_ino(sbi->sb) && sb_rdonly(sbi->sb)) {
+ if (f2fs_sb_has_quota_ino(sbi->sb) && f2fs_readonly(sbi->sb)) {
f2fs_msg(sbi->sb, KERN_INFO,
"Filesystem with quota feature cannot be mounted RDWR "
"without CONFIG_QUOTA");
@@ -403,14 +418,14 @@
q = bdev_get_queue(sb->s_bdev);
if (blk_queue_discard(q)) {
set_opt(sbi, DISCARD);
- } else if (!f2fs_sb_mounted_blkzoned(sb)) {
+ } else if (!f2fs_sb_has_blkzoned(sb)) {
f2fs_msg(sb, KERN_WARNING,
"mounting with \"discard\" option, but "
"the device does not support discard");
}
break;
case Opt_nodiscard:
- if (f2fs_sb_mounted_blkzoned(sb)) {
+ if (f2fs_sb_has_blkzoned(sb)) {
f2fs_msg(sb, KERN_WARNING,
"discard is required for zoned block devices");
return -EINVAL;
@@ -440,7 +455,7 @@
if (args->from && match_int(args, &arg))
return -EINVAL;
set_opt(sbi, INLINE_XATTR_SIZE);
- sbi->inline_xattr_size = arg;
+ F2FS_OPTION(sbi).inline_xattr_size = arg;
break;
#else
case Opt_user_xattr:
@@ -480,7 +495,7 @@
return -EINVAL;
if (arg != 2 && arg != 4 && arg != NR_CURSEG_TYPE)
return -EINVAL;
- sbi->active_logs = arg;
+ F2FS_OPTION(sbi).active_logs = arg;
break;
case Opt_disable_ext_identify:
set_opt(sbi, DISABLE_EXT_IDENTIFY);
@@ -524,9 +539,9 @@
if (test_opt(sbi, RESERVE_ROOT)) {
f2fs_msg(sb, KERN_INFO,
"Preserve previous reserve_root=%u",
- sbi->root_reserved_blocks);
+ F2FS_OPTION(sbi).root_reserved_blocks);
} else {
- sbi->root_reserved_blocks = arg;
+ F2FS_OPTION(sbi).root_reserved_blocks = arg;
set_opt(sbi, RESERVE_ROOT);
}
break;
@@ -539,7 +554,7 @@
"Invalid uid value %d", arg);
return -EINVAL;
}
- sbi->s_resuid = uid;
+ F2FS_OPTION(sbi).s_resuid = uid;
break;
case Opt_resgid:
if (args->from && match_int(args, &arg))
@@ -550,7 +565,7 @@
"Invalid gid value %d", arg);
return -EINVAL;
}
- sbi->s_resgid = gid;
+ F2FS_OPTION(sbi).s_resgid = gid;
break;
case Opt_mode:
name = match_strdup(&args[0]);
@@ -559,7 +574,7 @@
return -ENOMEM;
if (strlen(name) == 8 &&
!strncmp(name, "adaptive", 8)) {
- if (f2fs_sb_mounted_blkzoned(sb)) {
+ if (f2fs_sb_has_blkzoned(sb)) {
f2fs_msg(sb, KERN_WARNING,
"adaptive mode is not allowed with "
"zoned block device feature");
@@ -585,7 +600,7 @@
1 << arg, BIO_MAX_PAGES);
return -EINVAL;
}
- sbi->write_io_size_bits = arg;
+ F2FS_OPTION(sbi).write_io_size_bits = arg;
break;
case Opt_fault_injection:
if (args->from && match_int(args, &arg))
@@ -646,13 +661,13 @@
return ret;
break;
case Opt_jqfmt_vfsold:
- sbi->s_jquota_fmt = QFMT_VFS_OLD;
+ F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_OLD;
break;
case Opt_jqfmt_vfsv0:
- sbi->s_jquota_fmt = QFMT_VFS_V0;
+ F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_V0;
break;
case Opt_jqfmt_vfsv1:
- sbi->s_jquota_fmt = QFMT_VFS_V1;
+ F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_V1;
break;
case Opt_noquota:
clear_opt(sbi, QUOTA);
@@ -679,6 +694,73 @@
"quota operations not supported");
break;
#endif
+ case Opt_whint:
+ name = match_strdup(&args[0]);
+ if (!name)
+ return -ENOMEM;
+ if (strlen(name) == 10 &&
+ !strncmp(name, "user-based", 10)) {
+ F2FS_OPTION(sbi).whint_mode = WHINT_MODE_USER;
+ } else if (strlen(name) == 3 &&
+ !strncmp(name, "off", 3)) {
+ F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
+ } else if (strlen(name) == 8 &&
+ !strncmp(name, "fs-based", 8)) {
+ F2FS_OPTION(sbi).whint_mode = WHINT_MODE_FS;
+ } else {
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ break;
+ case Opt_alloc:
+ name = match_strdup(&args[0]);
+ if (!name)
+ return -ENOMEM;
+
+ if (strlen(name) == 7 &&
+ !strncmp(name, "default", 7)) {
+ F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
+ } else if (strlen(name) == 5 &&
+ !strncmp(name, "reuse", 5)) {
+ F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
+ } else {
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ break;
+ case Opt_fsync:
+ name = match_strdup(&args[0]);
+ if (!name)
+ return -ENOMEM;
+ if (strlen(name) == 5 &&
+ !strncmp(name, "posix", 5)) {
+ F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
+ } else if (strlen(name) == 6 &&
+ !strncmp(name, "strict", 6)) {
+ F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
+ } else {
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ break;
+ case Opt_test_dummy_encryption:
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+ if (!f2fs_sb_has_encrypt(sb)) {
+ f2fs_msg(sb, KERN_ERR, "Encrypt feature is off");
+ return -EINVAL;
+ }
+
+ F2FS_OPTION(sbi).test_dummy_encryption = true;
+ f2fs_msg(sb, KERN_INFO,
+ "Test dummy encryption mode enabled");
+#else
+ f2fs_msg(sb, KERN_INFO,
+ "Test dummy encryption mount option ignored");
+#endif
+ break;
default:
f2fs_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" or missing value",
@@ -699,14 +781,22 @@
}
if (test_opt(sbi, INLINE_XATTR_SIZE)) {
+ if (!f2fs_sb_has_extra_attr(sb) ||
+ !f2fs_sb_has_flexible_inline_xattr(sb)) {
+ f2fs_msg(sb, KERN_ERR,
+ "extra_attr or flexible_inline_xattr "
+ "feature is off");
+ return -EINVAL;
+ }
if (!test_opt(sbi, INLINE_XATTR)) {
f2fs_msg(sb, KERN_ERR,
"inline_xattr_size option should be "
"set with inline_xattr option");
return -EINVAL;
}
- if (!sbi->inline_xattr_size ||
- sbi->inline_xattr_size >= DEF_ADDRS_PER_INODE -
+ if (!F2FS_OPTION(sbi).inline_xattr_size ||
+ F2FS_OPTION(sbi).inline_xattr_size >=
+ DEF_ADDRS_PER_INODE -
F2FS_TOTAL_EXTRA_ATTR_SIZE -
DEF_INLINE_RESERVED_SIZE -
DEF_MIN_INLINE_SIZE) {
@@ -715,6 +805,12 @@
return -EINVAL;
}
}
+
+ /* Not pass down write hints if the number of active logs is lesser
+ * than NR_CURSEG_TYPE.
+ */
+ if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_TYPE)
+ F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
return 0;
}
@@ -731,7 +827,6 @@
/* Initialize f2fs-specific inode info */
atomic_set(&fi->dirty_pages, 0);
fi->i_current_depth = 1;
- fi->i_advise = 0;
init_rwsem(&fi->i_sem);
INIT_LIST_HEAD(&fi->dirty_list);
INIT_LIST_HEAD(&fi->gdirty_list);
@@ -743,10 +838,6 @@
init_rwsem(&fi->i_mmap_sem);
init_rwsem(&fi->i_xattr_sem);
-#ifdef CONFIG_QUOTA
- memset(&fi->i_dquot, 0, sizeof(fi->i_dquot));
- fi->i_reserved_quota = 0;
-#endif
/* Will be used by directory only */
fi->i_dir_level = F2FS_SB(sb)->dir_level;
@@ -956,7 +1047,7 @@
mempool_destroy(sbi->write_io_dummy);
#ifdef CONFIG_QUOTA
for (i = 0; i < MAXQUOTAS; i++)
- kfree(sbi->s_qf_names[i]);
+ kfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif
destroy_percpu_info(sbi);
for (i = 0; i < NR_PAGE_TYPE; i++)
@@ -1070,8 +1161,9 @@
buf->f_blocks = total_count - start_count;
buf->f_bfree = user_block_count - valid_user_blocks(sbi) -
sbi->current_reserved_blocks;
- if (buf->f_bfree > sbi->root_reserved_blocks)
- buf->f_bavail = buf->f_bfree - sbi->root_reserved_blocks;
+ if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks)
+ buf->f_bavail = buf->f_bfree -
+ F2FS_OPTION(sbi).root_reserved_blocks;
else
buf->f_bavail = 0;
@@ -1106,10 +1198,10 @@
#ifdef CONFIG_QUOTA
struct f2fs_sb_info *sbi = F2FS_SB(sb);
- if (sbi->s_jquota_fmt) {
+ if (F2FS_OPTION(sbi).s_jquota_fmt) {
char *fmtname = "";
- switch (sbi->s_jquota_fmt) {
+ switch (F2FS_OPTION(sbi).s_jquota_fmt) {
case QFMT_VFS_OLD:
fmtname = "vfsold";
break;
@@ -1123,14 +1215,17 @@
seq_printf(seq, ",jqfmt=%s", fmtname);
}
- if (sbi->s_qf_names[USRQUOTA])
- seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]);
+ if (F2FS_OPTION(sbi).s_qf_names[USRQUOTA])
+ seq_show_option(seq, "usrjquota",
+ F2FS_OPTION(sbi).s_qf_names[USRQUOTA]);
- if (sbi->s_qf_names[GRPQUOTA])
- seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]);
+ if (F2FS_OPTION(sbi).s_qf_names[GRPQUOTA])
+ seq_show_option(seq, "grpjquota",
+ F2FS_OPTION(sbi).s_qf_names[GRPQUOTA]);
- if (sbi->s_qf_names[PRJQUOTA])
- seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]);
+ if (F2FS_OPTION(sbi).s_qf_names[PRJQUOTA])
+ seq_show_option(seq, "prjjquota",
+ F2FS_OPTION(sbi).s_qf_names[PRJQUOTA]);
#endif
}
@@ -1165,7 +1260,7 @@
seq_puts(seq, ",noinline_xattr");
if (test_opt(sbi, INLINE_XATTR_SIZE))
seq_printf(seq, ",inline_xattr_size=%u",
- sbi->inline_xattr_size);
+ F2FS_OPTION(sbi).inline_xattr_size);
#endif
#ifdef CONFIG_F2FS_FS_POSIX_ACL
if (test_opt(sbi, POSIX_ACL))
@@ -1201,18 +1296,20 @@
seq_puts(seq, "adaptive");
else if (test_opt(sbi, LFS))
seq_puts(seq, "lfs");
- seq_printf(seq, ",active_logs=%u", sbi->active_logs);
+ seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
if (test_opt(sbi, RESERVE_ROOT))
seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
- sbi->root_reserved_blocks,
- from_kuid_munged(&init_user_ns, sbi->s_resuid),
- from_kgid_munged(&init_user_ns, sbi->s_resgid));
+ F2FS_OPTION(sbi).root_reserved_blocks,
+ from_kuid_munged(&init_user_ns,
+ F2FS_OPTION(sbi).s_resuid),
+ from_kgid_munged(&init_user_ns,
+ F2FS_OPTION(sbi).s_resgid));
if (F2FS_IO_SIZE_BITS(sbi))
seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi));
#ifdef CONFIG_F2FS_FAULT_INJECTION
if (test_opt(sbi, FAULT_INJECTION))
seq_printf(seq, ",fault_injection=%u",
- sbi->fault_info.inject_rate);
+ F2FS_OPTION(sbi).fault_info.inject_rate);
#endif
#ifdef CONFIG_QUOTA
if (test_opt(sbi, QUOTA))
@@ -1225,15 +1322,37 @@
seq_puts(seq, ",prjquota");
#endif
f2fs_show_quota_options(seq, sbi->sb);
+ if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_USER)
+ seq_printf(seq, ",whint_mode=%s", "user-based");
+ else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS)
+ seq_printf(seq, ",whint_mode=%s", "fs-based");
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+ if (F2FS_OPTION(sbi).test_dummy_encryption)
+ seq_puts(seq, ",test_dummy_encryption");
+#endif
+ if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_DEFAULT)
+ seq_printf(seq, ",alloc_mode=%s", "default");
+ else if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
+ seq_printf(seq, ",alloc_mode=%s", "reuse");
+
+ if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX)
+ seq_printf(seq, ",fsync_mode=%s", "posix");
+ else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
+ seq_printf(seq, ",fsync_mode=%s", "strict");
return 0;
}
static void default_options(struct f2fs_sb_info *sbi)
{
/* init some FS parameters */
- sbi->active_logs = NR_CURSEG_TYPE;
- sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
+ F2FS_OPTION(sbi).active_logs = NR_CURSEG_TYPE;
+ F2FS_OPTION(sbi).inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
+ F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
+ F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
+ F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
+ F2FS_OPTION(sbi).test_dummy_encryption = false;
+ sbi->readdir_ra = 1;
set_opt(sbi, BG_GC);
set_opt(sbi, INLINE_XATTR);
@@ -1243,7 +1362,7 @@
set_opt(sbi, NOHEAP);
sbi->sb->s_flags |= MS_LAZYTIME;
set_opt(sbi, FLUSH_MERGE);
- if (f2fs_sb_mounted_blkzoned(sbi->sb)) {
+ if (f2fs_sb_has_blkzoned(sbi->sb)) {
set_opt_mode(sbi, F2FS_MOUNT_LFS);
set_opt(sbi, DISCARD);
} else {
@@ -1270,16 +1389,11 @@
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct f2fs_mount_info org_mount_opt;
unsigned long old_sb_flags;
- int err, active_logs;
+ int err;
bool need_restart_gc = false;
bool need_stop_gc = false;
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
-#ifdef CONFIG_F2FS_FAULT_INJECTION
- struct f2fs_fault_info ffi = sbi->fault_info;
-#endif
#ifdef CONFIG_QUOTA
- int s_jquota_fmt;
- char *s_qf_names[MAXQUOTAS];
int i, j;
#endif
@@ -1289,21 +1403,21 @@
*/
org_mount_opt = sbi->mount_opt;
old_sb_flags = sb->s_flags;
- active_logs = sbi->active_logs;
#ifdef CONFIG_QUOTA
- s_jquota_fmt = sbi->s_jquota_fmt;
+ org_mount_opt.s_jquota_fmt = F2FS_OPTION(sbi).s_jquota_fmt;
for (i = 0; i < MAXQUOTAS; i++) {
- if (sbi->s_qf_names[i]) {
- s_qf_names[i] = kstrdup(sbi->s_qf_names[i],
- GFP_KERNEL);
- if (!s_qf_names[i]) {
+ if (F2FS_OPTION(sbi).s_qf_names[i]) {
+ org_mount_opt.s_qf_names[i] =
+ kstrdup(F2FS_OPTION(sbi).s_qf_names[i],
+ GFP_KERNEL);
+ if (!org_mount_opt.s_qf_names[i]) {
for (j = 0; j < i; j++)
- kfree(s_qf_names[j]);
+ kfree(org_mount_opt.s_qf_names[j]);
return -ENOMEM;
}
} else {
- s_qf_names[i] = NULL;
+ org_mount_opt.s_qf_names[i] = NULL;
}
}
#endif
@@ -1373,7 +1487,8 @@
need_stop_gc = true;
}
- if (*flags & MS_RDONLY) {
+ if (*flags & MS_RDONLY ||
+ F2FS_OPTION(sbi).whint_mode != org_mount_opt.whint_mode) {
writeback_inodes_sb(sb, WB_REASON_SYNC);
sync_inodes_sb(sb);
@@ -1399,7 +1514,7 @@
#ifdef CONFIG_QUOTA
/* Release old quota file names */
for (i = 0; i < MAXQUOTAS; i++)
- kfree(s_qf_names[i]);
+ kfree(org_mount_opt.s_qf_names[i]);
#endif
/* Update the POSIXACL Flag */
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
@@ -1417,18 +1532,14 @@
}
restore_opts:
#ifdef CONFIG_QUOTA
- sbi->s_jquota_fmt = s_jquota_fmt;
+ F2FS_OPTION(sbi).s_jquota_fmt = org_mount_opt.s_jquota_fmt;
for (i = 0; i < MAXQUOTAS; i++) {
- kfree(sbi->s_qf_names[i]);
- sbi->s_qf_names[i] = s_qf_names[i];
+ kfree(F2FS_OPTION(sbi).s_qf_names[i]);
+ F2FS_OPTION(sbi).s_qf_names[i] = org_mount_opt.s_qf_names[i];
}
#endif
sbi->mount_opt = org_mount_opt;
- sbi->active_logs = active_logs;
sb->s_flags = old_sb_flags;
-#ifdef CONFIG_F2FS_FAULT_INJECTION
- sbi->fault_info = ffi;
-#endif
return err;
}
@@ -1550,8 +1661,8 @@
static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type)
{
- return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type],
- sbi->s_jquota_fmt, type);
+ return dquot_quota_on_mount(sbi->sb, F2FS_OPTION(sbi).s_qf_names[type],
+ F2FS_OPTION(sbi).s_jquota_fmt, type);
}
int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly)
@@ -1570,7 +1681,7 @@
}
for (i = 0; i < MAXQUOTAS; i++) {
- if (sbi->s_qf_names[i]) {
+ if (F2FS_OPTION(sbi).s_qf_names[i]) {
err = f2fs_quota_on_mount(sbi, i);
if (!err) {
enabled = 1;
@@ -1797,23 +1908,47 @@
static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
void *fs_data)
{
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+ /*
+ * Encrypting the root directory is not allowed because fsck
+ * expects lost+found directory to exist and remain unencrypted
+ * if LOST_FOUND feature is enabled.
+ *
+ */
+ if (f2fs_sb_has_lost_found(sbi->sb) &&
+ inode->i_ino == F2FS_ROOT_INO(sbi))
+ return -EPERM;
+
return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
ctx, len, fs_data, XATTR_CREATE);
}
+static bool f2fs_dummy_context(struct inode *inode)
+{
+ return DUMMY_ENCRYPTION_ENABLED(F2FS_I_SB(inode));
+}
+
static unsigned f2fs_max_namelen(struct inode *inode)
{
return S_ISLNK(inode->i_mode) ?
inode->i_sb->s_blocksize : F2FS_NAME_LEN;
}
+static inline bool f2fs_is_encrypted(struct inode *inode)
+{
+ return f2fs_encrypted_file(inode);
+}
+
static const struct fscrypt_operations f2fs_cryptops = {
.key_prefix = "f2fs:",
.get_context = f2fs_get_context,
.set_context = f2fs_set_context,
+ .dummy_context = f2fs_dummy_context,
.empty_dir = f2fs_empty_dir,
.max_namelen = f2fs_max_namelen,
+ .is_encrypted = f2fs_is_encrypted,
};
#endif
@@ -1894,7 +2029,6 @@
lock_buffer(bh);
if (super)
memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super));
- set_buffer_uptodate(bh);
set_buffer_dirty(bh);
unlock_buffer(bh);
@@ -2181,6 +2315,8 @@
sbi->dirty_device = 0;
spin_lock_init(&sbi->dev_lock);
+
+ init_rwsem(&sbi->sb_lock);
}
static int init_percpu_info(struct f2fs_sb_info *sbi)
@@ -2206,7 +2342,7 @@
unsigned int n = 0;
int err = -EIO;
- if (!f2fs_sb_mounted_blkzoned(sbi->sb))
+ if (!f2fs_sb_has_blkzoned(sbi->sb))
return 0;
if (sbi->blocks_per_blkz && sbi->blocks_per_blkz !=
@@ -2334,7 +2470,7 @@
}
/* write back-up superblock first */
- bh = sb_getblk(sbi->sb, sbi->valid_super_block ? 0: 1);
+ bh = sb_bread(sbi->sb, sbi->valid_super_block ? 0 : 1);
if (!bh)
return -EIO;
err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi));
@@ -2345,7 +2481,7 @@
return err;
/* write current valid superblock */
- bh = sb_getblk(sbi->sb, sbi->valid_super_block);
+ bh = sb_bread(sbi->sb, sbi->valid_super_block);
if (!bh)
return -EIO;
err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi));
@@ -2417,7 +2553,7 @@
#ifdef CONFIG_BLK_DEV_ZONED
if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM &&
- !f2fs_sb_mounted_blkzoned(sbi->sb)) {
+ !f2fs_sb_has_blkzoned(sbi->sb)) {
f2fs_msg(sbi->sb, KERN_ERR,
"Zoned block device feature not enabled\n");
return -EINVAL;
@@ -2451,6 +2587,18 @@
return 0;
}
+static void f2fs_tuning_parameters(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_sm_info *sm_i = SM_I(sbi);
+
+ /* adjust parameters according to the volume size */
+ if (sm_i->main_segments <= SMALL_VOLUME_SEGMENTS) {
+ F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
+ sm_i->dcc_info->discard_granularity = 1;
+ sm_i->ipu_policy = 1 << F2FS_IPU_FORCE;
+ }
+}
+
static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
{
struct f2fs_sb_info *sbi;
@@ -2498,8 +2646,8 @@
sb->s_fs_info = sbi;
sbi->raw_super = raw_super;
- sbi->s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID);
- sbi->s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
+ F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID);
+ F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
/* precompute checksum seed for metadata */
if (f2fs_sb_has_inode_chksum(sb))
@@ -2512,7 +2660,7 @@
* devices, but mandatory for host-managed zoned block devices.
*/
#ifndef CONFIG_BLK_DEV_ZONED
- if (f2fs_sb_mounted_blkzoned(sb)) {
+ if (f2fs_sb_has_blkzoned(sb)) {
f2fs_msg(sb, KERN_ERR,
"Zoned block device support is not enabled\n");
err = -EOPNOTSUPP;
@@ -2728,7 +2876,7 @@
* Turn on quotas which were not enabled for read-only mounts if
* filesystem has quota feature, so that they are updated correctly.
*/
- if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) {
+ if (f2fs_sb_has_quota_ino(sb) && !f2fs_readonly(sb)) {
err = f2fs_enable_quotas(sb);
if (err) {
f2fs_msg(sb, KERN_ERR,
@@ -2803,6 +2951,8 @@
f2fs_join_shrinker(sbi);
+ f2fs_tuning_parameters(sbi);
+
f2fs_msg(sbi->sb, KERN_NOTICE, "Mounted with checkpoint version = %llx",
cur_cp_version(F2FS_CKPT(sbi)));
f2fs_update_time(sbi, CP_TIME);
@@ -2811,7 +2961,7 @@
free_meta:
#ifdef CONFIG_QUOTA
- if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb))
+ if (f2fs_sb_has_quota_ino(sb) && !f2fs_readonly(sb))
f2fs_quota_off_umount(sbi->sb);
#endif
f2fs_sync_inode_meta(sbi);
@@ -2855,7 +3005,7 @@
free_options:
#ifdef CONFIG_QUOTA
for (i = 0; i < MAXQUOTAS; i++)
- kfree(sbi->s_qf_names[i]);
+ kfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif
kfree(options);
free_sb_buf:
@@ -2952,8 +3102,13 @@
err = f2fs_create_root_stats();
if (err)
goto free_filesystem;
+ err = f2fs_init_post_read_processing();
+ if (err)
+ goto free_root_stats;
return 0;
+free_root_stats:
+ f2fs_destroy_root_stats();
free_filesystem:
unregister_filesystem(&f2fs_fs_type);
free_shrinker:
@@ -2976,6 +3131,7 @@
static void __exit exit_f2fs_fs(void)
{
+ f2fs_destroy_post_read_processing();
f2fs_destroy_root_stats();
unregister_filesystem(&f2fs_fs_type);
unregister_shrinker(&f2fs_shrinker_info);
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index d978c7b..f33a56d 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -58,7 +58,7 @@
#ifdef CONFIG_F2FS_FAULT_INJECTION
else if (struct_type == FAULT_INFO_RATE ||
struct_type == FAULT_INFO_TYPE)
- return (unsigned char *)&sbi->fault_info;
+ return (unsigned char *)&F2FS_OPTION(sbi).fault_info;
#endif
return NULL;
}
@@ -92,10 +92,10 @@
if (!sb->s_bdev->bd_part)
return snprintf(buf, PAGE_SIZE, "0\n");
- if (f2fs_sb_has_crypto(sb))
+ if (f2fs_sb_has_encrypt(sb))
len += snprintf(buf, PAGE_SIZE - len, "%s",
"encryption");
- if (f2fs_sb_mounted_blkzoned(sb))
+ if (f2fs_sb_has_blkzoned(sb))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "blkzoned");
if (f2fs_sb_has_extra_attr(sb))
@@ -116,6 +116,9 @@
if (f2fs_sb_has_inode_crtime(sb))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "inode_crtime");
+ if (f2fs_sb_has_lost_found(sb))
+ len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
+ len ? ", " : "", "lost_found");
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
@@ -136,6 +139,27 @@
if (!ptr)
return -EINVAL;
+ if (!strcmp(a->attr.name, "extension_list")) {
+ __u8 (*extlist)[F2FS_EXTENSION_LEN] =
+ sbi->raw_super->extension_list;
+ int cold_count = le32_to_cpu(sbi->raw_super->extension_count);
+ int hot_count = sbi->raw_super->hot_ext_count;
+ int len = 0, i;
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "cold file extenstion:\n");
+ for (i = 0; i < cold_count; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len, "%s\n",
+ extlist[i]);
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "hot file extenstion:\n");
+ for (i = cold_count; i < cold_count + hot_count; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len, "%s\n",
+ extlist[i]);
+ return len;
+ }
+
ui = (unsigned int *)(ptr + a->offset);
return snprintf(buf, PAGE_SIZE, "%u\n", *ui);
@@ -154,6 +178,41 @@
if (!ptr)
return -EINVAL;
+ if (!strcmp(a->attr.name, "extension_list")) {
+ const char *name = strim((char *)buf);
+ bool set = true, hot;
+
+ if (!strncmp(name, "[h]", 3))
+ hot = true;
+ else if (!strncmp(name, "[c]", 3))
+ hot = false;
+ else
+ return -EINVAL;
+
+ name += 3;
+
+ if (*name == '!') {
+ name++;
+ set = false;
+ }
+
+ if (strlen(name) >= F2FS_EXTENSION_LEN)
+ return -EINVAL;
+
+ down_write(&sbi->sb_lock);
+
+ ret = update_extension_list(sbi, name, hot, set);
+ if (ret)
+ goto out;
+
+ ret = f2fs_commit_super(sbi, false);
+ if (ret)
+ update_extension_list(sbi, name, hot, !set);
+out:
+ up_write(&sbi->sb_lock);
+ return ret ? ret : count;
+ }
+
ui = (unsigned int *)(ptr + a->offset);
ret = kstrtoul(skip_spaces(buf), 0, &t);
@@ -166,7 +225,7 @@
if (a->struct_type == RESERVED_BLOCKS) {
spin_lock(&sbi->stat_lock);
if (t > (unsigned long)(sbi->user_block_count -
- sbi->root_reserved_blocks)) {
+ F2FS_OPTION(sbi).root_reserved_blocks)) {
spin_unlock(&sbi->stat_lock);
return -EINVAL;
}
@@ -236,6 +295,7 @@
FEAT_FLEXIBLE_INLINE_XATTR,
FEAT_QUOTA_INO,
FEAT_INODE_CRTIME,
+ FEAT_LOST_FOUND,
};
static ssize_t f2fs_feature_show(struct f2fs_attr *a,
@@ -251,6 +311,7 @@
case FEAT_FLEXIBLE_INLINE_XATTR:
case FEAT_QUOTA_INO:
case FEAT_INODE_CRTIME:
+ case FEAT_LOST_FOUND:
return snprintf(buf, PAGE_SIZE, "supported\n");
}
return 0;
@@ -307,6 +368,7 @@
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_super_block, extension_list, extension_list);
#ifdef CONFIG_F2FS_FAULT_INJECTION
F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
@@ -329,6 +391,7 @@
F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR);
F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO);
F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME);
+F2FS_FEATURE_RO_ATTR(lost_found, FEAT_LOST_FOUND);
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = {
@@ -357,6 +420,7 @@
ATTR_LIST(iostat_enable),
ATTR_LIST(readdir_ra),
ATTR_LIST(gc_pin_file_thresh),
+ ATTR_LIST(extension_list),
#ifdef CONFIG_F2FS_FAULT_INJECTION
ATTR_LIST(inject_rate),
ATTR_LIST(inject_type),
@@ -383,6 +447,7 @@
ATTR_LIST(flexible_inline_xattr),
ATTR_LIST(quota_ino),
ATTR_LIST(inode_crtime),
+ ATTR_LIST(lost_found),
NULL,
};
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index ad2e55d..17ad41d 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -745,11 +745,12 @@
*/
if (inode && inode_to_wb_is_valid(inode)) {
struct bdi_writeback *wb;
- bool locked, congested;
+ struct wb_lock_cookie lock_cookie = {};
+ bool congested;
- wb = unlocked_inode_to_wb_begin(inode, &locked);
+ wb = unlocked_inode_to_wb_begin(inode, &lock_cookie);
congested = wb_congested(wb, cong_bits);
- unlocked_inode_to_wb_end(inode, locked);
+ unlocked_inode_to_wb_end(inode, &lock_cookie);
return congested;
}
@@ -1941,7 +1942,7 @@
}
if (!list_empty(&wb->work_list))
- mod_delayed_work(bdi_wq, &wb->dwork, 0);
+ wb_wakeup(wb);
else if (wb_has_dirty_io(wb) && dirty_writeback_interval)
wb_wakeup_delayed(wb);
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 658fa9e..a0b0683 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1891,8 +1891,10 @@
err = copy_out_args(cs, &req->out, nbytes);
if (req->in.h.opcode == FUSE_CANONICAL_PATH) {
- req->out.h.error = kern_path((char *)req->out.args[0].value, 0,
- req->canonical_path);
+ char *path = (char *)req->out.args[0].value;
+
+ path[req->out.args[0].size - 1] = 0;
+ req->out.h.error = kern_path(path, 0, req->canonical_path);
}
fuse_copy_finish(cs);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 21d829b..7adf871 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -857,9 +857,9 @@
unsigned nr_pages;
};
-static int fuse_readpages_fill(void *_data, struct page *page)
+static int fuse_readpages_fill(struct file *_data, struct page *page)
{
- struct fuse_fill_data *data = _data;
+ struct fuse_fill_data *data = (struct fuse_fill_data *)_data;
struct fuse_req *req = data->req;
struct inode *inode = data->inode;
struct fuse_conn *fc = get_fuse_conn(inode);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index f1512c8..8aa98b1 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -30,7 +30,7 @@
struct list_head fuse_conn_list;
DEFINE_MUTEX(fuse_mutex);
-static int set_global_limit(const char *val, struct kernel_param *kp);
+static int set_global_limit(const char *val, const struct kernel_param *kp);
unsigned max_user_bgreq;
module_param_call(max_user_bgreq, set_global_limit, param_get_uint,
@@ -827,7 +827,7 @@
*limit = (1 << 16) - 1;
}
-static int set_global_limit(const char *val, struct kernel_param *kp)
+static int set_global_limit(const char *val, const struct kernel_param *kp)
{
int rv;
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 5a6f52e..c6c507c 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -511,7 +511,7 @@
*
*/
-static int __gfs2_readpage(void *file, struct page *page)
+static int __gfs2_readpage(struct file *file, struct page *page)
{
struct gfs2_inode *ip = GFS2_I(page->mapping->host);
struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 11854dd..b9563cd 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -588,6 +588,7 @@
return 0;
out_put_hidden_dir:
+ cancel_delayed_work_sync(&sbi->sync_work);
iput(sbi->hidden_dir);
out_put_root:
dput(sb->s_root);
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 6cb3a8c..d10bb2c 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -691,8 +691,21 @@
{
int err = 0;
- jbd2_might_wait_for_commit(journal);
read_lock(&journal->j_state_lock);
+#ifdef CONFIG_PROVE_LOCKING
+ /*
+ * Some callers make sure transaction is already committing and in that
+ * case we cannot block on open handles anymore. So don't warn in that
+ * case.
+ */
+ if (tid_gt(tid, journal->j_commit_sequence) &&
+ (!journal->j_committing_transaction ||
+ journal->j_committing_transaction->t_tid != tid)) {
+ read_unlock(&journal->j_state_lock);
+ jbd2_might_wait_for_commit(journal);
+ read_lock(&journal->j_state_lock);
+ }
+#endif
#ifdef CONFIG_JBD2_DEBUG
if (!tid_geq(journal->j_commit_request, tid)) {
printk(KERN_ERR
@@ -938,7 +951,7 @@
}
/*
- * This is a variaon of __jbd2_update_log_tail which checks for validity of
+ * This is a variation of __jbd2_update_log_tail which checks for validity of
* provided log tail and locks j_checkpoint_mutex. So it is safe against races
* with other threads updating log tail.
*/
@@ -1381,6 +1394,9 @@
journal_superblock_t *sb = journal->j_superblock;
int ret;
+ if (is_journal_aborted(journal))
+ return -EIO;
+
BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex));
jbd_debug(1, "JBD2: updating superblock (start %lu, seq %u)\n",
tail_block, tail_tid);
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 4e5c610..9e9e093 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -528,6 +528,7 @@
*/
ret = start_this_handle(journal, handle, GFP_NOFS);
if (ret < 0) {
+ handle->h_journal = journal;
jbd2_journal_free_reserved(handle);
return ret;
}
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 5ef21f4..59c019a 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -342,7 +342,7 @@
static void jffs2_kill_sb(struct super_block *sb)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
- if (!(sb->s_flags & MS_RDONLY))
+ if (c && !(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c);
kill_mtd_super(sb);
kfree(c);
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 9d37324..d484c63 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -132,6 +132,8 @@
{
int err = 0;
struct svc_rqst *rqstp = vrqstp;
+ struct net *net = &init_net;
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
/* try_to_freeze() is called from svc_recv() */
set_freezable();
@@ -176,6 +178,8 @@
if (nlmsvc_ops)
nlmsvc_invalidate_all();
nlm_shutdown_hosts();
+ cancel_delayed_work_sync(&ln->grace_period_end);
+ locks_end_grace(&ln->lockd_manager);
return 0;
}
@@ -596,7 +600,7 @@
*/
#define param_set_min_max(name, type, which_strtol, min, max) \
-static int param_set_##name(const char *val, struct kernel_param *kp) \
+static int param_set_##name(const char *val, const struct kernel_param *kp) \
{ \
char *endp; \
__typeof__(type) num = which_strtol(val, &endp, 0); \
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c
index a8329cc..fdf844f 100644
--- a/fs/logfs/dev_bdev.c
+++ b/fs/logfs/dev_bdev.c
@@ -34,9 +34,9 @@
return submit_bio_wait(&bio);
}
-static int bdev_readpage(void *_sb, struct page *page)
+static int bdev_readpage(struct file *_sb, struct page *page)
{
- struct super_block *sb = _sb;
+ struct super_block *sb = (struct super_block *)_sb;
struct block_device *bdev = logfs_super(sb)->s_bdev;
int err;
diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c
index b76a62b..9ec8e8f 100644
--- a/fs/logfs/dev_mtd.c
+++ b/fs/logfs/dev_mtd.c
@@ -122,9 +122,9 @@
mtd_sync(mtd);
}
-static int logfs_mtd_readpage(void *_sb, struct page *page)
+static int logfs_mtd_readpage(struct file *_sb, struct page *page)
{
- struct super_block *sb = _sb;
+ struct super_block *sb = (struct super_block *)_sb;
int err;
err = logfs_mtd_read(sb, page->index << PAGE_SHIFT, PAGE_SIZE,
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index c87ea52..8ddacc1 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -174,7 +174,7 @@
if (!logfs_exist_block(dir, index))
continue;
page = read_cache_page(dir->i_mapping, index,
- (filler_t *)logfs_readpage, NULL);
+ logfs_readpage, NULL);
if (IS_ERR(page))
return page;
dd = kmap_atomic(page);
@@ -306,7 +306,7 @@
continue;
}
page = read_cache_page(dir->i_mapping, pos,
- (filler_t *)logfs_readpage, NULL);
+ logfs_readpage, NULL);
if (IS_ERR(page))
return PTR_ERR(page);
dd = kmap(page);
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h
index 27d040e..1a7c0b0 100644
--- a/fs/logfs/logfs.h
+++ b/fs/logfs/logfs.h
@@ -151,7 +151,7 @@
struct page *(*find_first_sb)(struct super_block *sb, u64 *ofs);
struct page *(*find_last_sb)(struct super_block *sb, u64 *ofs);
int (*write_sb)(struct super_block *sb, struct page *page);
- int (*readpage)(void *_sb, struct page *page);
+ int (*readpage)(struct file *_sb, struct page *page);
void (*writeseg)(struct super_block *sb, u64 ofs, size_t len);
int (*erase)(struct super_block *sb, loff_t ofs, size_t len,
int ensure_write);
diff --git a/fs/namei.c b/fs/namei.c
index 339f7c7..c138ab1 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -221,9 +221,10 @@
if (len <= EMBEDDED_NAME_MAX) {
result->name = (char *)result->iname;
} else if (len <= PATH_MAX) {
+ const size_t size = offsetof(struct filename, iname[1]);
struct filename *tmp;
- tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+ tmp = kmalloc(size, GFP_KERNEL);
if (unlikely(!tmp)) {
__putname(result);
return ERR_PTR(-ENOMEM);
@@ -593,9 +594,10 @@
static bool path_connected(const struct path *path)
{
struct vfsmount *mnt = path->mnt;
+ struct super_block *sb = mnt->mnt_sb;
- /* Only bind mounts can have disconnected paths */
- if (mnt->mnt_root == mnt->mnt_sb->s_root)
+ /* Bind mounts and multi-root filesystems can have disconnected paths */
+ if (!(sb->s_iflags & SB_I_MULTIROOT) && (mnt->mnt_root == sb->s_root))
return true;
return is_subdir(path->dentry, mnt->mnt_root);
@@ -1136,9 +1138,6 @@
path->dentry->d_inode)
return -EISDIR;
- if (path->dentry->d_sb->s_user_ns != &init_user_ns)
- return -EACCES;
-
nd->total_link_count++;
if (nd->total_link_count >= 40)
return -ELOOP;
@@ -2906,6 +2905,11 @@
if (error)
return error;
error = dir->i_op->create(dir, dentry, mode, want_excl);
+ if (error)
+ return error;
+ error = security_inode_post_create(dir, dentry, mode);
+ if (error)
+ return error;
if (!error)
fsnotify_create(dir, dentry);
return error;
@@ -3721,6 +3725,13 @@
return error;
error = dir->i_op->mknod(dir, dentry, mode, dev);
+ if (error)
+ return error;
+
+ error = security_inode_post_create(dir, dentry, mode);
+ if (error)
+ return error;
+
if (!error)
fsnotify_create(dir, dentry);
return error;
diff --git a/fs/namespace.c b/fs/namespace.c
index 2160bb9..4628d08c 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1051,7 +1051,8 @@
goto out_free;
}
- mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED);
+ mnt->mnt.mnt_flags = old->mnt.mnt_flags;
+ mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD|MNT_MARKED|MNT_INTERNAL);
/* Don't allow unprivileged users to change mount flags */
if (flag & CL_UNPRIVILEGED) {
mnt->mnt.mnt_flags |= MNT_LOCK_ATIME;
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
index 88dbbc9..f571570 100644
--- a/fs/ncpfs/ncplib_kernel.c
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -980,6 +980,10 @@
goto out;
}
*bytes_read = ncp_reply_be16(server, 0);
+ if (*bytes_read > to_read) {
+ result = -EINVAL;
+ goto out;
+ }
source = ncp_reply_data(server, 2 + (offset & 1));
memcpy(target, source, *bytes_read);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 1ac1593..1ab9112 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -86,10 +86,10 @@
struct nfs_direct_mirror mirrors[NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX];
int mirror_count;
+ loff_t io_start; /* Start offset for I/O */
ssize_t count, /* bytes actually processed */
max_count, /* max expected count */
bytes_left, /* bytes left to be sent */
- io_start, /* start of IO */
error; /* any reported error */
struct completion completion; /* wait for i/o completion */
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 13abd60..4539008 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -475,6 +475,7 @@
goto out_err_free;
/* fh */
+ rc = -EIO;
p = xdr_inline_decode(&stream, 4);
if (!p)
goto out_err_free;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 4638654..1b1b616 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3300,6 +3300,7 @@
.rpc_resp = &res,
};
int status;
+ int i;
bitmask[0] = FATTR4_WORD0_SUPPORTED_ATTRS |
FATTR4_WORD0_FH_EXPIRE_TYPE |
@@ -3365,8 +3366,13 @@
server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
server->cache_consistency_bitmask[2] = 0;
+
+ /* Avoid a regression due to buggy server */
+ for (i = 0; i < ARRAY_SIZE(res.exclcreat_bitmask); i++)
+ res.exclcreat_bitmask[i] &= res.attr_bitmask[i];
memcpy(server->exclcreat_bitmask, res.exclcreat_bitmask,
sizeof(server->exclcreat_bitmask));
+
server->acl_bitmask = res.acl_bitmask;
server->fh_expire_type = res.fh_expire_type;
}
@@ -8173,6 +8179,12 @@
/* fall through */
case -NFS4ERR_RETRY_UNCACHED_REP:
return -EAGAIN;
+ case -NFS4ERR_BADSESSION:
+ case -NFS4ERR_DEADSESSION:
+ case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+ nfs4_schedule_session_recovery(clp->cl_session,
+ task->tk_status);
+ break;
default:
nfs4_schedule_lease_recovery(clp);
}
@@ -8251,7 +8263,6 @@
if (status == 0)
status = task->tk_status;
rpc_put_task(task);
- return 0;
out:
dprintk("<-- %s status=%d\n", __func__, status);
return status;
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 71deeae..0bb0e62 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1637,13 +1637,14 @@
nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
}
-static void nfs4_reclaim_complete(struct nfs_client *clp,
+static int nfs4_reclaim_complete(struct nfs_client *clp,
const struct nfs4_state_recovery_ops *ops,
struct rpc_cred *cred)
{
/* Notify the server we're done reclaiming our state */
if (ops->reclaim_complete)
- (void)ops->reclaim_complete(clp, cred);
+ return ops->reclaim_complete(clp, cred);
+ return 0;
}
static void nfs4_clear_reclaim_server(struct nfs_server *server)
@@ -1690,13 +1691,16 @@
{
const struct nfs4_state_recovery_ops *ops;
struct rpc_cred *cred;
+ int err;
if (!nfs4_state_clear_reclaim_reboot(clp))
return;
ops = clp->cl_mvops->reboot_recovery_ops;
cred = nfs4_get_clid_cred(clp);
- nfs4_reclaim_complete(clp, ops, cred);
+ err = nfs4_reclaim_complete(clp, ops, cred);
put_rpccred(cred);
+ if (err == -NFS4ERR_CONN_NOT_BOUND_TO_SESSION)
+ set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
}
static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 3d17fc8..892c885 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -1262,8 +1262,10 @@
mirror = &desc->pg_mirrors[midx];
if (!list_empty(&mirror->pg_list)) {
prev = nfs_list_entry(mirror->pg_list.prev);
- if (index != prev->wb_index + 1)
- nfs_pageio_complete_mirror(desc, midx);
+ if (index != prev->wb_index + 1) {
+ nfs_pageio_complete(desc);
+ break;
+ }
}
}
}
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index b8e4474..0e008db 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1953,8 +1953,6 @@
spin_lock(&inode->i_lock);
pnfs_set_plh_return_info(lo, range.iomode, 0);
- /* Block LAYOUTGET */
- set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags);
/*
* mark all matching lsegs so that we are sure to have no live
* segments at hand when sending layoutreturn. See pnfs_put_lseg()
@@ -2308,10 +2306,20 @@
enum pnfs_try_status trypnfs;
trypnfs = pnfs_try_to_read_data(hdr, call_ops, lseg);
- if (trypnfs == PNFS_TRY_AGAIN)
- pnfs_read_resend_pnfs(hdr);
- if (trypnfs == PNFS_NOT_ATTEMPTED || hdr->task.tk_status)
+ switch (trypnfs) {
+ case PNFS_NOT_ATTEMPTED:
pnfs_read_through_mds(desc, hdr);
+ case PNFS_ATTEMPTED:
+ break;
+ case PNFS_TRY_AGAIN:
+ /* cleanup hdr and prepare to redo pnfs */
+ if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+ struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
+ list_splice_init(&hdr->pages, &mirror->pg_list);
+ mirror->pg_recoalesce = 1;
+ }
+ hdr->mds_ops->rpc_release(hdr);
+ }
}
static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index defc923..67b8000 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -346,7 +346,7 @@
};
static int
-readpage_async_filler(void *data, struct page *page)
+readpage_async_filler(struct file *data, struct page *page)
{
struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
struct nfs_page *new;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 51bf1f9..2fdb8f5 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2613,6 +2613,8 @@
/* initial superblock/root creation */
mount_info->fill_super(s, mount_info);
nfs_get_cache_cookie(s, mount_info->parsed, mount_info->cloned);
+ if (!(server->flags & NFS_MOUNT_UNSHARED))
+ s->s_iflags |= SB_I_MULTIROOT;
}
mntroot = nfs_get_root(s, mount_info->mntfh, dev_name);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 9a3b382..a8b786a 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1847,40 +1847,43 @@
return status;
}
-int nfs_commit_inode(struct inode *inode, int how)
+static int __nfs_commit_inode(struct inode *inode, int how,
+ struct writeback_control *wbc)
{
LIST_HEAD(head);
struct nfs_commit_info cinfo;
int may_wait = how & FLUSH_SYNC;
- int error = 0;
- int res;
+ int ret, nscan;
nfs_init_cinfo_from_inode(&cinfo, inode);
nfs_commit_begin(cinfo.mds);
- res = nfs_scan_commit(inode, &head, &cinfo);
- if (res)
- error = nfs_generic_commit_list(inode, &head, how, &cinfo);
+ for (;;) {
+ ret = nscan = nfs_scan_commit(inode, &head, &cinfo);
+ if (ret <= 0)
+ break;
+ ret = nfs_generic_commit_list(inode, &head, how, &cinfo);
+ if (ret < 0)
+ break;
+ ret = 0;
+ if (wbc && wbc->sync_mode == WB_SYNC_NONE) {
+ if (nscan < wbc->nr_to_write)
+ wbc->nr_to_write -= nscan;
+ else
+ wbc->nr_to_write = 0;
+ }
+ if (nscan < INT_MAX)
+ break;
+ cond_resched();
+ }
nfs_commit_end(cinfo.mds);
- if (res == 0)
- return res;
- if (error < 0)
- goto out_error;
- if (!may_wait)
- goto out_mark_dirty;
- error = wait_on_commit(cinfo.mds);
- if (error < 0)
- return error;
- return res;
-out_error:
- res = error;
- /* Note: If we exit without ensuring that the commit is complete,
- * we must mark the inode as dirty. Otherwise, future calls to
- * sync_inode() with the WB_SYNC_ALL flag set will fail to ensure
- * that the data is on the disk.
- */
-out_mark_dirty:
- __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
- return res;
+ if (ret || !may_wait)
+ return ret;
+ return wait_on_commit(cinfo.mds);
+}
+
+int nfs_commit_inode(struct inode *inode, int how)
+{
+ return __nfs_commit_inode(inode, how, NULL);
}
EXPORT_SYMBOL_GPL(nfs_commit_inode);
@@ -1890,11 +1893,11 @@
int flags = FLUSH_SYNC;
int ret = 0;
- /* no commits means nothing needs to be done */
- if (!nfsi->commit_info.ncommit)
- return ret;
-
if (wbc->sync_mode == WB_SYNC_NONE) {
+ /* no commits means nothing needs to be done */
+ if (!nfsi->commit_info.ncommit)
+ goto check_requests_outstanding;
+
/* Don't commit yet if this is a non-blocking flush and there
* are a lot of outstanding writes for this mapping.
*/
@@ -1905,16 +1908,16 @@
flags = 0;
}
- ret = nfs_commit_inode(inode, flags);
- if (ret >= 0) {
- if (wbc->sync_mode == WB_SYNC_NONE) {
- if (ret < wbc->nr_to_write)
- wbc->nr_to_write -= ret;
- else
- wbc->nr_to_write = 0;
- }
- return 0;
- }
+ ret = __nfs_commit_inode(inode, flags, wbc);
+ if (!ret) {
+ if (flags & FLUSH_SYNC)
+ return 0;
+ } else if (nfsi->commit_info.ncommit)
+ goto out_mark_dirty;
+
+check_requests_outstanding:
+ if (!atomic_read(&nfsi->commit_info.rpcs_out))
+ return ret;
out_mark_dirty:
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
return ret;
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 022d958..eef0caf 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1338,14 +1338,14 @@
const struct nfsd4_layout_ops *ops;
struct nfs4_layout_stateid *ls;
__be32 nfserr;
- int accmode;
+ int accmode = NFSD_MAY_READ_IF_EXEC;
switch (lgp->lg_seg.iomode) {
case IOMODE_READ:
- accmode = NFSD_MAY_READ;
+ accmode |= NFSD_MAY_READ;
break;
case IOMODE_RW:
- accmode = NFSD_MAY_READ | NFSD_MAY_WRITE;
+ accmode |= NFSD_MAY_READ | NFSD_MAY_WRITE;
break;
default:
dprintk("%s: invalid iomode %d\n",
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f463c4e..12d7807 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -263,6 +263,35 @@
kfree(nbl);
}
+static void
+remove_blocked_locks(struct nfs4_lockowner *lo)
+{
+ struct nfs4_client *clp = lo->lo_owner.so_client;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ struct nfsd4_blocked_lock *nbl;
+ LIST_HEAD(reaplist);
+
+ /* Dequeue all blocked locks */
+ spin_lock(&nn->blocked_locks_lock);
+ while (!list_empty(&lo->lo_blocked)) {
+ nbl = list_first_entry(&lo->lo_blocked,
+ struct nfsd4_blocked_lock,
+ nbl_list);
+ list_del_init(&nbl->nbl_list);
+ list_move(&nbl->nbl_lru, &reaplist);
+ }
+ spin_unlock(&nn->blocked_locks_lock);
+
+ /* Now free them */
+ while (!list_empty(&reaplist)) {
+ nbl = list_first_entry(&reaplist, struct nfsd4_blocked_lock,
+ nbl_lru);
+ list_del_init(&nbl->nbl_lru);
+ posix_unblock_lock(&nbl->nbl_lock);
+ free_blocked_lock(nbl);
+ }
+}
+
static int
nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
{
@@ -1854,6 +1883,7 @@
static void
__destroy_client(struct nfs4_client *clp)
{
+ int i;
struct nfs4_openowner *oo;
struct nfs4_delegation *dp;
struct list_head reaplist;
@@ -1883,6 +1913,16 @@
nfs4_get_stateowner(&oo->oo_owner);
release_openowner(oo);
}
+ for (i = 0; i < OWNER_HASH_SIZE; i++) {
+ struct nfs4_stateowner *so, *tmp;
+
+ list_for_each_entry_safe(so, tmp, &clp->cl_ownerstr_hashtbl[i],
+ so_strhash) {
+ /* Should be no openowners at this point */
+ WARN_ON_ONCE(so->so_is_open_owner);
+ remove_blocked_locks(lockowner(so));
+ }
+ }
nfsd4_return_all_client_layouts(clp);
nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
@@ -6266,6 +6306,7 @@
}
spin_unlock(&clp->cl_lock);
free_ol_stateid_reaplist(&reaplist);
+ remove_blocked_locks(lo);
nfs4_put_stateowner(&lo->lo_owner);
return status;
@@ -7051,6 +7092,8 @@
}
}
+ WARN_ON(!list_empty(&nn->blocked_locks_lru));
+
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
while (!list_empty(&nn->unconf_id_hashtbl[i])) {
clp = list_entry(nn->unconf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
@@ -7117,7 +7160,6 @@
struct nfs4_delegation *dp = NULL;
struct list_head *pos, *next, reaplist;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- struct nfsd4_blocked_lock *nbl;
cancel_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
@@ -7138,24 +7180,6 @@
nfs4_put_stid(&dp->dl_stid);
}
- BUG_ON(!list_empty(&reaplist));
- spin_lock(&nn->blocked_locks_lock);
- while (!list_empty(&nn->blocked_locks_lru)) {
- nbl = list_first_entry(&nn->blocked_locks_lru,
- struct nfsd4_blocked_lock, nbl_lru);
- list_move(&nbl->nbl_lru, &reaplist);
- list_del_init(&nbl->nbl_list);
- }
- spin_unlock(&nn->blocked_locks_lock);
-
- while (!list_empty(&reaplist)) {
- nbl = list_first_entry(&reaplist,
- struct nfsd4_blocked_lock, nbl_lru);
- list_del_init(&nbl->nbl_lru);
- posix_unblock_lock(&nbl->nbl_lock);
- free_blocked_lock(nbl);
- }
-
nfsd4_client_tracking_exit(net);
nfs4_state_destroy_net(net);
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index b829cc9..8f0b19a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -94,6 +94,12 @@
err = follow_down(&path);
if (err < 0)
goto out;
+ if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
+ nfsd_mountpoint(dentry, exp) == 2) {
+ /* This is only a mountpoint in some other namespace */
+ path_put(&path);
+ goto out;
+ }
exp2 = rqst_exp_get_by_name(rqstp, &path);
if (IS_ERR(exp2)) {
@@ -167,16 +173,26 @@
/*
* For nfsd purposes, we treat V4ROOT exports as though there was an
* export at *every* directory.
+ * We return:
+ * '1' if this dentry *must* be an export point,
+ * '2' if it might be, if there is really a mount here, and
+ * '0' if there is no chance of an export point here.
*/
int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
{
- if (d_mountpoint(dentry))
+ if (!d_inode(dentry))
+ return 0;
+ if (exp->ex_flags & NFSEXP_V4ROOT)
return 1;
if (nfsd4_is_junction(dentry))
return 1;
- if (!(exp->ex_flags & NFSEXP_V4ROOT))
- return 0;
- return d_inode(dentry) != NULL;
+ if (d_mountpoint(dentry))
+ /*
+ * Might only be a mountpoint in a different namespace,
+ * but we need to check.
+ */
+ return 2;
+ return 0;
}
__be32
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index e0e5f7c..8a459b1 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -92,7 +92,7 @@
u32 event_mask,
void *data, int data_type)
{
- __u32 marks_mask, marks_ignored_mask;
+ __u32 marks_mask = 0, marks_ignored_mask = 0;
struct path *path = data;
pr_debug("%s: inode_mark=%p vfsmnt_mark=%p mask=%x data=%p"
@@ -108,24 +108,20 @@
!d_can_lookup(path->dentry))
return false;
- if (inode_mark && vfsmnt_mark) {
- marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
- marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask);
- } else if (inode_mark) {
- /*
- * if the event is for a child and this inode doesn't care about
- * events on the child, don't send it!
- */
- if ((event_mask & FS_EVENT_ON_CHILD) &&
- !(inode_mark->mask & FS_EVENT_ON_CHILD))
- return false;
- marks_mask = inode_mark->mask;
- marks_ignored_mask = inode_mark->ignored_mask;
- } else if (vfsmnt_mark) {
- marks_mask = vfsmnt_mark->mask;
- marks_ignored_mask = vfsmnt_mark->ignored_mask;
- } else {
- BUG();
+ /*
+ * if the event is for a child and this inode doesn't care about
+ * events on the child, don't send it!
+ */
+ if (inode_mark &&
+ (!(event_mask & FS_EVENT_ON_CHILD) ||
+ (inode_mark->mask & FS_EVENT_ON_CHILD))) {
+ marks_mask |= inode_mark->mask;
+ marks_ignored_mask |= inode_mark->ignored_mask;
+ }
+
+ if (vfsmnt_mark) {
+ marks_mask |= vfsmnt_mark->mask;
+ marks_ignored_mask |= vfsmnt_mark->ignored_mask;
}
if (d_is_dir(path->dentry) &&
diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index 1079fae..47e08e4 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -88,13 +88,13 @@
*/
#define DLMFS_CAPABILITIES "bast stackglue"
static int param_set_dlmfs_capabilities(const char *val,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
printk(KERN_ERR "%s: readonly parameter\n", kp->name);
return -EINVAL;
}
static int param_get_dlmfs_capabilities(char *buffer,
- struct kernel_param *kp)
+ const struct kernel_param *kp)
{
return strlcpy(buffer, DLMFS_CAPABILITIES,
strlen(DLMFS_CAPABILITIES) + 1);
diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
index 629d8c9..6e35ef6 100644
--- a/fs/orangefs/super.c
+++ b/fs/orangefs/super.c
@@ -559,6 +559,11 @@
/* provided sb cleanup */
kill_anon_super(sb);
+ if (!ORANGEFS_SB(sb)) {
+ mutex_lock(&orangefs_request_mutex);
+ mutex_unlock(&orangefs_request_mutex);
+ return;
+ }
/*
* issue the unmount to userspace to tell it to remove the
* dynamic mount info it has for this superblock
diff --git a/fs/orangefs/waitqueue.c b/fs/orangefs/waitqueue.c
index f61b008..cbca58b 100644
--- a/fs/orangefs/waitqueue.c
+++ b/fs/orangefs/waitqueue.c
@@ -124,7 +124,14 @@
gossip_debug(GOSSIP_WAIT_DEBUG,
"%s:client core is NOT in service.\n",
__func__);
- timeout = op_timeout_secs * HZ;
+ /*
+ * Don't wait for the userspace component to return if
+ * the filesystem is being umounted anyway.
+ */
+ if (op->upcall.type == ORANGEFS_VFS_OP_FS_UMOUNT)
+ timeout = 0;
+ else
+ timeout = op_timeout_secs * HZ;
}
spin_unlock(&orangefs_request_list_lock);
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 306b6c1..8546384 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -180,6 +180,9 @@
inc_nlink(inode);
}
d_instantiate(dentry, inode);
+ /* Force lookup of new upper hardlink to find its lower */
+ if (hardlink)
+ d_drop(dentry);
}
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 7fb53d0..16f6db8 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -227,6 +227,16 @@
return res;
}
+static bool ovl_can_list(const char *s)
+{
+ /* List all non-trusted xatts */
+ if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
+ return true;
+
+ /* Never list trusted.overlay, list other trusted for superuser only */
+ return !ovl_is_private_xattr(s) && capable(CAP_SYS_ADMIN);
+}
+
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
struct dentry *realdentry = ovl_dentry_real(dentry);
@@ -250,7 +260,7 @@
return -EIO;
len -= slen;
- if (ovl_is_private_xattr(s)) {
+ if (!ovl_can_list(s)) {
res -= slen;
memmove(s, s + slen, len);
} else {
diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig
index 1ade120..08dce22 100644
--- a/fs/proc/Kconfig
+++ b/fs/proc/Kconfig
@@ -81,3 +81,10 @@
Say Y if you are running any user-space software which takes benefit from
this interface. For example, rkt is such a piece of software.
+
+config PROC_UID
+ bool "Include /proc/uid/ files"
+ default y
+ depends on PROC_FS && RT_MUTEXES
+ help
+ Provides aggregated per-uid information under /proc/uid.
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index 12c6922..dea53ba 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -25,6 +25,7 @@
proc-y += namespaces.o
proc-y += self.o
proc-y += thread_self.o
+proc-$(CONFIG_PROC_UID) += uid.o
proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o
proc-$(CONFIG_NET) += proc_net.o
proc-$(CONFIG_PROC_KCORE) += kcore.o
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 794b52a..94f83e7 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -80,6 +80,7 @@
#include <linux/delayacct.h>
#include <linux/seq_file.h>
#include <linux/pid_namespace.h>
+#include <linux/prctl.h>
#include <linux/ptrace.h>
#include <linux/tracehook.h>
#include <linux/string_helpers.h>
@@ -345,8 +346,32 @@
{
#ifdef CONFIG_SECCOMP
seq_put_decimal_ull(m, "Seccomp:\t", p->seccomp.mode);
- seq_putc(m, '\n');
#endif
+ seq_printf(m, "\nSpeculation_Store_Bypass:\t");
+ switch (arch_prctl_spec_ctrl_get(p, PR_SPEC_STORE_BYPASS)) {
+ case -EINVAL:
+ seq_printf(m, "unknown");
+ break;
+ case PR_SPEC_NOT_AFFECTED:
+ seq_printf(m, "not vulnerable");
+ break;
+ case PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE:
+ seq_printf(m, "thread force mitigated");
+ break;
+ case PR_SPEC_PRCTL | PR_SPEC_DISABLE:
+ seq_printf(m, "thread mitigated");
+ break;
+ case PR_SPEC_PRCTL | PR_SPEC_ENABLE:
+ seq_printf(m, "thread vulnerable");
+ break;
+ case PR_SPEC_DISABLE:
+ seq_printf(m, "globally mitigated");
+ break;
+ default:
+ seq_printf(m, "vulnerable");
+ break;
+ }
+ seq_putc(m, '\n');
}
static inline void task_context_switch_counts(struct seq_file *m,
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 1370a4e..5b2d1ea 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -87,6 +87,7 @@
#include <linux/slab.h>
#include <linux/flex_array.h>
#include <linux/posix-timers.h>
+#include <linux/cpufreq_times.h>
#ifdef CONFIG_HARDWALL
#include <asm/hardwall.h>
#endif
@@ -252,7 +253,7 @@
* Inherently racy -- command line shares address space
* with code and data.
*/
- rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0);
+ rv = access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON);
if (rv <= 0)
goto out_free_page;
@@ -270,7 +271,7 @@
int nr_read;
_count = min3(count, len, PAGE_SIZE);
- nr_read = access_remote_vm(mm, p, page, _count, 0);
+ nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
if (nr_read < 0)
rv = nr_read;
if (nr_read <= 0)
@@ -305,7 +306,7 @@
bool final;
_count = min3(count, len, PAGE_SIZE);
- nr_read = access_remote_vm(mm, p, page, _count, 0);
+ nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
if (nr_read < 0)
rv = nr_read;
if (nr_read <= 0)
@@ -354,7 +355,7 @@
bool final;
_count = min3(count, len, PAGE_SIZE);
- nr_read = access_remote_vm(mm, p, page, _count, 0);
+ nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
if (nr_read < 0)
rv = nr_read;
if (nr_read <= 0)
@@ -970,7 +971,7 @@
max_len = min_t(size_t, PAGE_SIZE, count);
this_len = min(max_len, this_len);
- retval = access_remote_vm(mm, (env_start + src), page, this_len, 0);
+ retval = access_remote_vm(mm, (env_start + src), page, this_len, FOLL_ANON);
if (retval <= 0) {
ret = retval;
@@ -3166,8 +3167,8 @@
ONE("cgroup", S_IRUGO, proc_cgroup_show),
#endif
ONE("oom_score", S_IRUGO, proc_oom_score),
- REG("oom_adj", S_IRUSR, proc_oom_adj_operations),
- REG("oom_score_adj", S_IRUSR, proc_oom_score_adj_operations),
+ REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adj_operations),
+ REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
REG("sessionid", S_IRUGO, proc_sessionid_operations),
@@ -3198,6 +3199,9 @@
REG("timers", S_IRUGO, proc_timers_operations),
#endif
REG("timerslack_ns", S_IRUGO|S_IWUGO, proc_pid_set_timerslack_ns_operations),
+#ifdef CONFIG_CPU_FREQ_TIMES
+ ONE("time_in_state", 0444, proc_time_in_state_show),
+#endif
};
static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -3561,8 +3565,8 @@
ONE("cgroup", S_IRUGO, proc_cgroup_show),
#endif
ONE("oom_score", S_IRUGO, proc_oom_score),
- REG("oom_adj", S_IRUSR, proc_oom_adj_operations),
- REG("oom_score_adj", S_IRUSR, proc_oom_score_adj_operations),
+ REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adj_operations),
+ REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
#ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
REG("sessionid", S_IRUGO, proc_sessionid_operations),
@@ -3586,6 +3590,9 @@
REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
REG("setgroups", S_IRUGO|S_IWUSR, proc_setgroups_operations),
#endif
+#ifdef CONFIG_CPU_FREQ_TIMES
+ ONE("time_in_state", 0444, proc_time_in_state_show),
+#endif
};
static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 6dfb414..d8105cd 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -257,6 +257,15 @@
#endif
/*
+ * uid.c
+ */
+#ifdef CONFIG_PROC_UID
+extern int proc_uid_init(void);
+#else
+static inline void proc_uid_init(void) { }
+#endif
+
+/*
* proc_tty.c
*/
#ifdef CONFIG_TTY
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 8d3e484..c2f5014 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -131,7 +131,7 @@
proc_symlink("mounts", NULL, "self/mounts");
proc_net_init();
-
+ proc_uid_init();
#ifdef CONFIG_SYSVIPC
proc_mkdir("sysvipc", NULL);
#endif
diff --git a/fs/proc/uid.c b/fs/proc/uid.c
new file mode 100644
index 0000000..b2bb085
--- /dev/null
+++ b/fs/proc/uid.c
@@ -0,0 +1,295 @@
+/*
+ * /proc/uid support
+ */
+
+#include <linux/cpufreq_times.h>
+#include <linux/fs.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/rtmutex.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include "internal.h"
+
+static struct proc_dir_entry *proc_uid;
+
+#define UID_HASH_BITS 10
+
+static DECLARE_HASHTABLE(proc_uid_hash_table, UID_HASH_BITS);
+
+/*
+ * use rt_mutex here to avoid priority inversion between high-priority readers
+ * of these files and tasks calling proc_register_uid().
+ */
+static DEFINE_RT_MUTEX(proc_uid_lock); /* proc_uid_hash_table */
+
+struct uid_hash_entry {
+ uid_t uid;
+ struct hlist_node hash;
+};
+
+/* Caller must hold proc_uid_lock */
+static bool uid_hash_entry_exists_locked(uid_t uid)
+{
+ struct uid_hash_entry *entry;
+
+ hash_for_each_possible(proc_uid_hash_table, entry, hash, uid) {
+ if (entry->uid == uid)
+ return true;
+ }
+ return false;
+}
+
+void proc_register_uid(kuid_t kuid)
+{
+ struct uid_hash_entry *entry;
+ bool exists;
+ uid_t uid = from_kuid_munged(current_user_ns(), kuid);
+
+ rt_mutex_lock(&proc_uid_lock);
+ exists = uid_hash_entry_exists_locked(uid);
+ rt_mutex_unlock(&proc_uid_lock);
+ if (exists)
+ return;
+
+ entry = kzalloc(sizeof(struct uid_hash_entry), GFP_KERNEL);
+ if (!entry)
+ return;
+ entry->uid = uid;
+
+ rt_mutex_lock(&proc_uid_lock);
+ if (uid_hash_entry_exists_locked(uid))
+ kfree(entry);
+ else
+ hash_add(proc_uid_hash_table, &entry->hash, uid);
+ rt_mutex_unlock(&proc_uid_lock);
+}
+
+struct uid_entry {
+ const char *name;
+ int len;
+ umode_t mode;
+ const struct inode_operations *iop;
+ const struct file_operations *fop;
+};
+
+#define NOD(NAME, MODE, IOP, FOP) { \
+ .name = (NAME), \
+ .len = sizeof(NAME) - 1, \
+ .mode = MODE, \
+ .iop = IOP, \
+ .fop = FOP, \
+}
+
+#ifdef CONFIG_CPU_FREQ_TIMES
+static const struct file_operations proc_uid_time_in_state_operations = {
+ .open = single_uid_time_in_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+static const struct uid_entry uid_base_stuff[] = {
+#ifdef CONFIG_CPU_FREQ_TIMES
+ NOD("time_in_state", 0444, NULL, &proc_uid_time_in_state_operations),
+#endif
+};
+
+static const struct inode_operations proc_uid_def_inode_operations = {
+ .setattr = proc_setattr,
+};
+
+static struct inode *proc_uid_make_inode(struct super_block *sb, kuid_t kuid)
+{
+ struct inode *inode;
+
+ inode = new_inode(sb);
+ if (!inode)
+ return NULL;
+
+ inode->i_ino = get_next_ino();
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = &proc_uid_def_inode_operations;
+ inode->i_uid = kuid;
+
+ return inode;
+}
+
+static int proc_uident_instantiate(struct inode *dir, struct dentry *dentry,
+ struct task_struct *unused, const void *ptr)
+{
+ const struct uid_entry *u = ptr;
+ struct inode *inode;
+
+ inode = proc_uid_make_inode(dir->i_sb, dir->i_uid);
+ if (!inode)
+ return -ENOENT;
+
+ inode->i_mode = u->mode;
+ if (S_ISDIR(inode->i_mode))
+ set_nlink(inode, 2);
+ if (u->iop)
+ inode->i_op = u->iop;
+ if (u->fop)
+ inode->i_fop = u->fop;
+ d_add(dentry, inode);
+ return 0;
+}
+
+static struct dentry *proc_uid_base_lookup(struct inode *dir,
+ struct dentry *dentry,
+ unsigned int flags)
+{
+ const struct uid_entry *u, *last;
+ unsigned int nents = ARRAY_SIZE(uid_base_stuff);
+
+ if (nents == 0)
+ return ERR_PTR(-ENOENT);
+
+ last = &uid_base_stuff[nents - 1];
+ for (u = uid_base_stuff; u <= last; u++) {
+ if (u->len != dentry->d_name.len)
+ continue;
+ if (!memcmp(dentry->d_name.name, u->name, u->len))
+ break;
+ }
+ if (u > last)
+ return ERR_PTR(-ENOENT);
+
+ return ERR_PTR(proc_uident_instantiate(dir, dentry, NULL, u));
+}
+
+static int proc_uid_base_readdir(struct file *file, struct dir_context *ctx)
+{
+ unsigned int nents = ARRAY_SIZE(uid_base_stuff);
+ const struct uid_entry *u;
+
+ if (!dir_emit_dots(file, ctx))
+ return 0;
+
+ if (ctx->pos >= nents + 2)
+ return 0;
+
+ for (u = uid_base_stuff + (ctx->pos - 2);
+ u < uid_base_stuff + nents; u++) {
+ if (!proc_fill_cache(file, ctx, u->name, u->len,
+ proc_uident_instantiate, NULL, u))
+ break;
+ ctx->pos++;
+ }
+
+ return 0;
+}
+
+static const struct inode_operations proc_uid_base_inode_operations = {
+ .lookup = proc_uid_base_lookup,
+ .setattr = proc_setattr,
+};
+
+static const struct file_operations proc_uid_base_operations = {
+ .read = generic_read_dir,
+ .iterate = proc_uid_base_readdir,
+ .llseek = default_llseek,
+};
+
+static int proc_uid_instantiate(struct inode *dir, struct dentry *dentry,
+ struct task_struct *unused, const void *ptr)
+{
+ unsigned int i, len;
+ nlink_t nlinks;
+ kuid_t *kuid = (kuid_t *)ptr;
+ struct inode *inode = proc_uid_make_inode(dir->i_sb, *kuid);
+
+ if (!inode)
+ return -ENOENT;
+
+ inode->i_mode = S_IFDIR | 0555;
+ inode->i_op = &proc_uid_base_inode_operations;
+ inode->i_fop = &proc_uid_base_operations;
+ inode->i_flags |= S_IMMUTABLE;
+
+ nlinks = 2;
+ len = ARRAY_SIZE(uid_base_stuff);
+ for (i = 0; i < len; ++i) {
+ if (S_ISDIR(uid_base_stuff[i].mode))
+ ++nlinks;
+ }
+ set_nlink(inode, nlinks);
+
+ d_add(dentry, inode);
+
+ return 0;
+}
+
+static int proc_uid_readdir(struct file *file, struct dir_context *ctx)
+{
+ int last_shown, i;
+ unsigned long bkt;
+ struct uid_hash_entry *entry;
+
+ if (!dir_emit_dots(file, ctx))
+ return 0;
+
+ i = 0;
+ last_shown = ctx->pos - 2;
+ rt_mutex_lock(&proc_uid_lock);
+ hash_for_each(proc_uid_hash_table, bkt, entry, hash) {
+ int len;
+ char buf[PROC_NUMBUF];
+
+ if (i < last_shown)
+ continue;
+ len = snprintf(buf, sizeof(buf), "%u", entry->uid);
+ if (!proc_fill_cache(file, ctx, buf, len,
+ proc_uid_instantiate, NULL, &entry->uid))
+ break;
+ i++;
+ ctx->pos++;
+ }
+ rt_mutex_unlock(&proc_uid_lock);
+ return 0;
+}
+
+static struct dentry *proc_uid_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ int result = -ENOENT;
+
+ uid_t uid = name_to_int(&dentry->d_name);
+ bool uid_exists;
+
+ rt_mutex_lock(&proc_uid_lock);
+ uid_exists = uid_hash_entry_exists_locked(uid);
+ rt_mutex_unlock(&proc_uid_lock);
+ if (uid_exists) {
+ kuid_t kuid = make_kuid(current_user_ns(), uid);
+
+ result = proc_uid_instantiate(dir, dentry, NULL, &kuid);
+ }
+ return ERR_PTR(result);
+}
+
+static const struct file_operations proc_uid_operations = {
+ .read = generic_read_dir,
+ .iterate = proc_uid_readdir,
+ .llseek = default_llseek,
+};
+
+static const struct inode_operations proc_uid_inode_operations = {
+ .lookup = proc_uid_lookup,
+ .setattr = proc_setattr,
+};
+
+int __init proc_uid_init(void)
+{
+ proc_uid = proc_mkdir("uid", NULL);
+ if (!proc_uid)
+ return -ENOMEM;
+ proc_uid->proc_iops = &proc_uid_inode_operations;
+ proc_uid->proc_fops = &proc_uid_operations;
+
+ return 0;
+}
diff --git a/fs/read_write.c b/fs/read_write.c
index ba28059..2f1027d 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -23,9 +23,6 @@
#include <asm/uaccess.h>
#include <asm/unistd.h>
-typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *);
-typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *);
-
const struct file_operations generic_ro_fops = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -675,7 +672,7 @@
EXPORT_SYMBOL(iov_shorten);
static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter,
- loff_t *ppos, iter_fn_t fn, int flags)
+ loff_t *ppos, int type, int flags)
{
struct kiocb kiocb;
ssize_t ret;
@@ -692,7 +689,10 @@
kiocb.ki_flags |= (IOCB_DSYNC | IOCB_SYNC);
kiocb.ki_pos = *ppos;
- ret = fn(&kiocb, iter);
+ if (type == READ)
+ ret = filp->f_op->read_iter(&kiocb, iter);
+ else
+ ret = filp->f_op->write_iter(&kiocb, iter);
BUG_ON(ret == -EIOCBQUEUED);
*ppos = kiocb.ki_pos;
return ret;
@@ -700,7 +700,7 @@
/* Do it by hand, with file-ops */
static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
- loff_t *ppos, io_fn_t fn, int flags)
+ loff_t *ppos, int type, int flags)
{
ssize_t ret = 0;
@@ -711,7 +711,13 @@
struct iovec iovec = iov_iter_iovec(iter);
ssize_t nr;
- nr = fn(filp, iovec.iov_base, iovec.iov_len, ppos);
+ if (type == READ) {
+ nr = filp->f_op->read(filp, iovec.iov_base,
+ iovec.iov_len, ppos);
+ } else {
+ nr = filp->f_op->write(filp, iovec.iov_base,
+ iovec.iov_len, ppos);
+ }
if (nr < 0) {
if (!ret)
@@ -844,8 +850,6 @@
struct iovec *iov = iovstack;
struct iov_iter iter;
ssize_t ret;
- io_fn_t fn;
- iter_fn_t iter_fn;
ret = import_iovec(type, uvector, nr_segs,
ARRAY_SIZE(iovstack), &iov, &iter);
@@ -859,19 +863,14 @@
if (ret < 0)
goto out;
- if (type == READ) {
- fn = file->f_op->read;
- iter_fn = file->f_op->read_iter;
- } else {
- fn = (io_fn_t)file->f_op->write;
- iter_fn = file->f_op->write_iter;
+ if (type != READ)
file_start_write(file);
- }
- if (iter_fn)
- ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
+ if ((type == READ && file->f_op->read_iter) ||
+ (type == WRITE && file->f_op->write_iter))
+ ret = do_iter_readv_writev(file, &iter, pos, type, flags);
else
- ret = do_loop_readv_writev(file, &iter, pos, fn, flags);
+ ret = do_loop_readv_writev(file, &iter, pos, type, flags);
if (type != READ)
file_end_write(file);
@@ -1069,8 +1068,6 @@
struct iovec *iov = iovstack;
struct iov_iter iter;
ssize_t ret;
- io_fn_t fn;
- iter_fn_t iter_fn;
ret = compat_import_iovec(type, uvector, nr_segs,
UIO_FASTIOV, &iov, &iter);
@@ -1084,19 +1081,14 @@
if (ret < 0)
goto out;
- if (type == READ) {
- fn = file->f_op->read;
- iter_fn = file->f_op->read_iter;
- } else {
- fn = (io_fn_t)file->f_op->write;
- iter_fn = file->f_op->write_iter;
+ if (type != READ)
file_start_write(file);
- }
- if (iter_fn)
- ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
+ if ((type == READ && file->f_op->read_iter) ||
+ (type == WRITE && file->f_op->write_iter))
+ ret = do_iter_readv_writev(file, &iter, pos, type, flags);
else
- ret = do_loop_readv_writev(file, &iter, pos, fn, flags);
+ ret = do_loop_readv_writev(file, &iter, pos, type, flags);
if (type != READ)
file_end_write(file);
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
index bc2dde2..2a5c481 100644
--- a/fs/reiserfs/journal.c
+++ b/fs/reiserfs/journal.c
@@ -1959,7 +1959,7 @@
* will be requeued because superblock is being shutdown and doesn't
* have MS_ACTIVE set.
*/
- cancel_delayed_work_sync(&REISERFS_SB(sb)->old_work);
+ reiserfs_cancel_old_flush(sb);
/* wait for all commits to finish */
cancel_delayed_work_sync(&SB_JOURNAL(sb)->j_work);
@@ -2640,7 +2640,7 @@
if (IS_ERR(journal->j_dev_bd)) {
result = PTR_ERR(journal->j_dev_bd);
journal->j_dev_bd = NULL;
- reiserfs_warning(super,
+ reiserfs_warning(super, "sh-457",
"journal_init_dev: Cannot open '%s': %i",
jdev_name, result);
return result;
diff --git a/fs/reiserfs/reiserfs.h b/fs/reiserfs/reiserfs.h
index 5dcf3ab..6ca0047 100644
--- a/fs/reiserfs/reiserfs.h
+++ b/fs/reiserfs/reiserfs.h
@@ -2948,6 +2948,7 @@
struct reiserfs_list_bitmap *, unsigned int);
void reiserfs_schedule_old_flush(struct super_block *s);
+void reiserfs_cancel_old_flush(struct super_block *s);
void add_save_link(struct reiserfs_transaction_handle *th,
struct inode *inode, int truncate);
int remove_save_link(struct inode *inode, int truncate);
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index e101d70..dec6c93 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -90,7 +90,9 @@
s = sbi->s_journal->j_work_sb;
spin_lock(&sbi->old_work_lock);
- sbi->work_queued = 0;
+ /* Avoid clobbering the cancel state... */
+ if (sbi->work_queued == 1)
+ sbi->work_queued = 0;
spin_unlock(&sbi->old_work_lock);
reiserfs_sync_fs(s, 1);
@@ -117,21 +119,22 @@
spin_unlock(&sbi->old_work_lock);
}
-static void cancel_old_flush(struct super_block *s)
+void reiserfs_cancel_old_flush(struct super_block *s)
{
struct reiserfs_sb_info *sbi = REISERFS_SB(s);
- cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
spin_lock(&sbi->old_work_lock);
- sbi->work_queued = 0;
+ /* Make sure no new flushes will be queued */
+ sbi->work_queued = 2;
spin_unlock(&sbi->old_work_lock);
+ cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
}
static int reiserfs_freeze(struct super_block *s)
{
struct reiserfs_transaction_handle th;
- cancel_old_flush(s);
+ reiserfs_cancel_old_flush(s);
reiserfs_write_lock(s);
if (!(s->s_flags & MS_RDONLY)) {
@@ -152,7 +155,13 @@
static int reiserfs_unfreeze(struct super_block *s)
{
+ struct reiserfs_sb_info *sbi = REISERFS_SB(s);
+
reiserfs_allow_writes(s);
+ spin_lock(&sbi->old_work_lock);
+ /* Allow old_work to run again */
+ sbi->work_queued = 0;
+ spin_unlock(&sbi->old_work_lock);
return 0;
}
@@ -2194,7 +2203,7 @@
if (sbi->commit_wq)
destroy_workqueue(sbi->commit_wq);
- cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
+ reiserfs_cancel_old_flush(s);
reiserfs_free_bitmap_cache(s);
if (SB_BUFFER_WITH_SB(s))
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index e9426a6..776d549 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -51,7 +51,6 @@
* whether the base obbpath has been changed or not
*/
if (is_obbpath_invalid(dentry)) {
- d_drop(dentry);
return 0;
}
@@ -65,7 +64,6 @@
if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
err = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
if (err == 0) {
- d_drop(dentry);
goto out;
}
}
@@ -73,14 +71,12 @@
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
- d_drop(dentry);
err = 0;
goto out;
}
spin_unlock(&lower_dentry->d_lock);
if (parent_lower_dentry != lower_cur_parent_dentry) {
- d_drop(dentry);
err = 0;
goto out;
}
@@ -94,7 +90,6 @@
}
if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) {
- __d_drop(dentry);
err = 0;
}
@@ -113,7 +108,6 @@
if (inode) {
data = top_data_get(SDCARDFS_I(inode));
if (!data || data->abandoned) {
- d_drop(dentry);
err = 0;
}
if (data)
@@ -131,6 +125,8 @@
static void sdcardfs_d_release(struct dentry *dentry)
{
+ if (!dentry || !dentry->d_fsdata)
+ return;
/* release and reset the lower paths */
if (has_graft_path(dentry))
sdcardfs_put_reset_orig_path(dentry);
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 2879d12..1461254 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -414,7 +414,7 @@
fsstack_copy_inode_size(inode, file_inode(lower_file));
fsstack_copy_attr_times(inode, file_inode(lower_file));
if (sizeof(loff_t) > sizeof(long))
- inode_lock(inode);
+ inode_unlock(inode);
}
out:
return err;
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index 843fcd2..98051996 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -41,8 +41,6 @@
void free_dentry_private_data(struct dentry *dentry)
{
- if (!dentry || !dentry->d_fsdata)
- return;
kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata);
dentry->d_fsdata = NULL;
}
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index e4fd3fb..30e0c43 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -316,7 +316,7 @@
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
err = -ENOMEM;
- goto out_iput;
+ goto out_sput;
}
d_set_d_op(sb->s_root, &sdcardfs_ci_dops);
@@ -361,8 +361,7 @@
/* no longer needed: free_dentry_private_data(sb->s_root); */
out_freeroot:
dput(sb->s_root);
-out_iput:
- iput(inode);
+ sb->s_root = NULL;
out_sput:
/* drop refs we took earlier */
atomic_dec(&lower_sb->s_active);
@@ -422,7 +421,7 @@
{
struct sdcardfs_sb_info *sbi;
- if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
+ if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) {
sbi = SDCARDFS_SB(sb);
mutex_lock(&sdcardfs_super_list_lock);
list_del(&sbi->list);
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 6da0c21..4b9a563 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -659,6 +659,7 @@
return ERR_PTR(-ENOMEM);
}
qstr_init(&extension_details->name, tmp);
+ extension_details->num = extensions_value->num;
ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num);
if (ret) {
diff --git a/fs/super.c b/fs/super.c
index 847d82d..ce404a2 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -519,7 +519,11 @@
hlist_add_head(&s->s_instances, &type->fs_supers);
spin_unlock(&sb_lock);
get_filesystem(type);
- register_shrinker(&s->s_shrink);
+ err = register_shrinker(&s->s_shrink);
+ if (err) {
+ deactivate_locked_super(s);
+ s = ERR_PTR(err);
+ }
return s;
}
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 4ec05108..03dda1c 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1728,8 +1728,11 @@
dbg_save_space_info(c);
- for (i = 0; i < c->jhead_cnt; i++)
- ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ ubifs_ro_mode(c, err);
+ }
c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS);
@@ -1795,8 +1798,11 @@
int err;
/* Synchronize write-buffers */
- for (i = 0; i < c->jhead_cnt; i++)
- ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ for (i = 0; i < c->jhead_cnt; i++) {
+ err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
+ if (err)
+ ubifs_ro_mode(c, err);
+ }
/*
* We are being cleanly unmounted which means the
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
index 695389a..3a3be23 100644
--- a/fs/udf/unicode.c
+++ b/fs/udf/unicode.c
@@ -28,6 +28,9 @@
#include "udf_sb.h"
+#define SURROGATE_MASK 0xfffff800
+#define SURROGATE_PAIR 0x0000d800
+
static int udf_uni2char_utf8(wchar_t uni,
unsigned char *out,
int boundlen)
@@ -37,6 +40,9 @@
if (boundlen <= 0)
return -ENAMETOOLONG;
+ if ((uni & SURROGATE_MASK) == SURROGATE_PAIR)
+ return -EINVAL;
+
if (uni < 0x80) {
out[u_len++] = (unsigned char)uni;
} else if (uni < 0x800) {
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 362c6b4..1410835 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -846,22 +846,26 @@
if (error)
goto out_unlock;
} else if (mode & FALLOC_FL_INSERT_RANGE) {
- unsigned int blksize_mask = i_blocksize(inode) - 1;
+ unsigned int blksize_mask = i_blocksize(inode) - 1;
+ loff_t isize = i_size_read(inode);
- new_size = i_size_read(inode) + len;
if (offset & blksize_mask || len & blksize_mask) {
error = -EINVAL;
goto out_unlock;
}
- /* check the new inode size does not wrap through zero */
- if (new_size > inode->i_sb->s_maxbytes) {
+ /*
+ * New inode size must not exceed ->s_maxbytes, accounting for
+ * possible signed overflow.
+ */
+ if (inode->i_sb->s_maxbytes - isize < len) {
error = -EFBIG;
goto out_unlock;
}
+ new_size = isize + len;
/* Offset should be less than i_size */
- if (offset >= i_size_read(inode)) {
+ if (offset >= isize) {
error = -EINVAL;
goto out_unlock;
}
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 1fdd3fa..e3edaa5 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -47,7 +47,7 @@
STATIC int xfs_qm_init_quotainos(xfs_mount_t *);
STATIC int xfs_qm_init_quotainfo(xfs_mount_t *);
-
+STATIC void xfs_qm_destroy_quotainos(xfs_quotainfo_t *qi);
STATIC void xfs_qm_dqfree_one(struct xfs_dquot *dqp);
/*
* We use the batch lookup interface to iterate over the dquots as it
@@ -694,9 +694,17 @@
qinf->qi_shrinker.scan_objects = xfs_qm_shrink_scan;
qinf->qi_shrinker.seeks = DEFAULT_SEEKS;
qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
- register_shrinker(&qinf->qi_shrinker);
+
+ error = register_shrinker(&qinf->qi_shrinker);
+ if (error)
+ goto out_free_inos;
+
return 0;
+out_free_inos:
+ mutex_destroy(&qinf->qi_quotaofflock);
+ mutex_destroy(&qinf->qi_tree_lock);
+ xfs_qm_destroy_quotainos(qinf);
out_free_lru:
list_lru_destroy(&qinf->qi_lru);
out_free_qinf:
@@ -705,7 +713,6 @@
return error;
}
-
/*
* Gets called when unmounting a filesystem or when all quotas get
* turned off.
@@ -722,19 +729,8 @@
unregister_shrinker(&qi->qi_shrinker);
list_lru_destroy(&qi->qi_lru);
-
- if (qi->qi_uquotaip) {
- IRELE(qi->qi_uquotaip);
- qi->qi_uquotaip = NULL; /* paranoia */
- }
- if (qi->qi_gquotaip) {
- IRELE(qi->qi_gquotaip);
- qi->qi_gquotaip = NULL;
- }
- if (qi->qi_pquotaip) {
- IRELE(qi->qi_pquotaip);
- qi->qi_pquotaip = NULL;
- }
+ xfs_qm_destroy_quotainos(qi);
+ mutex_destroy(&qi->qi_tree_lock);
mutex_destroy(&qi->qi_quotaofflock);
kmem_free(qi);
mp->m_quotainfo = NULL;
@@ -1620,6 +1616,24 @@
}
STATIC void
+xfs_qm_destroy_quotainos(
+ xfs_quotainfo_t *qi)
+{
+ if (qi->qi_uquotaip) {
+ IRELE(qi->qi_uquotaip);
+ qi->qi_uquotaip = NULL; /* paranoia */
+ }
+ if (qi->qi_gquotaip) {
+ IRELE(qi->qi_gquotaip);
+ qi->qi_gquotaip = NULL;
+ }
+ if (qi->qi_pquotaip) {
+ IRELE(qi->qi_pquotaip);
+ qi->qi_pquotaip = NULL;
+ }
+}
+
+STATIC void
xfs_qm_dqfree_one(
struct xfs_dquot *dqp)
{
diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index c93dbad..db7d15b 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -783,6 +783,15 @@
#define ACPI_IORT_SMMU_DVM_SUPPORTED (1)
#define ACPI_IORT_SMMU_COHERENT_WALK (1<<1)
+/* Global interrupt format */
+
+struct acpi_iort_smmu_gsi {
+ u32 nsg_irpt;
+ u32 nsg_irpt_flags;
+ u32 nsg_cfg_irpt;
+ u32 nsg_cfg_irpt_flags;
+};
+
struct acpi_iort_smmu_v3 {
u64 base_address; /* SMMUv3 base address */
u32 flags;
diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h
index 8f66aaa..9e3f761 100644
--- a/include/acpi/platform/acgcc.h
+++ b/include/acpi/platform/acgcc.h
@@ -48,7 +48,17 @@
* Use compiler specific <stdarg.h> is a good practice for even when
* -nostdinc is specified (i.e., ACPI_USE_STANDARD_HEADERS undefined.
*/
+#ifndef va_arg
+#ifdef ACPI_USE_BUILTIN_STDARG
+typedef __builtin_va_list va_list;
+#define va_start(v, l) __builtin_va_start(v, l)
+#define va_end(v) __builtin_va_end(v)
+#define va_arg(v, l) __builtin_va_arg(v, l)
+#define va_copy(d, s) __builtin_va_copy(d, s)
+#else
#include <stdarg.h>
+#endif
+#endif
#define ACPI_INLINE __inline__
diff --git a/include/acpi/platform/acintel.h b/include/acpi/platform/acintel.h
index 17bd3b7..bdb6858 100644
--- a/include/acpi/platform/acintel.h
+++ b/include/acpi/platform/acintel.h
@@ -48,7 +48,9 @@
* Use compiler specific <stdarg.h> is a good practice for even when
* -nostdinc is specified (i.e., ACPI_USE_STANDARD_HEADERS undefined.
*/
+#ifndef va_arg
#include <stdarg.h>
+#endif
/* Configuration specific to Intel 64-bit C compiler */
diff --git a/include/asm-generic/futex.h b/include/asm-generic/futex.h
index bf2d34c..f0d8b1c 100644
--- a/include/asm-generic/futex.h
+++ b/include/asm-generic/futex.h
@@ -13,7 +13,7 @@
*/
/**
- * futex_atomic_op_inuser() - Atomic arithmetic operation with constant
+ * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
* argument and comparison of the previous
* futex value with another constant.
*
@@ -25,18 +25,11 @@
* <0 - On error
*/
static inline int
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval, ret;
u32 tmp;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
preempt_disable();
pagefault_disable();
@@ -74,17 +67,9 @@
pagefault_enable();
preempt_enable();
- if (ret == 0) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (ret == 0)
+ *oval = oldval;
+
return ret;
}
@@ -126,18 +111,9 @@
#else
static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
- int op = (encoded_op >> 28) & 7;
- int cmp = (encoded_op >> 24) & 15;
- int oparg = (encoded_op << 8) >> 20;
- int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret;
- if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
- oparg = 1 << oparg;
-
- if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
- return -EFAULT;
pagefault_disable();
@@ -153,17 +129,9 @@
pagefault_enable();
- if (!ret) {
- switch (cmp) {
- case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
- case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
- case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
- case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
- case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
- case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
- default: ret = -ENOSYS;
- }
- }
+ if (!ret)
+ *oval = oldval;
+
return ret;
}
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index c4f8fd2..f6ea0f3 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -764,6 +764,8 @@
int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot);
int pud_clear_huge(pud_t *pud);
int pmd_clear_huge(pmd_t *pmd);
+int pud_free_pmd_page(pud_t *pud);
+int pmd_free_pte_page(pmd_t *pmd);
#else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
static inline int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
{
@@ -781,6 +783,14 @@
{
return 0;
}
+static inline int pud_free_pmd_page(pud_t *pud)
+{
+ return 0;
+}
+static inline int pmd_free_pte_page(pmd_t *pmd)
+{
+ return 0;
+}
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
#ifndef __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3a396a9..3c3519b 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -67,10 +67,12 @@
*/
#ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
#define TEXT_MAIN .text .text.[0-9a-zA-Z_]*
+#define TEXT_CFI_MAIN .text.cfi .text.[0-9a-zA-Z_]*.cfi
#define DATA_MAIN .data .data.[0-9a-zA-Z_]*
#define BSS_MAIN .bss .bss.[0-9a-zA-Z_]*
#else
#define TEXT_MAIN .text
+#define TEXT_CFI_MAIN .text.cfi
#define DATA_MAIN .data
#define BSS_MAIN .bss
#endif
@@ -105,7 +107,7 @@
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
#define MCOUNT_REC() . = ALIGN(8); \
VMLINUX_SYMBOL(__start_mcount_loc) = .; \
- *(__mcount_loc) \
+ KEEP(*(__mcount_loc)) \
VMLINUX_SYMBOL(__stop_mcount_loc) = .;
#else
#define MCOUNT_REC()
@@ -139,10 +141,10 @@
#ifdef CONFIG_EVENT_TRACING
#define FTRACE_EVENTS() . = ALIGN(8); \
VMLINUX_SYMBOL(__start_ftrace_events) = .; \
- *(_ftrace_events) \
+ KEEP(*(_ftrace_events)) \
VMLINUX_SYMBOL(__stop_ftrace_events) = .; \
VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \
- *(_ftrace_enum_map) \
+ KEEP(*(_ftrace_enum_map)) \
VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .;
#else
#define FTRACE_EVENTS()
@@ -170,7 +172,7 @@
#endif
#ifdef CONFIG_SERIAL_EARLYCON
-#define EARLYCON_TABLE() STRUCT_ALIGN(); \
+#define EARLYCON_TABLE() . = ALIGN(8); \
VMLINUX_SYMBOL(__earlycon_table) = .; \
*(__earlycon_table) \
VMLINUX_SYMBOL(__earlycon_table_end) = .;
@@ -185,8 +187,8 @@
#define _OF_TABLE_1(name) \
. = ALIGN(8); \
VMLINUX_SYMBOL(__##name##_of_table) = .; \
- *(__##name##_of_table) \
- *(__##name##_of_table_end)
+ KEEP(*(__##name##_of_table)) \
+ KEEP(*(__##name##_of_table_end))
#define CLKSRC_OF_TABLES() OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
#define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
@@ -304,28 +306,28 @@
/* PCI quirks */ \
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
- *(.pci_fixup_early) \
+ KEEP(*(.pci_fixup_early)) \
VMLINUX_SYMBOL(__end_pci_fixups_early) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_header) = .; \
- *(.pci_fixup_header) \
+ KEEP(*(.pci_fixup_header)) \
VMLINUX_SYMBOL(__end_pci_fixups_header) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_final) = .; \
- *(.pci_fixup_final) \
+ KEEP(*(.pci_fixup_final)) \
VMLINUX_SYMBOL(__end_pci_fixups_final) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_enable) = .; \
- *(.pci_fixup_enable) \
+ KEEP(*(.pci_fixup_enable)) \
VMLINUX_SYMBOL(__end_pci_fixups_enable) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \
- *(.pci_fixup_resume) \
+ KEEP(*(.pci_fixup_resume)) \
VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .; \
- *(.pci_fixup_resume_early) \
+ KEEP(*(.pci_fixup_resume_early)) \
VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \
- *(.pci_fixup_suspend) \
+ KEEP(*(.pci_fixup_suspend)) \
VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \
VMLINUX_SYMBOL(__start_pci_fixups_suspend_late) = .; \
- *(.pci_fixup_suspend_late) \
+ KEEP(*(.pci_fixup_suspend_late)) \
VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \
} \
\
@@ -423,7 +425,7 @@
/* Built-in module parameters. */ \
__param : AT(ADDR(__param) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___param) = .; \
- *(__param) \
+ KEEP(*(__param)) \
VMLINUX_SYMBOL(__stop___param) = .; \
} \
\
@@ -460,6 +462,8 @@
#define TEXT_TEXT \
ALIGN_FUNCTION(); \
*(.text.hot TEXT_MAIN .text.fixup .text.unlikely) \
+ *(.text..ftrace) \
+ *(TEXT_CFI_MAIN) \
*(.ref.text) \
MEM_KEEP(init.text) \
MEM_KEEP(exit.text) \
@@ -512,7 +516,7 @@
VMLINUX_SYMBOL(__softirqentry_text_end) = .;
/* Section used for early init (in .S files) */
-#define HEAD_TEXT *(.head.text)
+#define HEAD_TEXT KEEP(*(.head.text))
#define HEAD_TEXT_SECTION \
.head.text : AT(ADDR(.head.text) - LOAD_OFFSET) { \
@@ -526,7 +530,7 @@
. = ALIGN(align); \
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___ex_table) = .; \
- *(__ex_table) \
+ KEEP(*(__ex_table)) \
VMLINUX_SYMBOL(__stop___ex_table) = .; \
}
@@ -557,7 +561,7 @@
MEM_DISCARD(init.data) \
KERNEL_CTORS() \
MCOUNT_REC() \
- *(.init.rodata) \
+ *(.init.rodata .init.rodata.*) \
FTRACE_EVENTS() \
TRACE_SYSCALLS() \
KPROBE_BLACKLIST() \
@@ -575,7 +579,7 @@
EARLYCON_TABLE()
#define INIT_TEXT \
- *(.init.text) \
+ *(.init.text .init.text.*) \
*(.text.startup) \
MEM_DISCARD(init.text)
@@ -592,7 +596,7 @@
MEM_DISCARD(exit.text)
#define EXIT_CALL \
- *(.exitcall.exit)
+ KEEP(*(.exitcall.exit))
/*
* bss (Block Started by Symbol) - uninitialized data
@@ -668,7 +672,7 @@
. = ALIGN(8); \
__bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___bug_table) = .; \
- *(__bug_table) \
+ KEEP(*(__bug_table)) \
VMLINUX_SYMBOL(__stop___bug_table) = .; \
}
#else
@@ -697,7 +701,7 @@
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
- *(.init.setup) \
+ KEEP(*(.init.setup)) \
VMLINUX_SYMBOL(__setup_end) = .;
#define INIT_CALLS_LEVEL(level) \
diff --git a/include/crypto/ice.h b/include/crypto/ice.h
index b02a440..133041e 100644
--- a/include/crypto/ice.h
+++ b/include/crypto/ice.h
@@ -53,16 +53,22 @@
struct qcom_ice_variant_ops *qcom_ice_get_variant_ops(struct device_node *node);
struct platform_device *qcom_ice_get_pdevice(struct device_node *node);
-void qcom_ice_set_fde_flag(int flag);
-int qcom_ice_set_fde_conf(sector_t strt, sector_t size, int idx, int mode);
#ifdef CONFIG_CRYPTO_DEV_QCOM_ICE
int qcom_ice_setup_ice_hw(const char *storage_type, int enable);
+void qcom_ice_set_fde_flag(int flag);
+int qcom_ice_set_fde_conf(sector_t strt, sector_t size, int idx, int mode);
#else
static inline int qcom_ice_setup_ice_hw(const char *storage_type, int enable)
{
return 0;
}
+static inline void qcom_ice_set_fde_flag(int flag) {}
+static inline int qcom_ice_set_fde_conf(sector_t strt, sector_t size, int idx,
+ int mode)
+{
+ return 0;
+}
#endif
struct qcom_ice_variant_ops {
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index 982c299..642d059 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -74,5 +74,6 @@
extern void drm_kms_helper_poll_disable(struct drm_device *dev);
extern void drm_kms_helper_poll_enable(struct drm_device *dev);
extern void drm_kms_helper_poll_enable_locked(struct drm_device *dev);
+extern bool drm_kms_helper_is_poll_worker(void);
#endif
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 3c2024d..328f232 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -32,6 +32,7 @@
* @type: payload data type
* @flags: flags controlling this message transmission
* @ctrl: ctrl index to transmit on
+ * @wait_ms: duration in ms to wait after message transmission
* @tx_len: length of @tx_buf
* @tx_buf: data to be written
* @rx_len: length of @rx_buf
@@ -42,6 +43,7 @@
u8 type;
u16 flags;
u32 ctrl;
+ u32 wait_ms;
size_t tx_len;
const void *tx_buf;
diff --git a/include/dt-bindings/clock/msm-clocks-8952.h b/include/dt-bindings/clock/msm-clocks-8952.h
index 4547751..e66c5ed 100644
--- a/include/dt-bindings/clock/msm-clocks-8952.h
+++ b/include/dt-bindings/clock/msm-clocks-8952.h
@@ -235,6 +235,13 @@
#define clk_ext_pclk0_clk_src 0x087c1612
#define clk_ext_byte0_clk_src 0xfb32f31e
+#define clk_dsi0pll_byte_clk_src 0xbbaa30be
+#define clk_dsi0pll_pixel_clk_src 0x45b3260f
+#define clk_dsi0pll_vco_clk 0x15940d40
+#define clk_dsi1pll_byte_clk_src 0x63930a8f
+#define clk_dsi1pll_pixel_clk_src 0x0e4c9b56
+#define clk_dsi1pll_vco_clk 0x99797b50
+
#define clk_dsi_pll0_byte_clk_src 0x44539836
#define clk_dsi_pll0_pixel_clk_src 0x5767c287
#define clk_dsi_pll1_byte_clk_src 0x73e88d02
diff --git a/include/dt-bindings/clock/mt2701-clk.h b/include/dt-bindings/clock/mt2701-clk.h
index 2062c67..a72db8d 100644
--- a/include/dt-bindings/clock/mt2701-clk.h
+++ b/include/dt-bindings/clock/mt2701-clk.h
@@ -176,7 +176,8 @@
#define CLK_TOP_AUD_EXT1 156
#define CLK_TOP_AUD_EXT2 157
#define CLK_TOP_NFI1X_PAD 158
-#define CLK_TOP_NR 159
+#define CLK_TOP_AXISEL_D4 159
+#define CLK_TOP_NR 160
/* APMIXEDSYS */
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index c8696df..6da90d0 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,205 +14,213 @@
#ifndef _DT_BINDINGS_CLK_MSM_GCC_SDM845_H
#define _DT_BINDINGS_CLK_MSM_GCC_SDM845_H
+/* Dummy clocks for rate measurement */
+#define MEASURE_ONLY_SNOC_CLK 0
+#define MEASURE_ONLY_CNOC_CLK 1
+#define MEASURE_ONLY_BIMC_CLK 2
+#define MEASURE_ONLY_IPA_2X_CLK 3
+#define UFS_PHY_AXI_EMMC_VOTE_CLK 4
+#define UFS_PHY_AXI_UFS_VOTE_CLK 5
+
/* GCC clock registers */
-#define GCC_AGGRE_NOC_PCIE_TBU_CLK 0
-#define GCC_AGGRE_UFS_CARD_AXI_CLK 1
-#define GCC_AGGRE_UFS_PHY_AXI_CLK 2
-#define GCC_AGGRE_USB3_PRIM_AXI_CLK 3
-#define GCC_AGGRE_USB3_SEC_AXI_CLK 4
-#define GCC_BOOT_ROM_AHB_CLK 5
-#define GCC_CAMERA_AHB_CLK 6
-#define GCC_CAMERA_AXI_CLK 7
-#define GCC_CAMERA_XO_CLK 8
-#define GCC_CE1_AHB_CLK 9
-#define GCC_CE1_AXI_CLK 10
-#define GCC_CE1_CLK 11
-#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 12
-#define GCC_CFG_NOC_USB3_SEC_AXI_CLK 13
-#define GCC_CPUSS_AHB_CLK 14
-#define GCC_CPUSS_AHB_CLK_SRC 15
-#define GCC_CPUSS_DVM_BUS_CLK 16
-#define GCC_CPUSS_GNOC_CLK 17
-#define GCC_CPUSS_RBCPR_CLK 18
-#define GCC_CPUSS_RBCPR_CLK_SRC 19
-#define GCC_DDRSS_GPU_AXI_CLK 20
-#define GCC_DISP_AHB_CLK 21
-#define GCC_DISP_AXI_CLK 22
-#define GCC_DISP_GPLL0_CLK_SRC 23
-#define GCC_DISP_GPLL0_DIV_CLK_SRC 24
-#define GCC_DISP_XO_CLK 25
-#define GCC_GP1_CLK 26
-#define GCC_GP1_CLK_SRC 27
-#define GCC_GP2_CLK 28
-#define GCC_GP2_CLK_SRC 29
-#define GCC_GP3_CLK 30
-#define GCC_GP3_CLK_SRC 31
-#define GCC_GPU_CFG_AHB_CLK 32
-#define GCC_GPU_GPLL0_CLK_SRC 33
-#define GCC_GPU_GPLL0_DIV_CLK_SRC 34
-#define GCC_GPU_MEMNOC_GFX_CLK 35
-#define GCC_GPU_SNOC_DVM_GFX_CLK 36
-#define GCC_MSS_AXIS2_CLK 37
-#define GCC_MSS_CFG_AHB_CLK 38
-#define GCC_MSS_GPLL0_DIV_CLK_SRC 39
-#define GCC_MSS_MFAB_AXIS_CLK 40
-#define GCC_MSS_Q6_MEMNOC_AXI_CLK 41
-#define GCC_MSS_SNOC_AXI_CLK 42
-#define GCC_PCIE_0_AUX_CLK 43
-#define GCC_PCIE_0_AUX_CLK_SRC 44
-#define GCC_PCIE_0_CFG_AHB_CLK 45
-#define GCC_PCIE_0_CLKREF_CLK 46
-#define GCC_PCIE_0_MSTR_AXI_CLK 47
-#define GCC_PCIE_0_PIPE_CLK 48
-#define GCC_PCIE_0_SLV_AXI_CLK 49
-#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 50
-#define GCC_PCIE_1_AUX_CLK 51
-#define GCC_PCIE_1_AUX_CLK_SRC 52
-#define GCC_PCIE_1_CFG_AHB_CLK 53
-#define GCC_PCIE_1_CLKREF_CLK 54
-#define GCC_PCIE_1_MSTR_AXI_CLK 55
-#define GCC_PCIE_1_PIPE_CLK 56
-#define GCC_PCIE_1_SLV_AXI_CLK 57
-#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 58
-#define GCC_PCIE_PHY_AUX_CLK 59
-#define GCC_PCIE_PHY_REFGEN_CLK 60
-#define GCC_PCIE_PHY_REFGEN_CLK_SRC 61
-#define GCC_PDM2_CLK 62
-#define GCC_PDM2_CLK_SRC 63
-#define GCC_PDM_AHB_CLK 64
-#define GCC_PDM_XO4_CLK 65
-#define GCC_PRNG_AHB_CLK 66
-#define GCC_QMIP_CAMERA_AHB_CLK 67
-#define GCC_QMIP_DISP_AHB_CLK 68
-#define GCC_QMIP_VIDEO_AHB_CLK 69
-#define GCC_QUPV3_WRAP0_S0_CLK 70
-#define GCC_QUPV3_WRAP0_S0_CLK_SRC 71
-#define GCC_QUPV3_WRAP0_S1_CLK 72
-#define GCC_QUPV3_WRAP0_S1_CLK_SRC 73
-#define GCC_QUPV3_WRAP0_S2_CLK 74
-#define GCC_QUPV3_WRAP0_S2_CLK_SRC 75
-#define GCC_QUPV3_WRAP0_S3_CLK 76
-#define GCC_QUPV3_WRAP0_S3_CLK_SRC 77
-#define GCC_QUPV3_WRAP0_S4_CLK 78
-#define GCC_QUPV3_WRAP0_S4_CLK_SRC 79
-#define GCC_QUPV3_WRAP0_S5_CLK 80
-#define GCC_QUPV3_WRAP0_S5_CLK_SRC 81
-#define GCC_QUPV3_WRAP0_S6_CLK 82
-#define GCC_QUPV3_WRAP0_S6_CLK_SRC 83
-#define GCC_QUPV3_WRAP0_S7_CLK 84
-#define GCC_QUPV3_WRAP0_S7_CLK_SRC 85
-#define GCC_QUPV3_WRAP1_S0_CLK 86
-#define GCC_QUPV3_WRAP1_S0_CLK_SRC 87
-#define GCC_QUPV3_WRAP1_S1_CLK 88
-#define GCC_QUPV3_WRAP1_S1_CLK_SRC 89
-#define GCC_QUPV3_WRAP1_S2_CLK 90
-#define GCC_QUPV3_WRAP1_S2_CLK_SRC 91
-#define GCC_QUPV3_WRAP1_S3_CLK 92
-#define GCC_QUPV3_WRAP1_S3_CLK_SRC 93
-#define GCC_QUPV3_WRAP1_S4_CLK 94
-#define GCC_QUPV3_WRAP1_S4_CLK_SRC 95
-#define GCC_QUPV3_WRAP1_S5_CLK 96
-#define GCC_QUPV3_WRAP1_S5_CLK_SRC 97
-#define GCC_QUPV3_WRAP1_S6_CLK 98
-#define GCC_QUPV3_WRAP1_S6_CLK_SRC 99
-#define GCC_QUPV3_WRAP1_S7_CLK 100
-#define GCC_QUPV3_WRAP1_S7_CLK_SRC 101
-#define GCC_QUPV3_WRAP_0_M_AHB_CLK 102
-#define GCC_QUPV3_WRAP_0_S_AHB_CLK 103
-#define GCC_QUPV3_WRAP_1_M_AHB_CLK 104
-#define GCC_QUPV3_WRAP_1_S_AHB_CLK 105
-#define GCC_SDCC2_AHB_CLK 106
-#define GCC_SDCC2_APPS_CLK 107
-#define GCC_SDCC2_APPS_CLK_SRC 108
-#define GCC_SDCC4_AHB_CLK 109
-#define GCC_SDCC4_APPS_CLK 110
-#define GCC_SDCC4_APPS_CLK_SRC 111
-#define GCC_SYS_NOC_CPUSS_AHB_CLK 112
-#define GCC_TSIF_AHB_CLK 113
-#define GCC_TSIF_INACTIVITY_TIMERS_CLK 114
-#define GCC_TSIF_REF_CLK 115
-#define GCC_TSIF_REF_CLK_SRC 116
-#define GCC_UFS_CARD_AHB_CLK 117
-#define GCC_UFS_CARD_AXI_CLK 118
-#define GCC_UFS_CARD_AXI_CLK_SRC 119
-#define GCC_UFS_CARD_CLKREF_CLK 120
-#define GCC_UFS_CARD_ICE_CORE_CLK 121
-#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 122
-#define GCC_UFS_CARD_PHY_AUX_CLK 123
-#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 124
-#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 125
-#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 126
-#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 127
-#define GCC_UFS_CARD_UNIPRO_CORE_CLK 128
-#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 129
-#define GCC_UFS_MEM_CLKREF_CLK 130
-#define GCC_UFS_PHY_AHB_CLK 131
-#define GCC_UFS_PHY_AXI_CLK 132
-#define GCC_UFS_PHY_AXI_CLK_SRC 133
-#define GCC_UFS_PHY_ICE_CORE_CLK 134
-#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 135
-#define GCC_UFS_PHY_PHY_AUX_CLK 136
-#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 137
-#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 138
-#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 139
-#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 140
-#define GCC_UFS_PHY_UNIPRO_CORE_CLK 141
-#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 142
-#define GCC_USB30_PRIM_MASTER_CLK 143
-#define GCC_USB30_PRIM_MASTER_CLK_SRC 144
-#define GCC_USB30_PRIM_MOCK_UTMI_CLK 145
-#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 146
-#define GCC_USB30_PRIM_SLEEP_CLK 147
-#define GCC_USB30_SEC_MASTER_CLK 148
-#define GCC_USB30_SEC_MASTER_CLK_SRC 149
-#define GCC_USB30_SEC_MOCK_UTMI_CLK 150
-#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 151
-#define GCC_USB30_SEC_SLEEP_CLK 152
-#define GCC_USB3_PRIM_CLKREF_CLK 153
-#define GCC_USB3_PRIM_PHY_AUX_CLK 154
-#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 155
-#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 156
-#define GCC_USB3_PRIM_PHY_PIPE_CLK 157
-#define GCC_USB3_SEC_CLKREF_CLK 158
-#define GCC_USB3_SEC_PHY_AUX_CLK 159
-#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 160
-#define GCC_USB3_SEC_PHY_COM_AUX_CLK 161
-#define GCC_USB3_SEC_PHY_PIPE_CLK 162
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 163
-#define GCC_VIDEO_AHB_CLK 164
-#define GCC_VIDEO_AXI_CLK 165
-#define GCC_VIDEO_XO_CLK 166
-#define GPLL0 167
-#define GPLL0_OUT_EVEN 168
-#define GPLL0_OUT_MAIN 169
-#define GCC_UFS_CARD_AXI_HW_CTL_CLK 170
-#define GCC_UFS_PHY_AXI_HW_CTL_CLK 171
-#define GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK 172
-#define GCC_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK 173
-#define GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK 174
-#define GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK 175
-#define GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK 176
-#define GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK 177
-#define GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK 178
-#define GCC_UFS_PHY_PHY_AUX_HW_CTL_CLK 179
-#define GCC_GPU_IREF_CLK 180
-#define GCC_SDCC1_AHB_CLK 181
-#define GCC_SDCC1_APPS_CLK 182
-#define GCC_SDCC1_ICE_CORE_CLK 183
-#define GCC_SDCC1_APPS_CLK_SRC 184
-#define GCC_SDCC1_ICE_CORE_CLK_SRC 185
-#define GCC_APC_VS_CLK 186
-#define GCC_GPU_VS_CLK 187
-#define GCC_MSS_VS_CLK 188
-#define GCC_VDDA_VS_CLK 189
-#define GCC_VDDCX_VS_CLK 190
-#define GCC_VDDMX_VS_CLK 191
-#define GCC_VS_CTRL_AHB_CLK 192
-#define GCC_VS_CTRL_CLK 193
-#define GCC_VS_CTRL_CLK_SRC 194
-#define GCC_VSENSOR_CLK_SRC 195
-#define GPLL4 196
-#define GPLL6 197
+#define GCC_AGGRE_NOC_PCIE_TBU_CLK 6
+#define GCC_AGGRE_UFS_CARD_AXI_CLK 7
+#define GCC_AGGRE_UFS_PHY_AXI_CLK 8
+#define GCC_AGGRE_USB3_PRIM_AXI_CLK 9
+#define GCC_AGGRE_USB3_SEC_AXI_CLK 10
+#define GCC_BOOT_ROM_AHB_CLK 11
+#define GCC_CAMERA_AHB_CLK 12
+#define GCC_CAMERA_AXI_CLK 13
+#define GCC_CAMERA_XO_CLK 14
+#define GCC_CE1_AHB_CLK 15
+#define GCC_CE1_AXI_CLK 16
+#define GCC_CE1_CLK 17
+#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 18
+#define GCC_CFG_NOC_USB3_SEC_AXI_CLK 19
+#define GCC_CPUSS_AHB_CLK 20
+#define GCC_CPUSS_AHB_CLK_SRC 21
+#define GCC_CPUSS_DVM_BUS_CLK 22
+#define GCC_CPUSS_GNOC_CLK 23
+#define GCC_CPUSS_RBCPR_CLK 24
+#define GCC_CPUSS_RBCPR_CLK_SRC 25
+#define GCC_DDRSS_GPU_AXI_CLK 26
+#define GCC_DISP_AHB_CLK 27
+#define GCC_DISP_AXI_CLK 28
+#define GCC_DISP_GPLL0_CLK_SRC 29
+#define GCC_DISP_GPLL0_DIV_CLK_SRC 30
+#define GCC_DISP_XO_CLK 31
+#define GCC_GP1_CLK 32
+#define GCC_GP1_CLK_SRC 33
+#define GCC_GP2_CLK 34
+#define GCC_GP2_CLK_SRC 35
+#define GCC_GP3_CLK 36
+#define GCC_GP3_CLK_SRC 37
+#define GCC_GPU_CFG_AHB_CLK 38
+#define GCC_GPU_GPLL0_CLK_SRC 39
+#define GCC_GPU_GPLL0_DIV_CLK_SRC 40
+#define GCC_GPU_MEMNOC_GFX_CLK 41
+#define GCC_GPU_SNOC_DVM_GFX_CLK 42
+#define GCC_MSS_AXIS2_CLK 43
+#define GCC_MSS_CFG_AHB_CLK 44
+#define GCC_MSS_GPLL0_DIV_CLK_SRC 45
+#define GCC_MSS_MFAB_AXIS_CLK 46
+#define GCC_MSS_Q6_MEMNOC_AXI_CLK 47
+#define GCC_MSS_SNOC_AXI_CLK 48
+#define GCC_PCIE_0_AUX_CLK 49
+#define GCC_PCIE_0_AUX_CLK_SRC 50
+#define GCC_PCIE_0_CFG_AHB_CLK 51
+#define GCC_PCIE_0_CLKREF_CLK 52
+#define GCC_PCIE_0_MSTR_AXI_CLK 53
+#define GCC_PCIE_0_PIPE_CLK 54
+#define GCC_PCIE_0_SLV_AXI_CLK 55
+#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 56
+#define GCC_PCIE_1_AUX_CLK 57
+#define GCC_PCIE_1_AUX_CLK_SRC 58
+#define GCC_PCIE_1_CFG_AHB_CLK 59
+#define GCC_PCIE_1_CLKREF_CLK 60
+#define GCC_PCIE_1_MSTR_AXI_CLK 61
+#define GCC_PCIE_1_PIPE_CLK 62
+#define GCC_PCIE_1_SLV_AXI_CLK 63
+#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 64
+#define GCC_PCIE_PHY_AUX_CLK 65
+#define GCC_PCIE_PHY_REFGEN_CLK 66
+#define GCC_PCIE_PHY_REFGEN_CLK_SRC 67
+#define GCC_PDM2_CLK 68
+#define GCC_PDM2_CLK_SRC 69
+#define GCC_PDM_AHB_CLK 70
+#define GCC_PDM_XO4_CLK 71
+#define GCC_PRNG_AHB_CLK 72
+#define GCC_QMIP_CAMERA_AHB_CLK 73
+#define GCC_QMIP_DISP_AHB_CLK 74
+#define GCC_QMIP_VIDEO_AHB_CLK 75
+#define GCC_QUPV3_WRAP0_S0_CLK 76
+#define GCC_QUPV3_WRAP0_S0_CLK_SRC 77
+#define GCC_QUPV3_WRAP0_S1_CLK 78
+#define GCC_QUPV3_WRAP0_S1_CLK_SRC 79
+#define GCC_QUPV3_WRAP0_S2_CLK 80
+#define GCC_QUPV3_WRAP0_S2_CLK_SRC 81
+#define GCC_QUPV3_WRAP0_S3_CLK 82
+#define GCC_QUPV3_WRAP0_S3_CLK_SRC 83
+#define GCC_QUPV3_WRAP0_S4_CLK 84
+#define GCC_QUPV3_WRAP0_S4_CLK_SRC 85
+#define GCC_QUPV3_WRAP0_S5_CLK 86
+#define GCC_QUPV3_WRAP0_S5_CLK_SRC 87
+#define GCC_QUPV3_WRAP0_S6_CLK 88
+#define GCC_QUPV3_WRAP0_S6_CLK_SRC 89
+#define GCC_QUPV3_WRAP0_S7_CLK 90
+#define GCC_QUPV3_WRAP0_S7_CLK_SRC 91
+#define GCC_QUPV3_WRAP1_S0_CLK 92
+#define GCC_QUPV3_WRAP1_S0_CLK_SRC 93
+#define GCC_QUPV3_WRAP1_S1_CLK 94
+#define GCC_QUPV3_WRAP1_S1_CLK_SRC 95
+#define GCC_QUPV3_WRAP1_S2_CLK 96
+#define GCC_QUPV3_WRAP1_S2_CLK_SRC 97
+#define GCC_QUPV3_WRAP1_S3_CLK 98
+#define GCC_QUPV3_WRAP1_S3_CLK_SRC 99
+#define GCC_QUPV3_WRAP1_S4_CLK 100
+#define GCC_QUPV3_WRAP1_S4_CLK_SRC 101
+#define GCC_QUPV3_WRAP1_S5_CLK 102
+#define GCC_QUPV3_WRAP1_S5_CLK_SRC 103
+#define GCC_QUPV3_WRAP1_S6_CLK 104
+#define GCC_QUPV3_WRAP1_S6_CLK_SRC 105
+#define GCC_QUPV3_WRAP1_S7_CLK 106
+#define GCC_QUPV3_WRAP1_S7_CLK_SRC 107
+#define GCC_QUPV3_WRAP_0_M_AHB_CLK 108
+#define GCC_QUPV3_WRAP_0_S_AHB_CLK 109
+#define GCC_QUPV3_WRAP_1_M_AHB_CLK 110
+#define GCC_QUPV3_WRAP_1_S_AHB_CLK 111
+#define GCC_SDCC2_AHB_CLK 112
+#define GCC_SDCC2_APPS_CLK 113
+#define GCC_SDCC2_APPS_CLK_SRC 114
+#define GCC_SDCC4_AHB_CLK 115
+#define GCC_SDCC4_APPS_CLK 116
+#define GCC_SDCC4_APPS_CLK_SRC 117
+#define GCC_SYS_NOC_CPUSS_AHB_CLK 118
+#define GCC_TSIF_AHB_CLK 119
+#define GCC_TSIF_INACTIVITY_TIMERS_CLK 120
+#define GCC_TSIF_REF_CLK 121
+#define GCC_TSIF_REF_CLK_SRC 122
+#define GCC_UFS_CARD_AHB_CLK 123
+#define GCC_UFS_CARD_AXI_CLK 124
+#define GCC_UFS_CARD_AXI_CLK_SRC 125
+#define GCC_UFS_CARD_CLKREF_CLK 126
+#define GCC_UFS_CARD_ICE_CORE_CLK 127
+#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 128
+#define GCC_UFS_CARD_PHY_AUX_CLK 129
+#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 130
+#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 131
+#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 132
+#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 133
+#define GCC_UFS_CARD_UNIPRO_CORE_CLK 134
+#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 135
+#define GCC_UFS_MEM_CLKREF_CLK 136
+#define GCC_UFS_PHY_AHB_CLK 137
+#define GCC_UFS_PHY_AXI_CLK 138
+#define GCC_UFS_PHY_AXI_CLK_SRC 139
+#define GCC_UFS_PHY_ICE_CORE_CLK 140
+#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 141
+#define GCC_UFS_PHY_PHY_AUX_CLK 142
+#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 143
+#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 144
+#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 145
+#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 146
+#define GCC_UFS_PHY_UNIPRO_CORE_CLK 147
+#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 148
+#define GCC_USB30_PRIM_MASTER_CLK 149
+#define GCC_USB30_PRIM_MASTER_CLK_SRC 150
+#define GCC_USB30_PRIM_MOCK_UTMI_CLK 151
+#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 152
+#define GCC_USB30_PRIM_SLEEP_CLK 153
+#define GCC_USB30_SEC_MASTER_CLK 154
+#define GCC_USB30_SEC_MASTER_CLK_SRC 155
+#define GCC_USB30_SEC_MOCK_UTMI_CLK 156
+#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 157
+#define GCC_USB30_SEC_SLEEP_CLK 158
+#define GCC_USB3_PRIM_CLKREF_CLK 159
+#define GCC_USB3_PRIM_PHY_AUX_CLK 160
+#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 161
+#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 162
+#define GCC_USB3_PRIM_PHY_PIPE_CLK 163
+#define GCC_USB3_SEC_CLKREF_CLK 164
+#define GCC_USB3_SEC_PHY_AUX_CLK 165
+#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 166
+#define GCC_USB3_SEC_PHY_COM_AUX_CLK 167
+#define GCC_USB3_SEC_PHY_PIPE_CLK 168
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 169
+#define GCC_VIDEO_AHB_CLK 170
+#define GCC_VIDEO_AXI_CLK 171
+#define GCC_VIDEO_XO_CLK 172
+#define GPLL0 173
+#define GPLL0_OUT_EVEN 174
+#define GPLL0_OUT_MAIN 175
+#define GCC_UFS_CARD_AXI_HW_CTL_CLK 176
+#define GCC_UFS_PHY_AXI_HW_CTL_CLK 177
+#define GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK 178
+#define GCC_UFS_PHY_UNIPRO_CORE_HW_CTL_CLK 179
+#define GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK 180
+#define GCC_UFS_PHY_ICE_CORE_HW_CTL_CLK 181
+#define GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK 182
+#define GCC_AGGRE_UFS_PHY_AXI_HW_CTL_CLK 183
+#define GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK 184
+#define GCC_UFS_PHY_PHY_AUX_HW_CTL_CLK 185
+#define GCC_GPU_IREF_CLK 186
+#define GCC_SDCC1_AHB_CLK 187
+#define GCC_SDCC1_APPS_CLK 188
+#define GCC_SDCC1_ICE_CORE_CLK 189
+#define GCC_SDCC1_APPS_CLK_SRC 190
+#define GCC_SDCC1_ICE_CORE_CLK_SRC 191
+#define GCC_APC_VS_CLK 192
+#define GCC_GPU_VS_CLK 193
+#define GCC_MSS_VS_CLK 194
+#define GCC_VDDA_VS_CLK 195
+#define GCC_VDDCX_VS_CLK 196
+#define GCC_VDDMX_VS_CLK 197
+#define GCC_VS_CTRL_AHB_CLK 198
+#define GCC_VS_CTRL_CLK 199
+#define GCC_VS_CTRL_CLK_SRC 200
+#define GCC_VSENSOR_CLK_SRC 201
+#define GPLL4 202
+#define GPLL6 203
/* GCC reset clocks */
#define GCC_MMSS_BCR 0
@@ -243,10 +251,4 @@
#define GCC_PCIE_1_PHY_BCR 25
#define GCC_SDCC1_BCR 26
-/* Dummy clocks for rate measurement */
-#define MEASURE_ONLY_SNOC_CLK 0
-#define MEASURE_ONLY_CNOC_CLK 1
-#define MEASURE_ONLY_BIMC_CLK 2
-#define MEASURE_ONLY_IPA_2X_CLK 3
-
#endif
diff --git a/include/dt-bindings/clock/r8a7794-clock.h b/include/dt-bindings/clock/r8a7794-clock.h
index 88e6484..cdeafd9 100644
--- a/include/dt-bindings/clock/r8a7794-clock.h
+++ b/include/dt-bindings/clock/r8a7794-clock.h
@@ -81,6 +81,7 @@
#define R8A7794_CLK_SCIF2 19
#define R8A7794_CLK_SCIF1 20
#define R8A7794_CLK_SCIF0 21
+#define R8A7794_CLK_DU1 23
#define R8A7794_CLK_DU0 24
/* MSTP8 */
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
index 64e2dc7..7ac6f16 100644
--- a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
+++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
@@ -11,9 +11,14 @@
#define PMIC_GPIO_PULL_UP_1P5_30 3
#define PMIC_GPIO_STRENGTH_NO 0
-#define PMIC_GPIO_STRENGTH_HIGH 1
+#define PMIC_GPIO_STRENGTH_LOW 1
#define PMIC_GPIO_STRENGTH_MED 2
-#define PMIC_GPIO_STRENGTH_LOW 3
+#define PMIC_GPIO_STRENGTH_HIGH 3
+
+#define PM8921_GPIO_STRENGTH_NO 0
+#define PM8921_GPIO_STRENGTH_HIGH 1
+#define PM8921_GPIO_STRENGTH_MED 2
+#define PM8921_GPIO_STRENGTH_LOW 3
/*
* Note: PM8018 GPIO3 and GPIO4 are supporting
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
new file mode 100644
index 0000000..4b15481
--- /dev/null
+++ b/include/kvm/arm_psci.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012,2013 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __KVM_ARM_PSCI_H__
+#define __KVM_ARM_PSCI_H__
+
+#include <linux/kvm_host.h>
+#include <uapi/linux/psci.h>
+
+#define KVM_ARM_PSCI_0_1 PSCI_VERSION(0, 1)
+#define KVM_ARM_PSCI_0_2 PSCI_VERSION(0, 2)
+#define KVM_ARM_PSCI_1_0 PSCI_VERSION(1, 0)
+
+#define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_0
+
+/*
+ * We need the KVM pointer independently from the vcpu as we can call
+ * this from HYP, and need to apply kern_hyp_va on it...
+ */
+static inline int kvm_psci_version(struct kvm_vcpu *vcpu, struct kvm *kvm)
+{
+ /*
+ * Our PSCI implementation stays the same across versions from
+ * v0.2 onward, only adding the few mandatory functions (such
+ * as FEATURES with 1.0) that are required by newer
+ * revisions. It is thus safe to return the latest, unless
+ * userspace has instructed us otherwise.
+ */
+ if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features)) {
+ if (vcpu->kvm->arch.psci_version)
+ return vcpu->kvm->arch.psci_version;
+
+ return KVM_ARM_PSCI_LATEST;
+ }
+
+ return KVM_ARM_PSCI_0_1;
+}
+
+
+int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
+
+struct kvm_one_reg;
+
+int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
+int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
+
+#endif /* __KVM_ARM_PSCI_H__ */
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 4c5bca38..43690f5 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -14,14 +14,16 @@
#ifndef __LINUX_ARM_SMCCC_H
#define __LINUX_ARM_SMCCC_H
+#include <uapi/linux/const.h>
+
/*
* This file provides common defines for ARM SMC Calling Convention as
* specified in
* http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
*/
-#define ARM_SMCCC_STD_CALL 0
-#define ARM_SMCCC_FAST_CALL 1
+#define ARM_SMCCC_STD_CALL _AC(0,U)
+#define ARM_SMCCC_FAST_CALL _AC(1,U)
#define ARM_SMCCC_TYPE_SHIFT 31
#define ARM_SMCCC_SMC_32 0
@@ -60,6 +62,24 @@
#define ARM_SMCCC_QUIRK_NONE 0
#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */
+#define ARM_SMCCC_VERSION_1_0 0x10000
+#define ARM_SMCCC_VERSION_1_1 0x10001
+
+#define ARM_SMCCC_VERSION_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 0)
+
+#define ARM_SMCCC_ARCH_FEATURES_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 1)
+
+#define ARM_SMCCC_ARCH_WORKAROUND_1 \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 0x8000)
+
#ifndef __ASSEMBLY__
#include <linux/linkage.h>
@@ -130,5 +150,148 @@
#define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__)
+/* SMCCC v1.1 implementation madness follows */
+#ifdef CONFIG_ARM64
+
+#define SMCCC_SMC_INST "smc #0"
+#define SMCCC_HVC_INST "hvc #0"
+#define SMCCC_REG(n) asm("x" # n)
+
+#elif defined(CONFIG_ARM)
+#include <asm/opcodes-sec.h>
+#include <asm/opcodes-virt.h>
+
+#define SMCCC_SMC_INST __SMC(0)
+#define SMCCC_HVC_INST __HVC(0)
+#define SMCCC_REG(n) asm("r" # n)
+
+#endif
+
+#define ___count_args(_0, _1, _2, _3, _4, _5, _6, _7, _8, x, ...) x
+
+#define __count_args(...) \
+ ___count_args(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
+
+#define __constraint_write_0 \
+ "+r" (r0), "=&r" (r1), "=&r" (r2), "=&r" (r3)
+#define __constraint_write_1 \
+ "+r" (r0), "+r" (r1), "=&r" (r2), "=&r" (r3)
+#define __constraint_write_2 \
+ "+r" (r0), "+r" (r1), "+r" (r2), "=&r" (r3)
+#define __constraint_write_3 \
+ "+r" (r0), "+r" (r1), "+r" (r2), "+r" (r3)
+#define __constraint_write_4 __constraint_write_3
+#define __constraint_write_5 __constraint_write_4
+#define __constraint_write_6 __constraint_write_5
+#define __constraint_write_7 __constraint_write_6
+
+#define __constraint_read_0
+#define __constraint_read_1
+#define __constraint_read_2
+#define __constraint_read_3
+#define __constraint_read_4 "r" (r4)
+#define __constraint_read_5 __constraint_read_4, "r" (r5)
+#define __constraint_read_6 __constraint_read_5, "r" (r6)
+#define __constraint_read_7 __constraint_read_6, "r" (r7)
+
+#define __declare_arg_0(a0, res) \
+ struct arm_smccc_res *___res = res; \
+ register u32 r0 SMCCC_REG(0) = a0; \
+ register unsigned long r1 SMCCC_REG(1); \
+ register unsigned long r2 SMCCC_REG(2); \
+ register unsigned long r3 SMCCC_REG(3)
+
+#define __declare_arg_1(a0, a1, res) \
+ struct arm_smccc_res *___res = res; \
+ register u32 r0 SMCCC_REG(0) = a0; \
+ register typeof(a1) r1 SMCCC_REG(1) = a1; \
+ register unsigned long r2 SMCCC_REG(2); \
+ register unsigned long r3 SMCCC_REG(3)
+
+#define __declare_arg_2(a0, a1, a2, res) \
+ struct arm_smccc_res *___res = res; \
+ register u32 r0 SMCCC_REG(0) = a0; \
+ register typeof(a1) r1 SMCCC_REG(1) = a1; \
+ register typeof(a2) r2 SMCCC_REG(2) = a2; \
+ register unsigned long r3 SMCCC_REG(3)
+
+#define __declare_arg_3(a0, a1, a2, a3, res) \
+ struct arm_smccc_res *___res = res; \
+ register u32 r0 SMCCC_REG(0) = a0; \
+ register typeof(a1) r1 SMCCC_REG(1) = a1; \
+ register typeof(a2) r2 SMCCC_REG(2) = a2; \
+ register typeof(a3) r3 SMCCC_REG(3) = a3
+
+#define __declare_arg_4(a0, a1, a2, a3, a4, res) \
+ __declare_arg_3(a0, a1, a2, a3, res); \
+ register typeof(a4) r4 SMCCC_REG(4) = a4
+
+#define __declare_arg_5(a0, a1, a2, a3, a4, a5, res) \
+ __declare_arg_4(a0, a1, a2, a3, a4, res); \
+ register typeof(a5) r5 SMCCC_REG(5) = a5
+
+#define __declare_arg_6(a0, a1, a2, a3, a4, a5, a6, res) \
+ __declare_arg_5(a0, a1, a2, a3, a4, a5, res); \
+ register typeof(a6) r6 SMCCC_REG(6) = a6
+
+#define __declare_arg_7(a0, a1, a2, a3, a4, a5, a6, a7, res) \
+ __declare_arg_6(a0, a1, a2, a3, a4, a5, a6, res); \
+ register typeof(a7) r7 SMCCC_REG(7) = a7
+
+#define ___declare_args(count, ...) __declare_arg_ ## count(__VA_ARGS__)
+#define __declare_args(count, ...) ___declare_args(count, __VA_ARGS__)
+
+#define ___constraints(count) \
+ : __constraint_write_ ## count \
+ : __constraint_read_ ## count \
+ : "memory"
+#define __constraints(count) ___constraints(count)
+
+/*
+ * We have an output list that is not necessarily used, and GCC feels
+ * entitled to optimise the whole sequence away. "volatile" is what
+ * makes it stick.
+ */
+#define __arm_smccc_1_1(inst, ...) \
+ do { \
+ __declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
+ asm volatile(inst "\n" \
+ __constraints(__count_args(__VA_ARGS__))); \
+ if (___res) \
+ *___res = (typeof(*___res)){r0, r1, r2, r3}; \
+ } while (0)
+
+/*
+ * arm_smccc_1_1_smc() - make an SMCCC v1.1 compliant SMC call
+ *
+ * This is a variadic macro taking one to eight source arguments, and
+ * an optional return structure.
+ *
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This macro is used to make SMC calls following SMC Calling Convention v1.1.
+ * The content of the supplied param are copied to registers 0 to 7 prior
+ * to the SMC instruction. The return values are updated with the content
+ * from register 0 to 3 on return from the SMC instruction if not NULL.
+ */
+#define arm_smccc_1_1_smc(...) __arm_smccc_1_1(SMCCC_SMC_INST, __VA_ARGS__)
+
+/*
+ * arm_smccc_1_1_hvc() - make an SMCCC v1.1 compliant HVC call
+ *
+ * This is a variadic macro taking one to eight source arguments, and
+ * an optional return structure.
+ *
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This macro is used to make HVC calls following SMC Calling Convention v1.1.
+ * The content of the supplied param are copied to registers 0 to 7 prior
+ * to the HVC instruction. The return values are updated with the content
+ * from register 0 to 3 on return from the HVC instruction if not NULL.
+ */
+#define arm_smccc_1_1_hvc(...) __arm_smccc_1_1(SMCCC_HVC_INST, __VA_ARGS__)
+
#endif /*__ASSEMBLY__*/
#endif /*__LINUX_ARM_SMCCC_H*/
diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h
index 0691068..002f87c 100644
--- a/include/linux/backing-dev-defs.h
+++ b/include/linux/backing-dev-defs.h
@@ -195,6 +195,11 @@
set_wb_congested(bdi->wb.congested, sync);
}
+struct wb_lock_cookie {
+ bool locked;
+ unsigned long flags;
+};
+
#ifdef CONFIG_CGROUP_WRITEBACK
/**
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index c52a48c..8272fcf7 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -374,7 +374,7 @@
/**
* unlocked_inode_to_wb_begin - begin unlocked inode wb access transaction
* @inode: target inode
- * @lockedp: temp bool output param, to be passed to the end function
+ * @cookie: output param, to be passed to the end function
*
* The caller wants to access the wb associated with @inode but isn't
* holding inode->i_lock, mapping->tree_lock or wb->list_lock. This
@@ -382,12 +382,12 @@
* association doesn't change until the transaction is finished with
* unlocked_inode_to_wb_end().
*
- * The caller must call unlocked_inode_to_wb_end() with *@lockdep
- * afterwards and can't sleep during transaction. IRQ may or may not be
- * disabled on return.
+ * The caller must call unlocked_inode_to_wb_end() with *@cookie afterwards and
+ * can't sleep during the transaction. IRQs may or may not be disabled on
+ * return.
*/
static inline struct bdi_writeback *
-unlocked_inode_to_wb_begin(struct inode *inode, bool *lockedp)
+unlocked_inode_to_wb_begin(struct inode *inode, struct wb_lock_cookie *cookie)
{
rcu_read_lock();
@@ -395,10 +395,10 @@
* Paired with store_release in inode_switch_wb_work_fn() and
* ensures that we see the new wb if we see cleared I_WB_SWITCH.
*/
- *lockedp = smp_load_acquire(&inode->i_state) & I_WB_SWITCH;
+ cookie->locked = smp_load_acquire(&inode->i_state) & I_WB_SWITCH;
- if (unlikely(*lockedp))
- spin_lock_irq(&inode->i_mapping->tree_lock);
+ if (unlikely(cookie->locked))
+ spin_lock_irqsave(&inode->i_mapping->tree_lock, cookie->flags);
/*
* Protected by either !I_WB_SWITCH + rcu_read_lock() or tree_lock.
@@ -410,12 +410,13 @@
/**
* unlocked_inode_to_wb_end - end inode wb access transaction
* @inode: target inode
- * @locked: *@lockedp from unlocked_inode_to_wb_begin()
+ * @cookie: @cookie from unlocked_inode_to_wb_begin()
*/
-static inline void unlocked_inode_to_wb_end(struct inode *inode, bool locked)
+static inline void unlocked_inode_to_wb_end(struct inode *inode,
+ struct wb_lock_cookie *cookie)
{
- if (unlikely(locked))
- spin_unlock_irq(&inode->i_mapping->tree_lock);
+ if (unlikely(cookie->locked))
+ spin_unlock_irqrestore(&inode->i_mapping->tree_lock, cookie->flags);
rcu_read_unlock();
}
@@ -462,12 +463,13 @@
}
static inline struct bdi_writeback *
-unlocked_inode_to_wb_begin(struct inode *inode, bool *lockedp)
+unlocked_inode_to_wb_begin(struct inode *inode, struct wb_lock_cookie *cookie)
{
return inode_to_wb(inode);
}
-static inline void unlocked_inode_to_wb_end(struct inode *inode, bool locked)
+static inline void unlocked_inode_to_wb_end(struct inode *inode,
+ struct wb_lock_cookie *cookie)
{
}
diff --git a/include/linux/batterydata-interface.h b/include/linux/batterydata-interface.h
new file mode 100644
index 0000000..4318eb9
--- /dev/null
+++ b/include/linux/batterydata-interface.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014-2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <uapi/linux/batterydata-interface.h>
+
+int config_battery_data(struct bms_battery_data *profile);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 97cb48f..0885c9f 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -61,6 +61,9 @@
((bio)->bi_iter.bi_size != bio_iovec(bio).bv_len)
#define bio_sectors(bio) ((bio)->bi_iter.bi_size >> 9)
#define bio_end_sector(bio) ((bio)->bi_iter.bi_sector + bio_sectors((bio)))
+#define bio_dun(bio) ((bio)->bi_iter.bi_dun)
+#define bio_duns(bio) (bio_sectors(bio) >> 3) /* 4KB unit */
+#define bio_end_dun(bio) (bio_dun(bio) + bio_duns(bio))
/*
* Check whether this bio carries any data or not. A NULL bio is allowed.
@@ -169,6 +172,11 @@
{
iter->bi_sector += bytes >> 9;
+#ifdef CONFIG_PFK
+ if (iter->bi_dun)
+ iter->bi_dun += bytes >> 12;
+#endif
+
if (bio_no_advance_iter(bio))
iter->bi_size -= bytes;
else
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 2b8b6e0..5a3712c 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -25,6 +25,7 @@
struct bio {
struct bio *bi_next; /* request queue link */
struct block_device *bi_bdev;
+ unsigned short bi_write_hint;
int bi_error;
unsigned int bi_opf; /* bottom bits req flags,
* top bits REQ_OP. Use
@@ -65,6 +66,10 @@
struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif
};
+#ifdef CONFIG_PFK
+ /* Encryption key to use (NULL if none) */
+ const struct blk_encryption_key *bi_crypt_key;
+#endif
unsigned short bi_vcnt; /* how many bio_vec's */
@@ -81,6 +86,12 @@
struct bio_set *bi_pool;
/*
+ * When using dircet-io (O_DIRECT), we can't get the inode from a bio
+ * by walking bio->bi_io_vec->bv_page->mapping->host
+ * since the page is anon.
+ */
+ struct inode *bi_dio_inode;
+ /*
* We can inline a number of vecs at the end of the bio, to avoid
* double allocations for a small number of bio_vecs. This member
* MUST obviously be kept at the very end of the bio.
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index b0f981a..661dcec 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -509,6 +509,7 @@
#define QUEUE_FLAG_FLUSH_NQ 25 /* flush not queueuable */
#define QUEUE_FLAG_DAX 26 /* device supports DAX */
#define QUEUE_FLAG_FAST 27 /* fast block device (e.g. ram based) */
+#define QUEUE_FLAG_INLINECRYPT 28 /* inline encryption support */
#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_STACKABLE) | \
@@ -600,6 +601,8 @@
(test_bit(QUEUE_FLAG_SECERASE, &(q)->queue_flags))
#define blk_queue_dax(q) test_bit(QUEUE_FLAG_DAX, &(q)->queue_flags)
#define blk_queue_fast(q) test_bit(QUEUE_FLAG_FAST, &(q)->queue_flags)
+#define blk_queue_inlinecrypt(q) \
+ test_bit(QUEUE_FLAG_INLINECRYPT, &(q)->queue_flags)
#define blk_noretry_request(rq) \
((rq)->cmd_flags & (REQ_FAILFAST_DEV|REQ_FAILFAST_TRANSPORT| \
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 193c40e..cd6aaf0 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -306,6 +306,9 @@
/* verify correctness of eBPF program */
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
+
+struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type type);
+
#else
static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl)
{
@@ -333,6 +336,16 @@
{
return ERR_PTR(-EOPNOTSUPP);
}
+static inline int bpf_obj_get_user(const char __user *pathname)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline struct bpf_prog *bpf_prog_get_type_path(const char *name,
+ enum bpf_prog_type type)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
#endif /* CONFIG_BPF_SYSCALL */
/* verifier prototypes for helper functions called from eBPF programs */
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 89b65b8..dbf1f2c 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -41,6 +41,7 @@
unsigned int bi_bvec_done; /* number of bytes completed in
current bvec */
+ u64 bi_dun; /* DUN setting for bio */
};
/*
diff --git a/include/linux/cfi.h b/include/linux/cfi.h
new file mode 100644
index 0000000..e27033d
--- /dev/null
+++ b/include/linux/cfi.h
@@ -0,0 +1,38 @@
+#ifndef _LINUX_CFI_H
+#define _LINUX_CFI_H
+
+#include <linux/stringify.h>
+
+#ifdef CONFIG_CFI_CLANG
+#ifdef CONFIG_MODULES
+
+typedef void (*cfi_check_fn)(uint64_t, void *, void *);
+
+/* Compiler-generated function in each module, and the kernel */
+#define CFI_CHECK_FN __cfi_check
+#define CFI_CHECK_FN_NAME __stringify(CFI_CHECK_FN)
+
+extern void CFI_CHECK_FN(uint64_t, void *, void *);
+
+#ifdef CONFIG_CFI_CLANG_SHADOW
+extern void cfi_module_add(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr);
+
+extern void cfi_module_remove(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr);
+#else
+static inline void cfi_module_add(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+}
+
+static inline void cfi_module_remove(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+}
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+#endif /* CONFIG_MODULES */
+#endif /* CONFIG_CFI_CLANG */
+
+#endif /* _LINUX_CFI_H */
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index c5a8afd..34d3f27 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -614,13 +614,13 @@
* updaters and return part of the previous pointer as the prioidx or
* classid. Such races are short-lived and the result isn't critical.
*/
-static inline u16 sock_cgroup_prioidx(struct sock_cgroup_data *skcd)
+static inline u16 sock_cgroup_prioidx(const struct sock_cgroup_data *skcd)
{
/* fallback to 1 which is always the ID of the root cgroup */
return (skcd->is_data & 1) ? skcd->prioidx : 1;
}
-static inline u32 sock_cgroup_classid(struct sock_cgroup_data *skcd)
+static inline u32 sock_cgroup_classid(const struct sock_cgroup_data *skcd)
{
/* fallback to 0 which is the unconfigured default classid */
return (skcd->is_data & 1) ? skcd->classid : 0;
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 3a4f264..a8b4284 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -117,7 +117,7 @@
#define CLOCK_SOURCE_RESELECT 0x100
/* simplify initialization of mask field */
-#define CLOCKSOURCE_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1)
+#define CLOCKSOURCE_MASK(bits) GENMASK_ULL((bits) - 1, 0)
static inline u32 clocksource_freq2mult(u32 freq, u32 shift_constant, u64 from)
{
diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index de17999..e4f142c 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -15,3 +15,25 @@
* with any version that can compile the kernel
*/
#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
+
+#ifdef CONFIG_LTO_CLANG
+#ifdef CONFIG_FTRACE_MCOUNT_RECORD
+#define __norecordmcount \
+ __attribute__((__section__(".text..ftrace")))
+#endif
+
+#define __nocfi __attribute__((no_sanitize("cfi")))
+#endif
+
+/* all clang versions usable with the kernel support KASAN ABI version 5 */
+#define KASAN_ABI_VERSION 5
+
+/* emulate gcc's __SANITIZE_ADDRESS__ flag */
+#if __has_feature(address_sanitizer)
+#define __SANITIZE_ADDRESS__
+#endif
+
+/* Clang doesn't have a way to turn it off per-function, yet. */
+#ifdef __noretpoline
+#undef __noretpoline
+#endif
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 3a8c897..27101bb 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -92,6 +92,10 @@
#define __weak __attribute__((weak))
#define __alias(symbol) __attribute__((alias(#symbol)))
+#ifdef RETPOLINE
+#define __noretpoline __attribute__((indirect_branch("keep")))
+#endif
+
/*
* it doesn't make sense on ARM (currently the only user of __naked)
* to trace naked functions because then mcount is called without
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 5ce911d..81bcdca 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -451,6 +451,14 @@
#define __visible
#endif
+#ifndef __norecordmcount
+#define __norecordmcount
+#endif
+
+#ifndef __nocfi
+#define __nocfi
+#endif
+
/*
* Assume alignment of return value.
*/
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 1f7e4ec..912d945 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -50,6 +50,8 @@
struct device_attribute *attr, char *buf);
extern ssize_t cpu_show_spectre_v2(struct device *dev,
struct device_attribute *attr, char *buf);
+extern ssize_t cpu_show_spec_store_bypass(struct device *dev,
+ struct device_attribute *attr, char *buf);
extern __printf(4, 5)
struct device *cpu_device_create(struct device *parent, void *drvdata,
diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h
new file mode 100644
index 0000000..3fb3875
--- /dev/null
+++ b/include/linux/cpufreq_times.h
@@ -0,0 +1,40 @@
+/* drivers/cpufreq/cpufreq_times.c
+ *
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_CPUFREQ_TIMES_H
+#define _LINUX_CPUFREQ_TIMES_H
+
+#include <linux/cpufreq.h>
+#include <linux/cputime.h>
+#include <linux/pid.h>
+
+#ifdef CONFIG_CPU_FREQ_TIMES
+void cpufreq_task_times_init(struct task_struct *p);
+void cpufreq_task_times_exit(struct task_struct *p);
+int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *p);
+void cpufreq_acct_update_power(struct task_struct *p, cputime_t cputime);
+void cpufreq_times_create_policy(struct cpufreq_policy *policy);
+void cpufreq_times_record_transition(struct cpufreq_freqs *freq);
+void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end);
+int single_uid_time_in_state_open(struct inode *inode, struct file *file);
+#else
+static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {}
+static inline void cpufreq_times_record_transition(
+ struct cpufreq_freqs *freq) {}
+static inline void cpufreq_task_times_remove_uids(uid_t uid_start,
+ uid_t uid_end) {}
+#endif /* CONFIG_CPU_FREQ_TIMES */
+#endif /* _LINUX_CPUFREQ_TIMES_H */
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index b49f866..bd96bb2 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -695,6 +695,11 @@
void free_cpumask_var(cpumask_var_t mask);
void free_bootmem_cpumask_var(cpumask_var_t mask);
+static inline bool cpumask_available(cpumask_var_t mask)
+{
+ return mask != NULL;
+}
+
#else
typedef struct cpumask cpumask_var_t[1];
@@ -735,6 +740,11 @@
static inline void free_bootmem_cpumask_var(cpumask_var_t mask)
{
}
+
+static inline bool cpumask_available(cpumask_var_t mask)
+{
+ return true;
+}
#endif /* CONFIG_CPUMASK_OFFSTACK */
/* It's common to want to use cpu_all_mask in struct member initializers,
diff --git a/include/linux/dax.h b/include/linux/dax.h
index add6c4b..ed9cf2f 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -61,11 +61,6 @@
int dax_pfn_mkwrite(struct vm_area_struct *, struct vm_fault *);
#define dax_mkwrite(vma, vmf, gb) dax_fault(vma, vmf, gb)
-static inline bool vma_is_dax(struct vm_area_struct *vma)
-{
- return vma->vm_file && IS_DAX(vma->vm_file->f_mapping->host);
-}
-
static inline bool dax_mapping(struct address_space *mapping)
{
return mapping->host && IS_DAX(mapping->host);
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index b20a094..2e75ae2 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -23,7 +23,6 @@
struct device;
struct file_operations;
-struct srcu_struct;
struct debugfs_blob_wrapper {
void *data;
@@ -43,25 +42,6 @@
extern struct dentry *arch_debugfs_dir;
-extern struct srcu_struct debugfs_srcu;
-
-/**
- * debugfs_real_fops - getter for the real file operation
- * @filp: a pointer to a struct file
- *
- * Must only be called under the protection established by
- * debugfs_use_file_start().
- */
-static inline const struct file_operations *debugfs_real_fops(struct file *filp)
- __must_hold(&debugfs_srcu)
-{
- /*
- * Neither the pointer to the struct file_operations, nor its
- * contents ever change -- srcu_dereference() is not needed here.
- */
- return filp->f_path.dentry->d_fsdata;
-}
-
#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
static int __fops ## _open(struct inode *inode, struct file *file) \
{ \
@@ -105,10 +85,10 @@
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);
-int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
- __acquires(&debugfs_srcu);
+const struct file_operations *debugfs_real_fops(const struct file *filp);
-void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);
+int debugfs_file_get(struct dentry *dentry);
+void debugfs_file_put(struct dentry *dentry);
ssize_t debugfs_attr_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos);
@@ -223,15 +203,12 @@
static inline void debugfs_remove_recursive(struct dentry *dentry)
{ }
-static inline int debugfs_use_file_start(const struct dentry *dentry,
- int *srcu_idx)
- __acquires(&debugfs_srcu)
+static inline int debugfs_file_get(struct dentry *dentry)
{
return 0;
}
-static inline void debugfs_use_file_finish(int srcu_idx)
- __releases(&debugfs_srcu)
+static inline void debugfs_file_put(struct dentry *dentry)
{ }
static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 95fe239..62f4fa8 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -146,10 +146,10 @@
* a new RANGE of SSIDs to the msg_mask_tbl.
*/
#define MSG_MASK_TBL_CNT 26
-#define APPS_EVENT_LAST_ID 0x0B3F
+#define APPS_EVENT_LAST_ID 0xC7A
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 121
+#define MSG_SSID_0_LAST 125
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -161,11 +161,11 @@
#define MSG_SSID_5 4000
#define MSG_SSID_5_LAST 4010
#define MSG_SSID_6 4500
-#define MSG_SSID_6_LAST 4583
+#define MSG_SSID_6_LAST 4584
#define MSG_SSID_7 4600
-#define MSG_SSID_7_LAST 4615
+#define MSG_SSID_7_LAST 4616
#define MSG_SSID_8 5000
-#define MSG_SSID_8_LAST 5033
+#define MSG_SSID_8_LAST 5034
#define MSG_SSID_9 5500
#define MSG_SSID_9_LAST 5516
#define MSG_SSID_10 6000
@@ -265,7 +265,7 @@
MSG_MASK_9 | MSG_MASK_10,
MSG_LVL_MED,
MSG_LVL_LOW,
- MSG_LVL_LOW,
+ MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_LOW,
MSG_LVL_LOW,
@@ -319,7 +319,7 @@
MSG_LVL_FATAL,
MSG_LVL_MED,
MSG_LVL_HIGH,
- MSG_LVL_LOW,
+ MSG_LVL_MED,
MSG_LVL_HIGH,
MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR |
MSG_LVL_FATAL,
@@ -497,6 +497,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
MSG_LVL_LOW
};
@@ -516,7 +517,9 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
- MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR | MSG_LVL_FATAL
+ MSG_LVL_LOW | MSG_LVL_MED | MSG_LVL_HIGH | MSG_LVL_ERROR |
+ MSG_LVL_FATAL,
+ MSG_LVL_LOW
};
static const uint32_t msg_bld_masks_8[] = {
@@ -536,9 +539,6 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
@@ -553,6 +553,10 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_HIGH,
MSG_LVL_HIGH
};
@@ -655,14 +659,14 @@
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
- MSG_LVL_LOW,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
+ MSG_LVL_MED,
MSG_LVL_MED
};
@@ -812,7 +816,9 @@
};
static const uint32_t msg_bld_masks_20[] = {
- MSG_LVL_LOW,
+ MSG_LVL_LOW | MSG_MASK_5 | MSG_MASK_6 | MSG_MASK_7 |
+ MSG_MASK_8 | MSG_MASK_9 | MSG_MASK_10 | MSG_MASK_11 |
+ MSG_MASK_12,
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
@@ -890,7 +896,7 @@
/* LOG CODES */
static const uint32_t log_code_last_tbl[] = {
0x0, /* EQUIP ID 0 */
- 0x1A11, /* EQUIP ID 1 */
+ 0x1C6A, /* EQUIP ID 1 */
0x0, /* EQUIP ID 2 */
0x0, /* EQUIP ID 3 */
0x4910, /* EQUIP ID 4 */
@@ -900,7 +906,7 @@
0x0, /* EQUIP ID 8 */
0x0, /* EQUIP ID 9 */
0xA38A, /* EQUIP ID 10 */
- 0xB201, /* EQUIP ID 11 */
+ 0xB9FF, /* EQUIP ID 11 */
0x0, /* EQUIP ID 12 */
0xD1FF, /* EQUIP ID 13 */
0x0, /* EQUIP ID 14 */
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5e204a5..2877ccb 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -380,8 +380,8 @@
u32 attributes;
u32 get_bar_attributes;
u32 set_bar_attributes;
- uint64_t romsize;
- void *romimage;
+ u64 romsize;
+ u32 romimage;
} efi_pci_io_protocol_32;
typedef struct {
@@ -400,8 +400,8 @@
u64 attributes;
u64 get_bar_attributes;
u64 set_bar_attributes;
- uint64_t romsize;
- void *romimage;
+ u64 romsize;
+ u64 romimage;
} efi_pci_io_protocol_64;
typedef struct {
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 58aecb6..aa5db8b 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -21,6 +21,7 @@
#define F2FS_BLKSIZE 4096 /* support only 4KB block */
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
+#define F2FS_EXTENSION_LEN 8 /* max size of extension */
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS)
#define NULL_ADDR ((block_t)0) /* used as block_t addresses */
@@ -38,15 +39,14 @@
#define F2FS_MAX_QUOTAS 3
-#define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */
-#define F2FS_IO_SIZE_KB(sbi) (1 << ((sbi)->write_io_size_bits + 2)) /* KB */
-#define F2FS_IO_SIZE_BYTES(sbi) (1 << ((sbi)->write_io_size_bits + 12)) /* B */
-#define F2FS_IO_SIZE_BITS(sbi) ((sbi)->write_io_size_bits) /* power of 2 */
+#define F2FS_IO_SIZE(sbi) (1 << F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */
+#define F2FS_IO_SIZE_KB(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 2)) /* KB */
+#define F2FS_IO_SIZE_BYTES(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 12)) /* B */
+#define F2FS_IO_SIZE_BITS(sbi) (F2FS_OPTION(sbi).write_io_size_bits) /* power of 2 */
#define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1)
/* This flag is used by node and meta inodes, and by recovery */
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
-#define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM)
/*
* For further optimization on multi-head logs, on-disk layout supports maximum
@@ -102,7 +102,7 @@
__u8 uuid[16]; /* 128-bit uuid for volume */
__le16 volume_name[MAX_VOLUME_NAME]; /* volume name */
__le32 extension_count; /* # of extensions below */
- __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
+ __u8 extension_list[F2FS_MAX_EXTENSION][F2FS_EXTENSION_LEN];/* extension array */
__le32 cp_payload;
__u8 version[VERSION_LEN]; /* the kernel version */
__u8 init_version[VERSION_LEN]; /* the initial kernel version */
@@ -111,12 +111,14 @@
__u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
struct f2fs_device devs[MAX_DEVICES]; /* device list */
__le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */
- __u8 reserved[315]; /* valid reserved region */
+ __u8 hot_ext_count; /* # of hot file extension */
+ __u8 reserved[314]; /* valid reserved region */
} __packed;
/*
* For checkpoint
*/
+#define CP_LARGE_NAT_BITMAP_FLAG 0x00000400
#define CP_NOCRC_RECOVERY_FLAG 0x00000200
#define CP_TRIMMED_FLAG 0x00000100
#define CP_NAT_BITS_FLAG 0x00000080
@@ -303,6 +305,10 @@
*/
#define NAT_ENTRY_PER_BLOCK (PAGE_SIZE / sizeof(struct f2fs_nat_entry))
#define NAT_ENTRY_BITMAP_SIZE ((NAT_ENTRY_PER_BLOCK + 7) / 8)
+#define NAT_ENTRY_BITMAP_SIZE_ALIGNED \
+ ((NAT_ENTRY_BITMAP_SIZE + BITS_PER_LONG - 1) / \
+ BITS_PER_LONG * BITS_PER_LONG)
+
struct f2fs_nat_entry {
__u8 version; /* latest version of cached nat entry */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4abfa9c..1c4da43 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -18,8 +18,10 @@
#include <linux/bug.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
+#include <linux/mm_types.h>
#include <linux/capability.h>
#include <linux/semaphore.h>
+#include <linux/fcntl.h>
#include <linux/fiemap.h>
#include <linux/rculist_bl.h>
#include <linux/atomic.h>
@@ -143,6 +145,9 @@
/* File was opened by fanotify and shouldn't generate fanotify events */
#define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
+/* File is capable of returning -EAGAIN if I/O will block */
+#define FMODE_NOWAIT ((__force fmode_t)0x8000000)
+
/*
* Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
* that indicates that they should check the contents of the iovec are
@@ -315,6 +320,18 @@
struct address_space;
struct writeback_control;
+/*
+ * Write life time hint values.
+ */
+enum rw_hint {
+ WRITE_LIFE_NOT_SET = 0,
+ WRITE_LIFE_NONE = RWH_WRITE_LIFE_NONE,
+ WRITE_LIFE_SHORT = RWH_WRITE_LIFE_SHORT,
+ WRITE_LIFE_MEDIUM = RWH_WRITE_LIFE_MEDIUM,
+ WRITE_LIFE_LONG = RWH_WRITE_LIFE_LONG,
+ WRITE_LIFE_EXTREME = RWH_WRITE_LIFE_EXTREME,
+};
+
#define IOCB_EVENTFD (1 << 0)
#define IOCB_APPEND (1 << 1)
#define IOCB_DIRECT (1 << 2)
@@ -322,6 +339,7 @@
#define IOCB_DSYNC (1 << 4)
#define IOCB_SYNC (1 << 5)
#define IOCB_WRITE (1 << 6)
+#define IOCB_NOWAIT (1 << 7)
struct kiocb {
struct file *ki_filp;
@@ -329,6 +347,7 @@
void (*ki_complete)(struct kiocb *iocb, long ret, long ret2);
void *private;
int ki_flags;
+ enum rw_hint ki_hint;
};
static inline bool is_sync_kiocb(struct kiocb *kiocb)
@@ -643,6 +662,7 @@
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
unsigned int i_blkbits;
+ enum rw_hint i_write_hint;
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
@@ -1070,8 +1090,6 @@
#define OFFT_OFFSET_MAX INT_LIMIT(off_t)
#endif
-#include <linux/fcntl.h>
-
extern void send_sigio(struct fown_struct *fown, int fd, int band);
/*
@@ -1319,6 +1337,7 @@
#define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */
#define SB_I_NOEXEC 0x00000002 /* Ignore executables on this fs */
#define SB_I_NODEV 0x00000004 /* Ignore devices on this fs */
+#define SB_I_MULTIROOT 0x00000008 /* Multiple roots to the dentry tree */
/* sb->s_iflags to limit user namespace mounts */
#define SB_I_USERNS_VISIBLE 0x00000010 /* fstype already mounted */
@@ -2929,6 +2948,8 @@
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
}
+struct inode *dio_bio_get_inode(struct bio *bio);
+
extern void inode_set_flags(struct inode *inode, unsigned int flags,
unsigned int mask);
@@ -3059,6 +3080,25 @@
return (filp->f_flags & O_DIRECT) || IS_DAX(filp->f_mapping->host);
}
+static inline bool vma_is_dax(struct vm_area_struct *vma)
+{
+ return vma->vm_file && IS_DAX(vma->vm_file->f_mapping->host);
+}
+
+static inline bool vma_is_fsdax(struct vm_area_struct *vma)
+{
+ struct inode *inode;
+
+ if (!vma->vm_file)
+ return false;
+ if (!vma_is_dax(vma))
+ return false;
+ inode = file_inode(vma->vm_file);
+ if (S_ISCHR(inode->i_mode))
+ return false; /* device-dax */
+ return true;
+}
+
static inline int iocb_flags(struct file *file)
{
int res = 0;
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 8641e56..8f4e90c 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -13,41 +13,20 @@
#ifndef _LINUX_FSCRYPT_H
#define _LINUX_FSCRYPT_H
-#include <linux/key.h>
#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/bio.h>
-#include <linux/dcache.h>
-#include <crypto/skcipher.h>
-#include <uapi/linux/fs.h>
#define FS_CRYPTO_BLOCK_SIZE 16
-struct fscrypt_info;
+struct fscrypt_ctx;
-struct fscrypt_ctx {
- union {
- struct {
- struct page *bounce_page; /* Ciphertext page */
- struct page *control_page; /* Original page */
- } w;
- struct {
- struct bio *bio;
- struct work_struct work;
- } r;
- struct list_head free_list; /* Free list */
- };
- u8 flags; /* Flags */
-};
-
-/**
- * For encrypted symlinks, the ciphertext length is stored at the beginning
- * of the string in little-endian format.
+/* iv sector for security/pfe/pfk_fscrypt.c and f2fs. sizeof is required
+ * to accommodate 32 bit targets.
*/
-struct fscrypt_symlink_data {
- __le16 len;
- char encrypted_path[1];
-} __packed;
+#define PG_DUN(i, p) \
+ ((((i)->i_ino & 0xffffffff) << (sizeof((i)->i_ino)/2)) | \
+ ((p)->index & 0xffffffff))
+
+struct fscrypt_info;
struct fscrypt_str {
unsigned char *name;
@@ -67,86 +46,11 @@
#define fname_name(p) ((p)->disk_name.name)
#define fname_len(p) ((p)->disk_name.len)
-/*
- * fscrypt superblock flags
- */
-#define FS_CFLG_OWN_PAGES (1U << 1)
-
-/*
- * crypto opertions for filesystems
- */
-struct fscrypt_operations {
- unsigned int flags;
- const char *key_prefix;
- int (*get_context)(struct inode *, void *, size_t);
- int (*set_context)(struct inode *, const void *, size_t, void *);
- bool (*dummy_context)(struct inode *);
- bool (*empty_dir)(struct inode *);
- unsigned (*max_namelen)(struct inode *);
-};
-
-static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
-{
- if (inode->i_sb->s_cop->dummy_context &&
- inode->i_sb->s_cop->dummy_context(inode))
- return true;
- return false;
-}
-
-static inline bool fscrypt_valid_enc_modes(u32 contents_mode,
- u32 filenames_mode)
-{
- if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC &&
- filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS)
- return true;
-
- if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS &&
- filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS)
- return true;
-
- return false;
-}
-
-static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
-{
- if (str->len == 1 && str->name[0] == '.')
- return true;
-
- if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
- return true;
-
- return false;
-}
-
#if __FS_HAS_ENCRYPTION
-
-static inline struct page *fscrypt_control_page(struct page *page)
-{
- return ((struct fscrypt_ctx *)page_private(page))->w.control_page;
-}
-
-static inline bool fscrypt_has_encryption_key(const struct inode *inode)
-{
- return (inode->i_crypt_info != NULL);
-}
-
#include <linux/fscrypt_supp.h>
-
-#else /* !__FS_HAS_ENCRYPTION */
-
-static inline struct page *fscrypt_control_page(struct page *page)
-{
- WARN_ON_ONCE(1);
- return ERR_PTR(-EINVAL);
-}
-
-static inline bool fscrypt_has_encryption_key(const struct inode *inode)
-{
- return 0;
-}
-
+#else
#include <linux/fscrypt_notsupp.h>
-#endif /* __FS_HAS_ENCRYPTION */
+#endif
/**
* fscrypt_require_key - require an inode's encryption key
@@ -287,4 +191,68 @@
return 0;
}
+/**
+ * fscrypt_prepare_symlink - prepare to create a possibly-encrypted symlink
+ * @dir: directory in which the symlink is being created
+ * @target: plaintext symlink target
+ * @len: length of @target excluding null terminator
+ * @max_len: space the filesystem has available to store the symlink target
+ * @disk_link: (out) the on-disk symlink target being prepared
+ *
+ * This function computes the size the symlink target will require on-disk,
+ * stores it in @disk_link->len, and validates it against @max_len. An
+ * encrypted symlink may be longer than the original.
+ *
+ * Additionally, @disk_link->name is set to @target if the symlink will be
+ * unencrypted, but left NULL if the symlink will be encrypted. For encrypted
+ * symlinks, the filesystem must call fscrypt_encrypt_symlink() to create the
+ * on-disk target later. (The reason for the two-step process is that some
+ * filesystems need to know the size of the symlink target before creating the
+ * inode, e.g. to determine whether it will be a "fast" or "slow" symlink.)
+ *
+ * Return: 0 on success, -ENAMETOOLONG if the symlink target is too long,
+ * -ENOKEY if the encryption key is missing, or another -errno code if a problem
+ * occurred while setting up the encryption key.
+ */
+static inline int fscrypt_prepare_symlink(struct inode *dir,
+ const char *target,
+ unsigned int len,
+ unsigned int max_len,
+ struct fscrypt_str *disk_link)
+{
+ if (IS_ENCRYPTED(dir) || fscrypt_dummy_context_enabled(dir))
+ return __fscrypt_prepare_symlink(dir, len, max_len, disk_link);
+
+ disk_link->name = (unsigned char *)target;
+ disk_link->len = len + 1;
+ if (disk_link->len > max_len)
+ return -ENAMETOOLONG;
+ return 0;
+}
+
+/**
+ * fscrypt_encrypt_symlink - encrypt the symlink target if needed
+ * @inode: symlink inode
+ * @target: plaintext symlink target
+ * @len: length of @target excluding null terminator
+ * @disk_link: (in/out) the on-disk symlink target being prepared
+ *
+ * If the symlink target needs to be encrypted, then this function encrypts it
+ * into @disk_link->name. fscrypt_prepare_symlink() must have been called
+ * previously to compute @disk_link->len. If the filesystem did not allocate a
+ * buffer for @disk_link->name after calling fscrypt_prepare_link(), then one
+ * will be kmalloc()'ed and the filesystem will be responsible for freeing it.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static inline int fscrypt_encrypt_symlink(struct inode *inode,
+ const char *target,
+ unsigned int len,
+ struct fscrypt_str *disk_link)
+{
+ if (IS_ENCRYPTED(inode))
+ return __fscrypt_encrypt_symlink(inode, target, len, disk_link);
+ return 0;
+}
+
#endif /* _LINUX_FSCRYPT_H */
diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h
index c4c6bf2..ce4df7a 100644
--- a/include/linux/fscrypt_notsupp.h
+++ b/include/linux/fscrypt_notsupp.h
@@ -13,7 +13,21 @@
#ifndef _LINUX_FSCRYPT_NOTSUPP_H
#define _LINUX_FSCRYPT_NOTSUPP_H
+static inline bool fscrypt_has_encryption_key(const struct inode *inode)
+{
+ return false;
+}
+
+static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
+{
+ return false;
+}
+
/* crypto.c */
+static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work)
+{
+}
+
static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode,
gfp_t gfp_flags)
{
@@ -42,6 +56,11 @@
return -EOPNOTSUPP;
}
+static inline struct page *fscrypt_control_page(struct page *page)
+{
+ WARN_ON_ONCE(1);
+ return ERR_PTR(-EINVAL);
+}
static inline void fscrypt_restore_control_page(struct page *page)
{
@@ -89,8 +108,7 @@
return -EOPNOTSUPP;
}
-static inline void fscrypt_put_encryption_info(struct inode *inode,
- struct fscrypt_info *ci)
+static inline void fscrypt_put_encryption_info(struct inode *inode)
{
return;
}
@@ -115,16 +133,8 @@
return;
}
-static inline u32 fscrypt_fname_encrypted_size(const struct inode *inode,
- u32 ilen)
-{
- /* never happens */
- WARN_ON(1);
- return 0;
-}
-
static inline int fscrypt_fname_alloc_buffer(const struct inode *inode,
- u32 ilen,
+ u32 max_encrypted_len,
struct fscrypt_str *crypto_str)
{
return -EOPNOTSUPP;
@@ -143,13 +153,6 @@
return -EOPNOTSUPP;
}
-static inline int fscrypt_fname_usr_to_disk(struct inode *inode,
- const struct qstr *iname,
- struct fscrypt_str *oname)
-{
- return -EOPNOTSUPP;
-}
-
static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
const u8 *de_name, u32 de_name_len)
{
@@ -160,10 +163,13 @@
}
/* bio.c */
-static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx,
- struct bio *bio)
+static inline void fscrypt_decrypt_bio(struct bio *bio)
{
- return;
+}
+
+static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx,
+ struct bio *bio)
+{
}
static inline void fscrypt_pullback_bio_page(struct page **page, bool restore)
@@ -177,6 +183,21 @@
return -EOPNOTSUPP;
}
+/* fscrypt_ice.c */
+static inline int fscrypt_using_hardware_encryption(const struct inode *inode)
+{
+ return 0;
+}
+
+static inline void fscrypt_set_ice_dun(const struct inode *inode,
+ struct bio *bio, u64 dun){}
+
+static inline bool fscrypt_mergeable_bio(struct bio *bio,
+ sector_t iv_block, bool bio_encrypted)
+{
+ return true;
+}
+
/* hooks.c */
static inline int fscrypt_file_open(struct inode *inode, struct file *filp)
@@ -207,4 +228,28 @@
return -EOPNOTSUPP;
}
+static inline int __fscrypt_prepare_symlink(struct inode *dir,
+ unsigned int len,
+ unsigned int max_len,
+ struct fscrypt_str *disk_link)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int __fscrypt_encrypt_symlink(struct inode *inode,
+ const char *target,
+ unsigned int len,
+ struct fscrypt_str *disk_link)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline const char *fscrypt_get_symlink(struct inode *inode,
+ const void *caddr,
+ unsigned int max_size,
+ struct delayed_call *done)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
#endif /* _LINUX_FSCRYPT_NOTSUPP_H */
diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h
index 2db5e970..32e2b6c 100644
--- a/include/linux/fscrypt_supp.h
+++ b/include/linux/fscrypt_supp.h
@@ -10,8 +10,56 @@
#ifndef _LINUX_FSCRYPT_SUPP_H
#define _LINUX_FSCRYPT_SUPP_H
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+/*
+ * fscrypt superblock flags
+ */
+#define FS_CFLG_OWN_PAGES (1U << 1)
+
+/*
+ * crypto operations for filesystems
+ */
+struct fscrypt_operations {
+ unsigned int flags;
+ const char *key_prefix;
+ int (*get_context)(struct inode *, void *, size_t);
+ int (*set_context)(struct inode *, const void *, size_t, void *);
+ bool (*dummy_context)(struct inode *);
+ bool (*empty_dir)(struct inode *);
+ unsigned (*max_namelen)(struct inode *);
+ bool (*is_encrypted)(struct inode *);
+};
+
+struct fscrypt_ctx {
+ union {
+ struct {
+ struct page *bounce_page; /* Ciphertext page */
+ struct page *control_page; /* Original page */
+ } w;
+ struct {
+ struct bio *bio;
+ struct work_struct work;
+ } r;
+ struct list_head free_list; /* Free list */
+ };
+ u8 flags; /* Flags */
+};
+
+static inline bool fscrypt_has_encryption_key(const struct inode *inode)
+{
+ return (inode->i_crypt_info != NULL);
+}
+
+static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
+{
+ return inode->i_sb->s_cop->dummy_context &&
+ inode->i_sb->s_cop->dummy_context(inode);
+}
+
/* crypto.c */
-extern struct kmem_cache *fscrypt_info_cachep;
+extern void fscrypt_enqueue_decrypt_work(struct work_struct *);
extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t);
extern void fscrypt_release_ctx(struct fscrypt_ctx *);
extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *,
@@ -19,6 +67,12 @@
u64, gfp_t);
extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int,
unsigned int, u64);
+
+static inline struct page *fscrypt_control_page(struct page *page)
+{
+ return ((struct fscrypt_ctx *)page_private(page))->w.control_page;
+}
+
extern void fscrypt_restore_control_page(struct page *);
extern const struct dentry_operations fscrypt_d_ops;
@@ -43,7 +97,7 @@
void *, bool);
/* keyinfo.c */
extern int fscrypt_get_encryption_info(struct inode *);
-extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *);
+extern void fscrypt_put_encryption_info(struct inode *);
/* fname.c */
extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
@@ -54,14 +108,11 @@
kfree(fname->crypto_buf.name);
}
-extern u32 fscrypt_fname_encrypted_size(const struct inode *, u32);
extern int fscrypt_fname_alloc_buffer(const struct inode *, u32,
struct fscrypt_str *);
extern void fscrypt_fname_free_buffer(struct fscrypt_str *);
extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32,
const struct fscrypt_str *, struct fscrypt_str *);
-extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
- struct fscrypt_str *);
#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32
@@ -138,11 +189,20 @@
}
/* bio.c */
-extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
+extern void fscrypt_decrypt_bio(struct bio *);
+extern void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx,
+ struct bio *bio);
extern void fscrypt_pullback_bio_page(struct page **, bool);
extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
unsigned int);
+/* fscrypt_ice.c */
+extern int fscrypt_using_hardware_encryption(const struct inode *inode);
+extern void fscrypt_set_ice_dun(const struct inode *inode,
+ struct bio *bio, u64 dun);
+extern bool fscrypt_mergeable_bio(struct bio *bio, u64 dun, bool bio_encrypted);
+
+
/* hooks.c */
extern int fscrypt_file_open(struct inode *inode, struct file *filp);
extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir);
@@ -152,5 +212,14 @@
struct dentry *new_dentry,
unsigned int flags);
extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry);
+extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
+ unsigned int max_len,
+ struct fscrypt_str *disk_link);
+extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
+ unsigned int len,
+ struct fscrypt_str *disk_link);
+extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
+ unsigned int max_size,
+ struct delayed_call *done);
#endif /* _LINUX_FSCRYPT_SUPP_H */
diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h
index c332f0a..3fdfede 100644
--- a/include/linux/fsl_ifc.h
+++ b/include/linux/fsl_ifc.h
@@ -734,11 +734,7 @@
u32 res19[0x10];
__be32 nand_fsr;
u32 res20;
- /* The V1 nand_eccstat is actually 4 words that overlaps the
- * V2 nand_eccstat.
- */
- __be32 v1_nand_eccstat[2];
- __be32 v2_nand_eccstat[6];
+ __be32 nand_eccstat[8];
u32 res21[0x1c];
__be32 nanndcr;
u32 res22[0x2];
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index f4c0d36..ab7938a 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -244,8 +244,16 @@
return *this_cpu_ptr(ops->disabled);
}
+#ifdef CONFIG_CFI_CLANG
+/* Use a C stub with the correct type for CFI */
+static inline void ftrace_stub(unsigned long a0, unsigned long a1,
+ struct ftrace_ops *op, struct pt_regs *regs)
+{
+}
+#else
extern void ftrace_stub(unsigned long a0, unsigned long a1,
struct ftrace_ops *op, struct pt_regs *regs);
+#endif
#else /* !CONFIG_FUNCTION_TRACER */
/*
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 16ef407..51649c9 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -523,7 +523,7 @@
void page_alloc_init(void);
void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp);
void drain_all_pages(struct zone *zone);
-void drain_local_pages(struct zone *zone);
+void drain_local_pages(void *zone);
void page_alloc_init_late(void);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index b2ec827..fab65b6 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -801,7 +801,7 @@
extern void hidinput_disconnect(struct hid_device *);
int hid_set_field(struct hid_field *, unsigned, __s32);
-int hid_input_report(struct hid_device *, int type, u8 *, int, int);
+int hid_input_report(struct hid_device *, int type, u8 *, u32, int);
int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
struct hid_field *hidinput_get_led_field(struct hid_device *hid);
unsigned int hidinput_count_leds(struct hid_device *hid);
@@ -1106,13 +1106,13 @@
*
* @report: the report we want to know the length
*/
-static inline int hid_report_len(struct hid_report *report)
+static inline u32 hid_report_len(struct hid_report *report)
{
/* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
return ((report->size - 1) >> 3) + 1 + (report->id > 0);
}
-int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
int interrupt);
/* HID quirks API */
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 8feecd5..7e39719 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -600,7 +600,7 @@
* Returns true if the skb is tagged with multiple vlan headers, regardless
* of whether it is hardware accelerated or not.
*/
-static inline bool skb_vlan_tagged_multi(const struct sk_buff *skb)
+static inline bool skb_vlan_tagged_multi(struct sk_buff *skb)
{
__be16 protocol = skb->protocol;
@@ -610,6 +610,9 @@
if (likely(!eth_type_vlan(protocol)))
return false;
+ if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
+ return false;
+
veh = (struct vlan_ethhdr *)skb->data;
protocol = veh->h_vlan_encapsulated_proto;
}
@@ -627,7 +630,7 @@
*
* Returns features without unsafe ones if the skb has multiple tags.
*/
-static inline netdev_features_t vlan_features_check(const struct sk_buff *skb,
+static inline netdev_features_t vlan_features_check(struct sk_buff *skb,
netdev_features_t features)
{
if (skb_vlan_tagged_multi(skb)) {
diff --git a/include/linux/init.h b/include/linux/init.h
index 8e346d1..d4b13a4 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -5,10 +5,10 @@
#include <linux/types.h>
/* Built-in __init functions needn't be compiled with retpoline */
-#if defined(RETPOLINE) && !defined(MODULE)
-#define __noretpoline __attribute__((indirect_branch("keep")))
+#if defined(__noretpoline) && !defined(MODULE)
+#define __noinitretpoline __noretpoline
#else
-#define __noretpoline
+#define __noinitretpoline
#endif
/* These macros are used to mark some functions or
@@ -46,7 +46,7 @@
/* These are for everybody (although not all archs will actually
discard it in modules) */
-#define __init __section(.init.text) __cold notrace __latent_entropy __noretpoline
+#define __init __section(.init.text) __cold notrace __latent_entropy __noinitretpoline __nocfi
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
@@ -133,6 +133,9 @@
void __init load_default_modules(void);
int __init init_rootfs(void);
+#if defined(CONFIG_DEBUG_RODATA) || defined(CONFIG_DEBUG_SET_MODULE_RONX)
+extern bool rodata_enabled;
+#endif
#ifdef CONFIG_DEBUG_RODATA
void mark_rodata_ro(void);
#endif
@@ -147,6 +150,15 @@
#ifndef __ASSEMBLY__
+#ifdef CONFIG_LTO_CLANG
+ /* prepend the variable name with __COUNTER__ to ensure correct ordering */
+ #define ___initcall_name2(c, fn, id) __initcall_##c##_##fn##id
+ #define ___initcall_name1(c, fn, id) ___initcall_name2(c, fn, id)
+ #define __initcall_name(fn, id) ___initcall_name1(__COUNTER__, fn, id)
+#else
+ #define __initcall_name(fn, id) __initcall_##fn##id
+#endif
+
/*
* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
@@ -164,7 +176,7 @@
*/
#define __define_initcall(fn, id) \
- static initcall_t __initcall_##fn##id __used \
+ static initcall_t __initcall_name(fn, id) __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
/*
diff --git a/include/linux/input/gen_vkeys.h b/include/linux/input/gen_vkeys.h
new file mode 100644
index 0000000..b152147
--- /dev/null
+++ b/include/linux/input/gen_vkeys.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2013, 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __GEN_VKEYS_
+struct vkeys_platform_data {
+ const char *name;
+ int disp_maxx;
+ int disp_maxy;
+ int panel_maxx;
+ int panel_maxy;
+ int *keycodes;
+ int num_keys;
+ int y_offset;
+};
+#endif
diff --git a/include/linux/input/synaptics_dsx_v2_6.h b/include/linux/input/synaptics_dsx_v2_6.h
index 5cd26bb..75d4b77 100644
--- a/include/linux/input/synaptics_dsx_v2_6.h
+++ b/include/linux/input/synaptics_dsx_v2_6.h
@@ -59,6 +59,7 @@
* @y_flip: y flip flag
* @swap_axes: swap axes flag
* @resume_in_workqueue: defer resume function to workqueue
+ * @resume_in_workqueue: do not disable/enable regulators in suspend/resume
* @irq_gpio: attention interrupt GPIO
* @irq_on_state: attention interrupt active state
* @power_gpio: power switch GPIO
@@ -75,6 +76,7 @@
* @power_delay_ms: delay time to wait after powering up device
* @reset_delay_ms: delay time to wait after resetting device
* @reset_active_ms: reset active time
+ * @bus_lpm_cur_uA: low power mode current setting for bus
* @byte_delay_us: delay time between two bytes of SPI data
* @block_delay_us: delay time between two SPI transfers
* @pwr_reg_name: pointer to name of regulator for power control
@@ -88,12 +90,14 @@
bool swap_axes;
bool resume_in_workqueue;
bool wakeup_gesture_en;
+ bool dont_disable_regs;
int irq_gpio;
int irq_on_state;
int power_gpio;
int power_on_state;
int reset_gpio;
int reset_on_state;
+ int bus_lpm_cur_uA;
int max_y_for_2d;
unsigned long irq_flags;
unsigned short i2c_addr;
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index f966e0c..2c31666 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -117,19 +117,6 @@
};
/**
-* enum ipa_vlan_ifaces - vlan interfaces types
-* @IPA_VLAN_IF_EMAC: used for EMAC ethernet device
-* @IPA_VLAN_IF_RNDIS: used for RNDIS USB device
-* @IPA_VLAN_IF_ECM: used for ECM USB device
-*/
-enum ipa_vlan_ifaces {
- IPA_VLAN_IF_EMAC,
- IPA_VLAN_IF_RNDIS,
- IPA_VLAN_IF_ECM,
- IPA_VLAN_IF_MAX
-};
-
-/**
* struct ipa_ep_cfg_nat - NAT configuration in IPA end-point
* @nat_en: This defines the default NAT mode for the pipe: in case of
* filter miss - the default NAT mode defines the NATing operation
@@ -1169,6 +1156,7 @@
* @ipa_if_tlv: number of IPA_IF TLV
* @ipa_if_aos: number of IPA_IF AOS
* @ee: Execution environment
+ * @prefetch_mode: Prefetch mode to be used
*/
struct ipa_gsi_ep_config {
int ipa_ep_num;
@@ -1176,6 +1164,7 @@
int ipa_if_tlv;
int ipa_if_aos;
int ee;
+ enum gsi_prefetch_mode prefetch_mode;
};
/**
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index c122409..914eb4a 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -451,6 +451,8 @@
}
void gic_show_pending_irqs(void);
+void gic_v3_dist_save(void);
+void gic_v3_dist_restore(void);
unsigned int get_gic_highpri_irq(void);
#endif
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index 589d14e..c2a0f00 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -1,6 +1,7 @@
#ifndef _LINUX_JIFFIES_H
#define _LINUX_JIFFIES_H
+#include <linux/cache.h>
#include <linux/math64.h>
#include <linux/kernel.h>
#include <linux/types.h>
@@ -63,19 +64,17 @@
/* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */
#define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ)
-/* some arch's have a small-data section that can be accessed register-relative
- * but that can only take up to, say, 4-byte variables. jiffies being part of
- * an 8-byte variable may not be correctly accessed unless we force the issue
- */
-#define __jiffy_data __attribute__((section(".data")))
+#ifndef __jiffy_arch_data
+#define __jiffy_arch_data
+#endif
/*
* The 64-bit value is not atomic - you MUST NOT read it
* without sampling the sequence number in jiffies_lock.
* get_jiffies_64() will do this for you as appropriate.
*/
-extern u64 __jiffy_data jiffies_64;
-extern unsigned long volatile __jiffy_data jiffies;
+extern u64 __cacheline_aligned_in_smp jiffies_64;
+extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void);
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index bc6ed52..61054f1 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -46,6 +46,7 @@
#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
+#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))
#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 66a1f4e..b27315c 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -35,6 +35,7 @@
const char *name;
enum led_brightness brightness;
enum led_brightness max_brightness;
+ enum led_brightness usr_brightness_req;
int flags;
/* Lower 16 bits reflect status */
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index be65c03..f510c68 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1452,6 +1452,8 @@
size_t *len);
int (*inode_create)(struct inode *dir, struct dentry *dentry,
umode_t mode);
+ int (*inode_post_create)(struct inode *dir, struct dentry *dentry,
+ umode_t mode);
int (*inode_link)(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
int (*inode_unlink)(struct inode *dir, struct dentry *dentry);
@@ -1750,6 +1752,7 @@
struct list_head inode_free_security;
struct list_head inode_init_security;
struct list_head inode_create;
+ struct list_head inode_post_create;
struct list_head inode_link;
struct list_head inode_unlink;
struct list_head inode_symlink;
diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h
index b4ee8f6..8e2828d 100644
--- a/include/linux/mlx4/qp.h
+++ b/include/linux/mlx4/qp.h
@@ -470,6 +470,7 @@
u16 rate_val;
};
+struct mlx4_qp *mlx4_qp_lookup(struct mlx4_dev *dev, u32 qpn);
int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn,
enum mlx4_update_qp_attr attr,
struct mlx4_update_qp_params *params);
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 5827614..5f3e622 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -750,8 +750,14 @@
};
enum {
- CQE_RSS_HTYPE_IP = 0x3 << 6,
- CQE_RSS_HTYPE_L4 = 0x3 << 2,
+ CQE_RSS_HTYPE_IP = 0x3 << 2,
+ /* cqe->rss_hash_type[3:2] - IP destination selected for hash
+ * (00 = none, 01 = IPv4, 10 = IPv6, 11 = Reserved)
+ */
+ CQE_RSS_HTYPE_L4 = 0x3 << 6,
+ /* cqe->rss_hash_type[7:6] - L4 destination selected for hash
+ * (00 = none, 01 = TCP. 10 = UDP, 11 = IPSEC.SPI
+ */
};
enum {
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 6a620e0..7751d72 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -380,8 +380,8 @@
struct mlx5_core_rsc_common common; /* must be first */
u32 srqn;
int max;
- int max_gs;
- int max_avail_gather;
+ size_t max_gs;
+ size_t max_avail_gather;
int wqe_shift;
void (*event) (struct mlx5_core_srq *, enum mlx5_event);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 932e99c..b328cca 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1291,6 +1291,19 @@
struct page **pages, unsigned int gup_flags);
long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
struct page **pages, unsigned int gup_flags);
+#ifdef CONFIG_FS_DAX
+long get_user_pages_longterm(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages,
+ struct vm_area_struct **vmas);
+#else
+static inline long get_user_pages_longterm(unsigned long start,
+ unsigned long nr_pages, unsigned int gup_flags,
+ struct page **pages, struct vm_area_struct **vmas)
+{
+ return get_user_pages(start, nr_pages, gup_flags, pages, vmas);
+}
+#endif /* CONFIG_FS_DAX */
+
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct page **pages);
@@ -2233,6 +2246,7 @@
#define FOLL_MLOCK 0x1000 /* lock present pages */
#define FOLL_REMOTE 0x2000 /* we are working on non-current tsk/mm */
#define FOLL_COW 0x4000 /* internal GUP flag */
+#define FOLL_ANON 0x8000 /* don't do file mappings */
typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
void *data);
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index 41d376e..e030a68 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -50,6 +50,13 @@
list_add(&page->lru, &lruvec->lists[lru]);
}
+static __always_inline void add_page_to_lru_list_tail(struct page *page,
+ struct lruvec *lruvec, enum lru_list lru)
+{
+ update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page));
+ list_add_tail(&page->lru, &lruvec->lists[lru]);
+}
+
static __always_inline void del_page_from_lru_list(struct page *page,
struct lruvec *lruvec, enum lru_list lru)
{
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index c5a4a25..48b7281 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -138,6 +138,8 @@
int (*shutdown)(struct mmc_host *);
int (*reset)(struct mmc_host *);
int (*change_bus_speed)(struct mmc_host *, unsigned long *);
+ int (*pre_hibernate)(struct mmc_host *);
+ int (*post_hibernate)(struct mmc_host *);
};
struct mmc_card;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 2530fcc..9a20d3c 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -454,6 +454,7 @@
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
u32 caps2; /* More host capabilities */
+ u32 cached_caps2;
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */
@@ -651,6 +652,8 @@
void *cmdq_private;
struct mmc_request *err_mrq;
+ bool inlinecrypt_support; /* Inline encryption support */
+
atomic_t rpmb_req_pending;
struct mutex rpmb_req_mutex;
unsigned long private[0] ____cacheline_aligned;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 0c28c28..815d0f4 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -245,8 +245,6 @@
#define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
#define LRU_ALL ((1 << NR_LRU_LISTS) - 1)
-/* Isolate clean file */
-#define ISOLATE_CLEAN ((__force isolate_mode_t)0x1)
/* Isolate unmapped file */
#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x2)
/* Isolate for asynchronous migration */
diff --git a/include/linux/module.h b/include/linux/module.h
index fd9e121..9d6fd1d 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -20,6 +20,7 @@
#include <linux/export.h>
#include <linux/extable.h> /* only as arch move module.h -> extable.h */
#include <linux/rbtree_latch.h>
+#include <linux/cfi.h>
#include <linux/percpu.h>
#include <asm/module.h>
@@ -349,6 +350,10 @@
const unsigned long *crcs;
unsigned int num_syms;
+#ifdef CONFIG_CFI_CLANG
+ cfi_check_fn cfi_check;
+#endif
+
/* Kernel parameters. */
#ifdef CONFIG_SYSFS
struct mutex param_lock;
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 52666d9..060db5c 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -225,19 +225,11 @@
VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }
/* Obsolete - use module_param_cb() */
-#define module_param_call(name, set, get, arg, perm) \
- static const struct kernel_param_ops __param_ops_##name = \
- { .flags = 0, (void *)set, (void *)get }; \
+#define module_param_call(name, _set, _get, arg, perm) \
+ static const struct kernel_param_ops __param_ops_##name = \
+ { .flags = 0, .set = _set, .get = _get }; \
__module_param_call(MODULE_PARAM_PREFIX, \
- name, &__param_ops_##name, arg, \
- (perm) + sizeof(__check_old_set_param(set))*0, -1, 0)
-
-/* We don't get oldget: it's often a new-style param_get_uint, etc. */
-static inline int
-__check_old_set_param(int (*oldset)(const char *, struct kernel_param *))
-{
- return 0;
-}
+ name, &__param_ops_##name, arg, perm, -1, 0)
#ifdef CONFIG_SYSFS
extern void kernel_param_lock(struct module *mod);
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index 6e0b439..79e7daa 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,6 +12,7 @@
#ifndef MSM_GSI_H
#define MSM_GSI_H
#include <linux/types.h>
+#include <linux/interrupt.h>
enum gsi_ver {
GSI_VER_ERR = 0,
@@ -82,6 +83,9 @@
* @irq: IRQ number
* @phys_addr: physical address of GSI block
* @size: register size of GSI block
+ * @emulator_intcntrlr_addr: the location of emulator's interrupt control block
+ * @emulator_intcntrlr_size: the sise of emulator_intcntrlr_addr
+ * @emulator_intcntrlr_client_isr: client's isr. Called by the emulator's isr
* @mhi_er_id_limits_valid: valid flag for mhi_er_id_limits
* @mhi_er_id_limits: MHI event ring start and end ids
* @notify_cb: general notification callback
@@ -107,6 +111,9 @@
unsigned int irq;
phys_addr_t phys_addr;
unsigned long size;
+ phys_addr_t emulator_intcntrlr_addr;
+ unsigned long emulator_intcntrlr_size;
+ irq_handler_t emulator_intcntrlr_client_isr;
bool mhi_er_id_limits_valid;
uint32_t mhi_er_id_limits[2];
void (*notify_cb)(struct gsi_per_notify *notify);
@@ -219,6 +226,11 @@
GSI_TWO_PREFETCH_SEG = 0x1
};
+enum gsi_prefetch_mode {
+ GSI_USE_PREFETCH_BUFS = 0x0,
+ GSI_ESCAPE_BUF_ONLY = 0x1
+};
+
enum gsi_chan_evt {
GSI_CHAN_EVT_INVALID = 0x0,
GSI_CHAN_EVT_SUCCESS = 0x1,
@@ -357,6 +369,7 @@
enum gsi_chan_use_db_eng use_db_eng;
enum gsi_max_prefetch max_prefetch;
uint8_t low_weight;
+ enum gsi_prefetch_mode prefetch_mode;
void (*xfer_cb)(struct gsi_chan_xfer_notify *notify);
void (*err_cb)(struct gsi_chan_err_notify *notify);
void *chan_user_data;
@@ -841,6 +854,18 @@
union __packed gsi_channel_scratch val);
/**
+ * gsi_read_channel_scratch - Peripheral should call this function to
+ * read the scratch area of the channel context
+ *
+ * @chan_hdl: Client handle previously obtained from
+ * gsi_alloc_channel
+ *
+ * @Return gsi_status
+ */
+int gsi_read_channel_scratch(unsigned long chan_hdl,
+ union __packed gsi_channel_scratch *ch_scratch);
+
+/**
* gsi_start_channel - Peripheral should call this function to
* start a channel i.e put into running state
*
@@ -1098,6 +1123,7 @@
* gsi_alloc_channel (for as many channels as needed; channels can have
* no event ring, an exclusive event ring or a shared event ring)
* gsi_write_channel_scratch
+ * gsi_read_channel_scratch
* gsi_start_channel
* gsi_queue_xfer/gsi_start_xfer
* gsi_config_channel_mode/gsi_poll_channel (if clients wants to poll on
@@ -1182,6 +1208,12 @@
return -GSI_STATUS_UNSUPPORTED_OP;
}
+static inline int gsi_read_channel_scratch(unsigned long chan_hdl,
+ union __packed gsi_channel_scratch *ch_scratch)
+{
+ return -GSI_STATUS_UNSUPPORTED_OP;
+}
+
static inline int gsi_start_channel(unsigned long chan_hdl)
{
return -GSI_STATUS_UNSUPPORTED_OP;
@@ -1279,7 +1311,8 @@
return -GSI_STATUS_UNSUPPORTED_OP;
}
-static inline int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size)
+static inline int gsi_enable_fw(
+ phys_addr_t gsi_base_addr, u32 gsi_size, enum gsi_ver ver)
{
return -GSI_STATUS_UNSUPPORTED_OP;
}
diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h
index b63fa45..3529683 100644
--- a/include/linux/mtd/flashchip.h
+++ b/include/linux/mtd/flashchip.h
@@ -85,6 +85,7 @@
unsigned int write_suspended:1;
unsigned int erase_suspended:1;
unsigned long in_progress_block_addr;
+ unsigned long in_progress_block_mask;
struct mutex mutex;
wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index 2ad1a2b..69111fa 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -254,6 +254,8 @@
bool xt_find_jump_offset(const unsigned int *offsets,
unsigned int target, unsigned int size);
+int xt_check_proc_name(const char *name, unsigned int size);
+
int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
bool inv_proto);
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
@@ -375,38 +377,14 @@
return ret;
}
+struct xt_percpu_counter_alloc_state {
+ unsigned int off;
+ const char __percpu *mem;
+};
-/* On SMP, ip(6)t_entry->counters.pcnt holds address of the
- * real (percpu) counter. On !SMP, its just the packet count,
- * so nothing needs to be done there.
- *
- * xt_percpu_counter_alloc returns the address of the percpu
- * counter, or 0 on !SMP. We force an alignment of 16 bytes
- * so that bytes/packets share a common cache line.
- *
- * Hence caller must use IS_ERR_VALUE to check for error, this
- * allows us to return 0 for single core systems without forcing
- * callers to deal with SMP vs. NONSMP issues.
- */
-static inline unsigned long xt_percpu_counter_alloc(void)
-{
- if (nr_cpu_ids > 1) {
- void __percpu *res = __alloc_percpu(sizeof(struct xt_counters),
- sizeof(struct xt_counters));
-
- if (res == NULL)
- return -ENOMEM;
-
- return (__force unsigned long) res;
- }
-
- return 0;
-}
-static inline void xt_percpu_counter_free(u64 pcnt)
-{
- if (nr_cpu_ids > 1)
- free_percpu((void __percpu *) (unsigned long) pcnt);
-}
+bool xt_percpu_counter_alloc(struct xt_percpu_counter_alloc_state *state,
+ struct xt_counters *counter);
+void xt_percpu_counter_free(struct xt_counters *cnt);
static inline struct xt_counters *
xt_get_this_cpu_counter(struct xt_counters *cnt)
diff --git a/include/linux/nospec.h b/include/linux/nospec.h
index fbc98e2..0c5ef54 100644
--- a/include/linux/nospec.h
+++ b/include/linux/nospec.h
@@ -5,6 +5,9 @@
#ifndef _LINUX_NOSPEC_H
#define _LINUX_NOSPEC_H
+#include <asm/barrier.h>
+
+struct task_struct;
/**
* array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
@@ -30,26 +33,6 @@
#endif
/*
- * Warn developers about inappropriate array_index_nospec() usage.
- *
- * Even if the CPU speculates past the WARN_ONCE branch, the
- * sign bit of @index is taken into account when generating the
- * mask.
- *
- * This warning is compiled out when the compiler can infer that
- * @index and @size are less than LONG_MAX.
- */
-#define array_index_mask_nospec_check(index, size) \
-({ \
- if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, \
- "array_index_nospec() limited to range of [0, LONG_MAX]\n")) \
- _mask = 0; \
- else \
- _mask = array_index_mask_nospec(index, size); \
- _mask; \
-})
-
-/*
* array_index_nospec - sanitize an array index after a bounds check
*
* For a code sequence like:
@@ -67,12 +50,19 @@
({ \
typeof(index) _i = (index); \
typeof(size) _s = (size); \
- unsigned long _mask = array_index_mask_nospec_check(_i, _s); \
+ unsigned long _mask = array_index_mask_nospec(_i, _s); \
\
BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \
BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \
\
- _i &= _mask; \
- _i; \
+ (typeof(_i)) (_i & _mask); \
})
+
+/* Speculation control prctl */
+int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which);
+int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
+ unsigned long ctrl);
+/* Speculation control for seccomp enforced mitigation */
+void arch_seccomp_spec_mitigate(struct task_struct *task);
+
#endif /* _LINUX_NOSPEC_H */
diff --git a/include/linux/of.h b/include/linux/of.h
index 299aeb1..e44e9a3 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -39,7 +39,9 @@
struct property *next;
unsigned long _flags;
unsigned int unique_id;
+#if defined(CONFIG_OF_KOBJ)
struct bin_attribute attr;
+#endif
};
#if defined(CONFIG_SPARC)
@@ -58,7 +60,9 @@
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
+#if defined(CONFIG_OF_KOBJ)
struct kobject kobj;
+#endif
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
@@ -102,21 +106,17 @@
extern struct kobj_type of_node_ktype;
static inline void of_node_init(struct device_node *node)
{
+#if defined(CONFIG_OF_KOBJ)
kobject_init(&node->kobj, &of_node_ktype);
+#endif
node->fwnode.type = FWNODE_OF;
}
-/* true when node is initialized */
-static inline int of_node_is_initialized(struct device_node *node)
-{
- return node && node->kobj.state_initialized;
-}
-
-/* true when node is attached (i.e. present on sysfs) */
-static inline int of_node_is_attached(struct device_node *node)
-{
- return node && node->kobj.state_in_sysfs;
-}
+#if defined(CONFIG_OF_KOBJ)
+#define of_node_kobj(n) (&(n)->kobj)
+#else
+#define of_node_kobj(n) NULL
+#endif
#ifdef CONFIG_OF_DYNAMIC
extern struct device_node *of_node_get(struct device_node *node);
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 7dbe914..791c547 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -148,7 +148,7 @@
#ifdef CONFIG_TINY_RCU
# ifdef CONFIG_PREEMPT_COUNT
- VM_BUG_ON(!in_atomic());
+ VM_BUG_ON(!in_atomic() && !irqs_disabled());
# endif
/*
* Preempt must be disabled here - we rely on rcu_read_lock doing
@@ -186,7 +186,7 @@
#if !defined(CONFIG_SMP) && defined(CONFIG_TREE_RCU)
# ifdef CONFIG_PREEMPT_COUNT
- VM_BUG_ON(!in_atomic());
+ VM_BUG_ON(!in_atomic() && !irqs_disabled());
# endif
VM_BUG_ON_PAGE(page_count(page) == 0, page);
page_ref_add(page, count);
@@ -225,7 +225,7 @@
__GFP_COLD | __GFP_NORETRY | __GFP_NOWARN;
}
-typedef int filler_t(void *, struct page *);
+typedef int filler_t(struct file *, struct page *);
pgoff_t page_cache_next_hole(struct address_space *mapping,
pgoff_t index, unsigned long max_scan);
@@ -369,7 +369,7 @@
static inline struct page *read_mapping_page(struct address_space *mapping,
pgoff_t index, void *data)
{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
+ filler_t *filler = mapping->a_ops->readpage;
return read_cache_page(mapping, index, filler, data);
}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 1b71179..3652290 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1348,9 +1348,9 @@
unsigned int min_vecs, unsigned int max_vecs,
unsigned int flags)
{
- if (min_vecs > 1)
- return -EINVAL;
- return 1;
+ if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1 && dev->irq)
+ return 1;
+ return -ENOSPC;
}
static inline void pci_free_irq_vectors(struct pci_dev *dev)
{
diff --git a/include/linux/pfk.h b/include/linux/pfk.h
new file mode 100644
index 0000000..d7405ea
--- /dev/null
+++ b/include/linux/pfk.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PFK_H_
+#define PFK_H_
+
+#include <linux/bio.h>
+
+struct ice_crypto_setting;
+
+#ifdef CONFIG_PFK
+
+/*
+ * Default key for inline encryption.
+ *
+ * For now only AES-256-XTS is supported, so this is a fixed length. But if
+ * ever needed, this should be made variable-length with a 'mode' and 'size'.
+ * (Remember to update pfk_allow_merge_bio() when doing so!)
+ */
+#define BLK_ENCRYPTION_KEY_SIZE_AES_256_XTS 64
+
+struct blk_encryption_key {
+ u8 raw[BLK_ENCRYPTION_KEY_SIZE_AES_256_XTS];
+};
+
+int pfk_load_key_start(const struct bio *bio,
+ struct ice_crypto_setting *ice_setting, bool *is_pfe, bool);
+int pfk_load_key_end(const struct bio *bio, bool *is_pfe);
+int pfk_remove_key(const unsigned char *key, size_t key_size);
+bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2);
+void pfk_clear_on_reset(void);
+
+#else
+static inline int pfk_load_key_start(const struct bio *bio,
+ struct ice_crypto_setting *ice_setting, bool *is_pfe, bool async)
+{
+ return -ENODEV;
+}
+
+static inline int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
+{
+ return -ENODEV;
+}
+
+static inline int pfk_remove_key(const unsigned char *key, size_t key_size)
+{
+ return -ENODEV;
+}
+
+static inline bool pfk_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2)
+{
+ return true;
+}
+
+static inline void pfk_clear_on_reset(void)
+{}
+
+#endif /* CONFIG_PFK */
+
+#endif /* PFK_H */
diff --git a/include/linux/platform_data/isl9305.h b/include/linux/platform_data/isl9305.h
index 1419133..4ac1a07 100644
--- a/include/linux/platform_data/isl9305.h
+++ b/include/linux/platform_data/isl9305.h
@@ -24,7 +24,7 @@
struct regulator_init_data;
struct isl9305_pdata {
- struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR];
+ struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR + 1];
};
#endif
diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
index 34c4498..83b22ae 100644
--- a/include/linux/posix-clock.h
+++ b/include/linux/posix-clock.h
@@ -59,23 +59,23 @@
int (*clock_adjtime)(struct posix_clock *pc, struct timex *tx);
- int (*clock_gettime)(struct posix_clock *pc, struct timespec *ts);
+ int (*clock_gettime)(struct posix_clock *pc, struct timespec64 *ts);
- int (*clock_getres) (struct posix_clock *pc, struct timespec *ts);
+ int (*clock_getres) (struct posix_clock *pc, struct timespec64 *ts);
int (*clock_settime)(struct posix_clock *pc,
- const struct timespec *ts);
+ const struct timespec64 *ts);
int (*timer_create) (struct posix_clock *pc, struct k_itimer *kit);
int (*timer_delete) (struct posix_clock *pc, struct k_itimer *kit);
void (*timer_gettime)(struct posix_clock *pc,
- struct k_itimer *kit, struct itimerspec *tsp);
+ struct k_itimer *kit, struct itimerspec64 *tsp);
int (*timer_settime)(struct posix_clock *pc,
struct k_itimer *kit, int flags,
- struct itimerspec *tsp, struct itimerspec *old);
+ struct itimerspec64 *tsp, struct itimerspec64 *old);
/*
* Optional character device methods:
*/
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 2fb9f03..25d7806 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -125,6 +125,12 @@
POWER_SUPPLY_PL_NON_STACKED_BATFET,
};
+enum {
+ POWER_SUPPLY_PD_INACTIVE = 0,
+ POWER_SUPPLY_PD_ACTIVE,
+ POWER_SUPPLY_PD_PPS_ACTIVE,
+};
+
enum power_supply_property {
/* Properties of type `int' */
POWER_SUPPLY_PROP_STATUS = 0,
@@ -282,6 +288,13 @@
POWER_SUPPLY_PROP_ALLOW_HVDCP3,
POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED,
POWER_SUPPLY_PROP_MAX_PULSE_ALLOWED,
+ POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE,
+ POWER_SUPPLY_PROP_BATTERY_INFO,
+ POWER_SUPPLY_PROP_BATTERY_INFO_ID,
+ POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION,
+ POWER_SUPPLY_PROP_ESR_ACTUAL,
+ POWER_SUPPLY_PROP_ESR_NOMINAL,
+ POWER_SUPPLY_PROP_SOH,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index b97bf2e..b326d0a 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -74,6 +74,12 @@
#endif /* CONFIG_PROC_FS */
+#ifdef CONFIG_PROC_UID
+extern void proc_register_uid(kuid_t uid);
+#else
+static inline void proc_register_uid(kuid_t uid) {}
+#endif
+
struct net;
static inline struct proc_dir_entry *proc_net_mkdir(
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 6306ab1..347077c 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -25,6 +25,17 @@
int psci_cpu_init_idle(unsigned int cpu);
int psci_cpu_suspend_enter(unsigned long index);
+enum psci_conduit {
+ PSCI_CONDUIT_NONE,
+ PSCI_CONDUIT_SMC,
+ PSCI_CONDUIT_HVC,
+};
+
+enum smccc_version {
+ SMCCC_VERSION_1_0,
+ SMCCC_VERSION_1_1,
+};
+
struct psci_operations {
u32 (*get_version)(void);
int (*cpu_suspend)(u32 state, unsigned long entry_point);
@@ -34,6 +45,8 @@
int (*affinity_info)(unsigned long target_affinity,
unsigned long lowest_affinity_level);
int (*migrate_info_type)(void);
+ enum psci_conduit conduit;
+ enum smccc_version smccc_version;
};
extern struct psci_operations psci_ops;
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index a083370..48fe2e9 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -401,6 +401,10 @@
* %SCALE_USBIN_I: Conversion for USB input current.
* %SCALE_BATT_THERM_TEMP_QRD: Conversion to temperature(decidegC) based on btm
* parameters for QRD.
+ * %SCALE_BATT_THERM_TEMP_PU30: Conversion to temperature(decidegC) for 30k
+ * pullup.
+ * %SCALE_BATT_THERM_TEMP_PU400: Conversion to temperature(decidegC) for 400k
+ * pullup.
* %SCALE_SMB1390_DIE_TEMP: Conversion for SMB1390 die temp
* %SCALE_NONE: Do not use this scaling type.
*/
@@ -426,6 +430,8 @@
SCALE_USBIN_I,
SCALE_BATT_THERM_TEMP_QRD,
SCALE_SMB1390_DIE_TEMP,
+ SCALE_BATT_THERM_TEMP_PU30,
+ SCALE_BATT_THERM_TEMP_PU400,
SCALE_NONE,
};
@@ -1059,12 +1065,14 @@
* @full_scale_code: Full scale value with intrinsic offset removed.
* @biploar: Polarity for QPNP ADC.
* @adc_hc: Represents using HC variant of the ADC controller.
+ * @is_pmic_5: To check if PMIC5 is used.
*/
struct qpnp_adc_properties {
uint32_t adc_vdd_reference;
uint32_t full_scale_code;
bool bipolar;
bool adc_hc;
+ bool is_pmic_5;
};
/**
@@ -1465,12 +1473,48 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt);
/**
+ * qpnp_adc_batt_therm_pu30() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset. Returns the temperature in decidegC.
+ * It uses a mapping table computed for a 30K pull-up.
+ * @dev: Structure device for qpnp vadc
+ * @adc_code: pre-calibrated digital output of the ADC.
+ * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t qpnp_adc_batt_therm_pu30(struct qpnp_vadc_chip *dev,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_batt_therm_pu400() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset. Returns the temperature in decidegC.
+ * It uses a mapping table computed for a 400K pull-up.
+ * @dev: Structure device for qpnp vadc
+ * @adc_code: pre-calibrated digital output of the ADC.
+ * @adc_prop: adc properties of the adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t qpnp_adc_batt_therm_pu400(struct qpnp_vadc_chip *dev,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt);
+/**
* qpnp_adc_scale_batt_therm() - Scales the pre-calibrated digital output
* of an ADC to the ADC reference and compensates for the
* gain and offset. Returns the temperature in decidegC.
* @dev: Structure device for qpnp vadc
* @adc_code: pre-calibrated digital output of the ADC.
- * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * @adc_prop: adc properties of the adc such as bit resolution,
* reference voltage.
* @chan_prop: individual channel properties to compensate the i/p scaling,
* slope and offset.
@@ -2091,6 +2135,18 @@
const struct qpnp_vadc_chan_properties *chan_prop,
struct qpnp_vadc_result *chan_rslt)
{ return -ENXIO; }
+static inline int32_t qpnp_adc_batt_therm_pu30(struct qpnp_vadc_chip *vadc,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_batt_therm_pu400(struct qpnp_vadc_chip *vadc,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_prop,
+ const struct qpnp_vadc_chan_properties *chan_prop,
+ struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
static inline int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *vadc,
int32_t adc_code,
const struct qpnp_adc_properties *adc_prop,
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
new file mode 100644
index 0000000..600aadf
--- /dev/null
+++ b/include/linux/refcount.h
@@ -0,0 +1,294 @@
+#ifndef _LINUX_REFCOUNT_H
+#define _LINUX_REFCOUNT_H
+
+/*
+ * Variant of atomic_t specialized for reference counts.
+ *
+ * The interface matches the atomic_t interface (to aid in porting) but only
+ * provides the few functions one should use for reference counting.
+ *
+ * It differs in that the counter saturates at UINT_MAX and will not move once
+ * there. This avoids wrapping the counter and causing 'spurious'
+ * use-after-free issues.
+ *
+ * Memory ordering rules are slightly relaxed wrt regular atomic_t functions
+ * and provide only what is strictly required for refcounts.
+ *
+ * The increments are fully relaxed; these will not provide ordering. The
+ * rationale is that whatever is used to obtain the object we're increasing the
+ * reference count on will provide the ordering. For locked data structures,
+ * its the lock acquire, for RCU/lockless data structures its the dependent
+ * load.
+ *
+ * Do note that inc_not_zero() provides a control dependency which will order
+ * future stores against the inc, this ensures we'll never modify the object
+ * if we did not in fact acquire a reference.
+ *
+ * The decrements will provide release order, such that all the prior loads and
+ * stores will be issued before, it also provides a control dependency, which
+ * will order us against the subsequent free().
+ *
+ * The control dependency is against the load of the cmpxchg (ll/sc) that
+ * succeeded. This means the stores aren't fully ordered, but this is fine
+ * because the 1->0 transition indicates no concurrency.
+ *
+ * Note that the allocator is responsible for ordering things between free()
+ * and alloc().
+ *
+ */
+
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_DEBUG_REFCOUNT
+#define REFCOUNT_WARN(cond, str) WARN_ON(cond)
+#define __refcount_check __must_check
+#else
+#define REFCOUNT_WARN(cond, str) (void)(cond)
+#define __refcount_check
+#endif
+
+typedef struct refcount_struct {
+ atomic_t refs;
+} refcount_t;
+
+#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
+
+static inline void refcount_set(refcount_t *r, unsigned int n)
+{
+ atomic_set(&r->refs, n);
+}
+
+static inline unsigned int refcount_read(const refcount_t *r)
+{
+ return atomic_read(&r->refs);
+}
+
+static inline __refcount_check
+bool refcount_add_not_zero(unsigned int i, refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ if (!val)
+ return false;
+
+ if (unlikely(val == UINT_MAX))
+ return true;
+
+ new = val + i;
+ if (new < val)
+ new = UINT_MAX;
+ old = atomic_cmpxchg_relaxed(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+
+ return true;
+}
+
+static inline void refcount_add(unsigned int i, refcount_t *r)
+{
+ REFCOUNT_WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
+}
+
+/*
+ * Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN.
+ *
+ * Provides no memory ordering, it is assumed the caller has guaranteed the
+ * object memory to be stable (RCU, etc.). It does provide a control dependency
+ * and thereby orders future stores. See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_inc_not_zero(refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ new = val + 1;
+
+ if (!val)
+ return false;
+
+ if (unlikely(!new))
+ return true;
+
+ old = atomic_cmpxchg_relaxed(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+
+ return true;
+}
+
+/*
+ * Similar to atomic_inc(), will saturate at UINT_MAX and WARN.
+ *
+ * Provides no memory ordering, it is assumed the caller already has a
+ * reference on the object, will WARN when this is not so.
+ */
+static inline void refcount_inc(refcount_t *r)
+{
+ REFCOUNT_WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
+}
+
+/*
+ * Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
+ * decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_sub_and_test(unsigned int i, refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ if (unlikely(val == UINT_MAX))
+ return false;
+
+ new = val - i;
+ if (new > val) {
+ REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+ return false;
+ }
+
+ old = atomic_cmpxchg_release(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ return !new;
+}
+
+static inline __refcount_check
+bool refcount_dec_and_test(refcount_t *r)
+{
+ return refcount_sub_and_test(1, r);
+}
+
+/*
+ * Similar to atomic_dec(), it will WARN on underflow and fail to decrement
+ * when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before.
+ */
+static inline
+void refcount_dec(refcount_t *r)
+{
+ REFCOUNT_WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
+}
+
+/*
+ * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
+ * success thereof.
+ *
+ * Like all decrement operations, it provides release memory order and provides
+ * a control dependency.
+ *
+ * It can be used like a try-delete operator; this explicit case is provided
+ * and not cmpxchg in generic, because that would allow implementing unsafe
+ * operations.
+ */
+static inline __refcount_check
+bool refcount_dec_if_one(refcount_t *r)
+{
+ return atomic_cmpxchg_release(&r->refs, 1, 0) == 1;
+}
+
+/*
+ * No atomic_t counterpart, it decrements unless the value is 1, in which case
+ * it will return false.
+ *
+ * Was often done like: atomic_add_unless(&var, -1, 1)
+ */
+static inline __refcount_check
+bool refcount_dec_not_one(refcount_t *r)
+{
+ unsigned int old, new, val = atomic_read(&r->refs);
+
+ for (;;) {
+ if (unlikely(val == UINT_MAX))
+ return true;
+
+ if (val == 1)
+ return false;
+
+ new = val - 1;
+ if (new > val) {
+ REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+ return true;
+ }
+
+ old = atomic_cmpxchg_release(&r->refs, val, new);
+ if (old == val)
+ break;
+
+ val = old;
+ }
+
+ return true;
+}
+
+/*
+ * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
+ * to decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
+{
+ if (refcount_dec_not_one(r))
+ return false;
+
+ mutex_lock(lock);
+ if (!refcount_dec_and_test(r)) {
+ mutex_unlock(lock);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
+ * decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+static inline __refcount_check
+bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
+{
+ if (refcount_dec_not_one(r))
+ return false;
+
+ spin_lock(lock);
+ if (!refcount_dec_and_test(r)) {
+ spin_unlock(lock);
+ return false;
+ }
+
+ return true;
+}
+
+#endif /* _LINUX_REFCOUNT_H */
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index ee79113..9760b53 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -432,6 +432,8 @@
struct regulator_enable_gpio *ena_pin;
unsigned int ena_gpio_state:1;
+ unsigned int is_switch:1;
+
/* time when this regulator was disabled last time */
unsigned long last_off_jiffy;
struct proxy_consumer *proxy_consumer;
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
index 5c132d3..85d1ffc 100644
--- a/include/linux/rhashtable.h
+++ b/include/linux/rhashtable.h
@@ -706,8 +706,10 @@
if (!key ||
(params.obj_cmpfn ?
params.obj_cmpfn(&arg, rht_obj(ht, head)) :
- rhashtable_compare(&arg, rht_obj(ht, head))))
+ rhashtable_compare(&arg, rht_obj(ht, head)))) {
+ pprev = &head->next;
continue;
+ }
data = rht_obj(ht, head);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 4b2a1bc..6b03b08 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1664,6 +1664,7 @@
u64 dl_deadline; /* relative deadline of each instance */
u64 dl_period; /* separation of two instances (period) */
u64 dl_bw; /* dl_runtime / dl_deadline */
+ u64 dl_density; /* dl_runtime / dl_deadline */
/*
* Actual scheduling parameters. Initialized with the values above,
@@ -1913,6 +1914,10 @@
cputime_t utime, stime, utimescaled, stimescaled;
cputime_t gtime;
+#ifdef CONFIG_CPU_FREQ_TIMES
+ u64 *time_in_state;
+ unsigned int max_state;
+#endif
struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
seqcount_t vtime_seqcount;
@@ -2624,6 +2629,8 @@
#define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */
#define PFA_SPREAD_SLAB 2 /* Spread some slab caches over cpuset */
#define PFA_LMK_WAITING 3 /* Lowmemorykiller is waiting */
+#define PFA_SPEC_SSB_DISABLE 4 /* Speculative Store Bypass disabled */
+#define PFA_SPEC_SSB_FORCE_DISABLE 5 /* Speculative Store Bypass force disabled*/
#define TASK_PFA_TEST(name, func) \
@@ -2650,6 +2657,13 @@
TASK_PFA_TEST(LMK_WAITING, lmk_waiting)
TASK_PFA_SET(LMK_WAITING, lmk_waiting)
+TASK_PFA_TEST(SPEC_SSB_DISABLE, spec_ssb_disable)
+TASK_PFA_SET(SPEC_SSB_DISABLE, spec_ssb_disable)
+TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ssb_disable)
+
+TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
+TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
+
/*
* task->jobctl flags
*/
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
index ecc296c..50c460a 100644
--- a/include/linux/seccomp.h
+++ b/include/linux/seccomp.h
@@ -3,7 +3,8 @@
#include <uapi/linux/seccomp.h>
-#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC)
+#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC | \
+ SECCOMP_FILTER_FLAG_SPEC_ALLOW)
#ifdef CONFIG_SECCOMP
diff --git a/include/linux/security.h b/include/linux/security.h
index 3632428..bfb1b74 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -30,6 +30,7 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fs.h>
+#include <linux/bio.h>
struct linux_binprm;
struct cred;
@@ -256,6 +257,8 @@
const struct qstr *qstr, const char **name,
void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
+int security_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
@@ -304,6 +307,7 @@
struct fown_struct *fown, int sig);
int security_file_receive(struct file *file);
int security_file_open(struct file *file, const struct cred *cred);
+
int security_task_create(unsigned long clone_flags);
void security_task_free(struct task_struct *task);
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
@@ -637,6 +641,13 @@
return 0;
}
+static inline int security_inode_post_create(struct inode *dir,
+ struct dentry *dentry,
+ umode_t mode)
+{
+ return 0;
+}
+
static inline int security_inode_link(struct dentry *old_dentry,
struct inode *dir,
struct dentry *new_dentry)
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 1a94397..42e3f06 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -348,10 +348,10 @@
char name[16];
char compatible[128];
int (*setup)(struct earlycon_device *, const char *options);
-} __aligned(32);
+};
-extern const struct earlycon_id __earlycon_table[];
-extern const struct earlycon_id __earlycon_table_end[];
+extern const struct earlycon_id *__earlycon_table[];
+extern const struct earlycon_id *__earlycon_table_end[];
#if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE)
#define EARLYCON_USED_OR_UNUSED __used
@@ -359,12 +359,19 @@
#define EARLYCON_USED_OR_UNUSED __maybe_unused
#endif
-#define OF_EARLYCON_DECLARE(_name, compat, fn) \
- static const struct earlycon_id __UNIQUE_ID(__earlycon_##_name) \
- EARLYCON_USED_OR_UNUSED __section(__earlycon_table) \
+#define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id) \
+ static const struct earlycon_id unique_id \
+ EARLYCON_USED_OR_UNUSED __initconst \
= { .name = __stringify(_name), \
.compatible = compat, \
- .setup = fn }
+ .setup = fn }; \
+ static const struct earlycon_id EARLYCON_USED_OR_UNUSED \
+ __section(__earlycon_table) \
+ * const __PASTE(__p, unique_id) = &unique_id
+
+#define OF_EARLYCON_DECLARE(_name, compat, fn) \
+ _OF_EARLYCON_DECLARE(_name, compat, fn, \
+ __UNIQUE_ID(__earlycon_##_name))
#define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn)
diff --git a/include/linux/signal.h b/include/linux/signal.h
index b63f63e..5308304 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -97,6 +97,23 @@
}
}
+static inline int sigequalsets(const sigset_t *set1, const sigset_t *set2)
+{
+ switch (_NSIG_WORDS) {
+ case 4:
+ return (set1->sig[3] == set2->sig[3]) &&
+ (set1->sig[2] == set2->sig[2]) &&
+ (set1->sig[1] == set2->sig[1]) &&
+ (set1->sig[0] == set2->sig[0]);
+ case 2:
+ return (set1->sig[1] == set2->sig[1]) &&
+ (set1->sig[0] == set2->sig[0]);
+ case 1:
+ return set1->sig[0] == set2->sig[0];
+ }
+ return 0;
+}
+
#define sigmask(sig) (1UL << ((sig) - 1))
#ifndef __HAVE_ARCH_SIG_SETOPS
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index c73bef9..3fbfe96 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -988,10 +988,10 @@
unsigned int headroom);
struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom,
int newtailroom, gfp_t priority);
-int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
- int offset, int len);
-int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset,
- int len);
+int __must_check skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
+ int offset, int len);
+int __must_check skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg,
+ int offset, int len);
int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer);
int skb_pad(struct sk_buff *skb, int pad);
#define dev_kfree_skb(a) consume_skb(a)
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 00a1f33..9c452f6d 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -518,7 +518,7 @@
}
static inline struct kernfs_node *sysfs_get_dirent(struct kernfs_node *parent,
- const unsigned char *name)
+ const char *name)
{
return kernfs_find_and_get(parent, name);
}
diff --git a/include/linux/tty.h b/include/linux/tty.h
index a41244f..fe1b862 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -355,6 +355,7 @@
#define TTY_PTY_LOCK 16 /* pty private */
#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
#define TTY_HUPPED 18 /* Post driver->hangup() */
+#define TTY_HUPPING 19 /* Hangup in progress */
#define TTY_LDISC_HALTED 22 /* Line discipline is halted */
/* Values for tty->flow_change */
@@ -656,7 +657,7 @@
extern int tty_set_ldisc(struct tty_struct *tty, int disc);
extern int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty);
extern void tty_ldisc_release(struct tty_struct *tty);
-extern void tty_ldisc_init(struct tty_struct *tty);
+extern int __must_check tty_ldisc_init(struct tty_struct *tty);
extern void tty_ldisc_deinit(struct tty_struct *tty);
extern int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
char *f, int count);
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 7f4367b..87e97bb 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -45,6 +45,9 @@
#define FUNC_SUSPEND_OPT_SUSP_MASK BIT(0)
#define FUNC_SUSPEND_OPT_RW_EN_MASK BIT(1)
+#define FUNC_WAKEUP_CAPABLE_SHIFT 0
+#define FUNC_WAKEUP_ENABLE_SHIFT 1
+
/*
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
* wish to delay the data/status stages of the control transfer till they
@@ -57,6 +60,9 @@
/* big enough to hold our biggest descriptor */
#define USB_COMP_EP0_BUFSIZ 4096
+/* OS feature descriptor length <= 4kB */
+#define USB_COMP_EP0_OS_DESC_BUFSIZ 4096
+
#define USB_MS_TO_HS_INTERVAL(x) (ilog2((x * 1000 / 125)) + 1)
struct usb_configuration;
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index b7ccd4e..d4f1759 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -44,6 +44,33 @@
};
/**
+ * Different states involved in USB charger detection.
+ *
+ * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
+ * process is not yet started.
+ * USB_CHG_STATE_IN_PROGRESS Charger detection in progress
+ * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
+ * USB_CHG_STATE_DCD_DONE Data pin contact is detected.
+ * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
+ * between SDP and DCP/CDP).
+ * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
+ * between DCP and CDP).
+ * USB_CHG_STATE_DETECTED USB charger type is determined.
+ * USB_CHG_STATE_QUEUE_SM_WORK SM work to start/stop gadget is queued.
+ *
+ */
+enum usb_chg_state {
+ USB_CHG_STATE_UNDEFINED = 0,
+ USB_CHG_STATE_IN_PROGRESS,
+ USB_CHG_STATE_WAIT_FOR_DCD,
+ USB_CHG_STATE_DCD_DONE,
+ USB_CHG_STATE_PRIMARY_DONE,
+ USB_CHG_STATE_SECONDARY_DONE,
+ USB_CHG_STATE_DETECTED,
+ USB_CHG_STATE_QUEUE_SM_WORK,
+};
+
+/**
* USB charger types
*
* USB_INVALID_CHARGER Invalid USB charger.
@@ -126,7 +153,10 @@
* @async_int: IRQ line on which ASYNC interrupt arrived in LPM.
* @cur_power: The amount of mA available from downstream port.
* @otg_wq: Strict order otg workqueue for OTG works (SM/ID/SUSPEND).
+ * @chg_work: Charger detection work.
+ * @chg_state: The state of charger detection process.
* @chg_type: The type of charger attached.
+ * @chg_detection: True if PHY is doing charger type detection.
* @bus_perf_client: Bus performance client handle to request BUS bandwidth
* @host_bus_suspend: indicates host bus suspend or not.
* @device_bus_suspend: indicates device bus suspend or not.
@@ -148,6 +178,8 @@
* @buf: Dynamic Debug Buffer.
* @max_nominal_system_clk_rate: max freq at which system clock can run in
nominal mode.
+ * @sdp_check: SDP detection work in case of USB_FLOAT power supply
+ * @notify_charger_work: Charger notification work.
*/
struct msm_otg {
struct usb_phy phy;
@@ -190,8 +222,11 @@
int async_int;
unsigned int cur_power;
struct workqueue_struct *otg_wq;
+ struct delayed_work chg_work;
struct delayed_work id_status_work;
+ enum usb_chg_state chg_state;
enum usb_chg_type chg_type;
+ bool chg_detection;
unsigned int dcd_time;
unsigned long caps;
uint32_t bus_perf_client;
@@ -276,7 +311,8 @@
unsigned int notify_current_mA;
struct pm_qos_request pm_qos_req_dma;
struct delayed_work perf_vote_work;
- struct work_struct notify_chg_current_work;
+ struct delayed_work sdp_check;
+ struct work_struct notify_charger_work;
};
struct ci13xxx_platform_data {
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index de2a722..ea4f81c 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -56,4 +56,7 @@
*/
#define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11)
+/* Device needs a pause after every control message. */
+#define USB_QUIRK_DELAY_CTRL_MSG BIT(13)
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index d5eb547..3f8f3505 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -143,6 +143,9 @@
int virtio_device_restore(struct virtio_device *dev);
#endif
+#define virtio_device_for_each_vq(vdev, vq) \
+ list_for_each_entry(vq, &vdev->vqs, list)
+
/**
* virtio_driver - operations for a virtio I/O driver
* @driver: underlying device driver (populate name and owner).
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index b37f8df..05fb7b5 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -63,6 +63,13 @@
WCNSS_WLAN_MAX_GPIO,
};
+enum wcnss_log_type {
+ ERR,
+ WARN,
+ INFO,
+ DBG,
+};
+
#define WCNSS_VBATT_THRESHOLD 3500000
#define WCNSS_VBATT_GUARD 20000
#define WCNSS_VBATT_HIGH 3700000
@@ -145,6 +152,7 @@
void wcnss_snoc_vote(bool clk_chk_en);
int wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config,
struct device *dev);
+void wcnss_log(enum wcnss_log_type type, const char *_fmt, ...);
#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
void wcnss_log_debug_regs_on_bite(void);
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 1061add..1def337 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -453,6 +453,7 @@
extern void workqueue_set_max_active(struct workqueue_struct *wq,
int max_active);
+extern struct work_struct *current_work(void);
extern bool current_is_workqueue_rescuer(void);
extern bool workqueue_congested(int cpu, struct workqueue_struct *wq);
extern unsigned int work_busy(struct work_struct *work);
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 9a8eb83..3eed4f1 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -43,7 +43,7 @@
*/
enum wb_reason {
WB_REASON_BACKGROUND,
- WB_REASON_TRY_TO_FREE_PAGES,
+ WB_REASON_VMSCAN,
WB_REASON_SYNC,
WB_REASON_PERIODIC,
WB_REASON_LAPTOP_TIMER,
diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h
index 95679cb..176dd2e 100644
--- a/include/media/msmb_isp.h
+++ b/include/media/msmb_isp.h
@@ -28,6 +28,19 @@
struct msm_isp_sof_info sof_info;
} u;
};
+
+struct msm_isp32_event_data32 {
+ struct compat_timeval timestamp;
+ struct compat_timeval mono_timestamp;
+ enum msm_vfe_input_src input_intf;
+ uint32_t frame_id;
+ union {
+ struct msm_isp_stats_event stats;
+ struct msm_isp_buf_event buf_done;
+ struct msm_isp32_error_info error_info;
+ } u;
+};
+
#endif
#ifdef CONFIG_MSM_AVTIMER
struct avtimer_fptr_t {
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 554671c..4931787 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -893,7 +893,7 @@
u16 conn_timeout);
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u16 conn_timeout,
- u8 role);
+ u8 role, bdaddr_t *direct_rpa);
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
u8 sec_level, u8 auth_type);
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
diff --git a/include/net/bonding.h b/include/net/bonding.h
index f32f7ef..7734cc9 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -197,6 +197,7 @@
struct slave __rcu *primary_slave;
struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves */
bool force_primary;
+ u32 nest_level;
s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
int (*recv_probe)(const struct sk_buff *, struct bonding *,
struct slave *);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 520c4c3..f656728 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -64,6 +64,9 @@
/* Indicate backport support for external authentication*/
#define CFG80211_EXTERNAL_AUTH_SUPPORT 1
+/* Indicate backport support for processing user cell base hint */
+#define CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED 1
+
/**
* DOC: Introduction
*
@@ -1011,9 +1014,9 @@
* @RATE_INFO_BW_160: 160 MHz bandwidth
*/
enum rate_info_bw {
+ RATE_INFO_BW_20 = 0,
RATE_INFO_BW_5,
RATE_INFO_BW_10,
- RATE_INFO_BW_20,
RATE_INFO_BW_40,
RATE_INFO_BW_80,
RATE_INFO_BW_160,
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index c9b3eb7..567017b 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -55,6 +55,7 @@
#define tw_family __tw_common.skc_family
#define tw_state __tw_common.skc_state
#define tw_reuse __tw_common.skc_reuse
+#define tw_reuseport __tw_common.skc_reuseport
#define tw_ipv6only __tw_common.skc_ipv6only
#define tw_bound_dev_if __tw_common.skc_bound_dev_if
#define tw_node __tw_common.skc_nulls_node
diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h
index fe994d2..ea985aa 100644
--- a/include/net/llc_conn.h
+++ b/include/net/llc_conn.h
@@ -97,6 +97,7 @@
struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority,
struct proto *prot, int kern);
+void llc_sk_stop_all_timers(struct sock *sk, bool sync);
void llc_sk_free(struct sock *sk);
void llc_sk_reset(struct sock *sk);
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 0406073..bc7a1d9 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -332,7 +332,7 @@
struct kernel_param;
-int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
+int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp);
int nf_conntrack_hash_resize(unsigned int hashsize);
extern struct hlist_nulls_head *nf_conntrack_hash;
diff --git a/include/net/nexthop.h b/include/net/nexthop.h
index 3334dbf..7fc7866 100644
--- a/include/net/nexthop.h
+++ b/include/net/nexthop.h
@@ -6,7 +6,7 @@
static inline int rtnh_ok(const struct rtnexthop *rtnh, int remaining)
{
- return remaining >= sizeof(*rtnh) &&
+ return remaining >= (int)sizeof(*rtnh) &&
rtnh->rtnh_len >= sizeof(*rtnh) &&
rtnh->rtnh_len <= remaining;
}
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index f18fc1a..538f3c4 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -675,6 +675,16 @@
*to_free = skb;
}
+static inline void __qdisc_drop_all(struct sk_buff *skb,
+ struct sk_buff **to_free)
+{
+ if (skb->prev)
+ skb->prev->next = *to_free;
+ else
+ skb->next = *to_free;
+ *to_free = skb;
+}
+
static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch,
struct qdisc_skb_head *qh,
struct sk_buff **to_free)
@@ -795,6 +805,15 @@
return NET_XMIT_DROP;
}
+static inline int qdisc_drop_all(struct sk_buff *skb, struct Qdisc *sch,
+ struct sk_buff **to_free)
+{
+ __qdisc_drop_all(skb, to_free);
+ qdisc_qstats_drop(sch);
+
+ return NET_XMIT_DROP;
+}
+
/* Length to Time (L2T) lookup in a qdisc_rate_table, to determine how
long it will take to send a packet given its size.
*/
diff --git a/include/net/slhc_vj.h b/include/net/slhc_vj.h
index 8716d59..8fcf890 100644
--- a/include/net/slhc_vj.h
+++ b/include/net/slhc_vj.h
@@ -127,6 +127,7 @@
*/
struct cstate {
byte_t cs_this; /* connection id number (xmit) */
+ bool initialized; /* true if initialized */
struct cstate *next; /* next in ring (xmit) */
struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
struct tcphdr cs_tcp;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index e7ac114..76d7c97 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1281,9 +1281,11 @@
static inline int tcp_win_from_space(int space)
{
- return sysctl_tcp_adv_win_scale<=0 ?
- (space>>(-sysctl_tcp_adv_win_scale)) :
- space - (space>>sysctl_tcp_adv_win_scale);
+ int tcp_adv_win_scale = sysctl_tcp_adv_win_scale;
+
+ return tcp_adv_win_scale <= 0 ?
+ (space>>(-tcp_adv_win_scale)) :
+ space - (space>>tcp_adv_win_scale);
}
/* Note: caller must be prepared to deal with negative returns */
diff --git a/include/net/udplite.h b/include/net/udplite.h
index 8076193..8228155 100644
--- a/include/net/udplite.h
+++ b/include/net/udplite.h
@@ -62,6 +62,7 @@
UDP_SKB_CB(skb)->cscov = cscov;
if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE;
+ skb->csum_valid = 0;
}
return 0;
diff --git a/include/net/x25.h b/include/net/x25.h
index c383aa4..6d30a01 100644
--- a/include/net/x25.h
+++ b/include/net/x25.h
@@ -298,10 +298,10 @@
/* sysctl_net_x25.c */
#ifdef CONFIG_SYSCTL
-void x25_register_sysctl(void);
+int x25_register_sysctl(void);
void x25_unregister_sysctl(void);
#else
-static inline void x25_register_sysctl(void) {};
+static inline int x25_register_sysctl(void) { return 0; };
static inline void x25_unregister_sysctl(void) {};
#endif /* CONFIG_SYSCTL */
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index 818a38f..f888263 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -129,6 +129,8 @@
const unsigned char *dst_dev_addr);
int rdma_addr_size(struct sockaddr *addr);
+int rdma_addr_size_in6(struct sockaddr_in6 *addr);
+int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr);
int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id);
int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid,
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 7e5430d..08b1b7b 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -662,6 +662,9 @@
/* The controller does not support WRITE SAME */
unsigned no_write_same:1;
+ /* Inline encryption support? */
+ unsigned inlinecrypt_support:1;
+
unsigned use_blk_mq:1;
unsigned use_cmd_list:1;
diff --git a/include/soc/fsl/qe/qe.h b/include/soc/fsl/qe/qe.h
index 70339d7..226f915 100644
--- a/include/soc/fsl/qe/qe.h
+++ b/include/soc/fsl/qe/qe.h
@@ -243,6 +243,7 @@
#define qe_muram_free cpm_muram_free
#define qe_muram_addr cpm_muram_addr
#define qe_muram_offset cpm_muram_offset
+#define qe_muram_dma cpm_muram_dma
#define qe_setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr))
#define qe_clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr))
@@ -667,6 +668,10 @@
#define UCC_FAST_GUMR_CTSS 0x00800000
#define UCC_FAST_GUMR_TXSY 0x00020000
#define UCC_FAST_GUMR_RSYN 0x00010000
+#define UCC_FAST_GUMR_SYNL_MASK 0x0000C000
+#define UCC_FAST_GUMR_SYNL_16 0x0000C000
+#define UCC_FAST_GUMR_SYNL_8 0x00008000
+#define UCC_FAST_GUMR_SYNL_AUTO 0x00004000
#define UCC_FAST_GUMR_RTSM 0x00002000
#define UCC_FAST_GUMR_REVD 0x00000400
#define UCC_FAST_GUMR_ENR 0x00000020
diff --git a/include/soc/qcom/clock-pll.h b/include/soc/qcom/clock-pll.h
index 1865e3c..dd7e186 100644
--- a/include/soc/qcom/clock-pll.h
+++ b/include/soc/qcom/clock-pll.h
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2015, 2017-2018, The Linux Foundation.
+ * All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -174,6 +175,7 @@
extern const struct clk_ops clk_ops_local_pll;
extern const struct clk_ops clk_ops_sr2_pll;
+extern const struct clk_ops clk_ops_acpu_pll;
extern const struct clk_ops clk_ops_variable_rate_pll;
extern const struct clk_ops clk_ops_variable_rate_pll_hwfsm;
diff --git a/include/soc/qcom/pm-legacy.h b/include/soc/qcom/pm-legacy.h
new file mode 100644
index 0000000..0ae5d7b
--- /dev/null
+++ b/include/soc/qcom/pm-legacy.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2016, 2018, The Linux Foundation. All rights reserved.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_PM_H
+#define __ARCH_ARM_MACH_MSM_PM_H
+
+#include <linux/types.h>
+#include <linux/cpuidle.h>
+#include <asm/smp_plat.h>
+#include <asm/barrier.h>
+#include <dt-bindings/msm/pm.h>
+
+#if !defined(CONFIG_SMP)
+#define msm_secondary_startup NULL
+#elif defined(CONFIG_CPU_V7)
+#define msm_secondary_startup secondary_startup
+#else
+#define msm_secondary_startup secondary_holding_pen
+#endif
+
+enum msm_pm_sleep_mode {
+ MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+ MSM_PM_SLEEP_MODE_RETENTION,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+ MSM_PM_SLEEP_MODE_FASTPC,
+ MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
+ MSM_PM_SLEEP_MODE_NR,
+ MSM_PM_SLEEP_MODE_NOT_SELECTED,
+};
+
+enum msm_pm_l2_scm_flag {
+ MSM_SCM_L2_ON = 0,
+ MSM_SCM_L2_OFF = 1,
+ MSM_SCM_L2_GDHS = 3,
+ MSM_SCM_L3_PC_OFF = 4,
+};
+
+#define MSM_PM_MODE(cpu, mode_nr) ((cpu) *MSM_PM_SLEEP_MODE_NR + (mode_nr))
+
+struct msm_pm_time_params {
+ uint32_t latency_us;
+ uint32_t sleep_us;
+ uint32_t next_event_us;
+ uint32_t modified_time_us;
+};
+
+struct msm_pm_sleep_status_data {
+ void __iomem *base_addr;
+ uint32_t mask;
+};
+
+struct latency_level {
+ int affinity_level;
+ int reset_level;
+ const char *level_name;
+};
+
+/**
+ * lpm_cpu_pre_pc_cb(): API to get the L2 flag to pass to TZ
+ *
+ * @cpu: cpuid of the CPU going down.
+ *
+ * Returns the l2 flush flag enum that is passed down to TZ during power
+ * collaps
+ */
+enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu);
+
+/**
+ * msm_pm_sleep_mode_allow() - API to determine if sleep mode is allowed.
+ * @cpu: CPU on which to check for the sleep mode.
+ * @mode: Sleep Mode to check for.
+ * @idle: Idle or Suspend Sleep Mode.
+ *
+ * Helper function to determine if a Idle or Suspend
+ * Sleep mode is allowed for a specific CPU.
+ *
+ * Return: 1 for allowed; 0 if not allowed.
+ */
+int msm_pm_sleep_mode_allow(unsigned int cpu, unsigned int mode, bool idle);
+
+/**
+ * msm_pm_sleep_mode_supported() - API to determine if sleep mode is
+ * supported.
+ * @cpu: CPU on which to check for the sleep mode.
+ * @mode: Sleep Mode to check for.
+ * @idle: Idle or Suspend Sleep Mode.
+ *
+ * Helper function to determine if a Idle or Suspend
+ * Sleep mode is allowed and enabled for a specific CPU.
+ *
+ * Return: 1 for supported; 0 if not supported.
+ */
+int msm_pm_sleep_mode_supported(unsigned int cpu, unsigned int mode, bool idle);
+
+struct msm_pm_cpr_ops {
+ void (*cpr_suspend)(void);
+ void (*cpr_resume)(void);
+};
+
+void __init msm_pm_set_tz_retention_flag(unsigned int flag);
+void msm_pm_enable_retention(bool enable);
+bool msm_pm_retention_enabled(void);
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
+static inline void msm_arch_idle(void)
+{
+ /* memory barrier */
+ mb();
+ wfi();
+}
+
+#ifdef CONFIG_MSM_PM_LEGACY
+
+void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
+int msm_pm_wait_cpu_shutdown(unsigned int cpu);
+int __init msm_pm_sleep_status_init(void);
+void lpm_cpu_hotplug_enter(unsigned int cpu);
+s32 msm_cpuidle_get_deep_idle_latency(void);
+int msm_pm_collapse(unsigned long unused);
+
+/**
+ * lpm_get_latency() - API to get latency for a low power mode
+ * @latency_level: pointer to structure with below elements
+ * affinity_level: The level (CPU/L2/CCI etc.) for which the
+ * latency is required.
+ * LPM_AFF_LVL_CPU : CPU level
+ * LPM_AFF_LVL_L2 : L2 level
+ * LPM_AFF_LVL_CCI : CCI level
+ * reset_level: Can be passed "LPM_RESET_LVL_GDHS" for
+ * low power mode with control logic power collapse or
+ * "LPM_RESET_LVL_PC" for low power mode with control and
+ * memory logic power collapse or "LPM_RESET_LVL_RET" for
+ * retention mode.
+ * level_name: Pointer to the cluster name for which the latency
+ * is required or NULL if the minimum value out of all the
+ * clusters is to be returned. For CPU level, the name of the
+ * L2 cluster to be passed. For CCI it has no effect.
+ * @latency: address to get the latency value.
+ *
+ * latency value will be for the particular cluster or the minimum
+ * value out of all the clusters at the particular affinity_level
+ * and reset_level.
+ *
+ * Return: 0 for success; Error number for failure.
+ */
+int lpm_get_latency(struct latency_level *level, uint32_t *latency);
+
+#else
+static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
+static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
+static inline int msm_pm_sleep_status_init(void) { return 0; };
+
+static inline void lpm_cpu_hotplug_enter(unsigned int cpu)
+{
+ msm_arch_idle();
+};
+
+static inline s32 msm_cpuidle_get_deep_idle_latency(void) { return 0; }
+#define msm_pm_collapse NULL
+
+static inline int lpm_get_latency(struct latency_level *level,
+ uint32_t *latency)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+void qcom_cpu_die_legacy(unsigned int cpu);
+int qcom_cpu_kill_legacy(unsigned int cpu);
+int msm_platform_secondary_init(unsigned int cpu);
+#else
+static inline int msm_platform_secondary_init(unsigned int cpu) { return 0; }
+static inline void qcom_cpu_die_legacy(unsigned int cpu) {}
+static inline int qcom_cpu_kill_legacy(unsigned int cpu) { return 0; }
+#endif
+
+enum msm_pm_time_stats_id {
+ MSM_PM_STAT_REQUESTED_IDLE = 0,
+ MSM_PM_STAT_IDLE_SPIN,
+ MSM_PM_STAT_IDLE_WFI,
+ MSM_PM_STAT_RETENTION,
+ MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_POWER_COLLAPSE,
+ MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
+ MSM_PM_STAT_SUSPEND,
+ MSM_PM_STAT_FAILED_SUSPEND,
+ MSM_PM_STAT_NOT_IDLE,
+ MSM_PM_STAT_COUNT
+};
+
+#ifdef CONFIG_MSM_IDLE_STATS
+void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, int size);
+void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t);
+void msm_pm_l2_add_stat(uint32_t id, int64_t t);
+#else
+static inline void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats,
+ int size) {}
+static inline void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) {}
+static inline void msm_pm_l2_add_stat(uint32_t id, int64_t t) {}
+#endif
+
+void msm_pm_set_cpr_ops(struct msm_pm_cpr_ops *ops);
+extern dma_addr_t msm_pc_debug_counters_phys;
+#endif /* __ARCH_ARM_MACH_MSM_PM_H */
diff --git a/include/soc/qcom/qseecomi.h b/include/soc/qcom/qseecomi.h
index a7d4190..d249168 100644
--- a/include/soc/qcom/qseecomi.h
+++ b/include/soc/qcom/qseecomi.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,7 @@
#define QSEECOM_KEY_ID_SIZE 32
#define QSEOS_RESULT_FAIL_SEND_CMD_NO_THREAD -19 /*0xFFFFFFED*/
+#define QSEOS_RESULT_FAIL_APP_ALREADY_LOADED -38 /*0xFFFFFFDA*/
#define QSEOS_RESULT_FAIL_UNSUPPORTED_CE_PIPE -63
#define QSEOS_RESULT_FAIL_KS_OP -64
#define QSEOS_RESULT_FAIL_KEY_ID_EXISTS -65
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index 5f496a8..d4ebb7d 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -102,16 +102,22 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm845")
#define early_machine_is_sdm670() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm670")
+#define early_machine_is_sxr1130() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sxr1130")
#define early_machine_is_qcs605() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,qcs605")
#define early_machine_is_sda670() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda670")
#define early_machine_is_sdm710() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm670")
+#define early_machine_is_sxr1120() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sxr1120")
#define early_machine_is_msm8953() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8953")
#define early_machine_is_msm8937() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8937")
+#define early_machine_is_msm8917() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8917")
#define early_machine_is_mdm9607() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,mdm9607")
#define early_machine_is_sdm450() \
@@ -169,11 +175,14 @@
#define early_machine_is_sdxpoorwills() 0
#define early_machine_is_sdm845() 0
#define early_machine_is_sdm670() 0
+#define early_machine_is_sxr1130() 0
#define early_machine_is_qcs605() 0
#define early_machine_is_sda670() 0
#define early_machine_is_sdm710() 0
+#define early_machine_is_sxr1120() 0
#define early_machine_is_msm8953() 0
#define early_machine_is_msm8937() 0
+#define early_machine_is_msm8917() 0
#define early_machine_is_sdm450() 0
#define early_machine_is_sdm632() 0
#define early_machine_is_sdm439() 0
@@ -243,14 +252,17 @@
SDX_CPU_SDXPOORWILLS,
MSM_CPU_SDM845,
MSM_CPU_SDM670,
+ MSM_CPU_SXR1130,
MSM_CPU_QCS605,
MSM_CPU_SDA670,
MSM_CPU_SDM710,
+ MSM_CPU_SXR1120,
MSM_CPU_8953,
MSM_CPU_SDM450,
MSM_CPU_SDM632,
MSM_CPU_SDA632,
MSM_CPU_8937,
+ MSM_CPU_8917,
MSM_CPU_9607,
MSM_CPU_SDM439,
MSM_CPU_SDM429,
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
index d0f76d0..a6f38cd 100644
--- a/include/soc/qcom/spm.h
+++ b/include/soc/qcom/spm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -35,6 +35,7 @@
#if defined(CONFIG_MSM_SPM)
int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm);
+void msm_spm_set_rpm_hs(bool allow_rpm_hs);
int msm_spm_probe_done(void);
int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
int msm_spm_get_vdd(unsigned int cpu);
@@ -43,6 +44,8 @@
struct msm_spm_device *msm_spm_get_device_by_name(const char *name);
int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
unsigned int mode, bool notify_rpm);
+int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev,
+ unsigned int mode, bool notify_rpm);
int msm_spm_device_init(void);
bool msm_spm_is_mode_avail(unsigned int mode);
void msm_spm_dump_regs(unsigned int cpu);
@@ -80,6 +83,8 @@
return -ENODEV;
}
+static inline void msm_spm_set_rpm_hs(bool allow_rpm_hs) {}
+
static inline int msm_spm_probe_done(void)
{
return -ENODEV;
@@ -114,6 +119,13 @@
{
return -ENODEV;
}
+
+static inline int msm_spm_config_low_power_mode_addr(
+ struct msm_spm_device *dev, unsigned int mode, bool notify_rpm)
+{
+ return -ENODEV;
+}
+
static inline struct msm_spm_device *msm_spm_get_device_by_name(
const char *name)
{
diff --git a/include/sound/control.h b/include/sound/control.h
index 21d047f..4142757 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -22,6 +22,7 @@
*
*/
+#include <linux/nospec.h>
#include <sound/asound.h>
#define snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data)
@@ -147,12 +148,14 @@
static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
{
- return id->numid - kctl->id.numid;
+ unsigned int ioff = id->numid - kctl->id.numid;
+ return array_index_nospec(ioff, kctl->count);
}
static inline unsigned int snd_ctl_get_ioffidx(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
{
- return id->index - kctl->id.index;
+ unsigned int ioff = id->index - kctl->id.index;
+ return array_index_nospec(ioff, kctl->count);
}
static inline unsigned int snd_ctl_get_ioff(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
diff --git a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h
index 760c969..12bbf8c 100644
--- a/include/sound/pcm_oss.h
+++ b/include/sound/pcm_oss.h
@@ -57,6 +57,7 @@
char *buffer; /* vmallocated period */
size_t buffer_used; /* used length from period buffer */
struct mutex params_lock;
+ atomic_t rw_ref; /* concurrent read/write accesses */
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
struct snd_pcm_plugin *plugin_first;
struct snd_pcm_plugin *plugin_last;
diff --git a/include/trace/events/pdc.h b/include/trace/events/pdc.h
index 400e959..fca0548 100644
--- a/include/trace/events/pdc.h
+++ b/include/trace/events/pdc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -41,7 +41,7 @@
),
TP_printk("%s hwirq:%u pin:%u type:%u enable:%u",
- __entry->func, __entry->pin, __entry->hwirq, __entry->type,
+ __entry->func, __entry->hwirq, __entry->pin, __entry->type,
__entry->enable)
);
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index e06df4d..b355ebf 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -713,10 +713,10 @@
TRACE_EVENT(sched_task_util,
TP_PROTO(struct task_struct *p, int next_cpu, int backup_cpu,
- int target_cpu, bool sync, bool need_idle,
+ int target_cpu, bool sync, bool need_idle, int fastpath,
bool placement_boost, int rtg_cpu, u64 start_t),
- TP_ARGS(p, next_cpu, backup_cpu, target_cpu, sync, need_idle,
+ TP_ARGS(p, next_cpu, backup_cpu, target_cpu, sync, need_idle, fastpath,
placement_boost, rtg_cpu, start_t),
TP_STRUCT__entry(
@@ -729,6 +729,7 @@
__field(int, target_cpu )
__field(bool, sync )
__field(bool, need_idle )
+ __field(int, fastpath )
__field(bool, placement_boost )
__field(int, rtg_cpu )
__field(u64, latency )
@@ -744,13 +745,14 @@
__entry->target_cpu = target_cpu;
__entry->sync = sync;
__entry->need_idle = need_idle;
+ __entry->fastpath = fastpath;
__entry->placement_boost = placement_boost;
__entry->rtg_cpu = rtg_cpu;
__entry->latency = (sched_clock() - start_t);
),
- TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d next_cpu=%d backup_cpu=%d target_cpu=%d sync=%d need_idle=%d placement_boost=%d rtg_cpu=%d latency=%llu",
- __entry->pid, __entry->comm, __entry->util, __entry->prev_cpu, __entry->next_cpu, __entry->backup_cpu, __entry->target_cpu, __entry->sync, __entry->need_idle, __entry->placement_boost, __entry->rtg_cpu, __entry->latency)
+ TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d next_cpu=%d backup_cpu=%d target_cpu=%d sync=%d need_idle=%d fastpath=%d placement_boost=%d rtg_cpu=%d latency=%llu",
+ __entry->pid, __entry->comm, __entry->util, __entry->prev_cpu, __entry->next_cpu, __entry->backup_cpu, __entry->target_cpu, __entry->sync, __entry->need_idle, __entry->fastpath, __entry->placement_boost, __entry->rtg_cpu, __entry->latency)
);
#endif
diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h
index c25da0e..54c1272 100644
--- a/include/trace/events/trace_msm_low_power.h
+++ b/include/trace/events/trace_msm_low_power.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -250,6 +250,25 @@
__entry->sample, __entry->tmr)
);
+TRACE_EVENT(pre_pc_cb,
+
+ TP_PROTO(int tzflag),
+
+ TP_ARGS(tzflag),
+
+ TP_STRUCT__entry(
+ __field(int, tzflag)
+ ),
+
+ TP_fast_assign(
+ __entry->tzflag = tzflag;
+ ),
+
+ TP_printk("tzflag:%d",
+ __entry->tzflag
+ )
+);
+
#endif
#define TRACE_INCLUDE_FILE trace_msm_low_power
#include <trace/define_trace.h>
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 2ccd9cc..7bd8783 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -31,7 +31,7 @@
#define WB_WORK_REASON \
EM( WB_REASON_BACKGROUND, "background") \
- EM( WB_REASON_TRY_TO_FREE_PAGES, "try_to_free_pages") \
+ EM( WB_REASON_VMSCAN, "vmscan") \
EM( WB_REASON_SYNC, "sync") \
EM( WB_REASON_PERIODIC, "periodic") \
EM( WB_REASON_LAPTOP_TIMER, "laptop_timer") \
diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h
index bce990f..d6be935 100644
--- a/include/trace/events/xen.h
+++ b/include/trace/events/xen.h
@@ -377,22 +377,6 @@
DEFINE_XEN_MMU_PGD_EVENT(xen_mmu_pgd_pin);
DEFINE_XEN_MMU_PGD_EVENT(xen_mmu_pgd_unpin);
-TRACE_EVENT(xen_mmu_flush_tlb_all,
- TP_PROTO(int x),
- TP_ARGS(x),
- TP_STRUCT__entry(__array(char, x, 0)),
- TP_fast_assign((void)x),
- TP_printk("%s", "")
- );
-
-TRACE_EVENT(xen_mmu_flush_tlb,
- TP_PROTO(int x),
- TP_ARGS(x),
- TP_STRUCT__entry(__array(char, x, 0)),
- TP_fast_assign((void)x),
- TP_printk("%s", "")
- );
-
TRACE_EVENT(xen_mmu_flush_tlb_single,
TP_PROTO(unsigned long addr),
TP_ARGS(addr),
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index d0341cd..4b26cba 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -180,8 +180,12 @@
*/
struct drm_msm_gem_submit_reloc {
__u32 submit_offset; /* in, offset from submit_bo */
- __u32 or; /* in, value OR'd with result */
- __s32 shift; /* in, amount of left shift (can be negative) */
+#ifdef __cplusplus
+ __u32 or_val;
+#else
+ __u32 or; /* in, value OR'd with result */
+#endif
+ __s32 shift; /* in, amount of left shift (can be negative) */
__u32 reloc_idx; /* in, index of reloc_bo buffer */
__u64 reloc_offset; /* in, offset from start of reloc_bo */
};
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 75aa98ff..5da3634 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -65,6 +65,7 @@
header-y += auxvec.h
header-y += ax25.h
header-y += b1lli.h
+header-y += batterydata-interface.h
header-y += baycom.h
header-y += bcm933xx_hcs.h
header-y += bfs_fs.h
@@ -471,6 +472,8 @@
header-y += v4l2-dv-timings.h
header-y += v4l2-mediabus.h
header-y += v4l2-subdev.h
+header-y += msm_vidc_dec.h
+header-y += msm_vidc_enc.h
header-y += veth.h
header-y += vfio.h
header-y += vhost.h
@@ -490,6 +493,7 @@
header-y += virtio_scsi.h
header-y += virtio_types.h
header-y += virtio_vsock.h
+header-y += vm_bms.h
header-y += vm_sockets.h
header-y += vt.h
header-y += vtpm_proxy.h
diff --git a/include/uapi/linux/batterydata-interface.h b/include/uapi/linux/batterydata-interface.h
new file mode 100644
index 0000000..498f4e0
--- /dev/null
+++ b/include/uapi/linux/batterydata-interface.h
@@ -0,0 +1,30 @@
+#ifndef __BATTERYDATA_LIB_H__
+#define __BATTERYDATA_LIB_H__
+
+#include <linux/ioctl.h>
+
+/**
+ * struct battery_params - Battery profile data to be exchanged.
+ * @soc: SOC (state of charge) of the battery
+ * @ocv_uv: OCV (open circuit voltage) of the battery
+ * @rbatt_sf: RBATT scaling factor
+ * @batt_temp: Battery temperature in deci-degree.
+ * @slope: Slope of the OCV-SOC curve.
+ * @fcc_mah: FCC (full charge capacity) of the battery.
+ */
+struct battery_params {
+ int soc;
+ int ocv_uv;
+ int rbatt_sf;
+ int batt_temp;
+ int slope;
+ int fcc_mah;
+};
+
+/* IOCTLs to query battery profile data */
+#define BPIOCXSOC _IOWR('B', 0x01, struct battery_params) /* SOC */
+#define BPIOCXRBATT _IOWR('B', 0x02, struct battery_params) /* RBATT SF */
+#define BPIOCXSLOPE _IOWR('B', 0x03, struct battery_params) /* SLOPE */
+#define BPIOCXFCC _IOWR('B', 0x04, struct battery_params) /* FCC */
+
+#endif
diff --git a/include/uapi/linux/bgcom_interface.h b/include/uapi/linux/bgcom_interface.h
index f18280a..1ee7b87 100644
--- a/include/uapi/linux/bgcom_interface.h
+++ b/include/uapi/linux/bgcom_interface.h
@@ -19,6 +19,7 @@
#define BGCOM_REG_WRITE 5
#define BGCOM_SOFT_RESET 6
#define BGCOM_MODEM_DOWN2_BG 7
+#define BGCOM_TWM_EXIT 8
#define EXCHANGE_CODE 'V'
struct bg_ui_data {
@@ -57,6 +58,9 @@
#define BG_SOFT_RESET \
_IOWR(EXCHANGE_CODE, BGCOM_SOFT_RESET, \
struct bg_ui_data)
+#define BG_TWM_EXIT \
+ _IOWR(EXCHANGE_CODE, BGCOM_TWM_EXIT, \
+ struct bg_ui_data)
#define BG_MODEM_DOWN2_BG_DONE \
_IOWR(EXCHANGE_CODE, BGCOM_MODEM_DOWN2_BG, \
struct bg_ui_data)
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index beed138..f85ed3a 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -43,6 +43,27 @@
/* (1U << 31) is reserved for signed error codes */
/*
+ * Set/Get write life time hints. {GET,SET}_RW_HINT operate on the
+ * underlying inode, while {GET,SET}_FILE_RW_HINT operate only on
+ * the specific file.
+ */
+#define F_GET_RW_HINT (F_LINUX_SPECIFIC_BASE + 11)
+#define F_SET_RW_HINT (F_LINUX_SPECIFIC_BASE + 12)
+#define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13)
+#define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14)
+
+/*
+ * Valid hint values for F_{GET,SET}_RW_HINT. 0 is "not set", or can be
+ * used to clear any hints previously set.
+ */
+#define RWF_WRITE_LIFE_NOT_SET 0
+#define RWH_WRITE_LIFE_NONE 1
+#define RWH_WRITE_LIFE_SHORT 2
+#define RWH_WRITE_LIFE_MEDIUM 3
+#define RWH_WRITE_LIFE_LONG 4
+#define RWH_WRITE_LIFE_EXTREME 5
+
+/*
* Types of directory notifications that may be requested.
*/
#define DN_ACCESS 0x00000001 /* File accessed */
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 12263e4..fb1ec56 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -226,7 +226,6 @@
#define BLKSECDISCARD _IO(0x12,125)
#define BLKROTATIONAL _IO(0x12,126)
#define BLKZEROOUT _IO(0x12,127)
-#define BLKGETSTPART _IO(0x12, 128)
#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
#define FIBMAP _IO(0x00,1) /* bmap access */
@@ -273,6 +272,7 @@
#define FS_ENCRYPTION_MODE_AES_256_CTS 4
#define FS_ENCRYPTION_MODE_AES_128_CBC 5
#define FS_ENCRYPTION_MODE_AES_128_CTS 6
+#define FS_ENCRYPTION_MODE_PRIVATE 127
struct fscrypt_policy {
__u8 version;
diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h
index 1917c0d..72efc86 100644
--- a/include/uapi/linux/ipa_qmi_service_v01.h
+++ b/include/uapi/linux/ipa_qmi_service_v01.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -54,6 +54,11 @@
#define QMI_IPA_MAX_CLIENT_DST_PIPES_V01 8
#define QMI_IPA_MAX_UL_FIREWALL_RULES_V01 64
+/*
+ * Indicates presence of newly added member to support HW stats.
+ */
+#define IPA_QMI_SUPPORTS_STATS
+
#define IPA_INT_MAX ((int)(~0U>>1))
#define IPA_INT_MIN (-IPA_INT_MAX - 1)
@@ -314,6 +319,38 @@
* table in IPAv3 onwards. Denotes the offset from the start of
* the IPA shared memory.
*/
+
+ /* Optional
+ * Modem HW Stats Quota Base address
+ * Must be set to true if hw_stats_quota_base_addr
+ * is being passed
+ */
+ uint8_t hw_stats_quota_base_addr_valid;
+ uint32_t hw_stats_quota_base_addr;
+
+ /* Optional
+ * Modem HW Stats Quota Size
+ * Must be set to true if hw_stats_quota_size
+ * is being passed
+ */
+ uint8_t hw_stats_quota_size_valid;
+ uint32_t hw_stats_quota_size;
+
+ /* Optional
+ * Modem HW Drop Stats Table Start Address
+ * Must be set to true if hw_drop_stats_base_addr
+ * is being passed
+ */
+ uint8_t hw_drop_stats_base_addr_valid;
+ uint32_t hw_drop_stats_base_addr;
+
+ /* Optional
+ * Modem HW Drop Stats Table size
+ * Must be set to true if hw_drop_stats_table_size
+ * is being passed
+ */
+ uint8_t hw_drop_stats_table_size_valid;
+ uint32_t hw_drop_stats_table_size;
}; /* Message */
/* Response Message; Requests the modem IPA driver about initialization */
@@ -1951,7 +1988,7 @@
#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_V01 0x003A
/* add for max length*/
-#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 134
+#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 162
#define QMI_IPA_INIT_MODEM_DRIVER_RESP_MAX_MSG_LEN_V01 25
#define QMI_IPA_INDICATION_REGISTER_REQ_MAX_MSG_LEN_V01 8
#define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 4ee67cb..05b9bb6 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -870,6 +870,7 @@
#define KVM_CAP_S390_USER_INSTR0 130
#define KVM_CAP_MSI_DEVID 131
#define KVM_CAP_PPC_HTM 132
+#define KVM_CAP_S390_BPB 152
#ifdef KVM_CAP_IRQ_ROUTING
diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h
index 44b8a6b..774cb2db 100644
--- a/include/uapi/linux/libc-compat.h
+++ b/include/uapi/linux/libc-compat.h
@@ -167,46 +167,99 @@
/* If we did not see any headers from any supported C libraries,
* or we are being included in the kernel, then define everything
- * that we need. */
+ * that we need. Check for previous __UAPI_* definitions to give
+ * unsupported C libraries a way to opt out of any kernel definition. */
#else /* !defined(__GLIBC__) */
/* Definitions for if.h */
+#ifndef __UAPI_DEF_IF_IFCONF
#define __UAPI_DEF_IF_IFCONF 1
+#endif
+#ifndef __UAPI_DEF_IF_IFMAP
#define __UAPI_DEF_IF_IFMAP 1
+#endif
+#ifndef __UAPI_DEF_IF_IFNAMSIZ
#define __UAPI_DEF_IF_IFNAMSIZ 1
+#endif
+#ifndef __UAPI_DEF_IF_IFREQ
#define __UAPI_DEF_IF_IFREQ 1
+#endif
/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS
#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1
+#endif
/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
+#endif
/* Definitions for in.h */
+#ifndef __UAPI_DEF_IN_ADDR
#define __UAPI_DEF_IN_ADDR 1
+#endif
+#ifndef __UAPI_DEF_IN_IPPROTO
#define __UAPI_DEF_IN_IPPROTO 1
+#endif
+#ifndef __UAPI_DEF_IN_PKTINFO
#define __UAPI_DEF_IN_PKTINFO 1
+#endif
+#ifndef __UAPI_DEF_IP_MREQ
#define __UAPI_DEF_IP_MREQ 1
+#endif
+#ifndef __UAPI_DEF_SOCKADDR_IN
#define __UAPI_DEF_SOCKADDR_IN 1
+#endif
+#ifndef __UAPI_DEF_IN_CLASS
#define __UAPI_DEF_IN_CLASS 1
+#endif
/* Definitions for in6.h */
+#ifndef __UAPI_DEF_IN6_ADDR
#define __UAPI_DEF_IN6_ADDR 1
+#endif
+#ifndef __UAPI_DEF_IN6_ADDR_ALT
#define __UAPI_DEF_IN6_ADDR_ALT 1
+#endif
+#ifndef __UAPI_DEF_SOCKADDR_IN6
#define __UAPI_DEF_SOCKADDR_IN6 1
+#endif
+#ifndef __UAPI_DEF_IPV6_MREQ
#define __UAPI_DEF_IPV6_MREQ 1
+#endif
+#ifndef __UAPI_DEF_IPPROTO_V6
#define __UAPI_DEF_IPPROTO_V6 1
+#endif
+#ifndef __UAPI_DEF_IPV6_OPTIONS
#define __UAPI_DEF_IPV6_OPTIONS 1
+#endif
+#ifndef __UAPI_DEF_IN6_PKTINFO
#define __UAPI_DEF_IN6_PKTINFO 1
+#endif
+#ifndef __UAPI_DEF_IP6_MTUINFO
#define __UAPI_DEF_IP6_MTUINFO 1
+#endif
/* Definitions for ipx.h */
+#ifndef __UAPI_DEF_SOCKADDR_IPX
#define __UAPI_DEF_SOCKADDR_IPX 1
+#endif
+#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION
#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1
+#endif
+#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION
#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1
+#endif
+#ifndef __UAPI_DEF_IPX_CONFIG_DATA
#define __UAPI_DEF_IPX_CONFIG_DATA 1
+#endif
+#ifndef __UAPI_DEF_IPX_ROUTE_DEF
#define __UAPI_DEF_IPX_ROUTE_DEF 1
+#endif
/* Definitions for xattr.h */
+#ifndef __UAPI_DEF_XATTR
#define __UAPI_DEF_XATTR 1
+#endif
#endif /* __GLIBC__ */
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 062a1b9..be4cb02 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -97,6 +97,9 @@
#define IPA_IOCTL_DEL_IPV6CT_TABLE 55
#define IPA_IOCTL_CLEANUP 56
#define IPA_IOCTL_QUERY_WLAN_CLIENT 57
+#define IPA_IOCTL_GET_VLAN_MODE 58
+#define IPA_IOCTL_ADD_BRIDGE_VLAN_MAPPING 59
+#define IPA_IOCTL_DEL_BRIDGE_VLAN_MAPPING 60
/**
* max size of the header to be inserted
@@ -511,7 +514,13 @@
IPA_PER_CLIENT_STATS_EVENT_MAX
};
-#define IPA_EVENT_MAX_NUM (IPA_PER_CLIENT_STATS_EVENT_MAX)
+enum ipa_vlan_bridge_event {
+ ADD_BRIDGE_VLAN_MAPPING = IPA_PER_CLIENT_STATS_EVENT_MAX,
+ DEL_BRIDGE_VLAN_MAPPING,
+ BRIDGE_VLAN_MAPPING_MAX
+};
+
+#define IPA_EVENT_MAX_NUM (BRIDGE_VLAN_MAPPING_MAX)
#define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM)
/**
@@ -1848,6 +1857,43 @@
};
/**
+ * enum ipa_vlan_ifaces - vlan interfaces types
+ */
+enum ipa_vlan_ifaces {
+ IPA_VLAN_IF_ETH,
+ IPA_VLAN_IF_RNDIS,
+ IPA_VLAN_IF_ECM
+};
+
+#define IPA_VLAN_IF_EMAC IPA_VLAN_IF_ETH
+#define IPA_VLAN_IF_MAX (IPA_VLAN_IF_ECM + 1)
+
+/**
+ * struct ipa_get_vlan_mode - get vlan mode of a Lan interface
+ * @iface: Lan interface type to be queried.
+ * @is_vlan_mode: output parameter, is interface in vlan mode, valid only when
+ * ioctl return val is non-negative
+ */
+struct ipa_ioc_get_vlan_mode {
+ enum ipa_vlan_ifaces iface;
+ uint32_t is_vlan_mode;
+};
+
+/**
+ * struct ipa_ioc_bridge_vlan_mapping_info - vlan to bridge mapping info
+ * @bridge_name: bridge interface name
+ * @vlan_id: vlan ID bridge is mapped to
+ * @bridge_ipv4: bridge interface ipv4 address
+ * @subnet_mask: bridge interface subnet mask
+ */
+struct ipa_ioc_bridge_vlan_mapping_info {
+ char bridge_name[IPA_RESOURCE_NAME_MAX];
+ uint16_t vlan_id;
+ uint32_t bridge_ipv4;
+ uint32_t subnet_mask;
+};
+
+/**
* actual IOCTLs supported by IPA driver
*/
#define IPA_IOC_ADD_HDR _IOWR(IPA_IOC_MAGIC, \
@@ -2029,6 +2075,18 @@
IPA_IOCTL_CLEANUP)
#define IPA_IOC_QUERY_WLAN_CLIENT _IO(IPA_IOC_MAGIC,\
IPA_IOCTL_QUERY_WLAN_CLIENT)
+#define IPA_IOC_GET_VLAN_MODE _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_GET_VLAN_MODE, \
+ struct ipa_ioc_get_vlan_mode *)
+
+#define IPA_IOC_ADD_BRIDGE_VLAN_MAPPING _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_ADD_BRIDGE_VLAN_MAPPING, \
+ struct ipa_ioc_bridge_vlan_mapping_info)
+
+#define IPA_IOC_DEL_BRIDGE_VLAN_MAPPING _IOWR(IPA_IOC_MAGIC, \
+ IPA_IOCTL_DEL_BRIDGE_VLAN_MAPPING, \
+ struct ipa_ioc_bridge_vlan_mapping_info)
+
/*
* unique magic number of the Tethering bridge ioctls
*/
diff --git a/include/uapi/linux/msm_vidc_dec.h b/include/uapi/linux/msm_vidc_dec.h
new file mode 100644
index 0000000..46af82b
--- /dev/null
+++ b/include/uapi/linux/msm_vidc_dec.h
@@ -0,0 +1,629 @@
+#ifndef _UAPI_MSM_VIDC_DEC_H_
+#define _UAPI_MSM_VIDC_DEC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* STATUS CODES */
+/* Base value for status codes */
+#define VDEC_S_BASE 0x40000000
+/* Success */
+#define VDEC_S_SUCCESS (VDEC_S_BASE)
+/* General failure */
+#define VDEC_S_EFAIL (VDEC_S_BASE + 1)
+/* Fatal irrecoverable failure. Need to tear down session. */
+#define VDEC_S_EFATAL (VDEC_S_BASE + 2)
+/* Error detected in the passed parameters */
+#define VDEC_S_EBADPARAM (VDEC_S_BASE + 3)
+/* Command called in invalid state. */
+#define VDEC_S_EINVALSTATE (VDEC_S_BASE + 4)
+ /* Insufficient OS resources - thread, memory etc. */
+#define VDEC_S_ENOSWRES (VDEC_S_BASE + 5)
+ /* Insufficient HW resources - core capacity maxed out. */
+#define VDEC_S_ENOHWRES (VDEC_S_BASE + 6)
+/* Invalid command called */
+#define VDEC_S_EINVALCMD (VDEC_S_BASE + 7)
+/* Command timeout. */
+#define VDEC_S_ETIMEOUT (VDEC_S_BASE + 8)
+/* Pre-requirement is not met for API. */
+#define VDEC_S_ENOPREREQ (VDEC_S_BASE + 9)
+/* Command queue is full. */
+#define VDEC_S_ECMDQFULL (VDEC_S_BASE + 10)
+/* Command is not supported by this driver */
+#define VDEC_S_ENOTSUPP (VDEC_S_BASE + 11)
+/* Command is not implemented by thedriver. */
+#define VDEC_S_ENOTIMPL (VDEC_S_BASE + 12)
+/* Command is not implemented by the driver. */
+#define VDEC_S_BUSY (VDEC_S_BASE + 13)
+#define VDEC_S_INPUT_BITSTREAM_ERR (VDEC_S_BASE + 14)
+
+#define VDEC_INTF_VER 1
+#define VDEC_MSG_BASE 0x0000000
+/*
+ *Codes to identify asynchronous message responses and events that driver
+ *wants to communicate to the app.
+ */
+#define VDEC_MSG_INVALID (VDEC_MSG_BASE + 0)
+#define VDEC_MSG_RESP_INPUT_BUFFER_DONE (VDEC_MSG_BASE + 1)
+#define VDEC_MSG_RESP_OUTPUT_BUFFER_DONE (VDEC_MSG_BASE + 2)
+#define VDEC_MSG_RESP_INPUT_FLUSHED (VDEC_MSG_BASE + 3)
+#define VDEC_MSG_RESP_OUTPUT_FLUSHED (VDEC_MSG_BASE + 4)
+#define VDEC_MSG_RESP_FLUSH_INPUT_DONE (VDEC_MSG_BASE + 5)
+#define VDEC_MSG_RESP_FLUSH_OUTPUT_DONE (VDEC_MSG_BASE + 6)
+#define VDEC_MSG_RESP_START_DONE (VDEC_MSG_BASE + 7)
+#define VDEC_MSG_RESP_STOP_DONE (VDEC_MSG_BASE + 8)
+#define VDEC_MSG_RESP_PAUSE_DONE (VDEC_MSG_BASE + 9)
+#define VDEC_MSG_RESP_RESUME_DONE (VDEC_MSG_BASE + 10)
+#define VDEC_MSG_RESP_RESOURCE_LOADED (VDEC_MSG_BASE + 11)
+#define VDEC_EVT_RESOURCES_LOST (VDEC_MSG_BASE + 12)
+#define VDEC_MSG_EVT_CONFIG_CHANGED (VDEC_MSG_BASE + 13)
+#define VDEC_MSG_EVT_HW_ERROR (VDEC_MSG_BASE + 14)
+#define VDEC_MSG_EVT_INFO_CONFIG_CHANGED (VDEC_MSG_BASE + 15)
+#define VDEC_MSG_EVT_INFO_FIELD_DROPPED (VDEC_MSG_BASE + 16)
+#define VDEC_MSG_EVT_HW_OVERLOAD (VDEC_MSG_BASE + 17)
+#define VDEC_MSG_EVT_MAX_CLIENTS (VDEC_MSG_BASE + 18)
+#define VDEC_MSG_EVT_HW_UNSUPPORTED (VDEC_MSG_BASE + 19)
+
+/*Buffer flags bits masks.*/
+#define VDEC_BUFFERFLAG_EOS 0x00000001
+#define VDEC_BUFFERFLAG_DECODEONLY 0x00000004
+#define VDEC_BUFFERFLAG_DATACORRUPT 0x00000008
+#define VDEC_BUFFERFLAG_ENDOFFRAME 0x00000010
+#define VDEC_BUFFERFLAG_SYNCFRAME 0x00000020
+#define VDEC_BUFFERFLAG_EXTRADATA 0x00000040
+#define VDEC_BUFFERFLAG_CODECCONFIG 0x00000080
+
+/*Post processing flags bit masks*/
+#define VDEC_EXTRADATA_NONE 0x001
+#define VDEC_EXTRADATA_QP 0x004
+#define VDEC_EXTRADATA_MB_ERROR_MAP 0x008
+#define VDEC_EXTRADATA_SEI 0x010
+#define VDEC_EXTRADATA_VUI 0x020
+#define VDEC_EXTRADATA_VC1 0x040
+
+#define VDEC_EXTRADATA_EXT_DATA 0x0800
+#define VDEC_EXTRADATA_USER_DATA 0x1000
+#define VDEC_EXTRADATA_EXT_BUFFER 0x2000
+
+#define VDEC_CMDBASE 0x800
+#define VDEC_CMD_SET_INTF_VERSION (VDEC_CMDBASE)
+
+#define VDEC_IOCTL_MAGIC 'v'
+
+struct vdec_ioctl_msg {
+ void __user *in;
+ void __user *out;
+};
+
+/*
+ * CMD params: InputParam:enum vdec_codec
+ * OutputParam: struct vdec_profile_level
+ */
+#define VDEC_IOCTL_GET_PROFILE_LEVEL_SUPPORTED \
+ _IOWR(VDEC_IOCTL_MAGIC, 0, struct vdec_ioctl_msg)
+
+/*
+ * CMD params:InputParam: NULL
+ * OutputParam: uint32_t(bitmask)
+ */
+#define VDEC_IOCTL_GET_INTERLACE_FORMAT \
+ _IOR(VDEC_IOCTL_MAGIC, 1, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: InputParam: enum vdec_codec
+ * OutputParam: struct vdec_profile_level
+ */
+#define VDEC_IOCTL_GET_CURRENT_PROFILE_LEVEL \
+ _IOWR(VDEC_IOCTL_MAGIC, 2, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam: enum vdec_output_fromat OutputParam: NULL
+ * GET: InputParam: NULL OutputParam: enum vdec_output_fromat
+ */
+#define VDEC_IOCTL_SET_OUTPUT_FORMAT \
+ _IOWR(VDEC_IOCTL_MAGIC, 3, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_OUTPUT_FORMAT \
+ _IOWR(VDEC_IOCTL_MAGIC, 4, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam: enum vdec_codec OutputParam: NULL
+ * GET: InputParam: NULL OutputParam: enum vdec_codec
+ */
+#define VDEC_IOCTL_SET_CODEC \
+ _IOW(VDEC_IOCTL_MAGIC, 5, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_CODEC \
+ _IOR(VDEC_IOCTL_MAGIC, 6, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam: struct vdec_picsize outputparam: NULL
+ * GET: InputParam: NULL outputparam: struct vdec_picsize
+ */
+#define VDEC_IOCTL_SET_PICRES \
+ _IOW(VDEC_IOCTL_MAGIC, 7, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_PICRES \
+ _IOR(VDEC_IOCTL_MAGIC, 8, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_EXTRADATA \
+ _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_EXTRADATA \
+ _IOR(VDEC_IOCTL_MAGIC, 10, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_SEQUENCE_HEADER \
+ _IOW(VDEC_IOCTL_MAGIC, 11, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam - vdec_allocatorproperty, OutputParam - NULL
+ * GET: InputParam - NULL, OutputParam - vdec_allocatorproperty
+ */
+#define VDEC_IOCTL_SET_BUFFER_REQ \
+ _IOW(VDEC_IOCTL_MAGIC, 12, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_BUFFER_REQ \
+ _IOR(VDEC_IOCTL_MAGIC, 13, struct vdec_ioctl_msg)
+/* CMD params: InputParam - vdec_buffer, OutputParam - uint8_t** */
+#define VDEC_IOCTL_ALLOCATE_BUFFER \
+ _IOWR(VDEC_IOCTL_MAGIC, 14, struct vdec_ioctl_msg)
+/* CMD params: InputParam - uint8_t *, OutputParam - NULL.*/
+#define VDEC_IOCTL_FREE_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 15, struct vdec_ioctl_msg)
+
+/*CMD params: CMD: InputParam - struct vdec_setbuffer_cmd, OutputParam - NULL*/
+#define VDEC_IOCTL_SET_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 16, struct vdec_ioctl_msg)
+
+/* CMD params: InputParam - struct vdec_fillbuffer_cmd, OutputParam - NULL*/
+#define VDEC_IOCTL_FILL_OUTPUT_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 17, struct vdec_ioctl_msg)
+
+/*CMD params: InputParam - struct vdec_frameinfo , OutputParam - NULL*/
+#define VDEC_IOCTL_DECODE_FRAME \
+ _IOW(VDEC_IOCTL_MAGIC, 18, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_LOAD_RESOURCES _IO(VDEC_IOCTL_MAGIC, 19)
+#define VDEC_IOCTL_CMD_START _IO(VDEC_IOCTL_MAGIC, 20)
+#define VDEC_IOCTL_CMD_STOP _IO(VDEC_IOCTL_MAGIC, 21)
+#define VDEC_IOCTL_CMD_PAUSE _IO(VDEC_IOCTL_MAGIC, 22)
+#define VDEC_IOCTL_CMD_RESUME _IO(VDEC_IOCTL_MAGIC, 23)
+
+/*CMD params: InputParam - enum vdec_bufferflush , OutputParam - NULL */
+#define VDEC_IOCTL_CMD_FLUSH _IOW(VDEC_IOCTL_MAGIC, 24, struct vdec_ioctl_msg)
+
+/* ========================================================
+ * IOCTL for getting asynchronous notification from driver
+ * ========================================================
+ */
+
+/*IOCTL params: InputParam - NULL, OutputParam - struct vdec_msginfo*/
+#define VDEC_IOCTL_GET_NEXT_MSG \
+ _IOR(VDEC_IOCTL_MAGIC, 25, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_STOP_NEXT_MSG _IO(VDEC_IOCTL_MAGIC, 26)
+
+#define VDEC_IOCTL_GET_NUMBER_INSTANCES \
+ _IOR(VDEC_IOCTL_MAGIC, 27, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_PICTURE_ORDER \
+ _IOW(VDEC_IOCTL_MAGIC, 28, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_FRAME_RATE \
+ _IOW(VDEC_IOCTL_MAGIC, 29, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_H264_MV_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 30, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_FREE_H264_MV_BUFFER \
+ _IOW(VDEC_IOCTL_MAGIC, 31, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_MV_BUFFER_SIZE \
+ _IOR(VDEC_IOCTL_MAGIC, 32, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_IDR_ONLY_DECODING \
+ _IO(VDEC_IOCTL_MAGIC, 33)
+
+#define VDEC_IOCTL_SET_CONT_ON_RECONFIG \
+ _IO(VDEC_IOCTL_MAGIC, 34)
+
+#define VDEC_IOCTL_SET_DISABLE_DMX \
+ _IOW(VDEC_IOCTL_MAGIC, 35, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_DISABLE_DMX \
+ _IOR(VDEC_IOCTL_MAGIC, 36, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT \
+ _IOR(VDEC_IOCTL_MAGIC, 37, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_PERF_CLK \
+ _IOR(VDEC_IOCTL_MAGIC, 38, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_META_BUFFERS \
+ _IOW(VDEC_IOCTL_MAGIC, 39, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_FREE_META_BUFFERS \
+ _IO(VDEC_IOCTL_MAGIC, 40)
+
+enum vdec_picture {
+ PICTURE_TYPE_I,
+ PICTURE_TYPE_P,
+ PICTURE_TYPE_B,
+ PICTURE_TYPE_BI,
+ PICTURE_TYPE_SKIP,
+ PICTURE_TYPE_IDR,
+ PICTURE_TYPE_UNKNOWN
+};
+
+enum vdec_buffer {
+ VDEC_BUFFER_TYPE_INPUT,
+ VDEC_BUFFER_TYPE_OUTPUT
+};
+
+struct vdec_allocatorproperty {
+ enum vdec_buffer buffer_type;
+ uint32_t mincount;
+ uint32_t maxcount;
+ uint32_t actualcount;
+ size_t buffer_size;
+ uint32_t alignment;
+ uint32_t buf_poolid;
+ size_t meta_buffer_size;
+};
+
+struct vdec_bufferpayload {
+ void __user *bufferaddr;
+ size_t buffer_len;
+ int pmem_fd;
+ size_t offset;
+ size_t mmaped_size;
+};
+
+struct vdec_setbuffer_cmd {
+ enum vdec_buffer buffer_type;
+ struct vdec_bufferpayload buffer;
+};
+
+struct vdec_fillbuffer_cmd {
+ struct vdec_bufferpayload buffer;
+ void *client_data;
+};
+
+enum vdec_bufferflush {
+ VDEC_FLUSH_TYPE_INPUT,
+ VDEC_FLUSH_TYPE_OUTPUT,
+ VDEC_FLUSH_TYPE_ALL
+};
+
+enum vdec_codec {
+ VDEC_CODECTYPE_H264 = 0x1,
+ VDEC_CODECTYPE_H263 = 0x2,
+ VDEC_CODECTYPE_MPEG4 = 0x3,
+ VDEC_CODECTYPE_DIVX_3 = 0x4,
+ VDEC_CODECTYPE_DIVX_4 = 0x5,
+ VDEC_CODECTYPE_DIVX_5 = 0x6,
+ VDEC_CODECTYPE_DIVX_6 = 0x7,
+ VDEC_CODECTYPE_XVID = 0x8,
+ VDEC_CODECTYPE_MPEG1 = 0x9,
+ VDEC_CODECTYPE_MPEG2 = 0xa,
+ VDEC_CODECTYPE_VC1 = 0xb,
+ VDEC_CODECTYPE_VC1_RCV = 0xc,
+ VDEC_CODECTYPE_HEVC = 0xd,
+ VDEC_CODECTYPE_MVC = 0xe,
+ VDEC_CODECTYPE_VP8 = 0xf,
+ VDEC_CODECTYPE_VP9 = 0x10,
+};
+
+enum vdec_mpeg2_profile {
+ VDEC_MPEG2ProfileSimple = 0x1,
+ VDEC_MPEG2ProfileMain = 0x2,
+ VDEC_MPEG2Profile422 = 0x4,
+ VDEC_MPEG2ProfileSNR = 0x8,
+ VDEC_MPEG2ProfileSpatial = 0x10,
+ VDEC_MPEG2ProfileHigh = 0x20,
+ VDEC_MPEG2ProfileKhronosExtensions = 0x6F000000,
+ VDEC_MPEG2ProfileVendorStartUnused = 0x7F000000,
+ VDEC_MPEG2ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg2_level {
+
+ VDEC_MPEG2LevelLL = 0x1,
+ VDEC_MPEG2LevelML = 0x2,
+ VDEC_MPEG2LevelH14 = 0x4,
+ VDEC_MPEG2LevelHL = 0x8,
+ VDEC_MPEG2LevelKhronosExtensions = 0x6F000000,
+ VDEC_MPEG2LevelVendorStartUnused = 0x7F000000,
+ VDEC_MPEG2LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg4_profile {
+ VDEC_MPEG4ProfileSimple = 0x01,
+ VDEC_MPEG4ProfileSimpleScalable = 0x02,
+ VDEC_MPEG4ProfileCore = 0x04,
+ VDEC_MPEG4ProfileMain = 0x08,
+ VDEC_MPEG4ProfileNbit = 0x10,
+ VDEC_MPEG4ProfileScalableTexture = 0x20,
+ VDEC_MPEG4ProfileSimpleFace = 0x40,
+ VDEC_MPEG4ProfileSimpleFBA = 0x80,
+ VDEC_MPEG4ProfileBasicAnimated = 0x100,
+ VDEC_MPEG4ProfileHybrid = 0x200,
+ VDEC_MPEG4ProfileAdvancedRealTime = 0x400,
+ VDEC_MPEG4ProfileCoreScalable = 0x800,
+ VDEC_MPEG4ProfileAdvancedCoding = 0x1000,
+ VDEC_MPEG4ProfileAdvancedCore = 0x2000,
+ VDEC_MPEG4ProfileAdvancedScalable = 0x4000,
+ VDEC_MPEG4ProfileAdvancedSimple = 0x8000,
+ VDEC_MPEG4ProfileKhronosExtensions = 0x6F000000,
+ VDEC_MPEG4ProfileVendorStartUnused = 0x7F000000,
+ VDEC_MPEG4ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg4_level {
+ VDEC_MPEG4Level0 = 0x01,
+ VDEC_MPEG4Level0b = 0x02,
+ VDEC_MPEG4Level1 = 0x04,
+ VDEC_MPEG4Level2 = 0x08,
+ VDEC_MPEG4Level3 = 0x10,
+ VDEC_MPEG4Level4 = 0x20,
+ VDEC_MPEG4Level4a = 0x40,
+ VDEC_MPEG4Level5 = 0x80,
+ VDEC_MPEG4LevelKhronosExtensions = 0x6F000000,
+ VDEC_MPEG4LevelVendorStartUnused = 0x7F000000,
+ VDEC_MPEG4LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_avc_profile {
+ VDEC_AVCProfileBaseline = 0x01,
+ VDEC_AVCProfileMain = 0x02,
+ VDEC_AVCProfileExtended = 0x04,
+ VDEC_AVCProfileHigh = 0x08,
+ VDEC_AVCProfileHigh10 = 0x10,
+ VDEC_AVCProfileHigh422 = 0x20,
+ VDEC_AVCProfileHigh444 = 0x40,
+ VDEC_AVCProfileKhronosExtensions = 0x6F000000,
+ VDEC_AVCProfileVendorStartUnused = 0x7F000000,
+ VDEC_AVCProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_avc_level {
+ VDEC_AVCLevel1 = 0x01,
+ VDEC_AVCLevel1b = 0x02,
+ VDEC_AVCLevel11 = 0x04,
+ VDEC_AVCLevel12 = 0x08,
+ VDEC_AVCLevel13 = 0x10,
+ VDEC_AVCLevel2 = 0x20,
+ VDEC_AVCLevel21 = 0x40,
+ VDEC_AVCLevel22 = 0x80,
+ VDEC_AVCLevel3 = 0x100,
+ VDEC_AVCLevel31 = 0x200,
+ VDEC_AVCLevel32 = 0x400,
+ VDEC_AVCLevel4 = 0x800,
+ VDEC_AVCLevel41 = 0x1000,
+ VDEC_AVCLevel42 = 0x2000,
+ VDEC_AVCLevel5 = 0x4000,
+ VDEC_AVCLevel51 = 0x8000,
+ VDEC_AVCLevelKhronosExtensions = 0x6F000000,
+ VDEC_AVCLevelVendorStartUnused = 0x7F000000,
+ VDEC_AVCLevelMax = 0x7FFFFFFF
+};
+
+enum vdec_divx_profile {
+ VDEC_DIVXProfile_qMobile = 0x01,
+ VDEC_DIVXProfile_Mobile = 0x02,
+ VDEC_DIVXProfile_HD = 0x04,
+ VDEC_DIVXProfile_Handheld = 0x08,
+ VDEC_DIVXProfile_Portable = 0x10,
+ VDEC_DIVXProfile_HomeTheater = 0x20
+};
+
+enum vdec_xvid_profile {
+ VDEC_XVIDProfile_Simple = 0x1,
+ VDEC_XVIDProfile_Advanced_Realtime_Simple = 0x2,
+ VDEC_XVIDProfile_Advanced_Simple = 0x4
+};
+
+enum vdec_xvid_level {
+ VDEC_XVID_LEVEL_S_L0 = 0x1,
+ VDEC_XVID_LEVEL_S_L1 = 0x2,
+ VDEC_XVID_LEVEL_S_L2 = 0x4,
+ VDEC_XVID_LEVEL_S_L3 = 0x8,
+ VDEC_XVID_LEVEL_ARTS_L1 = 0x10,
+ VDEC_XVID_LEVEL_ARTS_L2 = 0x20,
+ VDEC_XVID_LEVEL_ARTS_L3 = 0x40,
+ VDEC_XVID_LEVEL_ARTS_L4 = 0x80,
+ VDEC_XVID_LEVEL_AS_L0 = 0x100,
+ VDEC_XVID_LEVEL_AS_L1 = 0x200,
+ VDEC_XVID_LEVEL_AS_L2 = 0x400,
+ VDEC_XVID_LEVEL_AS_L3 = 0x800,
+ VDEC_XVID_LEVEL_AS_L4 = 0x1000
+};
+
+enum vdec_h263profile {
+ VDEC_H263ProfileBaseline = 0x01,
+ VDEC_H263ProfileH320Coding = 0x02,
+ VDEC_H263ProfileBackwardCompatible = 0x04,
+ VDEC_H263ProfileISWV2 = 0x08,
+ VDEC_H263ProfileISWV3 = 0x10,
+ VDEC_H263ProfileHighCompression = 0x20,
+ VDEC_H263ProfileInternet = 0x40,
+ VDEC_H263ProfileInterlace = 0x80,
+ VDEC_H263ProfileHighLatency = 0x100,
+ VDEC_H263ProfileKhronosExtensions = 0x6F000000,
+ VDEC_H263ProfileVendorStartUnused = 0x7F000000,
+ VDEC_H263ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_h263level {
+ VDEC_H263Level10 = 0x01,
+ VDEC_H263Level20 = 0x02,
+ VDEC_H263Level30 = 0x04,
+ VDEC_H263Level40 = 0x08,
+ VDEC_H263Level45 = 0x10,
+ VDEC_H263Level50 = 0x20,
+ VDEC_H263Level60 = 0x40,
+ VDEC_H263Level70 = 0x80,
+ VDEC_H263LevelKhronosExtensions = 0x6F000000,
+ VDEC_H263LevelVendorStartUnused = 0x7F000000,
+ VDEC_H263LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_wmv_format {
+ VDEC_WMVFormatUnused = 0x01,
+ VDEC_WMVFormat7 = 0x02,
+ VDEC_WMVFormat8 = 0x04,
+ VDEC_WMVFormat9 = 0x08,
+ VDEC_WMFFormatKhronosExtensions = 0x6F000000,
+ VDEC_WMFFormatVendorStartUnused = 0x7F000000,
+ VDEC_WMVFormatMax = 0x7FFFFFFF
+};
+
+enum vdec_vc1_profile {
+ VDEC_VC1ProfileSimple = 0x1,
+ VDEC_VC1ProfileMain = 0x2,
+ VDEC_VC1ProfileAdvanced = 0x4
+};
+
+enum vdec_vc1_level {
+ VDEC_VC1_LEVEL_S_Low = 0x1,
+ VDEC_VC1_LEVEL_S_Medium = 0x2,
+ VDEC_VC1_LEVEL_M_Low = 0x4,
+ VDEC_VC1_LEVEL_M_Medium = 0x8,
+ VDEC_VC1_LEVEL_M_High = 0x10,
+ VDEC_VC1_LEVEL_A_L0 = 0x20,
+ VDEC_VC1_LEVEL_A_L1 = 0x40,
+ VDEC_VC1_LEVEL_A_L2 = 0x80,
+ VDEC_VC1_LEVEL_A_L3 = 0x100,
+ VDEC_VC1_LEVEL_A_L4 = 0x200
+};
+
+struct vdec_profile_level {
+ uint32_t profiles;
+ uint32_t levels;
+};
+
+enum vdec_interlaced_format {
+ VDEC_InterlaceFrameProgressive = 0x1,
+ VDEC_InterlaceInterleaveFrameTopFieldFirst = 0x2,
+ VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4
+};
+
+#define VDEC_YUV_FORMAT_NV12_TP10_UBWC \
+ VDEC_YUV_FORMAT_NV12_TP10_UBWC
+
+enum vdec_output_fromat {
+ VDEC_YUV_FORMAT_NV12 = 0x1,
+ VDEC_YUV_FORMAT_TILE_4x2 = 0x2,
+ VDEC_YUV_FORMAT_NV12_UBWC = 0x3,
+ VDEC_YUV_FORMAT_NV12_TP10_UBWC = 0x4
+};
+
+enum vdec_output_order {
+ VDEC_ORDER_DISPLAY = 0x1,
+ VDEC_ORDER_DECODE = 0x2
+};
+
+struct vdec_picsize {
+ uint32_t frame_width;
+ uint32_t frame_height;
+ uint32_t stride;
+ uint32_t scan_lines;
+};
+
+struct vdec_seqheader {
+ void __user *ptr_seqheader;
+ size_t seq_header_len;
+ int pmem_fd;
+ size_t pmem_offset;
+};
+
+struct vdec_mberror {
+ void __user *ptr_errormap;
+ size_t err_mapsize;
+};
+
+struct vdec_input_frameinfo {
+ void __user *bufferaddr;
+ size_t offset;
+ size_t datalen;
+ uint32_t flags;
+ int64_t timestamp;
+ void *client_data;
+ int pmem_fd;
+ size_t pmem_offset;
+ void __user *desc_addr;
+ uint32_t desc_size;
+};
+
+struct vdec_framesize {
+ uint32_t left;
+ uint32_t top;
+ uint32_t right;
+ uint32_t bottom;
+};
+
+struct vdec_aspectratioinfo {
+ uint32_t aspect_ratio;
+ uint32_t par_width;
+ uint32_t par_height;
+};
+
+struct vdec_sep_metadatainfo {
+ void __user *metabufaddr;
+ uint32_t size;
+ int fd;
+ int offset;
+ uint32_t buffer_size;
+};
+
+struct vdec_output_frameinfo {
+ void __user *bufferaddr;
+ size_t offset;
+ size_t len;
+ uint32_t flags;
+ int64_t time_stamp;
+ enum vdec_picture pic_type;
+ void *client_data;
+ void *input_frame_clientdata;
+ struct vdec_picsize picsize;
+ struct vdec_framesize framesize;
+ enum vdec_interlaced_format interlaced_format;
+ struct vdec_aspectratioinfo aspect_ratio_info;
+ struct vdec_sep_metadatainfo metadata_info;
+};
+
+union vdec_msgdata {
+ struct vdec_output_frameinfo output_frame;
+ void *input_frame_clientdata;
+};
+
+struct vdec_msginfo {
+ uint32_t status_code;
+ uint32_t msgcode;
+ union vdec_msgdata msgdata;
+ size_t msgdatasize;
+};
+
+struct vdec_framerate {
+ unsigned long fps_denominator;
+ unsigned long fps_numerator;
+};
+
+struct vdec_h264_mv {
+ size_t size;
+ int count;
+ int pmem_fd;
+ int offset;
+};
+
+struct vdec_mv_buff_size {
+ int width;
+ int height;
+ int size;
+ int alignment;
+};
+
+struct vdec_meta_buffers {
+ size_t size;
+ int count;
+ int pmem_fd;
+ int pmem_fd_iommu;
+ int offset;
+};
+
+#endif /* end of macro _VDECDECODER_H_ */
diff --git a/include/uapi/linux/msm_vidc_enc.h b/include/uapi/linux/msm_vidc_enc.h
new file mode 100644
index 0000000..f4f1630
--- /dev/null
+++ b/include/uapi/linux/msm_vidc_enc.h
@@ -0,0 +1,752 @@
+#ifndef _UAPI_MSM_VIDC_ENC_H_
+#define _UAPI_MSM_VIDC_ENC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/** STATUS CODES*/
+/* Base value for status codes */
+#define VEN_S_BASE 0x00000000
+#define VEN_S_SUCCESS (VEN_S_BASE)/* Success */
+#define VEN_S_EFAIL (VEN_S_BASE+1)/* General failure */
+#define VEN_S_EFATAL (VEN_S_BASE+2)/* Fatal irrecoverable failure*/
+#define VEN_S_EBADPARAM (VEN_S_BASE+3)/* Error passed parameters*/
+/*Command called in invalid state*/
+#define VEN_S_EINVALSTATE (VEN_S_BASE+4)
+#define VEN_S_ENOSWRES (VEN_S_BASE+5)/* Insufficient OS resources*/
+#define VEN_S_ENOHWRES (VEN_S_BASE+6)/*Insufficient HW resources */
+#define VEN_S_EBUFFREQ (VEN_S_BASE+7)/* Buffer requirements were not met*/
+#define VEN_S_EINVALCMD (VEN_S_BASE+8)/* Invalid command called */
+#define VEN_S_ETIMEOUT (VEN_S_BASE+9)/* Command timeout. */
+/*Re-attempt was made when multiple invocation not supported for API.*/
+#define VEN_S_ENOREATMPT (VEN_S_BASE+10)
+#define VEN_S_ENOPREREQ (VEN_S_BASE+11)/*Pre-requirement is not met for API*/
+#define VEN_S_ECMDQFULL (VEN_S_BASE+12)/*Command queue is full*/
+#define VEN_S_ENOTSUPP (VEN_S_BASE+13)/*Command not supported*/
+#define VEN_S_ENOTIMPL (VEN_S_BASE+14)/*Command not implemented.*/
+#define VEN_S_ENOTPMEM (VEN_S_BASE+15)/*Buffer is not from PMEM*/
+#define VEN_S_EFLUSHED (VEN_S_BASE+16)/*returned buffer was flushed*/
+#define VEN_S_EINSUFBUF (VEN_S_BASE+17)/*provided buffer size insufficient*/
+#define VEN_S_ESAMESTATE (VEN_S_BASE+18)
+#define VEN_S_EINVALTRANS (VEN_S_BASE+19)
+
+#define VEN_INTF_VER 1
+
+/*Asynchronous messages from driver*/
+#define VEN_MSG_INDICATION 0
+#define VEN_MSG_INPUT_BUFFER_DONE 1
+#define VEN_MSG_OUTPUT_BUFFER_DONE 2
+#define VEN_MSG_NEED_OUTPUT_BUFFER 3
+#define VEN_MSG_FLUSH_INPUT_DONE 4
+#define VEN_MSG_FLUSH_OUTPUT_DONE 5
+#define VEN_MSG_START 6
+#define VEN_MSG_STOP 7
+#define VEN_MSG_PAUSE 8
+#define VEN_MSG_RESUME 9
+#define VEN_MSG_STOP_READING_MSG 10
+#define VEN_MSG_LTRUSE_FAILED 11
+#define VEN_MSG_HW_OVERLOAD 12
+#define VEN_MSG_MAX_CLIENTS 13
+
+
+/*Buffer flags bits masks*/
+#define VEN_BUFFLAG_EOS 0x00000001
+#define VEN_BUFFLAG_ENDOFFRAME 0x00000010
+#define VEN_BUFFLAG_SYNCFRAME 0x00000020
+#define VEN_BUFFLAG_EXTRADATA 0x00000040
+#define VEN_BUFFLAG_CODECCONFIG 0x00000080
+
+/*Post processing flags bit masks*/
+#define VEN_EXTRADATA_NONE 0x001
+#define VEN_EXTRADATA_QCOMFILLER 0x002
+#define VEN_EXTRADATA_SLICEINFO 0x100
+#define VEN_EXTRADATA_LTRINFO 0x200
+#define VEN_EXTRADATA_MBINFO 0x400
+
+/*ENCODER CONFIGURATION CONSTANTS*/
+
+/*Encoded video frame types*/
+#define VEN_FRAME_TYPE_I 1/* I frame type */
+#define VEN_FRAME_TYPE_P 2/* P frame type */
+#define VEN_FRAME_TYPE_B 3/* B frame type */
+
+/*Video codec types*/
+#define VEN_CODEC_MPEG4 1/* MPEG4 Codec */
+#define VEN_CODEC_H264 2/* H.264 Codec */
+#define VEN_CODEC_H263 3/* H.263 Codec */
+
+/*Video codec profile types.*/
+#define VEN_PROFILE_MPEG4_SP 1/* 1 - MPEG4 SP profile */
+#define VEN_PROFILE_MPEG4_ASP 2/* 2 - MPEG4 ASP profile */
+#define VEN_PROFILE_H264_BASELINE 3/* 3 - H264 Baseline profile */
+#define VEN_PROFILE_H264_MAIN 4/* 4 - H264 Main profile */
+#define VEN_PROFILE_H264_HIGH 5/* 5 - H264 High profile */
+#define VEN_PROFILE_H263_BASELINE 6/* 6 - H263 Baseline profile */
+
+/*Video codec profile level types.*/
+#define VEN_LEVEL_MPEG4_0 0x1/* MPEG4 Level 0 */
+#define VEN_LEVEL_MPEG4_1 0x2/* MPEG4 Level 1 */
+#define VEN_LEVEL_MPEG4_2 0x3/* MPEG4 Level 2 */
+#define VEN_LEVEL_MPEG4_3 0x4/* MPEG4 Level 3 */
+#define VEN_LEVEL_MPEG4_4 0x5/* MPEG4 Level 4 */
+#define VEN_LEVEL_MPEG4_5 0x6/* MPEG4 Level 5 */
+#define VEN_LEVEL_MPEG4_3b 0x7/* MPEG4 Level 3b */
+#define VEN_LEVEL_MPEG4_6 0x8/* MPEG4 Level 6 */
+
+#define VEN_LEVEL_H264_1 0x9/* H.264 Level 1 */
+#define VEN_LEVEL_H264_1b 0xA/* H.264 Level 1b */
+#define VEN_LEVEL_H264_1p1 0xB/* H.264 Level 1.1 */
+#define VEN_LEVEL_H264_1p2 0xC/* H.264 Level 1.2 */
+#define VEN_LEVEL_H264_1p3 0xD/* H.264 Level 1.3 */
+#define VEN_LEVEL_H264_2 0xE/* H.264 Level 2 */
+#define VEN_LEVEL_H264_2p1 0xF/* H.264 Level 2.1 */
+#define VEN_LEVEL_H264_2p2 0x10/* H.264 Level 2.2 */
+#define VEN_LEVEL_H264_3 0x11/* H.264 Level 3 */
+#define VEN_LEVEL_H264_3p1 0x12/* H.264 Level 3.1 */
+#define VEN_LEVEL_H264_3p2 0x13/* H.264 Level 3.2 */
+#define VEN_LEVEL_H264_4 0x14/* H.264 Level 4 */
+
+#define VEN_LEVEL_H263_10 0x15/* H.263 Level 10 */
+#define VEN_LEVEL_H263_20 0x16/* H.263 Level 20 */
+#define VEN_LEVEL_H263_30 0x17/* H.263 Level 30 */
+#define VEN_LEVEL_H263_40 0x18/* H.263 Level 40 */
+#define VEN_LEVEL_H263_45 0x19/* H.263 Level 45 */
+#define VEN_LEVEL_H263_50 0x1A/* H.263 Level 50 */
+#define VEN_LEVEL_H263_60 0x1B/* H.263 Level 60 */
+#define VEN_LEVEL_H263_70 0x1C/* H.263 Level 70 */
+
+/*Entropy coding model selection for H.264 encoder.*/
+#define VEN_ENTROPY_MODEL_CAVLC 1
+#define VEN_ENTROPY_MODEL_CABAC 2
+/*Cabac model number (0,1,2) for encoder.*/
+#define VEN_CABAC_MODEL_0 1/* CABAC Model 0. */
+#define VEN_CABAC_MODEL_1 2/* CABAC Model 1. */
+#define VEN_CABAC_MODEL_2 3/* CABAC Model 2. */
+
+/*Deblocking filter control type for encoder.*/
+#define VEN_DB_DISABLE 1/* 1 - Disable deblocking filter*/
+#define VEN_DB_ALL_BLKG_BNDRY 2/* 2 - All blocking boundary filtering*/
+#define VEN_DB_SKIP_SLICE_BNDRY 3/* 3 - Filtering except sliceboundary*/
+
+/*Different methods of Multi slice selection.*/
+#define VEN_MSLICE_OFF 1
+#define VEN_MSLICE_CNT_MB 2 /*number of MBscount per slice*/
+#define VEN_MSLICE_CNT_BYTE 3 /*number of bytes count per slice.*/
+#define VEN_MSLICE_GOB 4 /*Multi slice by GOB for H.263 only.*/
+
+/*Different modes for Rate Control.*/
+#define VEN_RC_OFF 1
+#define VEN_RC_VBR_VFR 2
+#define VEN_RC_VBR_CFR 3
+#define VEN_RC_CBR_VFR 4
+#define VEN_RC_CBR_CFR 5
+
+/*Different modes for flushing buffers*/
+#define VEN_FLUSH_INPUT 1
+#define VEN_FLUSH_OUTPUT 2
+#define VEN_FLUSH_ALL 3
+
+/*Different input formats for YUV data.*/
+#define VEN_INPUTFMT_NV12 1/* NV12 Linear */
+#define VEN_INPUTFMT_NV21 2/* NV21 Linear */
+#define VEN_INPUTFMT_NV12_16M2KA 3/* NV12 Linear */
+
+/*Different allowed rotation modes.*/
+#define VEN_ROTATION_0 1/* 0 degrees */
+#define VEN_ROTATION_90 2/* 90 degrees */
+#define VEN_ROTATION_180 3/* 180 degrees */
+#define VEN_ROTATION_270 4/* 270 degrees */
+
+/*IOCTL timeout values*/
+#define VEN_TIMEOUT_INFINITE 0xffffffff
+
+/*Different allowed intra refresh modes.*/
+#define VEN_IR_OFF 1
+#define VEN_IR_CYCLIC 2
+#define VEN_IR_RANDOM 3
+
+/*IOCTL BASE CODES Not to be used directly by the client.*/
+/* Base value for ioctls that are not related to encoder configuration.*/
+#define VEN_IOCTLBASE_NENC 0x800
+/* Base value for encoder configuration ioctls*/
+#define VEN_IOCTLBASE_ENC 0x850
+
+struct venc_ioctl_msg {
+ void __user *in;
+ void __user *out;
+};
+
+/*NON ENCODER CONFIGURATION IOCTLs*/
+
+/*IOCTL params:SET: InputData - unsigned long, OutputData - NULL*/
+#define VEN_IOCTL_SET_INTF_VERSION \
+ _IOW(VEN_IOCTLBASE_NENC, 0, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_timeout, OutputData - venc_msg*/
+#define VEN_IOCTL_CMD_READ_NEXT_MSG \
+ _IOWR(VEN_IOCTLBASE_NENC, 1, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - NULL, OutputData - NULL*/
+#define VEN_IOCTL_CMD_STOP_READ_MSG _IO(VEN_IOCTLBASE_NENC, 2)
+
+/*
+ * IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_allocatorproperty
+ */
+#define VEN_IOCTL_SET_INPUT_BUFFER_REQ \
+ _IOW(VEN_IOCTLBASE_NENC, 3, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INPUT_BUFFER_REQ \
+ _IOR(VEN_IOCTLBASE_NENC, 4, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_ALLOC_INPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 5, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_SET_INPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 6, struct venc_ioctl_msg)
+
+/*IOCTL params: CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_FREE_INPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 7, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_allocatorproperty
+ */
+#define VEN_IOCTL_SET_OUTPUT_BUFFER_REQ \
+ _IOW(VEN_IOCTLBASE_NENC, 8, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_OUTPUT_BUFFER_REQ \
+ _IOR(VEN_IOCTLBASE_NENC, 9, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_ALLOC_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 10, struct venc_ioctl_msg)
+
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_SET_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 11, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL.*/
+#define VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 12, struct venc_ioctl_msg)
+
+
+/* Asynchronous respone message code:* VEN_MSG_START*/
+#define VEN_IOCTL_CMD_START _IO(VEN_IOCTLBASE_NENC, 13)
+
+
+/*
+ * IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL
+ * Asynchronous respone message code:VEN_MSG_INPUT_BUFFER_DONE
+ */
+#define VEN_IOCTL_CMD_ENCODE_FRAME \
+ _IOW(VEN_IOCTLBASE_NENC, 14, struct venc_ioctl_msg)
+
+
+/*
+ *IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL
+ *Asynchronous response message code:VEN_MSG_OUTPUT_BUFFER_DONE
+ */
+#define VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 15, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:CMD: InputData - venc_bufferflush, OutputData - NULL
+ * Asynchronous response message code:VEN_MSG_INPUT_BUFFER_DONE
+ */
+#define VEN_IOCTL_CMD_FLUSH \
+ _IOW(VEN_IOCTLBASE_NENC, 16, struct venc_ioctl_msg)
+
+
+/*Asynchronous respone message code:VEN_MSG_PAUSE*/
+#define VEN_IOCTL_CMD_PAUSE _IO(VEN_IOCTLBASE_NENC, 17)
+
+/*Asynchronous respone message code:VEN_MSG_RESUME*/
+#define VEN_IOCTL_CMD_RESUME _IO(VEN_IOCTLBASE_NENC, 18)
+
+/* Asynchronous respone message code:VEN_MSG_STOP*/
+#define VEN_IOCTL_CMD_STOP _IO(VEN_IOCTLBASE_NENC, 19)
+
+#define VEN_IOCTL_SET_RECON_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 20, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_FREE_RECON_BUFFER \
+ _IOW(VEN_IOCTLBASE_NENC, 21, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_RECON_BUFFER_SIZE \
+ _IOW(VEN_IOCTLBASE_NENC, 22, struct venc_ioctl_msg)
+
+
+
+/*ENCODER PROPERTY CONFIGURATION & CAPABILITY IOCTLs*/
+
+/*
+ * IOCTL params:SET: InputData - venc_basecfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_basecfg
+ */
+#define VEN_IOCTL_SET_BASE_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 1, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_BASE_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 2, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_LIVE_MODE \
+ _IOW(VEN_IOCTLBASE_ENC, 3, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_LIVE_MODE \
+ _IOR(VEN_IOCTLBASE_ENC, 4, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_profile, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_profile
+ */
+#define VEN_IOCTL_SET_CODEC_PROFILE \
+ _IOW(VEN_IOCTLBASE_ENC, 5, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_CODEC_PROFILE \
+ _IOR(VEN_IOCTLBASE_ENC, 6, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - ven_profilelevel, OutputData - NULL
+ * GET: InputData - NULL, OutputData - ven_profilelevel
+ */
+#define VEN_IOCTL_SET_PROFILE_LEVEL \
+ _IOW(VEN_IOCTLBASE_ENC, 7, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_PROFILE_LEVEL \
+ _IOR(VEN_IOCTLBASE_ENC, 8, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_SHORT_HDR \
+ _IOW(VEN_IOCTLBASE_ENC, 9, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_SHORT_HDR \
+ _IOR(VEN_IOCTLBASE_ENC, 10, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params: SET: InputData - venc_sessionqp, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_sessionqp
+ */
+#define VEN_IOCTL_SET_SESSION_QP \
+ _IOW(VEN_IOCTLBASE_ENC, 11, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_SESSION_QP \
+ _IOR(VEN_IOCTLBASE_ENC, 12, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_intraperiod, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_intraperiod
+ */
+#define VEN_IOCTL_SET_INTRA_PERIOD \
+ _IOW(VEN_IOCTLBASE_ENC, 13, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INTRA_PERIOD \
+ _IOR(VEN_IOCTLBASE_ENC, 14, struct venc_ioctl_msg)
+
+
+/* Request an Iframe*/
+#define VEN_IOCTL_CMD_REQUEST_IFRAME _IO(VEN_IOCTLBASE_ENC, 15)
+
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_capability*/
+#define VEN_IOCTL_GET_CAPABILITY \
+ _IOR(VEN_IOCTLBASE_ENC, 16, struct venc_ioctl_msg)
+
+
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_seqheader*/
+#define VEN_IOCTL_GET_SEQUENCE_HDR \
+ _IOR(VEN_IOCTLBASE_ENC, 17, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_entropycfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_entropycfg
+ */
+#define VEN_IOCTL_SET_ENTROPY_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 18, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_ENTROPY_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 19, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_dbcfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_dbcfg
+ */
+#define VEN_IOCTL_SET_DEBLOCKING_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 20, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_DEBLOCKING_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 21, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_intrarefresh, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_intrarefresh
+ */
+#define VEN_IOCTL_SET_INTRA_REFRESH \
+ _IOW(VEN_IOCTLBASE_ENC, 22, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INTRA_REFRESH \
+ _IOR(VEN_IOCTLBASE_ENC, 23, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_multiclicecfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_multiclicecfg
+ */
+#define VEN_IOCTL_SET_MULTI_SLICE_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 24, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_MULTI_SLICE_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 25, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_ratectrlcfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_ratectrlcfg
+ */
+#define VEN_IOCTL_SET_RATE_CTRL_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 26, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_RATE_CTRL_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 27, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_voptimingcfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_voptimingcfg
+ */
+#define VEN_IOCTL_SET_VOP_TIMING_CFG \
+ _IOW(VEN_IOCTLBASE_ENC, 28, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_VOP_TIMING_CFG \
+ _IOR(VEN_IOCTLBASE_ENC, 29, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_framerate, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_framerate
+ */
+#define VEN_IOCTL_SET_FRAME_RATE \
+ _IOW(VEN_IOCTLBASE_ENC, 30, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_FRAME_RATE \
+ _IOR(VEN_IOCTLBASE_ENC, 31, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_targetbitrate, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_targetbitrate
+ */
+#define VEN_IOCTL_SET_TARGET_BITRATE \
+ _IOW(VEN_IOCTLBASE_ENC, 32, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_TARGET_BITRATE \
+ _IOR(VEN_IOCTLBASE_ENC, 33, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_rotation, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_rotation
+ */
+#define VEN_IOCTL_SET_ROTATION \
+ _IOW(VEN_IOCTLBASE_ENC, 34, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_ROTATION \
+ _IOR(VEN_IOCTLBASE_ENC, 35, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_headerextension, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_headerextension
+ */
+#define VEN_IOCTL_SET_HEC \
+ _IOW(VEN_IOCTLBASE_ENC, 36, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_HEC \
+ _IOR(VEN_IOCTLBASE_ENC, 37, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_DATA_PARTITION \
+ _IOW(VEN_IOCTLBASE_ENC, 38, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_DATA_PARTITION \
+ _IOR(VEN_IOCTLBASE_ENC, 39, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_RVLC \
+ _IOW(VEN_IOCTLBASE_ENC, 40, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_RVLC \
+ _IOR(VEN_IOCTLBASE_ENC, 41, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_AC_PREDICTION \
+ _IOW(VEN_IOCTLBASE_ENC, 42, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_AC_PREDICTION \
+ _IOR(VEN_IOCTLBASE_ENC, 43, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_qprange, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_qprange
+ */
+#define VEN_IOCTL_SET_QP_RANGE \
+ _IOW(VEN_IOCTLBASE_ENC, 44, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_QP_RANGE \
+ _IOR(VEN_IOCTLBASE_ENC, 45, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_NUMBER_INSTANCES \
+ _IOR(VEN_IOCTLBASE_ENC, 46, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_SET_METABUFFER_MODE \
+ _IOW(VEN_IOCTLBASE_ENC, 47, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - unsigned int, OutputData - NULL.*/
+#define VEN_IOCTL_SET_EXTRADATA \
+ _IOW(VEN_IOCTLBASE_ENC, 48, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - unsigned int.*/
+#define VEN_IOCTL_GET_EXTRADATA \
+ _IOR(VEN_IOCTLBASE_ENC, 49, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - NULL, OutputData - NULL.*/
+#define VEN_IOCTL_SET_SLICE_DELIVERY_MODE \
+ _IO(VEN_IOCTLBASE_ENC, 50)
+
+#define VEN_IOCTL_SET_H263_PLUSPTYPE \
+ _IOW(VEN_IOCTLBASE_ENC, 51, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_range, OutputData - NULL.*/
+#define VEN_IOCTL_SET_CAPABILITY_LTRCOUNT \
+ _IOW(VEN_IOCTLBASE_ENC, 52, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_range.*/
+#define VEN_IOCTL_GET_CAPABILITY_LTRCOUNT \
+ _IOR(VEN_IOCTLBASE_ENC, 53, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrmode, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRMODE \
+ _IOW(VEN_IOCTLBASE_ENC, 54, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrmode.*/
+#define VEN_IOCTL_GET_LTRMODE \
+ _IOR(VEN_IOCTLBASE_ENC, 55, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrcount, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRCOUNT \
+ _IOW(VEN_IOCTLBASE_ENC, 56, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrcount.*/
+#define VEN_IOCTL_GET_LTRCOUNT \
+ _IOR(VEN_IOCTLBASE_ENC, 57, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrperiod, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRPERIOD \
+ _IOW(VEN_IOCTLBASE_ENC, 58, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrperiod.*/
+#define VEN_IOCTL_GET_LTRPERIOD \
+ _IOR(VEN_IOCTLBASE_ENC, 59, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltruse, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRUSE \
+ _IOW(VEN_IOCTLBASE_ENC, 60, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltruse.*/
+#define VEN_IOCTL_GET_LTRUSE \
+ _IOR(VEN_IOCTLBASE_ENC, 61, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrmark, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRMARK \
+ _IOW(VEN_IOCTLBASE_ENC, 62, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrmark.*/
+#define VEN_IOCTL_GET_LTRMARK \
+ _IOR(VEN_IOCTLBASE_ENC, 63, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - unsigned int, OutputData - NULL*/
+#define VEN_IOCTL_SET_SPS_PPS_FOR_IDR \
+ _IOW(VEN_IOCTLBASE_ENC, 64, struct venc_ioctl_msg)
+
+struct venc_range {
+ unsigned long max;
+ unsigned long min;
+ unsigned long step_size;
+};
+
+struct venc_switch {
+ unsigned char status;
+};
+
+struct venc_allocatorproperty {
+ unsigned long mincount;
+ unsigned long maxcount;
+ unsigned long actualcount;
+ unsigned long datasize;
+ unsigned long suffixsize;
+ unsigned long alignment;
+ unsigned long bufpoolid;
+};
+
+struct venc_bufferpayload {
+ unsigned char *pbuffer;
+ size_t sz;
+ int fd;
+ unsigned int offset;
+ unsigned int maped_size;
+ unsigned long filled_len;
+};
+
+struct venc_buffer {
+ unsigned char *ptrbuffer;
+ unsigned long sz;
+ unsigned long len;
+ unsigned long offset;
+ long long timestamp;
+ unsigned long flags;
+ void *clientdata;
+};
+
+struct venc_basecfg {
+ unsigned long input_width;
+ unsigned long input_height;
+ unsigned long dvs_width;
+ unsigned long dvs_height;
+ unsigned long codectype;
+ unsigned long fps_num;
+ unsigned long fps_den;
+ unsigned long targetbitrate;
+ unsigned long inputformat;
+};
+
+struct venc_profile {
+ unsigned long profile;
+};
+struct ven_profilelevel {
+ unsigned long level;
+};
+
+struct venc_sessionqp {
+ unsigned long iframeqp;
+ unsigned long pframqp;
+};
+
+struct venc_qprange {
+ unsigned long maxqp;
+ unsigned long minqp;
+};
+
+struct venc_plusptype {
+ unsigned long plusptype_enable;
+};
+
+struct venc_intraperiod {
+ unsigned long num_pframes;
+ unsigned long num_bframes;
+};
+struct venc_seqheader {
+ unsigned char *hdrbufptr;
+ unsigned long bufsize;
+ unsigned long hdrlen;
+};
+
+struct venc_capability {
+ unsigned long codec_types;
+ unsigned long maxframe_width;
+ unsigned long maxframe_height;
+ unsigned long maxtarget_bitrate;
+ unsigned long maxframe_rate;
+ unsigned long input_formats;
+ unsigned char dvs;
+};
+
+struct venc_entropycfg {
+ unsigned int longentropysel;
+ unsigned long cabacmodel;
+};
+
+struct venc_dbcfg {
+ unsigned long db_mode;
+ unsigned long slicealpha_offset;
+ unsigned long slicebeta_offset;
+};
+
+struct venc_intrarefresh {
+ unsigned long irmode;
+ unsigned long mbcount;
+};
+
+struct venc_multiclicecfg {
+ unsigned long mslice_mode;
+ unsigned long mslice_size;
+};
+
+struct venc_bufferflush {
+ unsigned long flush_mode;
+};
+
+struct venc_ratectrlcfg {
+ unsigned long rcmode;
+};
+
+struct venc_voptimingcfg {
+ unsigned long voptime_resolution;
+};
+struct venc_framerate {
+ unsigned long fps_denominator;
+ unsigned long fps_numerator;
+};
+
+struct venc_targetbitrate {
+ unsigned long target_bitrate;
+};
+
+
+struct venc_rotation {
+ unsigned long rotation;
+};
+
+struct venc_timeout {
+ unsigned long millisec;
+};
+
+struct venc_headerextension {
+ unsigned long header_extension;
+};
+
+struct venc_msg {
+ unsigned long statuscode;
+ unsigned long msgcode;
+ struct venc_buffer buf;
+ unsigned long msgdata_size;
+};
+
+struct venc_recon_addr {
+ unsigned char *pbuffer;
+ unsigned long buffer_size;
+ unsigned long pmem_fd;
+ unsigned long offset;
+};
+
+struct venc_recon_buff_size {
+ int width;
+ int height;
+ int size;
+ int alignment;
+};
+
+struct venc_ltrmode {
+ unsigned long ltr_mode;
+};
+
+struct venc_ltrcount {
+ unsigned long ltr_count;
+};
+
+struct venc_ltrperiod {
+ unsigned long ltr_period;
+};
+
+struct venc_ltruse {
+ unsigned long ltr_id;
+ unsigned long ltr_frames;
+};
+
+#endif /* _UAPI_MSM_VIDC_ENC_H_ */
diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h
index 1fad2c2..da161b5 100644
--- a/include/uapi/linux/netfilter/xt_bpf.h
+++ b/include/uapi/linux/netfilter/xt_bpf.h
@@ -2,9 +2,11 @@
#define _XT_BPF_H
#include <linux/filter.h>
+#include <linux/limits.h>
#include <linux/types.h>
#define XT_BPF_MAX_NUM_INSTR 64
+#define XT_BPF_PATH_MAX (XT_BPF_MAX_NUM_INSTR * sizeof(struct sock_filter))
struct bpf_prog;
@@ -16,4 +18,24 @@
struct bpf_prog *filter __attribute__((aligned(8)));
};
+enum xt_bpf_modes {
+ XT_BPF_MODE_BYTECODE,
+ XT_BPF_MODE_FD_PINNED,
+ XT_BPF_MODE_FD_ELF,
+};
+#define XT_BPF_MODE_PATH_PINNED XT_BPF_MODE_FD_PINNED
+
+struct xt_bpf_info_v1 {
+ __u16 mode;
+ __u16 bpf_program_num_elem;
+ __s32 fd;
+ union {
+ struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
+ char path[XT_BPF_PATH_MAX];
+ };
+
+ /* only used in the kernel */
+ struct bpf_prog *filter __attribute__((aligned(8)));
+};
+
#endif /*_XT_BPF_H */
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9399a35..20a01ca 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2603,6 +2603,8 @@
#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+#define NL80211_WIPHY_NAME_MAXLEN 128
+
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_HT_RATES 77
#define NL80211_MAX_SUPP_REG_RULES 64
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 2c5810a..b67cfd8 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -106,7 +106,7 @@
#define PCI_SUBSYSTEM_ID 0x2e
#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
#define PCI_ROM_ADDRESS_ENABLE 0x01
-#define PCI_ROM_ADDRESS_MASK (~0x7ffUL)
+#define PCI_ROM_ADDRESS_MASK (~0x7ffU)
#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index c1af9b3..f0320a0 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -203,6 +203,18 @@
*/
#define PR_SET_TIMERSLACK_PID 127
+/* Per task speculation control */
+#define PR_GET_SPECULATION_CTRL 52
+#define PR_SET_SPECULATION_CTRL 53
+/* Speculation control variants */
+# define PR_SPEC_STORE_BYPASS 0
+/* Return and control values for PR_SET/GET_SPECULATION_CTRL */
+# define PR_SPEC_NOT_AFFECTED 0
+# define PR_SPEC_PRCTL (1UL << 0)
+# define PR_SPEC_ENABLE (1UL << 1)
+# define PR_SPEC_DISABLE (1UL << 2)
+# define PR_SPEC_FORCE_DISABLE (1UL << 3)
+
#define PR_SET_VMA 0x53564d41
# define PR_SET_VMA_ANON_NAME 0
diff --git a/include/uapi/linux/psci.h b/include/uapi/linux/psci.h
index 3d7a0fc..39930ca 100644
--- a/include/uapi/linux/psci.h
+++ b/include/uapi/linux/psci.h
@@ -87,6 +87,9 @@
(((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT)
#define PSCI_VERSION_MINOR(ver) \
((ver) & PSCI_VERSION_MINOR_MASK)
+#define PSCI_VERSION(maj, min) \
+ ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | \
+ ((min) & PSCI_VERSION_MINOR_MASK))
/* PSCI features decoding (>=1.0) */
#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1
diff --git a/include/uapi/linux/qg-profile.h b/include/uapi/linux/qg-profile.h
index bffddbb..0230b32 100644
--- a/include/uapi/linux/qg-profile.h
+++ b/include/uapi/linux/qg-profile.h
@@ -55,6 +55,8 @@
#define QG_MAX_FCC_MAH 16000
#define QG_MIN_SLOPE 1
#define QG_MAX_SLOPE 50000
+#define QG_ESR_SF_MIN 5000
+#define QG_ESR_SF_MAX 20000
/* IOCTLs to query battery profile data */
#define BPIOCXSOC _IOWR('B', 0x01, struct battery_params) /* SOC */
diff --git a/include/uapi/linux/qg.h b/include/uapi/linux/qg.h
index 54488ff..40882a7 100644
--- a/include/uapi/linux/qg.h
+++ b/include/uapi/linux/qg.h
@@ -12,19 +12,28 @@
QG_ESR,
QG_CHARGE_COUNTER,
QG_FIFO_TIME_DELTA,
- QG_RESERVED_1,
- QG_RESERVED_2,
- QG_RESERVED_3,
- QG_RESERVED_4,
- QG_RESERVED_5,
- QG_RESERVED_6,
- QG_RESERVED_7,
- QG_RESERVED_8,
+ QG_BATT_SOC,
+ QG_CC_SOC,
+ QG_ESR_CHARGE_DELTA,
+ QG_ESR_DISCHARGE_DELTA,
+ QG_ESR_CHARGE_SF,
+ QG_ESR_DISCHARGE_SF,
+ QG_FULL_SOC,
+ QG_CLEAR_LEARNT_DATA,
QG_RESERVED_9,
QG_RESERVED_10,
QG_MAX,
};
+#define QG_BATT_SOC QG_BATT_SOC
+#define QG_CC_SOC QG_CC_SOC
+#define QG_ESR_CHARGE_DELTA QG_ESR_CHARGE_DELTA
+#define QG_ESR_DISCHARGE_DELTA QG_ESR_DISCHARGE_DELTA
+#define QG_ESR_CHARGE_SF QG_ESR_CHARGE_SF
+#define QG_ESR_DISCHARGE_SF QG_ESR_DISCHARGE_SF
+#define QG_FULL_SOC QG_FULL_SOC
+#define QG_CLEAR_LEARNT_DATA QG_CLEAR_LEARNT_DATA
+
struct fifo_data {
unsigned int v;
unsigned int i;
diff --git a/include/uapi/linux/qseecom.h b/include/uapi/linux/qseecom.h
index f0a26b2..6de4c76 100644
--- a/include/uapi/linux/qseecom.h
+++ b/include/uapi/linux/qseecom.h
@@ -281,13 +281,6 @@
int flag;
};
-struct qseecom_encdec_conf_t {
- __le64 start_sector;
- size_t fs_size;
- int index;
- int mode;
-};
-
#define SG_ENTRY_SZ sizeof(struct qseecom_sg_entry)
#define SG_ENTRY_SZ_64BIT sizeof(struct qseecom_sg_entry_64bit)
@@ -399,7 +392,4 @@
#define QSEECOM_IOCTL_SET_ICE_INFO \
_IOWR(QSEECOM_IOC_MAGIC, 43, struct qseecom_ice_data_t)
-#define QSEECOM_IOCTL_SET_ENCDEC_INFO \
- _IOWR(QSEECOM_IOC_MAGIC, 44, struct qseecom_encdec_conf_t)
-
#endif /* _UAPI_QSEECOM_H_ */
diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h
index 3f93d16..b455b0d 100644
--- a/include/uapi/linux/random.h
+++ b/include/uapi/linux/random.h
@@ -34,6 +34,9 @@
/* Clear the entropy pool and associated counters. (Superuser only.) */
#define RNDCLEARPOOL _IO( 'R', 0x06 )
+/* Reseed CRNG. (Superuser only.) */
+#define RNDRESEEDCRNG _IO( 'R', 0x07 )
+
struct rand_pool_info {
int entropy_count;
int buf_size;
diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h
index 0f238a4..e4acb61 100644
--- a/include/uapi/linux/seccomp.h
+++ b/include/uapi/linux/seccomp.h
@@ -15,7 +15,9 @@
#define SECCOMP_SET_MODE_FILTER 1
/* Valid flags for SECCOMP_SET_MODE_FILTER */
-#define SECCOMP_FILTER_FLAG_TSYNC 1
+#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0)
+/* In v4.14+ SECCOMP_FILTER_FLAG_LOG is (1UL << 1) */
+#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
/*
* All BPF programs must return a 32-bit value.
diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h
index c6f5b09..6f6d93f 100644
--- a/include/uapi/linux/usb/audio.h
+++ b/include/uapi/linux/usb/audio.h
@@ -370,7 +370,7 @@
{
return (protocol == UAC_VERSION_1) ?
desc->baSourceID[desc->bNrInPins + 4] :
- desc->baSourceID[desc->bNrInPins + 6];
+ 2; /* in UAC2, this value is constant */
}
static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc,
@@ -378,7 +378,7 @@
{
return (protocol == UAC_VERSION_1) ?
&desc->baSourceID[desc->bNrInPins + 5] :
- &desc->baSourceID[desc->bNrInPins + 7];
+ &desc->baSourceID[desc->bNrInPins + 6];
}
static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc,
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 0b5ff0f..d14cd8f 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -696,6 +696,7 @@
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR = 4,
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR = 5,
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR = 6,
+ V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ = 7,
};
#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR \
V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR
@@ -1343,6 +1344,12 @@
V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6 = 2,
};
+#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QUALITY \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+160)
+
+#define V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+161)
+
enum v4l2_mpeg_vidc_video_mbi_statistics_mode {
V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT = 0,
V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_1 = 1,
diff --git a/include/uapi/linux/vm_bms.h b/include/uapi/linux/vm_bms.h
new file mode 100644
index 0000000..364efc8
--- /dev/null
+++ b/include/uapi/linux/vm_bms.h
@@ -0,0 +1,34 @@
+#ifndef __VM_BMS_H__
+#define __VM_BMS_H__
+
+#define VM_BMS_DEVICE "/dev/vm_bms"
+#define MAX_FIFO_REGS 8
+
+/**
+ * struct qpnp_vm_bms_data - vm-bms data (passed to usersapce)
+ * @data_type: type of data filled up
+ * @num_fifo: count of valid fifo averages
+ * @fifo_uv: array of fifo averages in uv
+ * @sample_interval sample interval of the fifo data in ms
+ * @sample_count total samples in one fifo
+ * @acc_uv averaged accumulator value in uv
+ * @acc_count num of accumulated samples
+ * @seq_num sequence number of the data
+ */
+struct qpnp_vm_bms_data {
+ unsigned int num_fifo;
+ unsigned int fifo_uv[MAX_FIFO_REGS];
+ unsigned int sample_interval_ms;
+ unsigned int sample_count;
+ unsigned int acc_uv;
+ unsigned int acc_count;
+ unsigned int seq_num;
+};
+
+enum vmbms_power_usecase {
+ VMBMS_IGNORE_ALL_BIT = 1,
+ VMBMS_VOICE_CALL_BIT = (1 << 4),
+ VMBMS_STATIC_DISPLAY_BIT = (1 << 5),
+};
+
+#endif /* __VM_BMS_H__ */
diff --git a/include/uapi/media/msm_camera.h b/include/uapi/media/msm_camera.h
index c58a273..7b2b966 100644
--- a/include/uapi/media/msm_camera.h
+++ b/include/uapi/media/msm_camera.h
@@ -1392,7 +1392,7 @@
uint16_t lane_mask;
uint8_t combo_mode;
uint8_t csid_core;
- unsigned long data_rate;
+ uint64_t data_rate;
};
struct msm_camera_csi2_params {
diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h
index 04cddb9..63abd156 100644
--- a/include/uapi/media/msm_camsensor_sdk.h
+++ b/include/uapi/media/msm_camsensor_sdk.h
@@ -370,7 +370,7 @@
unsigned char csid_core;
unsigned int csiphy_clk;
unsigned char csi_3phase;
- unsigned long data_rate;
+ uint64_t data_rate;
};
struct msm_camera_i2c_seq_reg_array {
diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h
index 4f12e5c..3fd0c88 100644
--- a/include/uapi/media/msm_media_info.h
+++ b/include/uapi/media/msm_media_info.h
@@ -1229,6 +1229,7 @@
{
const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height);
unsigned int uv_alignment = 0, size = 0;
+ unsigned int w_alignment = 512;
unsigned int y_plane, uv_plane, y_stride,
uv_stride, y_sclines, uv_sclines;
unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0;
@@ -1252,6 +1253,17 @@
switch (color_fmt) {
case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
+ uv_alignment = 4096;
+ y_plane = y_stride * y_sclines;
+ uv_plane = uv_stride * uv_sclines + uv_alignment;
+ size = y_plane + uv_plane +
+ MSM_MEDIA_MAX(extra_size, 8 * y_stride);
+ size = MSM_MEDIA_ALIGN(size, 4096);
+
+ /* Additional size to cover last row of non-aligned frame */
+ size += MSM_MEDIA_ALIGN(width, w_alignment) * w_alignment;
+ size = MSM_MEDIA_ALIGN(size, 4096);
+ break;
case COLOR_FMT_P010:
uv_alignment = 4096;
y_plane = y_stride * y_sclines;
@@ -1288,6 +1300,10 @@
uv_meta_plane)*2 +
MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride);
size = MSM_MEDIA_ALIGN(size, 4096);
+
+ /* Additional size to cover last row of non-aligned frame */
+ size += MSM_MEDIA_ALIGN(width, w_alignment) * w_alignment;
+ size = MSM_MEDIA_ALIGN(size, 4096);
break;
case COLOR_FMT_NV12_BPP10_UBWC:
y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096);
diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h
index 053fa76..74a8d93 100644
--- a/include/uapi/media/msmb_isp.h
+++ b/include/uapi/media/msmb_isp.h
@@ -18,6 +18,7 @@
#define ISP1_BIT (0x10000 << 2)
#define ISP_META_CHANNEL_BIT (0x10000 << 3)
#define ISP_SCRATCH_BUF_BIT (0x10000 << 4)
+#define ISP_PDAF_CHANNEL_BIT (0x10000 << 5)
#define ISP_OFFLINE_STATS_BIT (0x10000 << 5)
#define ISP_SVHDR_IN_BIT (0x10000 << 6) /* RDI hw stream for SVHDR */
#define ISP_SVHDR_OUT_BIT (0x10000 << 7) /* SVHDR output bufq stream*/
@@ -295,6 +296,11 @@
uint8_t rdi_cid;/*CID 1-16*/
};
+enum msm_stream_memory_input_t {
+ MEMORY_INPUT_DISABLED,
+ MEMORY_INPUT_ENABLED
+};
+
enum msm_stream_rdi_input_type {
MSM_CAMERA_RDI_MIN,
MSM_CAMERA_RDI_PDAF,
@@ -324,6 +330,29 @@
enum msm_stream_rdi_input_type rdi_input_type;
};
+struct msm_vfe32_axi_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t vt_enable;
+ uint32_t output_format;/*Planar/RAW/Misc*/
+ enum msm_vfe_axi_stream_src stream_src; /*CAMIF/IDEAL/RDIs*/
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
+
+ uint32_t burst_count;
+ uint32_t hfr_mode;
+ uint8_t frame_base;
+
+ uint32_t init_frame_drop; /*MAX 31 Frames*/
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern;
+ uint8_t buf_divert; /* if TRUE no vb2 buf done. */
+ /*Return values*/
+ uint32_t axi_stream_handle;
+ uint32_t controllable_output;
+ uint32_t burst_len;
+ /* Flag indicating memory input stream */
+ enum msm_stream_memory_input_t memory_input;
+};
+
struct msm_vfe_axi_stream_release_cmd {
uint32_t stream_handle;
};
@@ -680,7 +709,9 @@
ISP_PING_PONG_MISMATCH = 12,
ISP_REG_UPDATE_MISSING = 13,
ISP_BUF_FATAL_ERROR = 14,
- ISP_EVENT_MAX = 15
+ ISP_EVENT_MAX = 15,
+ ISP_WM_BUS_OVERFLOW = 16,
+ ISP_CAMIF_ERROR = 17,
};
#define ISP_EVENT_OFFSET 8
@@ -710,6 +741,7 @@
#define ISP_EVENT_REG_UPDATE_MISSING (ISP_EVENT_BASE + ISP_REG_UPDATE_MISSING)
#define ISP_EVENT_BUF_FATAL_ERROR (ISP_EVENT_BASE + ISP_BUF_FATAL_ERROR)
#define ISP_EVENT_STREAM_UPDATE_DONE (ISP_STREAM_EVENT_BASE)
+#define ISP_EVENT_WM_BUS_OVERFLOW (ISP_EVENT_BASE + ISP_WM_BUS_OVERFLOW)
/* The msm_v4l2_event_data structure should match the
* v4l2_event.u.data field.
@@ -759,6 +791,11 @@
uint32_t stream_id_mask;
};
+struct msm_isp32_error_info {
+ /* 1 << msm_isp_event_idx */
+ uint32_t error_mask;
+};
+
/* This structure reports delta between master and slave */
struct msm_isp_ms_delta_info {
uint8_t num_delta_info;
@@ -827,6 +864,25 @@
} u; /* union can have max 52 bytes */
};
+struct msm_isp32_event_data {
+ /*Wall clock except for buffer divert events
+ *which use monotonic clock
+ */
+ struct timeval timestamp;
+ /* Monotonic timestamp since bootup */
+ struct timeval mono_timestamp;
+ enum msm_vfe_input_src input_intf;
+ uint32_t frame_id;
+ union {
+ /* Sent for Stats_Done event */
+ struct msm_isp_stats_event stats;
+ /* Sent for Buf_Divert event */
+ struct msm_isp_buf_event buf_done;
+ struct msm_isp32_error_info error_info;
+ } u; /* union can have max 52 bytes */
+ uint32_t is_skip_pproc;
+};
+
enum msm_vfe_ahb_clk_vote {
MSM_ISP_CAMERA_AHB_SVS_VOTE = 1,
MSM_ISP_CAMERA_AHB_TURBO_VOTE = 2,
@@ -919,6 +975,7 @@
MSM_ISP_MAP_BUF_START_MULTI_PASS_FE,
MSM_ISP_REQUEST_BUF_VER2,
MSM_ISP_DUAL_HW_LPM_MODE,
+ MSM_ISP32_REQUEST_STREAM,
};
#define VIDIOC_MSM_VFE_REG_CFG \
@@ -941,6 +998,10 @@
_IOWR('V', MSM_ISP_REQUEST_STREAM, \
struct msm_vfe_axi_stream_request_cmd)
+#define VIDIOC_MSM_ISP32_REQUEST_STREAM \
+ _IOWR('V', MSM_ISP32_REQUEST_STREAM, \
+ struct msm_vfe32_axi_stream_request_cmd)
+
#define VIDIOC_MSM_ISP_CFG_STREAM \
_IOWR('V', MSM_ISP_CFG_STREAM, \
struct msm_vfe_axi_stream_cfg_cmd)
@@ -1038,6 +1099,8 @@
#define VIDIOC_MSM_ISP_REQUEST_BUF_VER2 \
_IOWR('V', MSM_ISP_REQUEST_BUF_VER2, struct msm_isp_buf_request_ver2)
+#define VIDIOC_MSM_ISP_BUF_DONE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+21, struct msm_isp32_event_data)
#define VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE \
_IOWR('V', MSM_ISP_DUAL_HW_LPM_MODE, \
diff --git a/init/Kconfig b/init/Kconfig
index 2e57658..e3929edd 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -840,6 +840,16 @@
13 => 8 KB
12 => 4 KB
+config CONSOLE_FLUSH_ON_HOTPLUG
+ bool "Enable console flush configurable in hot plug code path"
+ depends on HOTPLUG_CPU
+ def_bool n
+ help
+ In cpu hot plug path console lock acquire and release causes the
+ console to flush. If console lock is not free hot plug latency
+ increases. So make console flush configurable in hot plug path
+ and default disabled to help in cpu hot plug latencies.
+
config LOG_CPU_MAX_BUF_SHIFT
int "CPU kernel log buffer size contribution (13 => 8 KB, 17 => 128KB)"
depends on SMP
@@ -2267,7 +2277,7 @@
config MODULES_TREE_LOOKUP
def_bool y
- depends on PERF_EVENTS || TRACING
+ depends on PERF_EVENTS || TRACING || CFI_CLANG
config INIT_ALL_POSSIBLE
bool
diff --git a/init/main.c b/init/main.c
index 17e439f..f9cd3f0 100644
--- a/init/main.c
+++ b/init/main.c
@@ -81,6 +81,7 @@
#include <linux/proc_ns.h>
#include <linux/io.h>
#include <linux/kaiser.h>
+#include <linux/cache.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -913,14 +914,16 @@
static noinline void __init kernel_init_freeable(void);
-#ifdef CONFIG_DEBUG_RODATA
-static bool rodata_enabled = true;
+#if defined(CONFIG_DEBUG_RODATA) || defined(CONFIG_SET_MODULE_RONX)
+bool rodata_enabled __ro_after_init = true;
static int __init set_debug_rodata(char *str)
{
return strtobool(str, &rodata_enabled);
}
__setup("rodata=", set_debug_rodata);
+#endif
+#ifdef CONFIG_DEBUG_RODATA
static void mark_readonly(void)
{
if (rodata_enabled)
diff --git a/ipc/shm.c b/ipc/shm.c
index e2072ae..b626745 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -198,6 +198,12 @@
if (IS_ERR(shp))
return PTR_ERR(shp);
+ if (shp->shm_file != sfd->file) {
+ /* ID was reused */
+ shm_unlock(shp);
+ return -EINVAL;
+ }
+
shp->shm_atim = get_seconds();
shp->shm_lprid = task_tgid_vnr(current);
shp->shm_nattch++;
@@ -381,6 +387,17 @@
return sfd->vm_ops->fault(vma, vmf);
}
+static int shm_split(struct vm_area_struct *vma, unsigned long addr)
+{
+ struct file *file = vma->vm_file;
+ struct shm_file_data *sfd = shm_file_data(file);
+
+ if (sfd->vm_ops && sfd->vm_ops->split)
+ return sfd->vm_ops->split(vma, addr);
+
+ return 0;
+}
+
#ifdef CONFIG_NUMA
static int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
{
@@ -414,8 +431,9 @@
int ret;
/*
- * In case of remap_file_pages() emulation, the file can represent
- * removed IPC ID: propogate shm_lock() error to caller.
+ * In case of remap_file_pages() emulation, the file can represent an
+ * IPC ID that was removed, and possibly even reused by another shm
+ * segment already. Propagate this case as an error to caller.
*/
ret =__shm_open(vma);
if (ret)
@@ -439,6 +457,7 @@
struct shm_file_data *sfd = shm_file_data(file);
put_ipc_ns(sfd->ns);
+ fput(sfd->file);
shm_file_data(file) = NULL;
kfree(sfd);
return 0;
@@ -503,6 +522,7 @@
.open = shm_open, /* callback for a new vm-area open */
.close = shm_close, /* callback for when the vm-area is released */
.fault = shm_fault,
+ .split = shm_split,
#if defined(CONFIG_NUMA)
.set_policy = shm_set_policy,
.get_policy = shm_get_policy,
@@ -1200,7 +1220,16 @@
file->f_mapping = shp->shm_file->f_mapping;
sfd->id = shp->shm_perm.id;
sfd->ns = get_ipc_ns(ns);
- sfd->file = shp->shm_file;
+ /*
+ * We need to take a reference to the real shm file to prevent the
+ * pointer from becoming stale in cases where the lifetime of the outer
+ * file extends beyond that of the shm segment. It's not usually
+ * possible, but it can happen during remap_file_pages() emulation as
+ * that unmaps the memory, then does ->mmap() via file reference only.
+ * We'll deny the ->mmap() if the shm segment was since removed, but to
+ * detect shm ID reuse we need to compare the file pointers.
+ */
+ sfd->file = get_file(shp->shm_file);
sfd->vm_ops = NULL;
err = security_mmap_file(file, prot, flags);
diff --git a/kernel/Makefile b/kernel/Makefile
index 314e7d6..108f5be 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -32,6 +32,9 @@
# cond_syscall is currently not LTO compatible
CFLAGS_sys_ni.o = $(DISABLE_LTO)
+# Don't instrument error handlers
+CFLAGS_cfi.o = $(DISABLE_CFI_CLANG)
+
obj-y += sched/
obj-y += locking/
obj-y += power/
@@ -101,6 +104,7 @@
obj-$(CONFIG_IRQ_WORK) += irq_work.o
obj-$(CONFIG_CPU_PM) += cpu_pm.o
obj-$(CONFIG_BPF) += bpf/
+obj-$(CONFIG_CFI_CLANG) += cfi.o
obj-$(CONFIG_PERF_EVENTS) += events/
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 2cd5256..93648f6 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2252,10 +2252,11 @@
audit_sig_uid = uid;
security_task_getsecid(tsk, &audit_sig_sid);
}
- if (!audit_signals || audit_dummy_context())
- return 0;
}
+ if (!audit_signals || audit_dummy_context())
+ return 0;
+
/* optimize the common case by putting first signal recipient directly
* in audit_context */
if (!ctx->target_pid) {
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 2eb9b7f..b30ca0f 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -23,8 +23,10 @@
{
int i;
- for (i = 0; i < array->map.max_entries; i++)
+ for (i = 0; i < array->map.max_entries; i++) {
free_percpu(array->pptrs[i]);
+ cond_resched();
+ }
}
static int bpf_array_alloc_percpu(struct bpf_array *array)
@@ -40,6 +42,7 @@
return -ENOMEM;
}
array->pptrs[i] = ptr;
+ cond_resched();
}
return 0;
@@ -51,8 +54,9 @@
bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
u32 elem_size, index_mask, max_entries;
bool unpriv = !capable(CAP_SYS_ADMIN);
+ u64 cost, array_size, mask64;
struct bpf_array *array;
- u64 array_size, mask64;
+ int ret;
/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 4 ||
@@ -96,8 +100,19 @@
array_size += (u64) max_entries * elem_size;
/* make sure there is no u32 overflow later in round_up() */
- if (array_size >= U32_MAX - PAGE_SIZE)
+ cost = array_size;
+ if (cost >= U32_MAX - PAGE_SIZE)
return ERR_PTR(-ENOMEM);
+ if (percpu) {
+ cost += (u64)attr->max_entries * elem_size * num_possible_cpus();
+ if (cost >= U32_MAX - PAGE_SIZE)
+ return ERR_PTR(-ENOMEM);
+ }
+ cost = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
+
+ ret = bpf_map_precharge_memlock(cost);
+ if (ret < 0)
+ return ERR_PTR(ret);
/* allocate all map elements and zero-initialize them */
array = bpf_map_area_alloc(array_size);
@@ -111,20 +126,16 @@
array->map.key_size = attr->key_size;
array->map.value_size = attr->value_size;
array->map.max_entries = attr->max_entries;
+ array->map.map_flags = attr->map_flags;
+ array->map.pages = cost;
array->elem_size = elem_size;
- if (!percpu)
- goto out;
-
- array_size += (u64) attr->max_entries * elem_size * num_possible_cpus();
-
- if (array_size >= U32_MAX - PAGE_SIZE ||
- elem_size > PCPU_MIN_UNIT_SIZE || bpf_array_alloc_percpu(array)) {
+ if (percpu &&
+ (elem_size > PCPU_MIN_UNIT_SIZE ||
+ bpf_array_alloc_percpu(array))) {
bpf_map_area_free(array);
return ERR_PTR(-ENOMEM);
}
-out:
- array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT;
return &array->map;
}
@@ -183,7 +194,7 @@
static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
{
struct bpf_array *array = container_of(map, struct bpf_array, map);
- u32 index = *(u32 *)key;
+ u32 index = key ? *(u32 *)key : U32_MAX;
u32 *next = (u32 *)next_key;
if (index >= array->map.max_entries) {
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 879ca84..66d64db 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -466,7 +466,7 @@
*
* Decode and execute eBPF instructions.
*/
-static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn)
+static unsigned int __bpf_prog_run(const struct sk_buff *ctx, const struct bpf_insn *insn)
{
u64 stack[MAX_BPF_STACK / sizeof(u64)];
u64 regs[MAX_BPF_REG], tmp;
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 27f4f2c..9c86d5d 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -328,12 +328,15 @@
struct hlist_head *head;
struct htab_elem *l, *next_l;
u32 hash, key_size;
- int i;
+ int i = 0;
WARN_ON_ONCE(!rcu_read_lock_held());
key_size = map->key_size;
+ if (!key)
+ goto find_first_elem;
+
hash = htab_map_hash(key, key_size);
head = select_bucket(htab, hash);
@@ -341,10 +344,8 @@
/* lookup the key */
l = lookup_elem_raw(head, hash, key, key_size);
- if (!l) {
- i = 0;
+ if (!l)
goto find_first_elem;
- }
/* key was found, get next key in the same bucket */
next_l = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(&l->hash_node)),
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 2d23c32..7e48fa6 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -322,6 +322,42 @@
return ret;
}
+static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type type)
+{
+ struct bpf_prog *prog;
+ int ret = inode_permission(inode, MAY_READ | MAY_WRITE);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (inode->i_op == &bpf_map_iops)
+ return ERR_PTR(-EINVAL);
+ if (inode->i_op != &bpf_prog_iops)
+ return ERR_PTR(-EACCES);
+
+ prog = inode->i_private;
+
+ ret = security_bpf_prog(prog);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return bpf_prog_inc(prog);
+}
+
+struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type type)
+{
+ struct bpf_prog *prog;
+ struct path path;
+ int ret = kern_path(name, LOOKUP_FOLLOW, &path);
+ if (ret)
+ return ERR_PTR(ret);
+ prog = __get_prog_inode(d_backing_inode(path.dentry), type);
+ if (!IS_ERR(prog))
+ touch_atime(&path);
+ path_put(&path);
+ return prog;
+}
+EXPORT_SYMBOL(bpf_prog_get_type_path);
+
static void bpf_evict_inode(struct inode *inode)
{
enum bpf_type type;
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 230aced..6039db3 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -91,6 +91,7 @@
smap->map.key_size = attr->key_size;
smap->map.value_size = value_size;
smap->map.max_entries = attr->max_entries;
+ smap->map.map_flags = attr->map_flags;
smap->n_buckets = n_buckets;
smap->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 590ce0a..85ea598 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -579,14 +579,18 @@
goto err_put;
}
- err = -ENOMEM;
- key = kmalloc(map->key_size, GFP_USER);
- if (!key)
- goto err_put;
+ if (ukey) {
+ err = -ENOMEM;
+ key = kmalloc(map->key_size, GFP_USER);
+ if (!key)
+ goto err_put;
- err = -EFAULT;
- if (copy_from_user(key, ukey, map->key_size) != 0)
- goto free_key;
+ err = -EFAULT;
+ if (copy_from_user(key, ukey, map->key_size) != 0)
+ goto free_key;
+ } else {
+ key = NULL;
+ }
err = -ENOMEM;
next_key = kmalloc(map->key_size, GFP_USER);
@@ -970,7 +974,7 @@
union bpf_attr attr = {};
int err;
- if (!capable(CAP_SYS_ADMIN) && sysctl_unprivileged_bpf_disabled)
+ if (sysctl_unprivileged_bpf_disabled && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (!access_ok(VERIFY_READ, uattr, 1))
diff --git a/kernel/cfi.c b/kernel/cfi.c
new file mode 100644
index 0000000..6951c25
--- /dev/null
+++ b/kernel/cfi.c
@@ -0,0 +1,299 @@
+/*
+ * CFI (Control Flow Integrity) error and slowpath handling
+ *
+ * Copyright (C) 2017 Google, Inc.
+ */
+
+#include <linux/gfp.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+#include <asm/bug.h>
+#include <asm/cacheflush.h>
+#include <asm/memory.h>
+
+/* Compiler-defined handler names */
+#ifdef CONFIG_CFI_PERMISSIVE
+#define cfi_failure_handler __ubsan_handle_cfi_check_fail
+#define cfi_slowpath_handler __cfi_slowpath_diag
+#else /* enforcing */
+#define cfi_failure_handler __ubsan_handle_cfi_check_fail_abort
+#define cfi_slowpath_handler __cfi_slowpath
+#endif /* CONFIG_CFI_PERMISSIVE */
+
+static inline void handle_cfi_failure(void *ptr)
+{
+#ifdef CONFIG_CFI_PERMISSIVE
+ WARN_RATELIMIT(1, "CFI failure (target: [<%px>] %pF):\n", ptr, ptr);
+#else
+ pr_err("CFI failure (target: [<%px>] %pF):\n", ptr, ptr);
+ BUG();
+#endif
+}
+
+#ifdef CONFIG_MODULES
+#ifdef CONFIG_CFI_CLANG_SHADOW
+struct shadow_range {
+ /* Module address range */
+ unsigned long mod_min_addr;
+ unsigned long mod_max_addr;
+ /* Module page range */
+ unsigned long min_page;
+ unsigned long max_page;
+};
+
+#define SHADOW_ORDER 1
+#define SHADOW_PAGES (1 << SHADOW_ORDER)
+#define SHADOW_SIZE \
+ ((SHADOW_PAGES * PAGE_SIZE - sizeof(struct shadow_range)) / sizeof(u16))
+#define SHADOW_INVALID 0xFFFF
+
+struct cfi_shadow {
+ /* Page range covered by the shadow */
+ struct shadow_range r;
+ /* Page offsets to __cfi_check functions in modules */
+ u16 shadow[SHADOW_SIZE];
+};
+
+static DEFINE_SPINLOCK(shadow_update_lock);
+static struct cfi_shadow __rcu *cfi_shadow __read_mostly = NULL;
+
+static inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr)
+{
+ unsigned long index;
+ unsigned long page = ptr >> PAGE_SHIFT;
+
+ if (unlikely(page < s->r.min_page))
+ return -1; /* Outside of module area */
+
+ index = page - s->r.min_page;
+
+ if (index >= SHADOW_SIZE)
+ return -1; /* Cannot be addressed with shadow */
+
+ return (int)index;
+}
+
+static inline unsigned long shadow_to_ptr(const struct cfi_shadow *s,
+ int index)
+{
+ BUG_ON(index < 0 || index >= SHADOW_SIZE);
+
+ if (unlikely(s->shadow[index] == SHADOW_INVALID))
+ return 0;
+
+ return (s->r.min_page + s->shadow[index]) << PAGE_SHIFT;
+}
+
+static void prepare_next_shadow(const struct cfi_shadow __rcu *prev,
+ struct cfi_shadow *next)
+{
+ int i, index, check;
+
+ /* Mark everything invalid */
+ memset(next->shadow, 0xFF, sizeof(next->shadow));
+
+ if (!prev)
+ return; /* No previous shadow */
+
+ /* If the base address didn't change, update is not needed */
+ if (prev->r.min_page == next->r.min_page) {
+ memcpy(next->shadow, prev->shadow, sizeof(next->shadow));
+ return;
+ }
+
+ /* Convert the previous shadow to the new address range */
+ for (i = 0; i < SHADOW_SIZE; ++i) {
+ if (prev->shadow[i] == SHADOW_INVALID)
+ continue;
+
+ index = ptr_to_shadow(next, shadow_to_ptr(prev, i));
+ if (index < 0)
+ continue;
+
+ check = ptr_to_shadow(next,
+ shadow_to_ptr(prev, prev->shadow[i]));
+ if (check < 0)
+ continue;
+
+ next->shadow[index] = (u16)check;
+ }
+}
+
+static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod)
+{
+ unsigned long ptr;
+ unsigned long min_page_addr;
+ unsigned long max_page_addr;
+ unsigned long check = (unsigned long)mod->cfi_check;
+ int check_index = ptr_to_shadow(s, check);
+
+ BUG_ON((check & PAGE_MASK) != check); /* Must be page aligned */
+
+ if (check_index < 0)
+ return; /* Module not addressable with shadow */
+
+ min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK;
+ max_page_addr = (unsigned long)mod->core_layout.base +
+ mod->core_layout.text_size;
+ max_page_addr &= PAGE_MASK;
+
+ /* For each page, store the check function index in the shadow */
+ for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) {
+ int index = ptr_to_shadow(s, ptr);
+ if (index >= 0) {
+ /* Assume a page only contains code for one module */
+ BUG_ON(s->shadow[index] != SHADOW_INVALID);
+ s->shadow[index] = (u16)check_index;
+ }
+ }
+}
+
+static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod)
+{
+ unsigned long ptr;
+ unsigned long min_page_addr;
+ unsigned long max_page_addr;
+
+ min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK;
+ max_page_addr = (unsigned long)mod->core_layout.base +
+ mod->core_layout.text_size;
+ max_page_addr &= PAGE_MASK;
+
+ for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) {
+ int index = ptr_to_shadow(s, ptr);
+ if (index >= 0)
+ s->shadow[index] = SHADOW_INVALID;
+ }
+}
+
+typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *);
+
+static void update_shadow(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr, update_shadow_fn fn)
+{
+ struct cfi_shadow *prev;
+ struct cfi_shadow *next = (struct cfi_shadow *)
+ __get_free_pages(GFP_KERNEL, SHADOW_ORDER);
+
+ BUG_ON(!next);
+
+ next->r.mod_min_addr = min_addr;
+ next->r.mod_max_addr = max_addr;
+ next->r.min_page = min_addr >> PAGE_SHIFT;
+ next->r.max_page = max_addr >> PAGE_SHIFT;
+
+ spin_lock(&shadow_update_lock);
+ prev = rcu_dereference_protected(cfi_shadow, 1);
+ prepare_next_shadow(prev, next);
+
+ fn(next, mod);
+ set_memory_ro((unsigned long)next, SHADOW_PAGES);
+ rcu_assign_pointer(cfi_shadow, next);
+
+ spin_unlock(&shadow_update_lock);
+ synchronize_rcu();
+
+ if (prev) {
+ set_memory_rw((unsigned long)prev, SHADOW_PAGES);
+ free_pages((unsigned long)prev, SHADOW_ORDER);
+ }
+}
+
+void cfi_module_add(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+ update_shadow(mod, min_addr, max_addr, add_module_to_shadow);
+}
+EXPORT_SYMBOL(cfi_module_add);
+
+void cfi_module_remove(struct module *mod, unsigned long min_addr,
+ unsigned long max_addr)
+{
+ update_shadow(mod, min_addr, max_addr, remove_module_from_shadow);
+}
+EXPORT_SYMBOL(cfi_module_remove);
+
+static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s,
+ unsigned long ptr)
+{
+ int index;
+ unsigned long check;
+
+ if (unlikely(!s))
+ return NULL; /* No shadow available */
+
+ if (ptr < s->r.mod_min_addr || ptr > s->r.mod_max_addr)
+ return NULL; /* Not in a mapped module */
+
+ index = ptr_to_shadow(s, ptr);
+ if (index < 0)
+ return NULL; /* Cannot be addressed with shadow */
+
+ return (cfi_check_fn)shadow_to_ptr(s, index);
+}
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+static inline cfi_check_fn find_module_cfi_check(void *ptr)
+{
+ struct module *mod;
+
+ preempt_disable();
+ mod = __module_address((unsigned long)ptr);
+ preempt_enable();
+
+ if (mod)
+ return mod->cfi_check;
+
+ return CFI_CHECK_FN;
+}
+
+static inline cfi_check_fn find_cfi_check(void *ptr)
+{
+#ifdef CONFIG_CFI_CLANG_SHADOW
+ cfi_check_fn f;
+
+ if (!rcu_access_pointer(cfi_shadow))
+ return CFI_CHECK_FN; /* No loaded modules */
+
+ /* Look up the __cfi_check function to use */
+ rcu_read_lock();
+ f = ptr_to_check_fn(rcu_dereference(cfi_shadow), (unsigned long)ptr);
+ rcu_read_unlock();
+
+ if (f)
+ return f;
+
+ /*
+ * Fall back to find_module_cfi_check, which works also for a larger
+ * module address space, but is slower.
+ */
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+ return find_module_cfi_check(ptr);
+}
+
+void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag)
+{
+ cfi_check_fn check = find_cfi_check(ptr);
+
+ if (likely(check))
+ check(id, ptr, diag);
+ else /* Don't allow unchecked modules */
+ handle_cfi_failure(ptr);
+}
+EXPORT_SYMBOL(cfi_slowpath_handler);
+#endif /* CONFIG_MODULES */
+
+void cfi_failure_handler(void *data, void *ptr, void *vtable)
+{
+ handle_cfi_failure(ptr);
+}
+EXPORT_SYMBOL(cfi_failure_handler);
+
+void __cfi_check_fail(void *data, void *ptr)
+{
+ handle_cfi_failure(ptr);
+}
diff --git a/kernel/cpu.c b/kernel/cpu.c
index c68d150..3f3b7f8 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -64,6 +64,12 @@
static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state);
+#if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP)
+static struct lock_class_key cpuhp_state_key;
+static struct lockdep_map cpuhp_state_lock_map =
+ STATIC_LOCKDEP_MAP_INIT("cpuhp_state", &cpuhp_state_key);
+#endif
+
/**
* cpuhp_step - Hotplug state machine step
* @name: Name of the step
@@ -572,6 +578,7 @@
st->should_run = false;
+ lock_map_acquire(&cpuhp_state_lock_map);
/* Single callback invocation for [un]install ? */
if (st->single) {
if (st->cb_state < CPUHP_AP_ONLINE) {
@@ -603,6 +610,7 @@
else if (st->state > st->target)
ret = cpuhp_ap_offline(cpu, st);
}
+ lock_map_release(&cpuhp_state_lock_map);
st->result = ret;
complete(&st->done);
}
@@ -617,6 +625,9 @@
if (!cpu_online(cpu))
return 0;
+ lock_map_acquire(&cpuhp_state_lock_map);
+ lock_map_release(&cpuhp_state_lock_map);
+
/*
* If we are up and running, use the hotplug thread. For early calls
* we invoke the thread function directly.
@@ -660,6 +671,8 @@
enum cpuhp_state state = st->state;
trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work);
+ lock_map_acquire(&cpuhp_state_lock_map);
+ lock_map_release(&cpuhp_state_lock_map);
__cpuhp_kick_ap_work(st);
wait_for_completion(&st->done);
trace_cpuhp_exit(cpu, st->state, state, st->result);
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index e9fdb52..c265f1c 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -117,23 +117,20 @@
goto exit;
}
- if (count > 1) {
- /* If the allocation failed, give up */
- if (!callchain_cpus_entries)
- err = -ENOMEM;
- /*
- * If requesting per event more than the global cap,
- * return a different error to help userspace figure
- * this out.
- *
- * And also do it here so that we have &callchain_mutex held.
- */
- if (event_max_stack > sysctl_perf_event_max_stack)
- err = -EOVERFLOW;
+ /*
+ * If requesting per event more than the global cap,
+ * return a different error to help userspace figure
+ * this out.
+ *
+ * And also do it here so that we have &callchain_mutex held.
+ */
+ if (event_max_stack > sysctl_perf_event_max_stack) {
+ err = -EOVERFLOW;
goto exit;
}
- err = alloc_callchain_buffers();
+ if (count == 1)
+ err = alloc_callchain_buffers();
exit:
if (err)
atomic_dec(&nr_callchain_events);
@@ -227,12 +224,18 @@
}
if (regs) {
+ mm_segment_t fs;
+
if (crosstask)
goto exit_put;
if (add_mark)
perf_callchain_store_context(&ctx, PERF_CONTEXT_USER);
+
+ fs = get_fs();
+ set_fs(USER_DS);
perf_callchain_user(&ctx, regs);
+ set_fs(fs);
}
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index cd941f8..349bc92 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -486,7 +486,7 @@
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
- int ret = proc_dointvec(table, write, buffer, lenp, ppos);
+ int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
if (ret || !write)
return ret;
@@ -4181,6 +4181,9 @@
if (event->ctx)
put_ctx(event->ctx);
+ if (event->hw.target)
+ put_task_struct(event->hw.target);
+
exclusive_event_destroy(event);
module_put(event->pmu->module);
@@ -5799,9 +5802,6 @@
__output_copy(handle, values, n * sizeof(u64));
}
-/*
- * XXX PERF_FORMAT_GROUP vs inherited events seems difficult.
- */
static void perf_output_read_group(struct perf_output_handle *handle,
struct perf_event *event,
u64 enabled, u64 running)
@@ -5846,6 +5846,13 @@
#define PERF_FORMAT_TOTAL_TIMES (PERF_FORMAT_TOTAL_TIME_ENABLED|\
PERF_FORMAT_TOTAL_TIME_RUNNING)
+/*
+ * XXX PERF_SAMPLE_READ vs inherited events seems difficult.
+ *
+ * The problem is that its both hard and excessively expensive to iterate the
+ * child list, not to mention that its impossible to IPI the children running
+ * on another CPU, from interrupt/NMI context.
+ */
static void perf_output_read(struct perf_output_handle *handle,
struct perf_event *event)
{
@@ -9462,6 +9469,7 @@
* and we cannot use the ctx information because we need the
* pmu before we get a ctx.
*/
+ get_task_struct(task);
event->hw.target = task;
}
@@ -9511,9 +9519,10 @@
local64_set(&hwc->period_left, hwc->sample_period);
/*
- * we currently do not support PERF_FORMAT_GROUP on inherited events
+ * We currently do not support PERF_SAMPLE_READ on inherited events.
+ * See perf_output_read().
*/
- if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
+ if (attr->inherit && (attr->sample_type & PERF_SAMPLE_READ))
goto err_ns;
if (!has_branch_stack(event))
@@ -9541,8 +9550,10 @@
event->addr_filters_offs = kcalloc(pmu->nr_addr_filters,
sizeof(unsigned long),
GFP_KERNEL);
- if (!event->addr_filters_offs)
+ if (!event->addr_filters_offs) {
+ err = -ENOMEM;
goto err_per_task;
+ }
/* force hw sync on the address filters */
event->addr_filters_gen = 1;
@@ -9576,6 +9587,8 @@
perf_detach_cgroup(event);
if (event->ns)
put_pid_ns(event->ns);
+ if (event->hw.target)
+ put_task_struct(event->hw.target);
kfree(event);
return ERR_PTR(err);
@@ -9695,9 +9708,9 @@
* __u16 sample size limit.
*/
if (attr->sample_stack_user >= USHRT_MAX)
- ret = -EINVAL;
+ return -EINVAL;
else if (!IS_ALIGNED(attr->sample_stack_user, sizeof(u64)))
- ret = -EINVAL;
+ return -EINVAL;
}
if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index 3f8cb1e..253ae2d 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -427,16 +427,9 @@
* modify_user_hw_breakpoint - modify a user-space hardware breakpoint
* @bp: the breakpoint structure to modify
* @attr: new breakpoint attributes
- * @triggered: callback to trigger when we hit the breakpoint
- * @tsk: pointer to 'task_struct' of the process to which the address belongs
*/
int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
{
- u64 old_addr = bp->attr.bp_addr;
- u64 old_len = bp->attr.bp_len;
- int old_type = bp->attr.bp_type;
- int err = 0;
-
/*
* modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it
* will not be possible to raise IPIs that invoke __perf_event_disable.
@@ -451,27 +444,18 @@
bp->attr.bp_addr = attr->bp_addr;
bp->attr.bp_type = attr->bp_type;
bp->attr.bp_len = attr->bp_len;
+ bp->attr.disabled = 1;
- if (attr->disabled)
- goto end;
+ if (!attr->disabled) {
+ int err = validate_hw_breakpoint(bp);
- err = validate_hw_breakpoint(bp);
- if (!err)
+ if (err)
+ return err;
+
perf_event_enable(bp);
-
- if (err) {
- bp->attr.bp_addr = old_addr;
- bp->attr.bp_type = old_type;
- bp->attr.bp_len = old_len;
- if (!bp->attr.disabled)
- perf_event_enable(bp);
-
- return err;
+ bp->attr.disabled = 0;
}
-end:
- bp->attr.disabled = attr->disabled;
-
return 0;
}
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 257fa46..017f793 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/circ_buf.h>
#include <linux/poll.h>
+#include <linux/nospec.h>
#include "internal.h"
@@ -844,8 +845,10 @@
return NULL;
/* AUX space */
- if (pgoff >= rb->aux_pgoff)
- return virt_to_page(rb->aux_pages[pgoff - rb->aux_pgoff]);
+ if (pgoff >= rb->aux_pgoff) {
+ int aux_pgoff = array_index_nospec(pgoff - rb->aux_pgoff, rb->aux_nr_pages);
+ return virt_to_page(rb->aux_pages[aux_pgoff]);
+ }
}
return __perf_mmap_to_page(rb, pgoff);
diff --git a/kernel/exit.c b/kernel/exit.c
index 35ff283..2c18194 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -54,6 +54,7 @@
#include <linux/writeback.h>
#include <linux/shm.h>
#include <linux/kcov.h>
+#include <linux/cpufreq_times.h>
#include "sched/tune.h"
@@ -171,6 +172,9 @@
{
struct task_struct *leader;
int zap_leader;
+#ifdef CONFIG_CPU_FREQ_TIMES
+ cpufreq_task_times_exit(p);
+#endif
repeat:
/* don't need to get the RCU readlock here - the process is dead and
* can't be modifying its own credentials. But shut RCU-lockdep up */
@@ -765,7 +769,11 @@
* leave this task alone and wait for reboot.
*/
if (unlikely(tsk->flags & PF_EXITING)) {
+#ifdef CONFIG_PANIC_ON_RECURSIVE_FAULT
+ panic("Recursive fault!\n");
+#else
pr_alert("Fixing recursive fault but reboot is needed!\n");
+#endif
/*
* We can do this unlocked here. The futex code uses
* this flag just to verify whether the pi state
@@ -1672,6 +1680,10 @@
__WNOTHREAD|__WCLONE|__WALL))
return -EINVAL;
+ /* -INT_MIN is not defined */
+ if (upid == INT_MIN)
+ return -ESRCH;
+
if (upid == -1)
type = PIDTYPE_MAX;
else if (upid < 0) {
diff --git a/kernel/futex.c b/kernel/futex.c
index bb2265a..c3ea6f2 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1458,6 +1458,45 @@
return ret;
}
+static int futex_atomic_op_inuser(unsigned int encoded_op, u32 __user *uaddr)
+{
+ unsigned int op = (encoded_op & 0x70000000) >> 28;
+ unsigned int cmp = (encoded_op & 0x0f000000) >> 24;
+ int oparg = sign_extend32((encoded_op & 0x00fff000) >> 12, 11);
+ int cmparg = sign_extend32(encoded_op & 0x00000fff, 11);
+ int oldval, ret;
+
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) {
+ if (oparg < 0 || oparg > 31)
+ return -EINVAL;
+ oparg = 1 << oparg;
+ }
+
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+ return -EFAULT;
+
+ ret = arch_futex_atomic_op_inuser(op, oparg, &oldval, uaddr);
+ if (ret)
+ return ret;
+
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ:
+ return oldval == cmparg;
+ case FUTEX_OP_CMP_NE:
+ return oldval != cmparg;
+ case FUTEX_OP_CMP_LT:
+ return oldval < cmparg;
+ case FUTEX_OP_CMP_GE:
+ return oldval >= cmparg;
+ case FUTEX_OP_CMP_LE:
+ return oldval <= cmparg;
+ case FUTEX_OP_CMP_GT:
+ return oldval > cmparg;
+ default:
+ return -ENOSYS;
+ }
+}
+
/*
* Wake up all waiters hashed on the physical page that is mapped
* to this virtual address:
diff --git a/kernel/irq/debug.h b/kernel/irq/debug.h
index e75e29e..3514d95 100644
--- a/kernel/irq/debug.h
+++ b/kernel/irq/debug.h
@@ -11,6 +11,11 @@
static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc)
{
+ static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
+
+ if (!__ratelimit(&ratelimit))
+ return;
+
printk("irq %d, desc: %p, depth: %d, count: %d, unhandled: %d\n",
irq, desc, desc->depth, desc->irq_count, desc->irqs_unhandled);
printk("->handle_irq(): %p, ", desc->handle_irq);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index c1195eb..c93d4df 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -853,7 +853,7 @@
* This code is triggered unconditionally. Check the affinity
* mask pointer. For CPU_MASK_OFFSTACK=n this is optimized out.
*/
- if (desc->irq_common_data.affinity)
+ if (cpumask_available(desc->irq_common_data.affinity))
cpumask_copy(mask, desc->irq_common_data.affinity);
else
valid = false;
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index fafd1a3..a0c3365 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -306,6 +306,24 @@
return !!module_address_lookup(addr, symbolsize, offset, NULL, namebuf);
}
+#ifdef CONFIG_CFI_CLANG
+/*
+ * LLVM appends .cfi to function names when CONFIG_CFI_CLANG is enabled,
+ * which causes confusion and potentially breaks user space tools, so we
+ * will strip the postfix from expanded symbol names.
+ */
+static inline void cleanup_symbol_name(char *s)
+{
+ char *res;
+
+ res = strrchr(s, '.');
+ if (res && !strcmp(res, ".cfi"))
+ *res = '\0';
+}
+#else
+static inline void cleanup_symbol_name(char *s) {}
+#endif
+
/*
* Lookup an address
* - modname is set to NULL if it's in the kernel.
@@ -330,16 +348,23 @@
namebuf, KSYM_NAME_LEN);
if (modname)
*modname = NULL;
- return namebuf;
+ goto found;
}
/* See if it's in a module. */
- return module_address_lookup(addr, symbolsize, offset, modname,
- namebuf);
+ if (!module_address_lookup(addr, symbolsize, offset, modname,
+ namebuf))
+ return NULL;
+
+found:
+ cleanup_symbol_name(namebuf);
+ return namebuf;
}
int lookup_symbol_name(unsigned long addr, char *symname)
{
+ int res;
+
symname[0] = '\0';
symname[KSYM_NAME_LEN - 1] = '\0';
@@ -350,15 +375,23 @@
/* Grab name */
kallsyms_expand_symbol(get_symbol_offset(pos),
symname, KSYM_NAME_LEN);
- return 0;
+ goto found;
}
/* See if it's in a module. */
- return lookup_module_symbol_name(addr, symname);
+ res = lookup_module_symbol_name(addr, symname);
+ if (res)
+ return res;
+
+found:
+ cleanup_symbol_name(symname);
+ return 0;
}
int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
unsigned long *offset, char *modname, char *name)
{
+ int res;
+
name[0] = '\0';
name[KSYM_NAME_LEN - 1] = '\0';
@@ -370,10 +403,16 @@
kallsyms_expand_symbol(get_symbol_offset(pos),
name, KSYM_NAME_LEN);
modname[0] = '\0';
- return 0;
+ goto found;
}
/* See if it's in a module. */
- return lookup_module_symbol_attrs(addr, size, offset, modname, name);
+ res = lookup_module_symbol_attrs(addr, size, offset, modname, name);
+ if (res)
+ return res;
+
+found:
+ cleanup_symbol_name(name);
+ return 0;
}
/* Look up a kernel symbol and return it in a text buffer. */
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index a1a07cf..6948518 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -125,7 +125,7 @@
return module_alloc(PAGE_SIZE);
}
-static void free_insn_page(void *page)
+void __weak free_insn_page(void *page)
{
module_memfree(page);
}
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c
index d3de04b..babc67c 100644
--- a/kernel/locking/locktorture.c
+++ b/kernel/locking/locktorture.c
@@ -641,8 +641,7 @@
{
bool fail = 0;
int i, n_stress;
- long max = 0;
- long min = statp[0].n_lock_acquired;
+ long max = 0, min = statp ? statp[0].n_lock_acquired : 0;
long long sum = 0;
n_stress = write ? cxt.nrealwriters_stress : cxt.nrealreaders_stress;
@@ -749,7 +748,7 @@
* such, only perform the underlying torture-specific cleanups,
* and avoid anything related to locktorture.
*/
- if (!cxt.lwsa)
+ if (!cxt.lwsa && !cxt.lrsa)
goto end;
if (writer_tasks) {
@@ -823,6 +822,13 @@
firsterr = -EINVAL;
goto unwind;
}
+
+ if (nwriters_stress == 0 && nreaders_stress == 0) {
+ pr_alert("lock-torture: must run at least one locking thread\n");
+ firsterr = -EINVAL;
+ goto unwind;
+ }
+
if (cxt.cur_ops->init)
cxt.cur_ops->init();
@@ -846,17 +852,19 @@
#endif
/* Initialize the statistics so that each run gets its own numbers. */
+ if (nwriters_stress) {
+ lock_is_write_held = 0;
+ cxt.lwsa = kmalloc(sizeof(*cxt.lwsa) * cxt.nrealwriters_stress, GFP_KERNEL);
+ if (cxt.lwsa == NULL) {
+ VERBOSE_TOROUT_STRING("cxt.lwsa: Out of memory");
+ firsterr = -ENOMEM;
+ goto unwind;
+ }
- lock_is_write_held = 0;
- cxt.lwsa = kmalloc(sizeof(*cxt.lwsa) * cxt.nrealwriters_stress, GFP_KERNEL);
- if (cxt.lwsa == NULL) {
- VERBOSE_TOROUT_STRING("cxt.lwsa: Out of memory");
- firsterr = -ENOMEM;
- goto unwind;
- }
- for (i = 0; i < cxt.nrealwriters_stress; i++) {
- cxt.lwsa[i].n_lock_fail = 0;
- cxt.lwsa[i].n_lock_acquired = 0;
+ for (i = 0; i < cxt.nrealwriters_stress; i++) {
+ cxt.lwsa[i].n_lock_fail = 0;
+ cxt.lwsa[i].n_lock_acquired = 0;
+ }
}
if (cxt.cur_ops->readlock) {
@@ -873,19 +881,21 @@
cxt.nrealreaders_stress = cxt.nrealwriters_stress;
}
- lock_is_read_held = 0;
- cxt.lrsa = kmalloc(sizeof(*cxt.lrsa) * cxt.nrealreaders_stress, GFP_KERNEL);
- if (cxt.lrsa == NULL) {
- VERBOSE_TOROUT_STRING("cxt.lrsa: Out of memory");
- firsterr = -ENOMEM;
- kfree(cxt.lwsa);
- cxt.lwsa = NULL;
- goto unwind;
- }
+ if (nreaders_stress) {
+ lock_is_read_held = 0;
+ cxt.lrsa = kmalloc(sizeof(*cxt.lrsa) * cxt.nrealreaders_stress, GFP_KERNEL);
+ if (cxt.lrsa == NULL) {
+ VERBOSE_TOROUT_STRING("cxt.lrsa: Out of memory");
+ firsterr = -ENOMEM;
+ kfree(cxt.lwsa);
+ cxt.lwsa = NULL;
+ goto unwind;
+ }
- for (i = 0; i < cxt.nrealreaders_stress; i++) {
- cxt.lrsa[i].n_lock_fail = 0;
- cxt.lrsa[i].n_lock_acquired = 0;
+ for (i = 0; i < cxt.nrealreaders_stress; i++) {
+ cxt.lrsa[i].n_lock_fail = 0;
+ cxt.lrsa[i].n_lock_acquired = 0;
+ }
}
}
@@ -915,12 +925,14 @@
goto unwind;
}
- writer_tasks = kzalloc(cxt.nrealwriters_stress * sizeof(writer_tasks[0]),
- GFP_KERNEL);
- if (writer_tasks == NULL) {
- VERBOSE_TOROUT_ERRSTRING("writer_tasks: Out of memory");
- firsterr = -ENOMEM;
- goto unwind;
+ if (nwriters_stress) {
+ writer_tasks = kzalloc(cxt.nrealwriters_stress * sizeof(writer_tasks[0]),
+ GFP_KERNEL);
+ if (writer_tasks == NULL) {
+ VERBOSE_TOROUT_ERRSTRING("writer_tasks: Out of memory");
+ firsterr = -ENOMEM;
+ goto unwind;
+ }
}
if (cxt.cur_ops->readlock) {
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 2c49d76..196cc46 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -236,8 +236,7 @@
* then right waiter has a dl_prio() too.
*/
if (dl_prio(left->prio))
- return dl_time_before(left->task->dl.deadline,
- right->task->dl.deadline);
+ return dl_time_before(left->deadline, right->deadline);
return 0;
}
@@ -704,7 +703,26 @@
/* [7] Requeue the waiter in the lock waiter tree. */
rt_mutex_dequeue(lock, waiter);
+
+ /*
+ * Update the waiter prio fields now that we're dequeued.
+ *
+ * These values can have changed through either:
+ *
+ * sys_sched_set_scheduler() / sys_sched_setattr()
+ *
+ * or
+ *
+ * DL CBS enforcement advancing the effective deadline.
+ *
+ * Even though pi_waiters also uses these fields, and that tree is only
+ * updated in [11], we can do this here, since we hold [L], which
+ * serializes all pi_waiters access and rb_erase() does not care about
+ * the values of the node being removed.
+ */
waiter->prio = task->prio;
+ waiter->deadline = task->dl.deadline;
+
rt_mutex_enqueue(lock, waiter);
/* [8] Release the task */
@@ -831,6 +849,8 @@
static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
struct rt_mutex_waiter *waiter)
{
+ lockdep_assert_held(&lock->wait_lock);
+
/*
* Before testing whether we can acquire @lock, we set the
* RT_MUTEX_HAS_WAITERS bit in @lock->owner. This forces all
@@ -958,6 +978,8 @@
struct rt_mutex *next_lock;
int chain_walk = 0, res;
+ lockdep_assert_held(&lock->wait_lock);
+
/*
* Early deadlock detection. We really don't want the task to
* enqueue on itself just to untangle the mess later. It's not
@@ -975,6 +997,7 @@
waiter->task = task;
waiter->lock = lock;
waiter->prio = task->prio;
+ waiter->deadline = task->dl.deadline;
/* Get the top priority waiter on the lock */
if (rt_mutex_has_waiters(lock))
@@ -1080,6 +1103,8 @@
struct task_struct *owner = rt_mutex_owner(lock);
struct rt_mutex *next_lock;
+ lockdep_assert_held(&lock->wait_lock);
+
raw_spin_lock(¤t->pi_lock);
rt_mutex_dequeue(lock, waiter);
current->pi_blocked_on = NULL;
diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h
index e317e1c..50848b4 100644
--- a/kernel/locking/rtmutex_common.h
+++ b/kernel/locking/rtmutex_common.h
@@ -33,6 +33,7 @@
struct rt_mutex *deadlock_lock;
#endif
int prio;
+ u64 deadline;
};
/*
diff --git a/kernel/memremap.c b/kernel/memremap.c
index 426547a..f61a8c3 100644
--- a/kernel/memremap.c
+++ b/kernel/memremap.c
@@ -194,7 +194,7 @@
}
EXPORT_SYMBOL(put_zone_device_page);
-static void pgmap_radix_release(struct resource *res)
+static void pgmap_radix_release(struct resource *res, resource_size_t end_key)
{
resource_size_t key, align_start, align_size, align_end;
@@ -203,8 +203,11 @@
align_end = align_start + align_size - 1;
mutex_lock(&pgmap_lock);
- for (key = res->start; key <= res->end; key += SECTION_SIZE)
+ for (key = res->start; key <= res->end; key += SECTION_SIZE) {
+ if (key >= end_key)
+ break;
radix_tree_delete(&pgmap_radix, key >> PA_SECTION_SHIFT);
+ }
mutex_unlock(&pgmap_lock);
}
@@ -255,7 +258,7 @@
unlock_device_hotplug();
untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
- pgmap_radix_release(res);
+ pgmap_radix_release(res, -1);
dev_WARN_ONCE(dev, pgmap->altmap && pgmap->altmap->alloc,
"%s: failed to free all reserved pages\n", __func__);
}
@@ -289,7 +292,7 @@
void *devm_memremap_pages(struct device *dev, struct resource *res,
struct percpu_ref *ref, struct vmem_altmap *altmap)
{
- resource_size_t key, align_start, align_size, align_end;
+ resource_size_t key = 0, align_start, align_size, align_end;
pgprot_t pgprot = PAGE_KERNEL;
struct dev_pagemap *pgmap;
struct page_map *page_map;
@@ -392,7 +395,7 @@
untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
err_pfn_remap:
err_radix:
- pgmap_radix_release(res);
+ pgmap_radix_release(res, key);
devres_free(page_map);
return ERR_PTR(error);
}
diff --git a/kernel/module.c b/kernel/module.c
index 07bfb99..13ad2f8 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1911,6 +1911,9 @@
/* livepatching wants to disable read-only so it can frob module. */
void module_disable_ro(const struct module *mod)
{
+ if (!rodata_enabled)
+ return;
+
frob_text(&mod->core_layout, set_memory_rw);
frob_rodata(&mod->core_layout, set_memory_rw);
frob_ro_after_init(&mod->core_layout, set_memory_rw);
@@ -1920,6 +1923,9 @@
void module_enable_ro(const struct module *mod, bool after_init)
{
+ if (!rodata_enabled)
+ return;
+
frob_text(&mod->core_layout, set_memory_ro);
frob_rodata(&mod->core_layout, set_memory_ro);
frob_text(&mod->init_layout, set_memory_ro);
@@ -1952,6 +1958,9 @@
{
struct module *mod;
+ if (!rodata_enabled)
+ return;
+
mutex_lock(&module_mutex);
list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
@@ -1968,6 +1977,9 @@
{
struct module *mod;
+ if (!rodata_enabled)
+ return;
+
mutex_lock(&module_mutex);
list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
@@ -1981,10 +1993,12 @@
static void disable_ro_nx(const struct module_layout *layout)
{
- frob_text(layout, set_memory_rw);
- frob_rodata(layout, set_memory_rw);
+ if (rodata_enabled) {
+ frob_text(layout, set_memory_rw);
+ frob_rodata(layout, set_memory_rw);
+ frob_ro_after_init(layout, set_memory_rw);
+ }
frob_rodata(layout, set_memory_x);
- frob_ro_after_init(layout, set_memory_rw);
frob_ro_after_init(layout, set_memory_x);
frob_writable_data(layout, set_memory_x);
}
@@ -2085,6 +2099,8 @@
{
}
+static void cfi_cleanup(struct module *mod);
+
/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod)
{
@@ -2126,6 +2142,10 @@
/* This may be empty, but that's OK */
disable_ro_nx(&mod->init_layout);
+
+ /* Clean up CFI for the module. */
+ cfi_cleanup(mod);
+
module_arch_freeing_init(mod);
module_memfree(mod->init_layout.base);
kfree(mod->args);
@@ -3307,6 +3327,8 @@
return 0;
}
+static void cfi_init(struct module *mod);
+
static int post_relocation(struct module *mod, const struct load_info *info)
{
/* Sort exception table now relocations are done. */
@@ -3319,6 +3341,9 @@
/* Setup kallsyms-specific fields. */
add_kallsyms(mod, info);
+ /* Setup CFI for the module. */
+ cfi_init(mod);
+
/* Arch-specific module finalizing. */
return module_finalize(info->hdr, info->sechdrs, mod);
}
@@ -4053,6 +4078,22 @@
}
#endif /* CONFIG_KALLSYMS */
+static void cfi_init(struct module *mod)
+{
+#ifdef CONFIG_CFI_CLANG
+ mod->cfi_check =
+ (cfi_check_fn)mod_find_symname(mod, CFI_CHECK_FN_NAME);
+ cfi_module_add(mod, module_addr_min, module_addr_max);
+#endif
+}
+
+static void cfi_cleanup(struct module *mod)
+{
+#ifdef CONFIG_CFI_CLANG
+ cfi_module_remove(mod, module_addr_min, module_addr_max);
+#endif
+}
+
static char *module_flags(struct module *mod, char *buf)
{
int bx = 0;
diff --git a/kernel/pid.c b/kernel/pid.c
index 693a643..fa704f8 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -322,8 +322,10 @@
}
if (unlikely(is_child_reaper(pid))) {
- if (pid_ns_prepare_proc(ns))
+ if (pid_ns_prepare_proc(ns)) {
+ disable_pid_allocation(ns);
goto out_free;
+ }
}
get_pid_ns(ns);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index a29eaee..bf60b37 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -94,6 +94,16 @@
For more details, refer to the description of CONFIG_HIBERNATION
for booting without resuming.
+config HIBERNATION_SKIP_CRC
+ bool "Skip LZO image CRC check"
+ default n
+ depends on HIBERNATION
+ ---help---
+ Some filesystem devices may have hw based integrity checks. In this
+ scenario, repeating the integrity check in software is unnecessary
+ and wasteful. This config option has no effect if uncompressed
+ hibernation images are used.
+
config ARCH_SAVE_PAGE_KEYS
bool
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 0469d80..d6a0b0f 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -478,8 +478,6 @@
val = c->default_value;
for_each_cpu(cpu, mask) {
- if (cpu_isolated(cpu))
- continue;
switch (c->type) {
case PM_QOS_MIN:
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 95db6b79..1f6aef2 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -606,9 +606,10 @@
}
atomic_set(&d->ready, 0);
- for (i = 0; i < d->run_threads; i++)
- *d->crc32 = crc32_le(*d->crc32,
- d->unc[i], *d->unc_len[i]);
+ if (!IS_ENABLED(CONFIG_HIBERNATION_SKIP_CRC))
+ for (i = 0; i < d->run_threads; i++)
+ *d->crc32 = crc32_le(*d->crc32,
+ d->unc[i], *d->unc_len[i]);
atomic_set(&d->stop, 1);
wake_up(&d->done);
}
@@ -1455,7 +1456,8 @@
if (!snapshot_image_loaded(snapshot))
ret = -ENODATA;
if (!ret) {
- if (swsusp_header->flags & SF_CRC32_MODE) {
+ if ((swsusp_header->flags & SF_CRC32_MODE) &&
+ (!IS_ENABLED(CONFIG_HIBERNATION_SKIP_CRC))) {
if(handle->crc32 != swsusp_header->crc32) {
printk(KERN_ERR
"PM: Invalid image CRC32!\n");
diff --git a/kernel/printk/braille.c b/kernel/printk/braille.c
index d5760c4..61d41ca 100644
--- a/kernel/printk/braille.c
+++ b/kernel/printk/braille.c
@@ -2,12 +2,13 @@
#include <linux/kernel.h>
#include <linux/console.h>
+#include <linux/errno.h>
#include <linux/string.h>
#include "console_cmdline.h"
#include "braille.h"
-char *_braille_console_setup(char **str, char **brl_options)
+int _braille_console_setup(char **str, char **brl_options)
{
if (!strncmp(*str, "brl,", 4)) {
*brl_options = "";
@@ -15,14 +16,14 @@
} else if (!strncmp(*str, "brl=", 4)) {
*brl_options = *str + 4;
*str = strchr(*brl_options, ',');
- if (!*str)
+ if (!*str) {
pr_err("need port name after brl=\n");
- else
- *((*str)++) = 0;
- } else
- return NULL;
+ return -EINVAL;
+ }
+ *((*str)++) = 0;
+ }
- return *str;
+ return 0;
}
int
diff --git a/kernel/printk/braille.h b/kernel/printk/braille.h
index 769d771..749a675 100644
--- a/kernel/printk/braille.h
+++ b/kernel/printk/braille.h
@@ -9,7 +9,14 @@
c->brl_options = brl_options;
}
-char *
+/*
+ * Setup console according to braille options.
+ * Return -EINVAL on syntax error, 0 on success (or no braille option was
+ * actually given).
+ * Modifies str to point to the serial options
+ * Sets brl_options to the parsed braille options.
+ */
+int
_braille_console_setup(char **str, char **brl_options);
int
@@ -25,10 +32,10 @@
{
}
-static inline char *
+static inline int
_braille_console_setup(char **str, char **brl_options)
{
- return NULL;
+ return 0;
}
static inline int
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 20fc294..396b020 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2172,6 +2172,8 @@
console_unlock();
}
+#ifdef CONFIG_CONSOLE_FLUSH_ON_HOTPLUG
+
/**
* console_cpu_notify - print deferred console messages after CPU hotplug
* @self: notifier struct
@@ -2197,6 +2199,8 @@
return NOTIFY_OK;
}
+#endif
+
/**
* console_lock - lock the console system for exclusive use.
*
@@ -2350,7 +2354,7 @@
}
/*
- * Console drivers are called under logbuf_lock, so
+ * Console drivers are called with interrupts disabled, so
* @console_may_schedule should be cleared before; however, we may
* end up dumping a lot of lines, for example, if called from
* console registration path, and should invoke cond_resched()
@@ -2358,11 +2362,15 @@
* scheduling stall on a slow console leading to RCU stall and
* softlockup warnings which exacerbate the issue with more
* messages practically incapacitating the system.
+ *
+ * console_trylock() is not able to detect the preemptive
+ * context reliably. Therefore the value must be stored before
+ * and cleared after the the "again" goto label.
*/
do_cond_resched = console_may_schedule;
+again:
console_may_schedule = 0;
-again:
/*
* We released the console_sem lock, so we need to recheck if
* cpu is online and (if not) is there at least one CON_ANYTIME
@@ -2846,7 +2854,9 @@
unregister_console(con);
}
}
+#ifdef CONFIG_CONSOLE_FLUSH_ON_HOTPLUG
hotcpu_notifier(console_cpu_notify, 0);
+#endif
return 0;
}
late_initcall(printk_late_init);
diff --git a/kernel/resource.c b/kernel/resource.c
index 89778a3..fbea5af 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -633,7 +633,8 @@
alloc.start = constraint->alignf(constraint->alignf_data, &avail,
size, constraint->align);
alloc.end = alloc.start + size - 1;
- if (resource_contains(&avail, &alloc)) {
+ if (alloc.start <= alloc.end &&
+ resource_contains(&avail, &alloc)) {
new->start = alloc.start;
new->end = alloc.end;
return 0;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index ef34eb5..fb2e56c 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -76,6 +76,7 @@
#include <linux/frame.h>
#include <linux/prefetch.h>
#include <linux/irq.h>
+#include <linux/cpufreq_times.h>
#include <asm/switch_to.h>
#include <asm/tlb.h>
@@ -526,7 +527,8 @@
unsigned long flags;
raw_spin_lock_irqsave(&rq->lock, flags);
- resched_curr(rq);
+ if (cpu_online(cpu) || cpu == smp_processor_id())
+ resched_curr(rq);
raw_spin_unlock_irqrestore(&rq->lock, flags);
}
@@ -2173,7 +2175,6 @@
wallclock = ktime_get_ns();
update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
update_task_ravg(p, rq, TASK_WAKE, wallclock, 0);
- cpufreq_update_util(rq, 0);
raw_spin_unlock(&rq->lock);
rcu_read_lock();
@@ -2262,7 +2263,6 @@
update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
update_task_ravg(p, rq, TASK_WAKE, wallclock, 0);
- cpufreq_update_util(rq, 0);
ttwu_activate(rq, p, ENQUEUE_WAKEUP);
note_task_waking(p, wallclock);
}
@@ -2308,6 +2308,7 @@
dl_se->dl_period = 0;
dl_se->flags = 0;
dl_se->dl_bw = 0;
+ dl_se->dl_density = 0;
dl_se->dl_throttled = 0;
dl_se->dl_yielded = 0;
@@ -2343,6 +2344,10 @@
memset(&p->se.statistics, 0, sizeof(p->se.statistics));
#endif
+#ifdef CONFIG_CPU_FREQ_TIMES
+ cpufreq_task_times_init(p);
+#endif
+
RB_CLEAR_NODE(&p->dl.rb_node);
init_dl_task_timer(&p->dl);
__dl_clear_params(p);
@@ -3635,7 +3640,6 @@
update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0);
update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0);
- cpufreq_update_util(rq, 0);
rq->nr_switches++;
rq->curr = next;
++*switch_count;
@@ -3644,7 +3648,6 @@
rq = context_switch(rq, prev, next, cookie); /* unlocks the rq */
} else {
update_task_ravg(prev, rq, TASK_UPDATE, wallclock, 0);
- cpufreq_update_util(rq, 0);
lockdep_unpin_lock(&rq->lock, cookie);
raw_spin_unlock_irq(&rq->lock);
}
@@ -4155,6 +4158,7 @@
dl_se->dl_period = attr->sched_period ?: dl_se->dl_deadline;
dl_se->flags = attr->sched_flags;
dl_se->dl_bw = to_ratio(dl_se->dl_period, dl_se->dl_runtime);
+ dl_se->dl_density = to_ratio(dl_se->dl_deadline, dl_se->dl_runtime);
/*
* Changing the parameters of a task is 'tricky' and we're not doing
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index c0a8a2a..bd64b1a 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -588,7 +588,12 @@
if (new_need > cluster->active_cpus) {
ret = 1;
} else {
- if (new_need == last_need) {
+ /*
+ * When there is no change in need and there are no more
+ * active CPUs than currently needed, just update the
+ * need time stamp and return.
+ */
+ if (new_need == last_need && new_need == cluster->active_cpus) {
cluster->need_ts = now;
spin_unlock_irqrestore(&state_lock, flags);
return 0;
@@ -784,6 +789,7 @@
unsigned long flags;
unsigned int num_cpus = cluster->num_cpus;
unsigned int nr_isolated = 0;
+ bool first_pass = cluster->nr_not_preferred_cpus;
/*
* Protect against entry being removed (and added at tail) by other
@@ -829,6 +835,7 @@
cluster->nr_isolated_cpus += nr_isolated;
spin_unlock_irqrestore(&state_lock, flags);
+again:
/*
* If the number of active CPUs is within the limits, then
* don't force isolation of any busy CPUs.
@@ -848,6 +855,9 @@
if (cluster->active_cpus <= cluster->max_cpus)
break;
+ if (first_pass && !c->not_preferred)
+ continue;
+
spin_unlock_irqrestore(&state_lock, flags);
pr_debug("Trying to isolate CPU%u\n", c->cpu);
@@ -864,6 +874,10 @@
cluster->nr_isolated_cpus += nr_isolated;
spin_unlock_irqrestore(&state_lock, flags);
+ if (first_pass && cluster->active_cpus > cluster->max_cpus) {
+ first_pass = false;
+ goto again;
+ }
}
static void __try_to_unisolate(struct cluster_data *cluster,
diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c
index 87bea1e..aec86a26 100644
--- a/kernel/sched/cpupri.c
+++ b/kernel/sched/cpupri.c
@@ -133,6 +133,8 @@
if (lowest_mask) {
cpumask_and(lowest_mask, tsk_cpus_allowed(p), vec->mask);
+ cpumask_andnot(lowest_mask, lowest_mask,
+ cpu_isolated_mask);
if (drop_nopreempts)
drop_nopreempt_cpus(lowest_mask);
/*
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index e4f48f1..50f2ea8 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -4,6 +4,7 @@
#include <linux/kernel_stat.h>
#include <linux/static_key.h>
#include <linux/context_tracking.h>
+#include <linux/cpufreq_times.h>
#include "sched.h"
#ifdef CONFIG_PARAVIRT
#include <asm/paravirt.h>
@@ -157,6 +158,11 @@
/* Account for user time used */
acct_account_cputime(p);
+
+#ifdef CONFIG_CPU_FREQ_TIMES
+ /* Account power usage for user time */
+ cpufreq_acct_update_power(p, cputime);
+#endif
}
/*
@@ -207,6 +213,10 @@
/* Account for system time used */
acct_account_cputime(p);
+#ifdef CONFIG_CPU_FREQ_TIMES
+ /* Account power usage for system time */
+ cpufreq_acct_update_power(p, cputime);
+#endif
}
/*
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 0a295ac..d7c7dd6 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -485,13 +485,84 @@
}
/*
- * When a -deadline entity is queued back on the runqueue, its runtime and
- * deadline might need updating.
+ * Revised wakeup rule [1]: For self-suspending tasks, rather then
+ * re-initializing task's runtime and deadline, the revised wakeup
+ * rule adjusts the task's runtime to avoid the task to overrun its
+ * density.
*
- * The policy here is that we update the deadline of the entity only if:
- * - the current deadline is in the past,
- * - using the remaining runtime with the current deadline would make
- * the entity exceed its bandwidth.
+ * Reasoning: a task may overrun the density if:
+ * runtime / (deadline - t) > dl_runtime / dl_deadline
+ *
+ * Therefore, runtime can be adjusted to:
+ * runtime = (dl_runtime / dl_deadline) * (deadline - t)
+ *
+ * In such way that runtime will be equal to the maximum density
+ * the task can use without breaking any rule.
+ *
+ * [1] Luca Abeni, Giuseppe Lipari, and Juri Lelli. 2015. Constant
+ * bandwidth server revisited. SIGBED Rev. 11, 4 (January 2015), 19-24.
+ */
+static void
+update_dl_revised_wakeup(struct sched_dl_entity *dl_se, struct rq *rq)
+{
+ u64 laxity = dl_se->deadline - rq_clock(rq);
+
+ /*
+ * If the task has deadline < period, and the deadline is in the past,
+ * it should already be throttled before this check.
+ *
+ * See update_dl_entity() comments for further details.
+ */
+ WARN_ON(dl_time_before(dl_se->deadline, rq_clock(rq)));
+
+ dl_se->runtime = (dl_se->dl_density * laxity) >> 20;
+}
+
+/*
+ * Regarding the deadline, a task with implicit deadline has a relative
+ * deadline == relative period. A task with constrained deadline has a
+ * relative deadline <= relative period.
+ *
+ * We support constrained deadline tasks. However, there are some restrictions
+ * applied only for tasks which do not have an implicit deadline. See
+ * update_dl_entity() to know more about such restrictions.
+ *
+ * The dl_is_implicit() returns true if the task has an implicit deadline.
+ */
+static inline bool dl_is_implicit(struct sched_dl_entity *dl_se)
+{
+ return dl_se->dl_deadline == dl_se->dl_period;
+}
+
+/*
+ * When a deadline entity is placed in the runqueue, its runtime and deadline
+ * might need to be updated. This is done by a CBS wake up rule. There are two
+ * different rules: 1) the original CBS; and 2) the Revisited CBS.
+ *
+ * When the task is starting a new period, the Original CBS is used. In this
+ * case, the runtime is replenished and a new absolute deadline is set.
+ *
+ * When a task is queued before the begin of the next period, using the
+ * remaining runtime and deadline could make the entity to overflow, see
+ * dl_entity_overflow() to find more about runtime overflow. When such case
+ * is detected, the runtime and deadline need to be updated.
+ *
+ * If the task has an implicit deadline, i.e., deadline == period, the Original
+ * CBS is applied. the runtime is replenished and a new absolute deadline is
+ * set, as in the previous cases.
+ *
+ * However, the Original CBS does not work properly for tasks with
+ * deadline < period, which are said to have a constrained deadline. By
+ * applying the Original CBS, a constrained deadline task would be able to run
+ * runtime/deadline in a period. With deadline < period, the task would
+ * overrun the runtime/period allowed bandwidth, breaking the admission test.
+ *
+ * In order to prevent this misbehave, the Revisited CBS is used for
+ * constrained deadline tasks when a runtime overflow is detected. In the
+ * Revisited CBS, rather than replenishing & setting a new absolute deadline,
+ * the remaining runtime of the task is reduced to avoid runtime overflow.
+ * Please refer to the comments update_dl_revised_wakeup() function to find
+ * more about the Revised CBS rule.
*/
static void update_dl_entity(struct sched_dl_entity *dl_se,
struct sched_dl_entity *pi_se)
@@ -501,6 +572,14 @@
if (dl_time_before(dl_se->deadline, rq_clock(rq)) ||
dl_entity_overflow(dl_se, pi_se, rq_clock(rq))) {
+
+ if (unlikely(!dl_is_implicit(dl_se) &&
+ !dl_time_before(dl_se->deadline, rq_clock(rq)) &&
+ !dl_se->dl_boosted)){
+ update_dl_revised_wakeup(dl_se, rq);
+ return;
+ }
+
dl_se->deadline = rq_clock(rq) + pi_se->dl_deadline;
dl_se->runtime = pi_se->dl_runtime;
}
@@ -964,11 +1043,6 @@
__dequeue_dl_entity(dl_se);
}
-static inline bool dl_is_constrained(struct sched_dl_entity *dl_se)
-{
- return dl_se->dl_deadline < dl_se->dl_period;
-}
-
static void enqueue_task_dl(struct rq *rq, struct task_struct *p, int flags)
{
struct task_struct *pi_task = rt_mutex_get_top_task(p);
@@ -1000,7 +1074,7 @@
* If that is the case, the task will be throttled and
* the replenishment timer will be set to the next period.
*/
- if (!p->dl.dl_throttled && dl_is_constrained(&p->dl))
+ if (!p->dl.dl_throttled && !dl_is_implicit(&p->dl))
dl_check_constrained_dl(&p->dl);
/*
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 04ba6d0..5ad731a 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2511,7 +2511,8 @@
return;
- down_read(&mm->mmap_sem);
+ if (!down_read_trylock(&mm->mmap_sem))
+ return;
vma = find_vma(mm, start);
if (!vma) {
reset_ptenuma_scan(p);
@@ -5709,6 +5710,9 @@
for_each_cpu(i, sched_group_cpus(sg))
state = min(state, idle_get_state_idx(cpu_rq(i)));
+ if (unlikely(state == INT_MAX))
+ return -EINVAL;
+
/* Take non-cpuidle idling into account (active idle/arch_cpu_idle()) */
state++;
@@ -5775,7 +5779,7 @@
* The required scaling will be performed just one time, by the calling
* functions, once we accumulated the contributons for all the SGs.
*/
-static void calc_sg_energy(struct energy_env *eenv)
+static int calc_sg_energy(struct energy_env *eenv)
{
struct sched_group *sg = eenv->sg;
int busy_energy, idle_energy;
@@ -5804,6 +5808,11 @@
/* Compute IDLE energy */
idle_idx = group_idle_state(eenv, cpu_idx);
+ if (unlikely(idle_idx < 0))
+ return idle_idx;
+ if (idle_idx > sg->sge->nr_idle_states - 1)
+ idle_idx = sg->sge->nr_idle_states - 1;
+
idle_power = sg->sge->idle_states[idle_idx].power;
idle_energy = SCHED_CAPACITY_SCALE - sg_util;
@@ -5812,6 +5821,7 @@
total_energy = busy_energy + idle_energy;
eenv->cpu[cpu_idx].energy += total_energy;
}
+ return 0;
}
/*
@@ -5873,7 +5883,8 @@
* CPUs in the current visited SG.
*/
eenv->sg = sg;
- calc_sg_energy(eenv);
+ if (calc_sg_energy(eenv))
+ return -EINVAL;
/* remove CPUs we have just visited */
if (!sd->child) {
@@ -6154,7 +6165,8 @@
bool __cpu_overutilized(int cpu, int delta)
{
- return (capacity_of(cpu) * 1024) < ((cpu_util(cpu) + delta) * capacity_margin);
+ return (capacity_orig_of(cpu) * 1024) <
+ ((cpu_util(cpu) + delta) * capacity_margin);
}
bool cpu_overutilized(int cpu)
@@ -6871,6 +6883,13 @@
if (!sg->group_weight)
return true;
+ /*
+ * Don't skip a group if a task affinity allows it
+ * to run only on that group.
+ */
+ if (cpumask_subset(tsk_cpus_allowed(p), sched_group_cpus(sg)))
+ return false;
+
if (!task_fits_max(p, fcpu))
return true;
@@ -7357,6 +7376,12 @@
}
#endif
+enum fastpaths {
+ NONE = 0,
+ SYNC_WAKEUP,
+ PREV_CPU_BIAS,
+};
+
static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync)
{
bool boosted, prefer_idle;
@@ -7367,6 +7392,7 @@
struct cpumask *rtg_target = find_rtg_target(p);
struct find_best_target_env fbt_env;
u64 start_t = 0;
+ int fastpath = 0;
if (trace_sched_task_util_enabled())
start_t = sched_clock();
@@ -7403,12 +7429,17 @@
if (bias_to_waker_cpu(p, cpu, rtg_target)) {
schedstat_inc(p->se.statistics.nr_wakeups_secb_sync);
schedstat_inc(this_rq()->eas_stats.secb_sync);
- return cpu;
+ target_cpu = cpu;
+ fastpath = SYNC_WAKEUP;
+ goto out;
}
}
- if (bias_to_prev_cpu(p, rtg_target))
- return prev_cpu;
+ if (bias_to_prev_cpu(p, rtg_target)) {
+ target_cpu = prev_cpu;
+ fastpath = PREV_CPU_BIAS;
+ goto out;
+ }
rcu_read_lock();
@@ -7495,11 +7526,12 @@
schedstat_inc(this_rq()->eas_stats.secb_count);
unlock:
- trace_sched_task_util(p, next_cpu, backup_cpu, target_cpu, sync,
- fbt_env.need_idle, fbt_env.placement_boost,
- rtg_target ? cpumask_first(rtg_target) : -1,
- start_t);
rcu_read_unlock();
+out:
+ trace_sched_task_util(p, next_cpu, backup_cpu, target_cpu, sync,
+ fbt_env.need_idle, fastpath,
+ fbt_env.placement_boost, rtg_target ?
+ cpumask_first(rtg_target) : -1, start_t);
return target_cpu;
}
@@ -8790,13 +8822,12 @@
int max_cap_cpu;
unsigned long flags;
- capacity = min(capacity, thermal_cap(cpu));
-
- cpu_rq(cpu)->cpu_capacity_orig = capacity;
-
capacity *= arch_scale_max_freq_capacity(sd, cpu);
capacity >>= SCHED_CAPACITY_SHIFT;
+ capacity = min(capacity, thermal_cap(cpu));
+ cpu_rq(cpu)->cpu_capacity_orig = capacity;
+
mcc = &cpu_rq(cpu)->rd->max_cpu_capacity;
raw_spin_lock_irqsave(&mcc->lock, flags);
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index e6abbb4..f23f040 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1834,7 +1834,7 @@
if (avoid_prev_cpu && cpu == prev_cpu)
continue;
- if (__cpu_overutilized(cpu, util + tutil))
+ if (__cpu_overutilized(cpu, tutil))
continue;
if (cpu_isolated(cpu))
@@ -2492,7 +2492,7 @@
if (tsk_nr_cpus_allowed(p) > 1 && rq->rt.overloaded)
queue_push_tasks(rq);
#endif /* CONFIG_SMP */
- if (p->prio < rq->curr->prio)
+ if (p->prio < rq->curr->prio && cpu_online(cpu_of(rq)))
resched_curr(rq);
}
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index f27ab13..d73dfae 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -798,7 +798,6 @@
int cstate, wakeup_latency, wakeup_energy;
u64 window_start;
s64 cum_window_start;
- u64 load_reported_window;
unsigned long walt_flags;
u64 cur_irqload;
@@ -1862,6 +1861,8 @@
}
#ifdef CONFIG_SCHED_WALT
+extern u64 walt_load_reported_window;
+
static inline unsigned long
cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load)
{
@@ -1898,7 +1899,7 @@
walt_load->prev_window_util = util;
walt_load->nl = nl;
walt_load->pl = pl;
- walt_load->ws = rq->load_reported_window;
+ walt_load->ws = walt_load_reported_window;
}
return (util >= capacity) ? capacity : util;
@@ -2268,22 +2269,8 @@
struct update_util_data *data;
#ifdef CONFIG_SCHED_WALT
- unsigned int exception_flags = SCHED_CPUFREQ_INTERCLUSTER_MIG |
- SCHED_CPUFREQ_PL | SCHED_CPUFREQ_EARLY_DET |
- SCHED_CPUFREQ_FORCE_UPDATE;
-
- /*
- * Skip if we've already reported, but not if this is an inter-cluster
- * migration. Also only allow WALT update sites.
- */
if (!(flags & SCHED_CPUFREQ_WALT))
return;
- if (!sched_disable_window_stats &&
- (rq->load_reported_window == rq->window_start) &&
- !(flags & exception_flags))
- return;
- if (!(flags & exception_flags))
- rq->load_reported_window = rq->window_start;
#endif
data = rcu_dereference_sched(*per_cpu_ptr(&cpufreq_update_util_data,
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index a9fb367..8bacb6f 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -45,6 +45,7 @@
static bool use_cycle_counter;
DEFINE_MUTEX(cluster_lock);
static atomic64_t walt_irq_work_lastq_ws;
+u64 walt_load_reported_window;
static struct irq_work walt_cpufreq_irq_work;
static struct irq_work walt_migration_irq_work;
@@ -847,8 +848,7 @@
if (p == src_rq->ed_task) {
src_rq->ed_task = NULL;
- if (!dest_rq->ed_task)
- dest_rq->ed_task = p;
+ dest_rq->ed_task = p;
}
done:
@@ -867,6 +867,9 @@
rq->window_start = 1;
sync_cpu_available = 1;
atomic64_set(&walt_irq_work_lastq_ws, rq->window_start);
+ walt_load_reported_window =
+ atomic64_read(&walt_irq_work_lastq_ws);
+
} else {
struct rq *sync_rq = cpu_rq(cpumask_any(cpu_online_mask));
@@ -3143,7 +3146,7 @@
raw_spin_lock(&cpu_rq(cpu)->lock);
wc = ktime_get_ns();
-
+ walt_load_reported_window = atomic64_read(&walt_irq_work_lastq_ws);
for_each_sched_cluster(cluster) {
u64 aggr_grp_load = 0;
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index af182a6..3975856 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -16,6 +16,8 @@
#include <linux/atomic.h>
#include <linux/audit.h>
#include <linux/compat.h>
+#include <linux/nospec.h>
+#include <linux/prctl.h>
#include <linux/sched.h>
#include <linux/seccomp.h>
#include <linux/slab.h>
@@ -214,8 +216,11 @@
return true;
}
+void __weak arch_seccomp_spec_mitigate(struct task_struct *task) { }
+
static inline void seccomp_assign_mode(struct task_struct *task,
- unsigned long seccomp_mode)
+ unsigned long seccomp_mode,
+ unsigned long flags)
{
assert_spin_locked(&task->sighand->siglock);
@@ -225,6 +230,9 @@
* filter) is set.
*/
smp_mb__before_atomic();
+ /* Assume default seccomp processes want spec flaw mitigation. */
+ if ((flags & SECCOMP_FILTER_FLAG_SPEC_ALLOW) == 0)
+ arch_seccomp_spec_mitigate(task);
set_tsk_thread_flag(task, TIF_SECCOMP);
}
@@ -292,7 +300,7 @@
* without dropping the locks.
*
*/
-static inline void seccomp_sync_threads(void)
+static inline void seccomp_sync_threads(unsigned long flags)
{
struct task_struct *thread, *caller;
@@ -333,7 +341,8 @@
* allow one thread to transition the other.
*/
if (thread->seccomp.mode == SECCOMP_MODE_DISABLED)
- seccomp_assign_mode(thread, SECCOMP_MODE_FILTER);
+ seccomp_assign_mode(thread, SECCOMP_MODE_FILTER,
+ flags);
}
}
@@ -452,7 +461,7 @@
/* Now that the new filter is in place, synchronize to all threads. */
if (flags & SECCOMP_FILTER_FLAG_TSYNC)
- seccomp_sync_threads();
+ seccomp_sync_threads(flags);
return 0;
}
@@ -712,7 +721,7 @@
#ifdef TIF_NOTSC
disable_TSC();
#endif
- seccomp_assign_mode(current, seccomp_mode);
+ seccomp_assign_mode(current, seccomp_mode, 0);
ret = 0;
out:
@@ -770,7 +779,7 @@
/* Do not free the successfully attached filter. */
prepared = NULL;
- seccomp_assign_mode(current, seccomp_mode);
+ seccomp_assign_mode(current, seccomp_mode, flags);
out:
spin_unlock_irq(¤t->sighand->siglock);
if (flags & SECCOMP_FILTER_FLAG_TSYNC)
diff --git a/kernel/signal.c b/kernel/signal.c
index 7ebe236..17428fe 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2495,6 +2495,13 @@
{
struct task_struct *tsk = current;
+ /*
+ * In case the signal mask hasn't changed, there is nothing we need
+ * to do. The current->blocked shouldn't be modified by other task.
+ */
+ if (sigequalsets(&tsk->blocked, newset))
+ return;
+
spin_lock_irq(&tsk->sighand->siglock);
__set_task_blocked(tsk, newset);
spin_unlock_irq(&tsk->sighand->siglock);
diff --git a/kernel/smpboot.c b/kernel/smpboot.c
index 1650578..10c5a3a 100644
--- a/kernel/smpboot.c
+++ b/kernel/smpboot.c
@@ -121,7 +121,45 @@
}
if (kthread_should_park()) {
+ /*
+ * Serialize against wakeup. If we take the lock first,
+ * wakeup is skipped. If we run later, we observe,
+ * TASK_RUNNING update from wakeup path, before moving
+ * forward. This helps avoid the race, where wakeup
+ * observes TASK_INTERRUPTIBLE, and also observes
+ * the TASK_PARKED in kthread_parkme() before updating
+ * task state to TASK_RUNNING. In this case, kthread
+ * gets parked in TASK_RUNNING state. This results
+ * in panic later on in kthread_unpark(), as it sees
+ * KTHREAD_IS_PARKED flag set but fails to rebind the
+ * kthread, due to it being not in TASK_PARKED state.
+ *
+ * Control thread Hotplug Thread
+ *
+ * kthread_park()
+ * set KTHREAD_SHOULD_PARK
+ * smpboot_thread_fn()
+ * set_current_state(
+ * TASK_INTERRUPTIBLE);
+ * kthread_parkme()
+ *
+ * wake_up_process()
+ *
+ * raw_spin_lock_irqsave(&p->pi_lock, flags);
+ * if (!(p->state & state))
+ * goto out;
+ *
+ * __set_current_state(
+ * TASK_PARKED);
+ *
+ * if (p->on_rq && ttwu_remote(p, wake_flags))
+ * ttwu_remote()
+ * p->state = TASK_RUNNING;
+ * schedule();
+ */
+ raw_spin_lock(¤t->pi_lock);
__set_current_state(TASK_RUNNING);
+ raw_spin_unlock(¤t->pi_lock);
preempt_enable();
if (ht->park && td->status == HP_THREAD_ACTIVE) {
BUG_ON(td->cpu != smp_processor_id());
diff --git a/kernel/sys.c b/kernel/sys.c
index 4ccf5f0..1b59b6e 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -55,6 +55,8 @@
#include <linux/uidgid.h>
#include <linux/cred.h>
+#include <linux/nospec.h>
+
#include <linux/kmsg_dump.h>
/* Move somewhere else to avoid recompiling? */
#include <generated/utsrelease.h>
@@ -2221,6 +2223,17 @@
}
#endif
+int __weak arch_prctl_spec_ctrl_get(struct task_struct *t, unsigned long which)
+{
+ return -EINVAL;
+}
+
+int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which,
+ unsigned long ctrl)
+{
+ return -EINVAL;
+}
+
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
unsigned long, arg4, unsigned long, arg5)
{
@@ -2440,6 +2453,16 @@
case PR_GET_FP_MODE:
error = GET_FP_MODE(me);
break;
+ case PR_GET_SPECULATION_CTRL:
+ if (arg3 || arg4 || arg5)
+ return -EINVAL;
+ error = arch_prctl_spec_ctrl_get(me, arg2);
+ break;
+ case PR_SET_SPECULATION_CTRL:
+ if (arg4 || arg5)
+ return -EINVAL;
+ error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
+ break;
case PR_SET_VMA:
error = prctl_set_vma(arg2, arg3, arg4, arg5);
break;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f41c0e9..09a1611 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -398,22 +398,6 @@
.mode = 0644,
.proc_handler = proc_dointvec,
},
-#ifdef CONFIG_SCHED_WALT
- {
- .procname = "sched_use_walt_cpu_util",
- .data = &sysctl_sched_use_walt_cpu_util,
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "sched_use_walt_task_util",
- .data = &sysctl_sched_use_walt_task_util,
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
-#endif
{
.procname = "sched_cstate_aware",
.data = &sysctl_sched_cstate_aware,
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 38b008e..11e4af2f 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -1113,7 +1113,12 @@
cpu_base = raw_cpu_ptr(&hrtimer_bases);
- if (clock_id == CLOCK_REALTIME && mode != HRTIMER_MODE_ABS)
+ /*
+ * POSIX magic: Relative CLOCK_REALTIME timers are not affected by
+ * clock modifications, so they needs to become CLOCK_MONOTONIC to
+ * ensure POSIX compliance.
+ */
+ if (clock_id == CLOCK_REALTIME && mode & HRTIMER_MODE_REL)
clock_id = CLOCK_MONOTONIC;
base = hrtimer_clockid_to_base(clock_id);
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index 9cff0ab..e24008c 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -300,14 +300,17 @@
static int pc_clock_gettime(clockid_t id, struct timespec *ts)
{
struct posix_clock_desc cd;
+ struct timespec64 ts64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
- if (cd.clk->ops.clock_gettime)
- err = cd.clk->ops.clock_gettime(cd.clk, ts);
+ if (cd.clk->ops.clock_gettime) {
+ err = cd.clk->ops.clock_gettime(cd.clk, &ts64);
+ *ts = timespec64_to_timespec(ts64);
+ }
else
err = -EOPNOTSUPP;
@@ -319,14 +322,17 @@
static int pc_clock_getres(clockid_t id, struct timespec *ts)
{
struct posix_clock_desc cd;
+ struct timespec64 ts64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
- if (cd.clk->ops.clock_getres)
- err = cd.clk->ops.clock_getres(cd.clk, ts);
+ if (cd.clk->ops.clock_getres) {
+ err = cd.clk->ops.clock_getres(cd.clk, &ts64);
+ *ts = timespec64_to_timespec(ts64);
+ }
else
err = -EOPNOTSUPP;
@@ -337,6 +343,7 @@
static int pc_clock_settime(clockid_t id, const struct timespec *ts)
{
+ struct timespec64 ts64 = timespec_to_timespec64(*ts);
struct posix_clock_desc cd;
int err;
@@ -350,7 +357,7 @@
}
if (cd.clk->ops.clock_settime)
- err = cd.clk->ops.clock_settime(cd.clk, ts);
+ err = cd.clk->ops.clock_settime(cd.clk, &ts64);
else
err = -EOPNOTSUPP;
out:
@@ -403,29 +410,36 @@
{
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
+ struct itimerspec64 ts64;
if (get_clock_desc(id, &cd))
return;
- if (cd.clk->ops.timer_gettime)
- cd.clk->ops.timer_gettime(cd.clk, kit, ts);
-
+ if (cd.clk->ops.timer_gettime) {
+ cd.clk->ops.timer_gettime(cd.clk, kit, &ts64);
+ *ts = itimerspec64_to_itimerspec(&ts64);
+ }
put_clock_desc(&cd);
}
static int pc_timer_settime(struct k_itimer *kit, int flags,
struct itimerspec *ts, struct itimerspec *old)
{
+ struct itimerspec64 ts64 = itimerspec_to_itimerspec64(ts);
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
+ struct itimerspec64 old64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
- if (cd.clk->ops.timer_settime)
- err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old);
+ if (cd.clk->ops.timer_settime) {
+ err = cd.clk->ops.timer_settime(cd.clk, kit, flags, &ts64, &old64);
+ if (old)
+ *old = itimerspec64_to_itimerspec(&old64);
+ }
else
err = -EOPNOTSUPP;
diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c
index bad8a6b..4776585 100644
--- a/kernel/time/sched_clock.c
+++ b/kernel/time/sched_clock.c
@@ -208,6 +208,11 @@
update_clock_read_data(&rd);
+ if (sched_clock_timer.function != NULL) {
+ /* update timeout for clock wrap */
+ hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
+ }
+
r = rate;
if (r >= 4000000) {
r /= 1000000;
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index d2a20e8..22d7454 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -610,6 +610,14 @@
now = ktime_get();
/* Find all expired events */
for_each_cpu(cpu, tick_broadcast_oneshot_mask) {
+ /*
+ * Required for !SMP because for_each_cpu() reports
+ * unconditionally CPU0 as set on UP kernels.
+ */
+ if (!IS_ENABLED(CONFIG_SMP) &&
+ cpumask_empty(tick_broadcast_oneshot_mask))
+ break;
+
td = &per_cpu(tick_cpu_device, cpu);
if (td->evtdev->next_event.tv64 <= now.tv64) {
cpumask_set_cpu(cpu, tmpmask);
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 41481dc..d397432 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1902,6 +1902,12 @@
spin_lock_irqsave(&new_base->lock, flags);
spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
+ /*
+ * The current CPUs base clock might be stale. Update it
+ * before moving the timers over.
+ */
+ forward_timer_base(new_base);
+
if (!cpu_online(cpu))
BUG_ON(old_base->running_timer);
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c
index 83aa1f8..998d730 100644
--- a/kernel/time/timer_list.c
+++ b/kernel/time/timer_list.c
@@ -16,6 +16,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
+#include <linux/nmi.h>
#include <asm/uaccess.h>
@@ -86,6 +87,9 @@
next_one:
i = 0;
+
+ touch_nmi_watchdog();
+
raw_spin_lock_irqsave(&base->cpu_base->lock, flags);
curr = timerqueue_getnext(&base->active);
@@ -197,6 +201,8 @@
{
struct clock_event_device *dev = td->evtdev;
+ touch_nmi_watchdog();
+
SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
if (cpu < 0)
SEQ_printf(m, "Broadcast device\n");
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 2884fe0..5534be1 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -120,8 +120,9 @@
struct ftrace_ops *op, struct pt_regs *regs);
#else
/* See comment below, where ftrace_ops_list_func is defined */
-static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
-#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)
+static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs);
+#define ftrace_ops_list_func ftrace_ops_no_ops
#endif
/*
@@ -4874,9 +4875,9 @@
return 0;
}
-static int ftrace_process_locs(struct module *mod,
- unsigned long *start,
- unsigned long *end)
+static int __norecordmcount ftrace_process_locs(struct module *mod,
+ unsigned long *start,
+ unsigned long *end)
{
struct ftrace_page *start_pg;
struct ftrace_page *pg;
@@ -5309,7 +5310,8 @@
__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
}
#else
-static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip)
+static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
{
__ftrace_ops_list_func(ip, parent_ip, NULL, NULL);
}
@@ -5735,14 +5737,17 @@
fgraph_graph_time = enable;
}
+void ftrace_graph_return_stub(struct ftrace_graph_ret *trace)
+{
+}
+
int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
{
return 0;
}
/* The callbacks that hook a function */
-trace_func_graph_ret_t ftrace_graph_return =
- (trace_func_graph_ret_t)ftrace_stub;
+trace_func_graph_ret_t ftrace_graph_return = ftrace_graph_return_stub;
trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub;
static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub;
@@ -5970,7 +5975,7 @@
goto out;
ftrace_graph_active--;
- ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
+ ftrace_graph_return = ftrace_graph_return_stub;
ftrace_graph_entry = ftrace_graph_entry_stub;
__ftrace_graph_entry = ftrace_graph_entry_stub;
ftrace_shutdown(&graph_ops, FTRACE_STOP_FUNC_RET);
diff --git a/kernel/trace/ipc_logging_debug.c b/kernel/trace/ipc_logging_debug.c
index 7a4c630..3a13ef3 100644
--- a/kernel/trace/ipc_logging_debug.c
+++ b/kernel/trace/ipc_logging_debug.c
@@ -78,17 +78,16 @@
struct dentry *d = file->f_path.dentry;
char *buffer;
int bsize;
- int srcu_idx;
int r;
- r = debugfs_use_file_start(d, &srcu_idx);
+ r = debugfs_file_get(d);
if (!r) {
ilctxt = file->private_data;
r = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
- }
- debugfs_use_file_finish(srcu_idx);
- if (r)
+ } else {
return r;
+ }
+ debugfs_file_put(d);
buffer = kmalloc(count, GFP_KERNEL);
if (!buffer) {
@@ -119,18 +118,17 @@
struct ipc_log_context *ilctxt;
struct dentry *d = file->f_path.dentry;
int bsize = 1;
- int srcu_idx;
int r;
char local_buf[3];
- r = debugfs_use_file_start(d, &srcu_idx);
+ r = debugfs_file_get(d);
if (!r) {
ilctxt = file->private_data;
r = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
- }
- debugfs_use_file_finish(srcu_idx);
- if (r)
+ } else {
return r;
+ }
+ debugfs_file_put(d);
if (copy_from_user(local_buf, buff, bsize)) {
count = -EFAULT;
@@ -172,17 +170,16 @@
struct ipc_log_context *ilctxt;
struct dentry *d = file->f_path.dentry;
int bsize = 2;
- int srcu_idx;
int r;
- r = debugfs_use_file_start(d, &srcu_idx);
+ r = debugfs_file_get(d);
if (!r) {
ilctxt = file->private_data;
r = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
- }
- debugfs_use_file_finish(srcu_idx);
- if (r)
+ } else {
return r;
+ }
+ debugfs_file_put(d);
bsize = simple_read_from_buffer(buff, count, ppos,
ilctxt->disabled?"1\n":"0\n", bsize);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 046abb0..de11b81 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3078,13 +3078,14 @@
if (!(iter->iter_flags & TRACE_FILE_ANNOTATE))
return;
- if (iter->started && cpumask_test_cpu(iter->cpu, iter->started))
+ if (cpumask_available(iter->started) &&
+ cpumask_test_cpu(iter->cpu, iter->started))
return;
if (per_cpu_ptr(iter->trace_buffer->data, iter->cpu)->skipped_entries)
return;
- if (iter->started)
+ if (cpumask_available(iter->started))
cpumask_set_cpu(iter->cpu, iter->started);
/* Don't print started cpu buffer for the first entry of the trace */
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 0193f58..e35a411 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -322,6 +322,9 @@
static int regex_match_front(char *str, struct regex *r, int len)
{
+ if (len < r->len)
+ return 0;
+
if (strncmp(str, r->pattern, r->len) == 0)
return 1;
return 0;
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 79bbaf0..59b482f 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -478,14 +478,11 @@
#ifdef CONFIG_PREEMPTIRQ_EVENTS
struct irqsoff_store *is = &per_cpu(the_irqsoff,
raw_smp_processor_id());
+ u64 delta = sched_clock() - is->ts;
- if (!is->ts) {
- is->ts = sched_clock();
- is->caddr[0] = CALLER_ADDR0;
- is->caddr[1] = CALLER_ADDR1;
- is->caddr[2] = CALLER_ADDR2;
- is->caddr[3] = CALLER_ADDR3;
- }
+ if (delta > sysctl_irqsoff_tracing_threshold_ns)
+ trace_irqs_disable(delta, is->caddr[0], is->caddr[1],
+ is->caddr[2], is->caddr[3]);
#endif /* CONFIG_PREEMPTIRQ_EVENTS */
if (!preempt_trace() && irq_trace())
@@ -497,15 +494,12 @@
#ifdef CONFIG_PREEMPTIRQ_EVENTS
struct irqsoff_store *is = &per_cpu(the_irqsoff,
raw_smp_processor_id());
- u64 delta = 0;
- if (is->ts) {
- delta = sched_clock() - is->ts;
- is->ts = 0;
- }
- if (delta > sysctl_irqsoff_tracing_threshold_ns)
- trace_irqs_disable(delta, is->caddr[0], is->caddr[1],
- is->caddr[2], is->caddr[3]);
+ is->ts = sched_clock();
+ is->caddr[0] = CALLER_ADDR0;
+ is->caddr[1] = CALLER_ADDR1;
+ is->caddr[2] = CALLER_ADDR2;
+ is->caddr[3] = CALLER_ADDR3;
#endif /* CONFIG_PREEMPTIRQ_EVENTS */
if (!preempt_trace() && irq_trace())
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 5ff45ca..ea3ed03 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -607,7 +607,7 @@
bool is_return = false, is_delete = false;
char *symbol = NULL, *event = NULL, *group = NULL;
char *arg;
- unsigned long offset = 0;
+ long offset = 0;
void *addr = NULL;
char buf[MAX_EVENT_NAME_LEN];
@@ -675,7 +675,7 @@
symbol = argv[1];
/* TODO: support .init module functions */
ret = traceprobe_split_symbol_offset(symbol, &offset);
- if (ret) {
+ if (ret || offset < 0 || offset > UINT_MAX) {
pr_info("Failed to parse either an address or a symbol.\n");
return ret;
}
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 8c0553d..5ea191b 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -319,7 +319,7 @@
}
/* Split symbol and offset. */
-int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset)
+int traceprobe_split_symbol_offset(char *symbol, long *offset)
{
char *tmp;
int ret;
@@ -327,13 +327,11 @@
if (!offset)
return -EINVAL;
- tmp = strchr(symbol, '+');
+ tmp = strpbrk(symbol, "+-");
if (tmp) {
- /* skip sign because kstrtoul doesn't accept '+' */
- ret = kstrtoul(tmp + 1, 0, offset);
+ ret = kstrtol(tmp, 0, offset);
if (ret)
return ret;
-
*tmp = '\0';
} else
*offset = 0;
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 0c0ae54..2b84c0d 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -354,7 +354,7 @@
extern void traceprobe_update_arg(struct probe_arg *arg);
extern void traceprobe_free_probe_arg(struct probe_arg *arg);
-extern int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset);
+extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
extern ssize_t traceprobe_probes_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos,
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index bc6c6ec..83afbf2 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -149,6 +149,8 @@
return;
ret = strncpy_from_user(dst, src, maxlen);
+ if (ret == maxlen)
+ dst[--ret] = '\0';
if (ret < 0) { /* Failed to fetch string */
((u8 *)get_rloc_data(dest))[0] = '\0';
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index d0639d9..c8e7cc0 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -202,7 +202,7 @@
lockdep_is_held(&tracepoints_mutex));
old = func_add(&tp_funcs, func, prio);
if (IS_ERR(old)) {
- WARN_ON_ONCE(1);
+ WARN_ON_ONCE(PTR_ERR(old) != -ENOMEM);
return PTR_ERR(old);
}
@@ -235,7 +235,7 @@
lockdep_is_held(&tracepoints_mutex));
old = func_remove(&tp_funcs, func);
if (IS_ERR(old)) {
- WARN_ON_ONCE(1);
+ WARN_ON_ONCE(PTR_ERR(old) != -ENOMEM);
return PTR_ERR(old);
}
diff --git a/kernel/user.c b/kernel/user.c
index b069ccb..41e94e4 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/export.h>
#include <linux/user_namespace.h>
+#include <linux/proc_fs.h>
#include <linux/proc_ns.h>
/*
@@ -201,6 +202,7 @@
}
spin_unlock_irq(&uidhash_lock);
}
+ proc_register_uid(uid);
return up;
@@ -222,6 +224,7 @@
spin_lock_irq(&uidhash_lock);
uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID));
spin_unlock_irq(&uidhash_lock);
+ proc_register_uid(GLOBAL_ROOT_UID);
return 0;
}
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 5ef61bd..04c2289 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4137,6 +4137,22 @@
EXPORT_SYMBOL_GPL(workqueue_set_max_active);
/**
+ * current_work - retrieve %current task's work struct
+ *
+ * Determine if %current task is a workqueue worker and what it's working on.
+ * Useful to find out the context that the %current task is running in.
+ *
+ * Return: work struct if %current task is a workqueue worker, %NULL otherwise.
+ */
+struct work_struct *current_work(void)
+{
+ struct worker *worker = current_wq_worker();
+
+ return worker ? worker->current_work : NULL;
+}
+EXPORT_SYMBOL(current_work);
+
+/**
* current_is_workqueue_rescuer - is %current workqueue rescuer?
*
* Determine whether %current is a workqueue rescuer. Can be used from
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 558be53..640818c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -269,7 +269,6 @@
config DEBUG_FS
bool "Debug Filesystem"
- select SRCU
help
debugfs is a virtual file system that kernel developers use to put
debugging files into. Enable this option to be able to read and
@@ -707,6 +706,19 @@
source "lib/Kconfig.kasan"
+config DEBUG_REFCOUNT
+ bool "Verbose refcount checks"
+ help
+ Say Y here if you want reference counters (refcount_t and kref) to
+ generate WARNs on dubious usage. Without this refcount_t will still
+ be a saturating counter and avoid Use-After-Free by turning it into
+ a resource leak Denial-Of-Service.
+
+ Use of this option will increase kernel text size but will alert the
+ admin of potential abuse.
+
+ If in doubt, say "N".
+
endmenu # "Memory Debugging"
config ARCH_HAS_KCOV
@@ -834,6 +846,17 @@
default 0 if !BOOTPARAM_SOFTLOCKUP_PANIC
default 1 if BOOTPARAM_SOFTLOCKUP_PANIC
+config PANIC_ON_RECURSIVE_FAULT
+ bool "Panic on recursive faults during task exit"
+ help
+ Panic upon the detection of a recursive fault during task exit,
+ rather than putting the task into an uninterruptible sleep.
+ This is particularly useful for debugging system hangs in
+ scenarios where the task experiencing the fault is critical
+ for system operation, rendering the system inoperable.
+
+ Say N if unsure.
+
config DETECT_HUNG_TASK
bool "Detect Hung Tasks"
depends on DEBUG_KERNEL
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 056052dc..19572a4 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -293,10 +293,13 @@
return;
limit++;
- if (is_on_stack)
- pr_warn("object is on stack, but not annotated\n");
- else
- pr_warn("object is not on stack, but annotated\n");
+ if (is_on_stack) {
+ pr_warn("object %p is on stack %p, but NOT annotated\n", addr,
+ task_stack_page(current));
+ } else {
+ pr_warn("object %p is NOT on stack %p, but annotated\n", addr,
+ task_stack_page(current));
+ }
WARN_ON(1);
}
diff --git a/lib/ioremap.c b/lib/ioremap.c
index 86c8911..5323b59 100644
--- a/lib/ioremap.c
+++ b/lib/ioremap.c
@@ -83,7 +83,8 @@
if (ioremap_pmd_enabled() &&
((next - addr) == PMD_SIZE) &&
- IS_ALIGNED(phys_addr + addr, PMD_SIZE)) {
+ IS_ALIGNED(phys_addr + addr, PMD_SIZE) &&
+ pmd_free_pte_page(pmd)) {
if (pmd_set_huge(pmd, phys_addr + addr, prot))
continue;
}
@@ -109,7 +110,8 @@
if (ioremap_pud_enabled() &&
((next - addr) == PUD_SIZE) &&
- IS_ALIGNED(phys_addr + addr, PUD_SIZE)) {
+ IS_ALIGNED(phys_addr + addr, PUD_SIZE) &&
+ pud_free_pmd_page(pud)) {
if (pud_set_huge(pud, phys_addr + addr, prot))
continue;
}
diff --git a/lib/kobject.c b/lib/kobject.c
index 763d70a..34f8472 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -234,14 +234,12 @@
/* be noisy on error issues */
if (error == -EEXIST)
- WARN(1, "%s failed for %s with "
- "-EEXIST, don't try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
+ pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",
+ __func__, kobject_name(kobj));
else
- WARN(1, "%s failed for %s (error: %d parent: %s)\n",
- __func__, kobject_name(kobj), error,
- parent ? kobject_name(parent) : "'none'");
+ pr_err("%s failed for %s (error: %d parent: %s)\n",
+ __func__, kobject_name(kobj), error,
+ parent ? kobject_name(parent) : "'none'");
} else
kobj->state_in_sysfs = 1;
diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h
index 9333650..0f64fce 100644
--- a/lib/mpi/longlong.h
+++ b/lib/mpi/longlong.h
@@ -671,7 +671,23 @@
************** MIPS/64 **************
***************************************/
#if (defined(__mips) && __mips >= 3) && W_TYPE_SIZE == 64
-#if (__GNUC__ >= 5) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
+#if defined(__mips_isa_rev) && __mips_isa_rev >= 6
+/*
+ * GCC ends up emitting a __multi3 intrinsic call for MIPS64r6 with the plain C
+ * code below, so we special case MIPS64r6 until the compiler can do better.
+ */
+#define umul_ppmm(w1, w0, u, v) \
+do { \
+ __asm__ ("dmulu %0,%1,%2" \
+ : "=d" ((UDItype)(w0)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v))); \
+ __asm__ ("dmuhu %0,%1,%2" \
+ : "=d" ((UDItype)(w1)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v))); \
+} while (0)
+#elif (__GNUC__ >= 5) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
#define umul_ppmm(w1, w0, u, v) \
do { \
typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 32d0ad0..895961c 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -448,8 +448,10 @@
if (!key ||
(ht->p.obj_cmpfn ?
ht->p.obj_cmpfn(&arg, rht_obj(ht, head)) :
- rhashtable_compare(&arg, rht_obj(ht, head))))
+ rhashtable_compare(&arg, rht_obj(ht, head)))) {
+ pprev = &head->next;
continue;
+ }
if (!ht->rhlist)
return rht_obj(ht, head);
diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index 0e70ecc..8fcad61 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -440,6 +440,26 @@
p[1023] = 1;
}
+static noinline void __init kasan_alloca_oob_left(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array - 1;
+
+ pr_info("out-of-bounds to left on alloca\n");
+ *(volatile char *)p;
+}
+
+static noinline void __init kasan_alloca_oob_right(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array + i;
+
+ pr_info("out-of-bounds to right on alloca\n");
+ *(volatile char *)p;
+}
+
static int __init kmalloc_tests_init(void)
{
/*
@@ -469,6 +489,8 @@
kmem_cache_oob();
kasan_stack_oob();
kasan_global_oob();
+ kasan_alloca_oob_left();
+ kasan_alloca_oob_right();
ksize_unpoisons_memory();
copy_user_test();
use_after_scope_test();
diff --git a/mm/Kconfig b/mm/Kconfig
index 3363a70..051f7bc 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -675,6 +675,7 @@
depends on ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT
depends on NO_BOOTMEM && MEMORY_HOTPLUG
depends on !FLATMEM
+ depends on !NEED_PER_CPU_KM
help
Ordinarily all struct pages are initialised during early boot in a
single thread. On very large machines this can take a considerable
diff --git a/mm/cma.c b/mm/cma.c
index 5fc1809..e97ad01 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -438,6 +438,8 @@
struct page *page = NULL;
int retry_after_sleep = 0;
int ret = -ENOMEM;
+ int max_retries = 2;
+ int available_regions = 0;
if (!cma || !cma->count)
return NULL;
@@ -464,9 +466,16 @@
bitmap_maxno, start, bitmap_count, mask,
offset);
if (bitmap_no >= bitmap_maxno) {
- if (retry_after_sleep < 2) {
+ if (retry_after_sleep < max_retries) {
start = 0;
/*
+ * update max retries if available free regions
+ * are less.
+ */
+ if (available_regions < 3)
+ max_retries = 5;
+ available_regions = 0;
+ /*
* Page may be momentarily pinned by some other
* process which has been scheduled out, eg.
* in exit path, during unmap call, or process
@@ -483,6 +492,8 @@
break;
}
}
+
+ available_regions++;
bitmap_set(cma->bitmap, bitmap_no, bitmap_count);
/*
* It's safe to drop the lock here. We've marked this region for
diff --git a/mm/filemap.c b/mm/filemap.c
index b4c09ec..211df83 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -618,7 +618,7 @@
VM_BUG_ON_PAGE(!PageLocked(new), new);
VM_BUG_ON_PAGE(new->mapping, new);
- error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
+ error = radix_tree_preload(gfp_mask & GFP_RECLAIM_MASK);
if (!error) {
struct address_space *mapping = old->mapping;
void (*freepage)(struct page *);
@@ -674,7 +674,7 @@
return error;
}
- error = radix_tree_maybe_preload(gfp_mask & ~__GFP_HIGHMEM);
+ error = radix_tree_maybe_preload(gfp_mask & GFP_RECLAIM_MASK);
if (error) {
if (!huge)
mem_cgroup_cancel_charge(page, memcg, false);
@@ -1249,8 +1249,7 @@
if (fgp_flags & FGP_ACCESSED)
__SetPageReferenced(page);
- err = add_to_page_cache_lru(page, mapping, offset,
- gfp_mask & GFP_RECLAIM_MASK);
+ err = add_to_page_cache_lru(page, mapping, offset, gfp_mask);
if (unlikely(err)) {
put_page(page);
page = NULL;
@@ -1998,7 +1997,7 @@
if (!page)
return -ENOMEM;
- ret = add_to_page_cache_lru(page, mapping, offset, gfp_mask & GFP_KERNEL);
+ ret = add_to_page_cache_lru(page, mapping, offset, gfp_mask);
if (ret == 0)
ret = mapping->a_ops->readpage(file, page);
else if (ret == -EEXIST)
@@ -2390,7 +2389,7 @@
static struct page *do_read_cache_page(struct address_space *mapping,
pgoff_t index,
- int (*filler)(void *, struct page *),
+ int (*filler)(struct file *, struct page *),
void *data,
gfp_t gfp)
{
@@ -2497,7 +2496,7 @@
*/
struct page *read_cache_page(struct address_space *mapping,
pgoff_t index,
- int (*filler)(void *, struct page *),
+ int (*filler)(struct file *, struct page *),
void *data)
{
return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping));
@@ -2519,7 +2518,7 @@
pgoff_t index,
gfp_t gfp)
{
- filler_t *filler = (filler_t *)mapping->a_ops->readpage;
+ filler_t *filler = mapping->a_ops->readpage;
return do_read_cache_page(mapping, index, filler, NULL, gfp);
}
diff --git a/mm/frame_vector.c b/mm/frame_vector.c
index db77dcb..375a103 100644
--- a/mm/frame_vector.c
+++ b/mm/frame_vector.c
@@ -52,6 +52,18 @@
ret = -EFAULT;
goto out;
}
+
+ /*
+ * While get_vaddr_frames() could be used for transient (kernel
+ * controlled lifetime) pinning of memory pages all current
+ * users establish long term (userspace controlled lifetime)
+ * page pinning. Treat get_vaddr_frames() like
+ * get_user_pages_longterm() and disallow it for filesystem-dax
+ * mappings.
+ */
+ if (vma_is_fsdax(vma))
+ return -EOPNOTSUPP;
+
if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
vec->got_ref = true;
vec->is_pfns = false;
diff --git a/mm/gup.c b/mm/gup.c
index c63a034..be4ccdd 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -430,6 +430,9 @@
if (vm_flags & (VM_IO | VM_PFNMAP))
return -EFAULT;
+ if (gup_flags & FOLL_ANON && !vma_is_anonymous(vma))
+ return -EFAULT;
+
if (write) {
if (!(vm_flags & VM_WRITE)) {
if (!(gup_flags & FOLL_FORCE))
@@ -982,6 +985,70 @@
}
EXPORT_SYMBOL(get_user_pages);
+#ifdef CONFIG_FS_DAX
+/*
+ * This is the same as get_user_pages() in that it assumes we are
+ * operating on the current task's mm, but it goes further to validate
+ * that the vmas associated with the address range are suitable for
+ * longterm elevated page reference counts. For example, filesystem-dax
+ * mappings are subject to the lifetime enforced by the filesystem and
+ * we need guarantees that longterm users like RDMA and V4L2 only
+ * establish mappings that have a kernel enforced revocation mechanism.
+ *
+ * "longterm" == userspace controlled elevated page count lifetime.
+ * Contrast this to iov_iter_get_pages() usages which are transient.
+ */
+long get_user_pages_longterm(unsigned long start, unsigned long nr_pages,
+ unsigned int gup_flags, struct page **pages,
+ struct vm_area_struct **vmas_arg)
+{
+ struct vm_area_struct **vmas = vmas_arg;
+ struct vm_area_struct *vma_prev = NULL;
+ long rc, i;
+
+ if (!pages)
+ return -EINVAL;
+
+ if (!vmas) {
+ vmas = kcalloc(nr_pages, sizeof(struct vm_area_struct *),
+ GFP_KERNEL);
+ if (!vmas)
+ return -ENOMEM;
+ }
+
+ rc = get_user_pages(start, nr_pages, gup_flags, pages, vmas);
+
+ for (i = 0; i < rc; i++) {
+ struct vm_area_struct *vma = vmas[i];
+
+ if (vma == vma_prev)
+ continue;
+
+ vma_prev = vma;
+
+ if (vma_is_fsdax(vma))
+ break;
+ }
+
+ /*
+ * Either get_user_pages() failed, or the vma validation
+ * succeeded, in either case we don't need to put_page() before
+ * returning.
+ */
+ if (i >= rc)
+ goto out;
+
+ for (i = 0; i < rc; i++)
+ put_page(pages[i]);
+ rc = -EOPNOTSUPP;
+out:
+ if (vmas != vmas_arg)
+ kfree(vmas);
+ return rc;
+}
+EXPORT_SYMBOL(get_user_pages_longterm);
+#endif /* CONFIG_FS_DAX */
+
/**
* populate_vma_page_range() - populate a range of pages in the vma.
* @vma: target vma
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c234c07..e2982ea 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2279,11 +2279,13 @@
list_for_each_safe(pos, next, &list) {
page = list_entry((void *)pos, struct page, mapping);
- lock_page(page);
+ if (!trylock_page(page))
+ goto next;
/* split_huge_page() removes page from list on success */
if (!split_huge_page(page))
split++;
unlock_page(page);
+next:
put_page(page);
}
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index 7a6d1a1..ab47d93 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -794,6 +794,55 @@
}
EXPORT_SYMBOL(__asan_unpoison_stack_memory);
+/* Emitted by compiler to poison alloca()ed objects. */
+void __asan_alloca_poison(unsigned long addr, size_t size)
+{
+ size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+ size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
+ rounded_up_size;
+ size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+
+ const void *left_redzone = (const void *)(addr -
+ KASAN_ALLOCA_REDZONE_SIZE);
+ const void *right_redzone = (const void *)(addr + rounded_up_size);
+
+ WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
+
+ kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+ size - rounded_down_size);
+ kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_LEFT);
+ kasan_poison_shadow(right_redzone,
+ padding_size + KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_RIGHT);
+}
+EXPORT_SYMBOL(__asan_alloca_poison);
+
+/* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
+{
+ if (unlikely(!stack_top || stack_top > stack_bottom))
+ return;
+
+ kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+}
+EXPORT_SYMBOL(__asan_allocas_unpoison);
+
+/* Emitted by the compiler to [un]poison local variables. */
+#define DEFINE_ASAN_SET_SHADOW(byte) \
+ void __asan_set_shadow_##byte(const void *addr, size_t size) \
+ { \
+ __memset((void *)addr, 0x##byte, size); \
+ } \
+ EXPORT_SYMBOL(__asan_set_shadow_##byte)
+
+DEFINE_ASAN_SET_SHADOW(00);
+DEFINE_ASAN_SET_SHADOW(f1);
+DEFINE_ASAN_SET_SHADOW(f2);
+DEFINE_ASAN_SET_SHADOW(f3);
+DEFINE_ASAN_SET_SHADOW(f5);
+DEFINE_ASAN_SET_SHADOW(f8);
+
#ifdef CONFIG_MEMORY_HOTPLUG
static int kasan_mem_notifier(struct notifier_block *nb,
unsigned long action, void *data)
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 1229298..d9cf9e2 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -23,6 +23,14 @@
#define KASAN_STACK_PARTIAL 0xF4
#define KASAN_USE_AFTER_SCOPE 0xF8
+/*
+ * alloca redzone shadow values
+ */
+#define KASAN_ALLOCA_LEFT 0xCA
+#define KASAN_ALLOCA_RIGHT 0xCB
+
+#define KASAN_ALLOCA_REDZONE_SIZE 32
+
/* Don't break randconfig/all*config builds */
#ifndef KASAN_ABI_VERSION
#define KASAN_ABI_VERSION 1
@@ -112,4 +120,48 @@
static inline void quarantine_remove_cache(struct kmem_cache *cache) { }
#endif
+/*
+ * Exported functions for interfaces called from assembly or from generated
+ * code. Declarations here to avoid warning about missing declarations.
+ */
+asmlinkage void kasan_unpoison_task_stack_below(const void *watermark);
+void __asan_register_globals(struct kasan_global *globals, size_t size);
+void __asan_unregister_globals(struct kasan_global *globals, size_t size);
+void __asan_loadN(unsigned long addr, size_t size);
+void __asan_storeN(unsigned long addr, size_t size);
+void __asan_handle_no_return(void);
+void __asan_poison_stack_memory(const void *addr, size_t size);
+void __asan_unpoison_stack_memory(const void *addr, size_t size);
+void __asan_alloca_poison(unsigned long addr, size_t size);
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom);
+
+void __asan_load1(unsigned long addr);
+void __asan_store1(unsigned long addr);
+void __asan_load2(unsigned long addr);
+void __asan_store2(unsigned long addr);
+void __asan_load4(unsigned long addr);
+void __asan_store4(unsigned long addr);
+void __asan_load8(unsigned long addr);
+void __asan_store8(unsigned long addr);
+void __asan_load16(unsigned long addr);
+void __asan_store16(unsigned long addr);
+
+void __asan_load1_noabort(unsigned long addr);
+void __asan_store1_noabort(unsigned long addr);
+void __asan_load2_noabort(unsigned long addr);
+void __asan_store2_noabort(unsigned long addr);
+void __asan_load4_noabort(unsigned long addr);
+void __asan_store4_noabort(unsigned long addr);
+void __asan_load8_noabort(unsigned long addr);
+void __asan_store8_noabort(unsigned long addr);
+void __asan_load16_noabort(unsigned long addr);
+void __asan_store16_noabort(unsigned long addr);
+
+void __asan_set_shadow_00(const void *addr, size_t size);
+void __asan_set_shadow_f1(const void *addr, size_t size);
+void __asan_set_shadow_f2(const void *addr, size_t size);
+void __asan_set_shadow_f3(const void *addr, size_t size);
+void __asan_set_shadow_f5(const void *addr, size_t size);
+void __asan_set_shadow_f8(const void *addr, size_t size);
+
#endif
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 0b8cf43..5f2b9eb 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -102,6 +102,10 @@
case KASAN_USE_AFTER_SCOPE:
bug_type = "use-after-scope";
break;
+ case KASAN_ALLOCA_LEFT:
+ case KASAN_ALLOCA_RIGHT:
+ bug_type = "alloca-out-of-bounds";
+ break;
}
return bug_type;
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 5d7c006..898eb26 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -528,7 +528,12 @@
goto out;
}
- VM_BUG_ON_PAGE(PageCompound(page), page);
+ /* TODO: teach khugepaged to collapse THP mapped with pte */
+ if (PageCompound(page)) {
+ result = SCAN_PAGE_COMPOUND;
+ goto out;
+ }
+
VM_BUG_ON_PAGE(!PageAnon(page), page);
VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 43622c6..351d94a 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -921,6 +921,7 @@
int ret;
int kill = 1, forcekill;
struct page *hpage = *hpagep;
+ bool mlocked = PageMlocked(hpage);
/*
* Here we are interested only in user-mapped pages, so skip any
@@ -985,6 +986,13 @@
pfn, page_mapcount(hpage));
/*
+ * try_to_unmap() might put mlocked page in lru cache, so call
+ * shake_page() again to ensure that it's flushed.
+ */
+ if (mlocked)
+ shake_page(hpage, 0);
+
+ /*
* Now that the dirty bit has been propagated to the
* struct page and all unmaps done we can decide if
* killing is needed or not. Only kill when the page
diff --git a/mm/memory.c b/mm/memory.c
index f882280..cc6ab38 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2842,6 +2842,17 @@
return ret;
}
+/*
+ * The ordering of these checks is important for pmds with _PAGE_DEVMAP set.
+ * If we check pmd_trans_unstable() first we will trip the bad_pmd() check
+ * inside of pmd_none_or_trans_huge_or_clear_bad(). This will end up correctly
+ * returning 1 but not before it spams dmesg with the pmd_clear_bad() output.
+ */
+static int pmd_devmap_trans_unstable(pmd_t *pmd)
+{
+ return pmd_devmap(*pmd) || pmd_trans_unstable(pmd);
+}
+
static int pte_alloc_one_map(struct fault_env *fe)
{
struct vm_area_struct *vma = fe->vma;
@@ -2865,18 +2876,27 @@
map_pte:
/*
* If a huge pmd materialized under us just retry later. Use
- * pmd_trans_unstable() instead of pmd_trans_huge() to ensure the pmd
- * didn't become pmd_trans_huge under us and then back to pmd_none, as
- * a result of MADV_DONTNEED running immediately after a huge pmd fault
- * in a different thread of this mm, in turn leading to a misleading
- * pmd_trans_huge() retval. All we have to ensure is that it is a
- * regular pmd that we can walk with pte_offset_map() and we can do that
- * through an atomic read in C, which is what pmd_trans_unstable()
- * provides.
+ * pmd_trans_unstable() via pmd_devmap_trans_unstable() instead of
+ * pmd_trans_huge() to ensure the pmd didn't become pmd_trans_huge
+ * under us and then back to pmd_none, as a result of MADV_DONTNEED
+ * running immediately after a huge pmd fault in a different thread of
+ * this mm, in turn leading to a misleading pmd_trans_huge() retval.
+ * All we have to ensure is that it is a regular pmd that we can walk
+ * with pte_offset_map() and we can do that through an atomic read in
+ * C, which is what pmd_trans_unstable() provides.
*/
- if (pmd_trans_unstable(fe->pmd) || pmd_devmap(*fe->pmd))
+ if (pmd_devmap_trans_unstable(fe->pmd))
return VM_FAULT_NOPAGE;
+ /*
+ * At this point we know that our vmf->pmd points to a page of ptes
+ * and it cannot become pmd_none(), pmd_devmap() or pmd_trans_huge()
+ * for the duration of the fault. If a racing MADV_DONTNEED runs and
+ * we zap the ptes pointed to by our vmf->pmd, the vmf->ptl will still
+ * be valid and we will re-check to make sure the vmf->pte isn't
+ * pte_none() under vmf->ptl protection when we return to
+ * alloc_set_pte().
+ */
fe->pte = pte_offset_map_lock(vma->vm_mm, fe->pmd, fe->address,
&fe->ptl);
return 0;
@@ -3450,7 +3470,7 @@
fe->pte = NULL;
} else {
/* See comment in pte_alloc_one_map() */
- if (pmd_trans_unstable(fe->pmd) || pmd_devmap(*fe->pmd))
+ if (pmd_devmap_trans_unstable(fe->pmd))
return 0;
/*
* A regular pmd is established and it can't morph into a huge
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index e6fbdf4..ca18dc0 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2506,13 +2506,13 @@
if (mapping && mapping_cap_account_dirty(mapping)) {
struct inode *inode = mapping->host;
struct bdi_writeback *wb;
- bool locked;
+ struct wb_lock_cookie cookie = {};
- wb = unlocked_inode_to_wb_begin(inode, &locked);
+ wb = unlocked_inode_to_wb_begin(inode, &cookie);
current->nr_dirtied--;
dec_node_page_state(page, NR_DIRTIED);
dec_wb_stat(wb, WB_DIRTIED);
- unlocked_inode_to_wb_end(inode, locked);
+ unlocked_inode_to_wb_end(inode, &cookie);
}
}
EXPORT_SYMBOL(account_page_redirty);
@@ -2618,15 +2618,15 @@
if (mapping_cap_account_dirty(mapping)) {
struct inode *inode = mapping->host;
struct bdi_writeback *wb;
- bool locked;
+ struct wb_lock_cookie cookie = {};
lock_page_memcg(page);
- wb = unlocked_inode_to_wb_begin(inode, &locked);
+ wb = unlocked_inode_to_wb_begin(inode, &cookie);
if (TestClearPageDirty(page))
account_page_cleaned(page, mapping, wb);
- unlocked_inode_to_wb_end(inode, locked);
+ unlocked_inode_to_wb_end(inode, &cookie);
unlock_page_memcg(page);
} else {
ClearPageDirty(page);
@@ -2658,7 +2658,7 @@
if (mapping && mapping_cap_account_dirty(mapping)) {
struct inode *inode = mapping->host;
struct bdi_writeback *wb;
- bool locked;
+ struct wb_lock_cookie cookie = {};
/*
* Yes, Virginia, this is indeed insane.
@@ -2695,7 +2695,7 @@
* always locked coming in here, so we get the desired
* exclusion.
*/
- wb = unlocked_inode_to_wb_begin(inode, &locked);
+ wb = unlocked_inode_to_wb_begin(inode, &cookie);
if (TestClearPageDirty(page)) {
mem_cgroup_dec_page_stat(page, MEM_CGROUP_STAT_DIRTY);
dec_node_page_state(page, NR_FILE_DIRTY);
@@ -2703,7 +2703,7 @@
dec_wb_stat(wb, WB_RECLAIMABLE);
ret = 1;
}
- unlocked_inode_to_wb_end(inode, locked);
+ unlocked_inode_to_wb_end(inode, &cookie);
return ret;
}
return TestClearPageDirty(page);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index d3ea11f..b74f30e 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2402,8 +2402,9 @@
* The CPU has to be pinned. When zone parameter is non-NULL, spill just
* the single zone's pages.
*/
-void drain_local_pages(struct zone *zone)
+void drain_local_pages(void *z)
{
+ struct zone *zone = (struct zone *)z;
int cpu = smp_processor_id();
if (zone)
@@ -2463,8 +2464,7 @@
else
cpumask_clear_cpu(cpu, &cpus_with_pcps);
}
- on_each_cpu_mask(&cpus_with_pcps, (smp_call_func_t) drain_local_pages,
- zone, 1);
+ on_each_cpu_mask(&cpus_with_pcps, drain_local_pages, zone, 1);
}
#ifdef CONFIG_HIBERNATION
diff --git a/mm/percpu.c b/mm/percpu.c
index f014ceb..3794cfc 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -70,6 +70,7 @@
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/kmemleak.h>
+#include <linux/sched.h>
#include <asm/cacheflush.h>
#include <asm/sections.h>
diff --git a/mm/readahead.c b/mm/readahead.c
index 7dc48ba..8b2a755 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -81,7 +81,7 @@
* Hides the details of the LRU cache etc from the filesystems.
*/
int read_cache_pages(struct address_space *mapping, struct list_head *pages,
- int (*filler)(void *, struct page *), void *data)
+ int (*filler)(struct file *, struct page *), void *data)
{
struct page *page;
int ret = 0;
diff --git a/mm/shmem.c b/mm/shmem.c
index 0a6ac6a..61a39aa 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -466,36 +466,45 @@
info = list_entry(pos, struct shmem_inode_info, shrinklist);
inode = &info->vfs_inode;
- if (nr_to_split && split >= nr_to_split) {
- iput(inode);
- continue;
- }
+ if (nr_to_split && split >= nr_to_split)
+ goto leave;
- page = find_lock_page(inode->i_mapping,
+ page = find_get_page(inode->i_mapping,
(inode->i_size & HPAGE_PMD_MASK) >> PAGE_SHIFT);
if (!page)
goto drop;
+ /* No huge page at the end of the file: nothing to split */
if (!PageTransHuge(page)) {
- unlock_page(page);
put_page(page);
goto drop;
}
+ /*
+ * Leave the inode on the list if we failed to lock
+ * the page at this time.
+ *
+ * Waiting for the lock may lead to deadlock in the
+ * reclaim path.
+ */
+ if (!trylock_page(page)) {
+ put_page(page);
+ goto leave;
+ }
+
ret = split_huge_page(page);
unlock_page(page);
put_page(page);
- if (ret) {
- /* split failed: leave it on the list */
- iput(inode);
- continue;
- }
+ /* If split failed leave the inode on the list */
+ if (ret)
+ goto leave;
split++;
drop:
list_del_init(&info->shrinklist);
removed++;
+leave:
iput(inode);
}
diff --git a/mm/slab.c b/mm/slab.c
index 1f82d16..c59844d 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -4096,7 +4096,8 @@
next_reap_node();
out:
/* Set up the next iteration */
- schedule_delayed_work(work, round_jiffies_relative(REAPTIMEOUT_AC));
+ schedule_delayed_work_on(smp_processor_id(), work,
+ round_jiffies_relative(REAPTIMEOUT_AC));
}
#ifdef CONFIG_SLABINFO
diff --git a/mm/swap.c b/mm/swap.c
index 4dcf852..6f22754 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -208,9 +208,10 @@
{
int *pgmoved = arg;
- if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
- enum lru_list lru = page_lru_base_type(page);
- list_move_tail(&page->lru, &lruvec->lists[lru]);
+ if (PageLRU(page) && !PageUnevictable(page)) {
+ del_page_from_lru_list(page, lruvec, page_lru(page));
+ ClearPageActive(page);
+ add_page_to_lru_list_tail(page, lruvec, page_lru(page));
(*pgmoved)++;
}
}
@@ -234,7 +235,7 @@
*/
void rotate_reclaimable_page(struct page *page)
{
- if (!PageLocked(page) && !PageDirty(page) && !PageActive(page) &&
+ if (!PageLocked(page) && !PageDirty(page) &&
!PageUnevictable(page) && PageLRU(page)) {
struct pagevec *pvec;
unsigned long flags;
diff --git a/mm/vmpressure.c b/mm/vmpressure.c
index 1306f32..e468da6 100644
--- a/mm/vmpressure.c
+++ b/mm/vmpressure.c
@@ -423,7 +423,7 @@
void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
unsigned long scanned, unsigned long reclaimed)
{
- if (!memcg)
+ if (!memcg && tree)
vmpressure_global(gfp, scanned, reclaimed);
if (IS_ENABLED(CONFIG_MEMCG))
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 658d62c..c8e300c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -87,6 +87,7 @@
/* The highest zone to isolate pages for reclaim from */
enum zone_type reclaim_idx;
+ /* Writepage batching in laptop mode; RECLAIM_WRITE */
unsigned int may_writepage:1;
/* Can mapped pages be reclaimed? */
@@ -1057,6 +1058,15 @@
* throttling so we could easily OOM just because too many
* pages are in writeback and there is nothing else to
* reclaim. Wait for the writeback to complete.
+ *
+ * In cases 1) and 2) we activate the pages to get them out of
+ * the way while we continue scanning for clean pages on the
+ * inactive list and refilling from the active list. The
+ * observation here is that waiting for disk writes is more
+ * expensive than potentially causing reloads down the line.
+ * Since they're marked for immediate reclaim, they won't put
+ * memory pressure on the cache working set any longer than it
+ * takes to write them to disk.
*/
if (PageWriteback(page)) {
/* Case 1 above */
@@ -1064,7 +1074,7 @@
PageReclaim(page) &&
(pgdat && test_bit(PGDAT_WRITEBACK, &pgdat->flags))) {
nr_immediate++;
- goto keep_locked;
+ goto activate_locked;
/* Case 2 above */
} else if (sane_reclaim(sc) ||
@@ -1082,7 +1092,7 @@
*/
SetPageReclaim(page);
nr_writeback++;
- goto keep_locked;
+ goto activate_locked;
/* Case 3 above */
} else {
@@ -1153,14 +1163,18 @@
if (PageDirty(page)) {
/*
- * Only kswapd can writeback filesystem pages to
- * avoid risk of stack overflow but only writeback
- * if many dirty pages have been encountered.
+ * Only kswapd can writeback filesystem pages
+ * to avoid risk of stack overflow. But avoid
+ * injecting inefficient single-page IO into
+ * flusher writeback as much as possible: only
+ * write pages when we've encountered many
+ * dirty pages, and when we've already scanned
+ * the rest of the LRU for clean pages and see
+ * the same dirty pages again (PageReclaim).
*/
if (page_is_file_cache(page) &&
- (!current_is_kswapd() ||
- (pgdat &&
- !test_bit(PGDAT_DIRTY, &pgdat->flags)))) {
+ (!current_is_kswapd() || !PageReclaim(page) ||
+ !test_bit(PGDAT_DIRTY, &pgdat->flags))) {
/*
* Immediately reclaim when written back.
* Similar in principal to deactivate_page()
@@ -1170,7 +1184,7 @@
inc_node_page_state(page, NR_VMSCAN_IMMEDIATE);
SetPageReclaim(page);
- goto keep_locked;
+ goto activate_locked;
}
if (references == PAGEREF_RECLAIM_CLEAN)
@@ -1416,13 +1430,10 @@
* wants to isolate pages it will be able to operate on without
* blocking - clean pages for the most part.
*
- * ISOLATE_CLEAN means that only clean pages should be isolated. This
- * is used by reclaim when it is cannot write to backing storage
- *
* ISOLATE_ASYNC_MIGRATE is used to indicate that it only wants to pages
* that it is possible to migrate without blocking
*/
- if (mode & (ISOLATE_CLEAN|ISOLATE_ASYNC_MIGRATE)) {
+ if (mode & ISOLATE_ASYNC_MIGRATE) {
/* All the caller can do on PageWriteback is block */
if (PageWriteback(page))
return ret;
@@ -1430,10 +1441,6 @@
if (PageDirty(page)) {
struct address_space *mapping;
- /* ISOLATE_CLEAN means only clean pages */
- if (mode & ISOLATE_CLEAN)
- return ret;
-
/*
* Only pages without mappings or that have a
* ->migratepage callback are possible to migrate
@@ -1831,8 +1838,6 @@
if (!sc->may_unmap)
isolate_mode |= ISOLATE_UNMAPPED;
- if (!sc->may_writepage)
- isolate_mode |= ISOLATE_CLEAN;
spin_lock_irq(&pgdat->lru_lock);
@@ -1894,6 +1899,20 @@
set_bit(PGDAT_WRITEBACK, &pgdat->flags);
/*
+ * If dirty pages are scanned that are not queued for IO, it
+ * implies that flushers are not doing their job. This can
+ * happen when memory pressure pushes dirty pages to the end of
+ * the LRU before the dirty limits are breached and the dirty
+ * data has expired. It can also happen when the proportion of
+ * dirty pages grows not through writes but through memory
+ * pressure reclaiming all the clean cache. And in some cases,
+ * the flushers simply cannot keep up with the allocation
+ * rate. Nudge the flusher threads in case they are asleep.
+ */
+ if (nr_unqueued_dirty == nr_taken)
+ wakeup_flusher_threads(0, WB_REASON_VMSCAN);
+
+ /*
* Legacy memcg will stall in page writeback so avoid forcibly
* stalling here.
*/
@@ -1905,12 +1924,7 @@
if (nr_dirty && nr_dirty == nr_congested)
set_bit(PGDAT_CONGESTED, &pgdat->flags);
- /*
- * If dirty pages are scanned that are not queued for IO, it
- * implies that flushers are not keeping up. In this case, flag
- * the pgdat PGDAT_DIRTY and kswapd will start writing pages from
- * reclaim context.
- */
+ /* Allow kswapd to start writing pages during reclaim. */
if (nr_unqueued_dirty == nr_taken)
set_bit(PGDAT_DIRTY, &pgdat->flags);
@@ -2020,8 +2034,6 @@
if (!sc->may_unmap)
isolate_mode |= ISOLATE_UNMAPPED;
- if (!sc->may_writepage)
- isolate_mode |= ISOLATE_CLEAN;
spin_lock_irq(&pgdat->lru_lock);
@@ -2823,8 +2835,6 @@
struct scan_control *sc)
{
int initial_priority = sc->priority;
- unsigned long total_scanned = 0;
- unsigned long writeback_threshold;
retry:
delayacct_freepages_start();
@@ -2837,7 +2847,6 @@
sc->nr_scanned = 0;
shrink_zones(zonelist, sc);
- total_scanned += sc->nr_scanned;
if (sc->nr_reclaimed >= sc->nr_to_reclaim)
break;
@@ -2850,20 +2859,6 @@
*/
if (sc->priority < DEF_PRIORITY - 2)
sc->may_writepage = 1;
-
- /*
- * Try to write back as many pages as we just scanned. This
- * tends to cause slow streaming writers to write data to the
- * disk smoothly, at the dirtying rate, which is nice. But
- * that's undesirable in laptop mode, where we *want* lumpy
- * writeout. So in laptop mode, write out the whole world.
- */
- writeback_threshold = sc->nr_to_reclaim + sc->nr_to_reclaim / 2;
- if (total_scanned > writeback_threshold) {
- wakeup_flusher_threads(laptop_mode ? 0 : total_scanned,
- WB_REASON_TRY_TO_FREE_PAGES);
- sc->may_writepage = 1;
- }
} while (--sc->priority >= 0);
delayacct_freepages_end();
@@ -3023,7 +3018,7 @@
unsigned long nr_reclaimed;
struct scan_control sc = {
.nr_to_reclaim = SWAP_CLUSTER_MAX,
- .gfp_mask = (gfp_mask = memalloc_noio_flags(gfp_mask)),
+ .gfp_mask = memalloc_noio_flags(gfp_mask),
.reclaim_idx = gfp_zone(gfp_mask),
.order = order,
.nodemask = nodemask,
@@ -3038,12 +3033,12 @@
* 1 is returned so that the page allocator does not OOM kill at this
* point.
*/
- if (throttle_direct_reclaim(gfp_mask, zonelist, nodemask))
+ if (throttle_direct_reclaim(sc.gfp_mask, zonelist, nodemask))
return 1;
trace_mm_vmscan_direct_reclaim_begin(order,
sc.may_writepage,
- gfp_mask,
+ sc.gfp_mask,
sc.reclaim_idx);
nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
@@ -3827,16 +3822,15 @@
const unsigned long nr_pages = 1 << order;
struct task_struct *p = current;
struct reclaim_state reclaim_state;
- int classzone_idx = gfp_zone(gfp_mask);
struct scan_control sc = {
.nr_to_reclaim = max(nr_pages, SWAP_CLUSTER_MAX),
- .gfp_mask = (gfp_mask = memalloc_noio_flags(gfp_mask)),
+ .gfp_mask = memalloc_noio_flags(gfp_mask),
.order = order,
.priority = NODE_RECLAIM_PRIORITY,
.may_writepage = !!(node_reclaim_mode & RECLAIM_WRITE),
.may_unmap = !!(node_reclaim_mode & RECLAIM_UNMAP),
.may_swap = 1,
- .reclaim_idx = classzone_idx,
+ .reclaim_idx = gfp_zone(gfp_mask),
};
cond_resched();
@@ -3846,7 +3840,7 @@
* and RECLAIM_UNMAP.
*/
p->flags |= PF_MEMALLOC | PF_SWAPWRITE;
- lockdep_set_current_reclaim_state(gfp_mask);
+ lockdep_set_current_reclaim_state(sc.gfp_mask);
reclaim_state.reclaimed_slab = 0;
p->reclaim_state = &reclaim_state;
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 26f0e49..8bd62ed 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1356,8 +1356,6 @@
return zone == compare;
}
- /* The zone must be somewhere! */
- WARN_ON_ONCE(1);
return false;
}
@@ -1390,18 +1388,24 @@
zone->present_pages,
zone->managed_pages);
- for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
- seq_printf(m, "\n %-12s %lu", vmstat_text[i],
- zone_page_state(zone, i));
-
seq_printf(m,
"\n protection: (%ld",
zone->lowmem_reserve[0]);
for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++)
seq_printf(m, ", %ld", zone->lowmem_reserve[i]);
- seq_printf(m,
- ")"
- "\n pagesets");
+ seq_putc(m, ')');
+
+ /* If unpopulated, no other information is useful */
+ if (!populated_zone(zone)) {
+ seq_putc(m, '\n');
+ return;
+ }
+
+ for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+ seq_printf(m, "\n %-12s %lu", vmstat_text[i],
+ zone_page_state(zone, i));
+
+ seq_printf(m, "\n pagesets");
for_each_online_cpu(i) {
struct per_cpu_pageset *pageset;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index fbfacd5..fb3e2a5 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -29,6 +29,7 @@
#include <linux/net_tstamp.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/phy.h>
#include <net/arp.h>
#include <net/switchdev.h>
@@ -562,8 +563,7 @@
NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |
NETIF_F_ALL_FCOE;
- dev->features |= real_dev->vlan_features | NETIF_F_LLTX |
- NETIF_F_GSO_SOFTWARE;
+ dev->features |= dev->hw_features | NETIF_F_LLTX;
dev->gso_max_size = real_dev->gso_max_size;
dev->gso_max_segs = real_dev->gso_max_segs;
if (dev->features & NETIF_F_VLAN_FEATURES)
@@ -659,8 +659,11 @@
{
const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
const struct ethtool_ops *ops = vlan->real_dev->ethtool_ops;
+ struct phy_device *phydev = vlan->real_dev->phydev;
- if (ops->get_ts_info) {
+ if (phydev && phydev->drv && phydev->drv->ts_info) {
+ return phydev->drv->ts_info(phydev, info);
+ } else if (ops->get_ts_info) {
return ops->get_ts_info(vlan->real_dev, info);
} else {
info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
diff --git a/net/Kconfig b/net/Kconfig
index 0b8c255..78694c4 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -295,6 +295,7 @@
bool "enable BPF Just In Time compiler"
depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT
depends on MODULES
+ depends on !CFI
---help---
Berkeley Packet Filter filtering capabilities are normally handled
by an interpreter. This option allows kernel to generate a native
diff --git a/net/atm/lec.c b/net/atm/lec.c
index 5d26938..1e84c52 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -41,6 +41,9 @@
#include <linux/module.h>
#include <linux/init.h>
+/* Hardening for Spectre-v1 */
+#include <linux/nospec.h>
+
#include "lec.h"
#include "lec_arpc.h"
#include "resources.h"
@@ -697,8 +700,10 @@
bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc));
if (bytes_left != 0)
pr_info("copy from user failed for %d bytes\n", bytes_left);
- if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF ||
- !dev_lec[ioc_data.dev_num])
+ if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF)
+ return -EINVAL;
+ ioc_data.dev_num = array_index_nospec(ioc_data.dev_num, MAX_LEC_ITF);
+ if (!dev_lec[ioc_data.dev_num])
return -EINVAL;
vpriv = kmalloc(sizeof(struct lec_vcc_priv), GFP_KERNEL);
if (!vpriv)
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index e7f690b..5419b12 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -1964,10 +1964,22 @@
/* if yes, the client has roamed and we have
* to unclaim it.
*/
- batadv_handle_unclaim(bat_priv, primary_if,
- primary_if->net_dev->dev_addr,
- ethhdr->h_source, vid);
- goto allow;
+ if (batadv_has_timed_out(claim->lasttime, 100)) {
+ /* only unclaim if the last claim entry is
+ * older than 100 ms to make sure we really
+ * have a roaming client here.
+ */
+ batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_tx(): Roaming client %pM detected. Unclaim it.\n",
+ ethhdr->h_source);
+ batadv_handle_unclaim(bat_priv, primary_if,
+ primary_if->net_dev->dev_addr,
+ ethhdr->h_source, vid);
+ goto allow;
+ } else {
+ batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_tx(): Race for claim %pM detected. Drop packet.\n",
+ ethhdr->h_source);
+ goto handled;
+ }
}
/* check if it is a multicast/broadcast frame */
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 1904a93..de7b82e 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -755,7 +755,8 @@
}
static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
- struct lowpan_btle_dev *dev)
+ struct lowpan_btle_dev *dev,
+ bool new_netdev)
{
struct lowpan_peer *peer;
@@ -786,7 +787,8 @@
spin_unlock(&devices_lock);
/* Notifying peers about us needs to be done without locks held */
- INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
+ if (new_netdev)
+ INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
return peer->chan;
@@ -843,6 +845,7 @@
static inline void chan_ready_cb(struct l2cap_chan *chan)
{
struct lowpan_btle_dev *dev;
+ bool new_netdev = false;
dev = lookup_dev(chan->conn);
@@ -853,12 +856,13 @@
l2cap_chan_del(chan, -ENOENT);
return;
}
+ new_netdev = true;
}
if (!try_module_get(THIS_MODULE))
return;
- add_peer_chan(chan, dev);
+ add_peer_chan(chan, dev, new_netdev);
ifup(dev->netdev);
}
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 4b32525..ec313c9 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -192,6 +192,9 @@
}
EXPORT_SYMBOL(bt_accept_enqueue);
+/* Calling function must hold the sk lock.
+ * bt_sk(sk)->parent must be non-NULL meaning sk is in the parent list.
+ */
void bt_accept_unlink(struct sock *sk)
{
BT_DBG("sk %p state %d", sk, sk->sk_state);
@@ -210,11 +213,32 @@
BT_DBG("parent %p", parent);
+restart:
list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
sk = (struct sock *)s;
+ /* Prevent early freeing of sk due to unlink and sock_kill */
+ sock_hold(sk);
lock_sock(sk);
+ /* Check sk has not already been unlinked via
+ * bt_accept_unlink() due to serialisation caused by sk locking
+ */
+ if (!bt_sk(sk)->parent) {
+ BT_DBG("sk %p, already unlinked", sk);
+ release_sock(sk);
+ sock_put(sk);
+
+ /* Restart the loop as sk is no longer in the list
+ * and also avoid a potential infinite loop because
+ * list_for_each_entry_safe() is not thread safe.
+ */
+ goto restart;
+ }
+
+ /* sk is safely in the parent list so reduce reference count */
+ sock_put(sk);
+
/* FIXME: Is this check still needed */
if (sk->sk_state == BT_CLOSED) {
bt_accept_unlink(sk);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index dc59eae..cc06149 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -749,18 +749,31 @@
}
static void hci_req_add_le_create_conn(struct hci_request *req,
- struct hci_conn *conn)
+ struct hci_conn *conn,
+ bdaddr_t *direct_rpa)
{
struct hci_cp_le_create_conn cp;
struct hci_dev *hdev = conn->hdev;
u8 own_addr_type;
- /* Update random address, but set require_privacy to false so
- * that we never connect with an non-resolvable address.
+ /* If direct address was provided we use it instead of current
+ * address.
*/
- if (hci_update_random_address(req, false, conn_use_rpa(conn),
- &own_addr_type))
- return;
+ if (direct_rpa) {
+ if (bacmp(&req->hdev->random_addr, direct_rpa))
+ hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
+ direct_rpa);
+
+ /* direct address is always RPA */
+ own_addr_type = ADDR_LE_DEV_RANDOM;
+ } else {
+ /* Update random address, but set require_privacy to false so
+ * that we never connect with an non-resolvable address.
+ */
+ if (hci_update_random_address(req, false, conn_use_rpa(conn),
+ &own_addr_type))
+ return;
+ }
memset(&cp, 0, sizeof(cp));
@@ -825,7 +838,7 @@
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u16 conn_timeout,
- u8 role)
+ u8 role, bdaddr_t *direct_rpa)
{
struct hci_conn_params *params;
struct hci_conn *conn;
@@ -940,7 +953,7 @@
hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
}
- hci_req_add_le_create_conn(&req, conn);
+ hci_req_add_le_create_conn(&req, conn, direct_rpa);
create_conn:
err = hci_req_run(&req, create_le_conn_complete);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 3ac89e9..4bd72d2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -548,6 +548,7 @@
{
struct hci_dev *hdev = req->hdev;
u8 events[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ bool changed = false;
/* If Connectionless Slave Broadcast master role is supported
* enable all necessary events for it.
@@ -557,6 +558,7 @@
events[1] |= 0x80; /* Synchronization Train Complete */
events[2] |= 0x10; /* Slave Page Response Timeout */
events[2] |= 0x20; /* CSB Channel Map Change */
+ changed = true;
}
/* If Connectionless Slave Broadcast slave role is supported
@@ -567,13 +569,24 @@
events[2] |= 0x02; /* CSB Receive */
events[2] |= 0x04; /* CSB Timeout */
events[2] |= 0x08; /* Truncated Page Complete */
+ changed = true;
}
/* Enable Authenticated Payload Timeout Expired event if supported */
- if (lmp_ping_capable(hdev) || hdev->le_features[0] & HCI_LE_PING)
+ if (lmp_ping_capable(hdev) || hdev->le_features[0] & HCI_LE_PING) {
events[2] |= 0x80;
+ changed = true;
+ }
- hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
+ /* Some Broadcom based controllers indicate support for Set Event
+ * Mask Page 2 command, but then actually do not support it. Since
+ * the default value is all bits set to zero, the command is only
+ * required if the event mask has to be changed. In case no change
+ * to the event mask is needed, skip this command.
+ */
+ if (changed)
+ hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2,
+ sizeof(events), events);
}
static int hci_init3_req(struct hci_request *req, unsigned long opt)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index e17aacb..d2f9eb1 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -4646,7 +4646,8 @@
/* This function requires the caller holds hdev->lock */
static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
bdaddr_t *addr,
- u8 addr_type, u8 adv_type)
+ u8 addr_type, u8 adv_type,
+ bdaddr_t *direct_rpa)
{
struct hci_conn *conn;
struct hci_conn_params *params;
@@ -4697,7 +4698,8 @@
}
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
- HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER);
+ HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER,
+ direct_rpa);
if (!IS_ERR(conn)) {
/* If HCI_AUTO_CONN_EXPLICIT is set, conn is already owned
* by higher layer that tried to connect, if no then
@@ -4807,8 +4809,13 @@
bdaddr_type = irk->addr_type;
}
- /* Check if we have been requested to connect to this device */
- conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ /* Check if we have been requested to connect to this device.
+ *
+ * direct_addr is set only for directed advertising reports (it is NULL
+ * for advertising reports) and is already verified to be RPA above.
+ */
+ conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type,
+ direct_addr);
if (conn && type == LE_ADV_IND) {
/* Store report for later inclusion by
* mgmt_device_connected
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2bbca23..1fc23cb 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -7148,7 +7148,7 @@
hcon = hci_connect_le(hdev, dst, dst_type,
chan->sec_level,
HCI_LE_CONN_TIMEOUT,
- HCI_ROLE_SLAVE);
+ HCI_ROLE_SLAVE, NULL);
else
hcon = hci_connect_le_scan(hdev, dst, dst_type,
chan->sec_level,
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 658c900..ead4d1b 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -2233,8 +2233,14 @@
else
sec_level = authreq_to_seclevel(auth);
- if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
+ if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK)) {
+ /* If link is already encrypted with sufficient security we
+ * still need refresh encryption as per Core Spec 5.0 Vol 3,
+ * Part H 2.4.6
+ */
+ smp_ltk_encrypt(conn, hcon->sec_level);
return 0;
+ }
if (sec_level > hcon->pending_sec_level)
hcon->pending_sec_level = sec_level;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 9218931..f57de0a 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -504,8 +504,8 @@
if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit)
return -ELOOP;
- /* Device is already being bridged */
- if (br_port_exists(dev))
+ /* Device has master upper dev */
+ if (netdev_master_upper_dev_get(dev))
return -EBUSY;
/* No bridging devices that dislike that (e.g. wireless) */
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 8bd5696..abf7111 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -230,6 +230,9 @@
struct brport_attribute *brport_attr = to_brport_attr(attr);
struct net_bridge_port *p = to_brport(kobj);
+ if (!brport_attr->show)
+ return -EINVAL;
+
return brport_attr->show(p, buf);
}
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c
index 9024283..9adf162 100644
--- a/net/bridge/netfilter/ebt_among.c
+++ b/net/bridge/netfilter/ebt_among.c
@@ -172,18 +172,69 @@
return true;
}
+static bool poolsize_invalid(const struct ebt_mac_wormhash *w)
+{
+ return w && w->poolsize >= (INT_MAX / sizeof(struct ebt_mac_wormhash_tuple));
+}
+
+static bool wormhash_offset_invalid(int off, unsigned int len)
+{
+ if (off == 0) /* not present */
+ return false;
+
+ if (off < (int)sizeof(struct ebt_among_info) ||
+ off % __alignof__(struct ebt_mac_wormhash))
+ return true;
+
+ off += sizeof(struct ebt_mac_wormhash);
+
+ return off > len;
+}
+
+static bool wormhash_sizes_valid(const struct ebt_mac_wormhash *wh, int a, int b)
+{
+ if (a == 0)
+ a = sizeof(struct ebt_among_info);
+
+ return ebt_mac_wormhash_size(wh) + a == b;
+}
+
static int ebt_among_mt_check(const struct xt_mtchk_param *par)
{
const struct ebt_among_info *info = par->matchinfo;
const struct ebt_entry_match *em =
container_of(par->matchinfo, const struct ebt_entry_match, data);
- int expected_length = sizeof(struct ebt_among_info);
+ unsigned int expected_length = sizeof(struct ebt_among_info);
const struct ebt_mac_wormhash *wh_dst, *wh_src;
int err;
+ if (expected_length > em->match_size)
+ return -EINVAL;
+
+ if (wormhash_offset_invalid(info->wh_dst_ofs, em->match_size) ||
+ wormhash_offset_invalid(info->wh_src_ofs, em->match_size))
+ return -EINVAL;
+
wh_dst = ebt_among_wh_dst(info);
- wh_src = ebt_among_wh_src(info);
+ if (poolsize_invalid(wh_dst))
+ return -EINVAL;
+
expected_length += ebt_mac_wormhash_size(wh_dst);
+ if (expected_length > em->match_size)
+ return -EINVAL;
+
+ wh_src = ebt_among_wh_src(info);
+ if (poolsize_invalid(wh_src))
+ return -EINVAL;
+
+ if (info->wh_src_ofs < info->wh_dst_ofs) {
+ if (!wormhash_sizes_valid(wh_src, info->wh_src_ofs, info->wh_dst_ofs))
+ return -EINVAL;
+ } else {
+ if (!wormhash_sizes_valid(wh_dst, info->wh_dst_ofs, info->wh_src_ofs))
+ return -EINVAL;
+ }
+
expected_length += ebt_mac_wormhash_size(wh_src);
if (em->match_size != EBT_ALIGN(expected_length)) {
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f5c11bb..5a89a4a 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -2031,7 +2031,9 @@
if (match_kern)
match_kern->match_size = ret;
- WARN_ON(type == EBT_COMPAT_TARGET && size_left);
+ if (WARN_ON(type == EBT_COMPAT_TARGET && size_left))
+ return -EINVAL;
+
match32 = (struct compat_ebt_entry_mwt *) buf;
}
@@ -2087,6 +2089,15 @@
*
* offsets are relative to beginning of struct ebt_entry (i.e., 0).
*/
+ for (i = 0; i < 4 ; ++i) {
+ if (offsets[i] >= *total)
+ return -EINVAL;
+ if (i == 0)
+ continue;
+ if (offsets[i-1] > offsets[i])
+ return -EINVAL;
+ }
+
for (i = 0, j = 1 ; j < 4 ; j++, i++) {
struct compat_ebt_entry_mwt *match32;
unsigned int size;
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 25a30be..98ea28d 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -2512,6 +2512,11 @@
int ret = 1;
dout("try_write start %p state %lu\n", con, con->state);
+ if (con->state != CON_STATE_PREOPEN &&
+ con->state != CON_STATE_CONNECTING &&
+ con->state != CON_STATE_NEGOTIATING &&
+ con->state != CON_STATE_OPEN)
+ return 0;
more:
dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes);
@@ -2537,6 +2542,8 @@
}
more_kvec:
+ BUG_ON(!con->sock);
+
/* kvec data queued? */
if (con->out_kvec_left) {
ret = write_partial_kvec(con);
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index a8effc8..5004810 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -209,6 +209,14 @@
__open_session(monc);
}
+static void un_backoff(struct ceph_mon_client *monc)
+{
+ monc->hunt_mult /= 2; /* reduce by 50% */
+ if (monc->hunt_mult < 1)
+ monc->hunt_mult = 1;
+ dout("%s hunt_mult now %d\n", __func__, monc->hunt_mult);
+}
+
/*
* Reschedule delayed work timer.
*/
@@ -955,6 +963,7 @@
if (!monc->hunting) {
ceph_con_keepalive(&monc->con);
__validate_auth(monc);
+ un_backoff(monc);
}
if (is_auth) {
@@ -1114,9 +1123,8 @@
dout("%s found mon%d\n", __func__, monc->cur_mon);
monc->hunting = false;
monc->had_a_connection = true;
- monc->hunt_mult /= 2; /* reduce by 50% */
- if (monc->hunt_mult < 1)
- monc->hunt_mult = 1;
+ un_backoff(monc);
+ __schedule_delayed(monc);
}
}
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index d3f6c26..255c0a0 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -295,6 +295,7 @@
u32 yes;
struct crush_rule *r;
+ err = -EINVAL;
ceph_decode_32_safe(p, end, yes, bad);
if (!yes) {
dout("crush_decode NO rule %d off %x %p to %p\n",
diff --git a/net/compat.c b/net/compat.c
index a96fd2f..73671e6 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -372,7 +372,8 @@
optname == SO_ATTACH_REUSEPORT_CBPF)
return do_set_attach_filter(sock, level, optname,
optval, optlen);
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
+ if (!COMPAT_USE_64BIT_TIME &&
+ (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
return do_set_sock_timeout(sock, level, optname, optval, optlen);
return sock_setsockopt(sock, level, optname, optval, optlen);
@@ -437,7 +438,8 @@
static int compat_sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
+ if (!COMPAT_USE_64BIT_TIME &&
+ (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
return do_get_sock_timeout(sock, level, optname, optval, optlen);
return sock_getsockopt(sock, level, optname, optval, optlen);
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 72fb37c..c6a8932 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -995,7 +995,7 @@
{
if (*name == '\0')
return false;
- if (strlen(name) >= IFNAMSIZ)
+ if (strnlen(name, IFNAMSIZ) == IFNAMSIZ)
return false;
if (!strcmp(name, ".") || !strcmp(name, ".."))
return false;
@@ -2201,8 +2201,11 @@
*/
int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq)
{
+ bool disabling;
int rc;
+ disabling = txq < dev->real_num_tx_queues;
+
if (txq < 1 || txq > dev->num_tx_queues)
return -EINVAL;
@@ -2218,15 +2221,19 @@
if (dev->num_tc)
netif_setup_tc(dev, txq);
- if (txq < dev->real_num_tx_queues) {
+ dev->real_num_tx_queues = txq;
+
+ if (disabling) {
+ synchronize_net();
qdisc_reset_all_tx_gt(dev, txq);
#ifdef CONFIG_XPS
netif_reset_xps_queues_gt(dev, txq);
#endif
}
+ } else {
+ dev->real_num_tx_queues = txq;
}
- dev->real_num_tx_queues = txq;
return 0;
}
EXPORT_SYMBOL(netif_set_real_num_tx_queues);
@@ -2662,7 +2669,7 @@
if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
return 0;
- eth = (struct ethhdr *)skb_mac_header(skb);
+ eth = (struct ethhdr *)skb->data;
type = eth->h_proto;
}
@@ -2866,7 +2873,7 @@
}
EXPORT_SYMBOL(passthru_features_check);
-static netdev_features_t dflt_features_check(const struct sk_buff *skb,
+static netdev_features_t dflt_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
{
@@ -3228,15 +3235,23 @@
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
static void skb_update_prio(struct sk_buff *skb)
{
- struct netprio_map *map = rcu_dereference_bh(skb->dev->priomap);
+ const struct netprio_map *map;
+ const struct sock *sk;
+ unsigned int prioidx;
- if (!skb->priority && skb->sk && map) {
- unsigned int prioidx =
- sock_cgroup_prioidx(&skb->sk->sk_cgrp_data);
+ if (skb->priority)
+ return;
+ map = rcu_dereference_bh(skb->dev->priomap);
+ if (!map)
+ return;
+ sk = skb_to_full_sk(skb);
+ if (!sk)
+ return;
- if (prioidx < map->priomap_len)
- skb->priority = map->priomap[prioidx];
- }
+ prioidx = sock_cgroup_prioidx(&sk->sk_cgrp_data);
+
+ if (prioidx < map->priomap_len)
+ skb->priority = map->priomap[prioidx];
}
#else
#define skb_update_prio(skb)
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index c0548d2..e3e6a3e 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -57,8 +57,8 @@
return -EINVAL;
list_for_each_entry(ha, &list->list, list) {
- if (!memcmp(ha->addr, addr, addr_len) &&
- ha->type == addr_type) {
+ if (ha->type == addr_type &&
+ !memcmp(ha->addr, addr, addr_len)) {
if (global) {
/* check if addr is already used as global */
if (ha->global_use)
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index eecad95..340a3db 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -54,7 +54,8 @@
static void neigh_timer_handler(unsigned long arg);
static void __neigh_notify(struct neighbour *n, int type, int flags);
static void neigh_update_notify(struct neighbour *neigh);
-static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
+static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
+ struct net_device *dev);
static unsigned int neigh_probe_enable;
#ifdef CONFIG_PROC_FS
@@ -255,8 +256,7 @@
{
write_lock_bh(&tbl->lock);
neigh_flush_dev(tbl, dev);
- pneigh_ifdown(tbl, dev);
- write_unlock_bh(&tbl->lock);
+ pneigh_ifdown_and_unlock(tbl, dev);
del_timer_sync(&tbl->proxy_timer);
pneigh_queue_purge(&tbl->proxy_queue);
@@ -646,9 +646,10 @@
return -ENOENT;
}
-static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
+static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
+ struct net_device *dev)
{
- struct pneigh_entry *n, **np;
+ struct pneigh_entry *n, **np, *freelist = NULL;
u32 h;
for (h = 0; h <= PNEIGH_HASHMASK; h++) {
@@ -656,16 +657,23 @@
while ((n = *np) != NULL) {
if (!dev || n->dev == dev) {
*np = n->next;
- if (tbl->pdestructor)
- tbl->pdestructor(n);
- if (n->dev)
- dev_put(n->dev);
- kfree(n);
+ n->next = freelist;
+ freelist = n;
continue;
}
np = &n->next;
}
}
+ write_unlock_bh(&tbl->lock);
+ while ((n = freelist)) {
+ freelist = n->next;
+ n->next = NULL;
+ if (tbl->pdestructor)
+ tbl->pdestructor(n);
+ if (n->dev)
+ dev_put(n->dev);
+ kfree(n);
+ }
return -ENOENT;
}
@@ -1139,10 +1147,6 @@
lladdr = neigh->ha;
}
- if (new & NUD_CONNECTED)
- neigh->confirmed = jiffies;
- neigh->updated = jiffies;
-
/* If entry was valid and address is not changed,
do not change entry state, if new one is STALE.
*/
@@ -1164,6 +1168,16 @@
}
}
+ /* Update timestamps only once we know we will make a change to the
+ * neighbour entry. Otherwise we risk to move the locktime window with
+ * noop updates and ignore relevant ARP updates.
+ */
+ if (new != old || lladdr != neigh->ha) {
+ if (new & NUD_CONNECTED)
+ neigh->confirmed = jiffies;
+ neigh->updated = jiffies;
+ }
+
if (new != old) {
neigh_del_timer(neigh);
if (new & NUD_PROBE)
@@ -2293,12 +2307,16 @@
err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL);
if (!err) {
- if (tb[NDA_IFINDEX])
+ if (tb[NDA_IFINDEX]) {
+ if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32))
+ return -EINVAL;
filter_idx = nla_get_u32(tb[NDA_IFINDEX]);
-
- if (tb[NDA_MASTER])
+ }
+ if (tb[NDA_MASTER]) {
+ if (nla_len(tb[NDA_MASTER]) != sizeof(u32))
+ return -EINVAL;
filter_master_idx = nla_get_u32(tb[NDA_MASTER]);
-
+ }
if (filter_idx || filter_master_idx)
flags |= NLM_F_DUMP_FILTERED;
}
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index b7efe2f..04fd04c 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -312,6 +312,25 @@
goto out;
}
+static int __net_init net_defaults_init_net(struct net *net)
+{
+ net->core.sysctl_somaxconn = SOMAXCONN;
+ return 0;
+}
+
+static struct pernet_operations net_defaults_ops = {
+ .init = net_defaults_init_net,
+};
+
+static __init int net_defaults_init(void)
+{
+ if (register_pernet_subsys(&net_defaults_ops))
+ panic("Cannot initialize net default settings");
+
+ return 0;
+}
+
+core_initcall(net_defaults_init);
#ifdef CONFIG_NET_NS
static struct ucounts *inc_net_namespaces(struct user_namespace *ns)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 457f882..9b2d611 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -666,7 +666,7 @@
int err;
rtnl_lock();
- if (np->dev_name) {
+ if (np->dev_name[0]) {
struct net *net = current->nsproxy->net_ns;
ndev = __dev_get_by_name(net, np->dev_name);
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 26ef78a..9c2e60e 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -909,6 +909,7 @@
n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
n->cloned = 1;
n->nohdr = 0;
+ n->peeked = 0;
n->destructor = NULL;
C(tail);
C(end);
@@ -2621,7 +2622,8 @@
{
int pos = skb_headlen(skb);
- skb_shinfo(skb1)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG;
+ skb_shinfo(skb1)->tx_flags |= skb_shinfo(skb)->tx_flags &
+ SKBTX_SHARED_FRAG;
if (len < pos) /* Split line is inside header. */
skb_split_inside_header(skb, skb1, len, pos);
else /* Second chunk has no header, nothing to copy. */
@@ -3234,8 +3236,8 @@
skb_copy_from_linear_data_offset(head_skb, offset,
skb_put(nskb, hsize), hsize);
- skb_shinfo(nskb)->tx_flags = skb_shinfo(head_skb)->tx_flags &
- SKBTX_SHARED_FRAG;
+ skb_shinfo(nskb)->tx_flags |= skb_shinfo(head_skb)->tx_flags &
+ SKBTX_SHARED_FRAG;
while (pos < offset + len) {
if (i >= nfrags) {
@@ -3481,24 +3483,18 @@
NULL);
}
-/**
- * skb_to_sgvec - Fill a scatter-gather list from a socket buffer
- * @skb: Socket buffer containing the buffers to be mapped
- * @sg: The scatter-gather list to map into
- * @offset: The offset into the buffer's contents to start mapping
- * @len: Length of buffer space to be mapped
- *
- * Fill the specified scatter-gather list with mappings/pointers into a
- * region of the buffer space attached to a socket buffer.
- */
static int
-__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len,
+ unsigned int recursion_level)
{
int start = skb_headlen(skb);
int i, copy = start - offset;
struct sk_buff *frag_iter;
int elt = 0;
+ if (unlikely(recursion_level >= 24))
+ return -EMSGSIZE;
+
if (copy > 0) {
if (copy > len)
copy = len;
@@ -3517,6 +3513,8 @@
end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]);
if ((copy = end - offset) > 0) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ if (unlikely(elt && sg_is_last(&sg[elt - 1])))
+ return -EMSGSIZE;
if (copy > len)
copy = len;
@@ -3531,16 +3529,22 @@
}
skb_walk_frags(skb, frag_iter) {
- int end;
+ int end, ret;
WARN_ON(start > offset + len);
end = start + frag_iter->len;
if ((copy = end - offset) > 0) {
+ if (unlikely(elt && sg_is_last(&sg[elt - 1])))
+ return -EMSGSIZE;
+
if (copy > len)
copy = len;
- elt += __skb_to_sgvec(frag_iter, sg+elt, offset - start,
- copy);
+ ret = __skb_to_sgvec(frag_iter, sg+elt, offset - start,
+ copy, recursion_level + 1);
+ if (unlikely(ret < 0))
+ return ret;
+ elt += ret;
if ((len -= copy) == 0)
return elt;
offset += copy;
@@ -3551,6 +3555,31 @@
return elt;
}
+/**
+ * skb_to_sgvec - Fill a scatter-gather list from a socket buffer
+ * @skb: Socket buffer containing the buffers to be mapped
+ * @sg: The scatter-gather list to map into
+ * @offset: The offset into the buffer's contents to start mapping
+ * @len: Length of buffer space to be mapped
+ *
+ * Fill the specified scatter-gather list with mappings/pointers into a
+ * region of the buffer space attached to a socket buffer. Returns either
+ * the number of scatterlist items used, or -EMSGSIZE if the contents
+ * could not fit.
+ */
+int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+ int nsg = __skb_to_sgvec(skb, sg, offset, len, 0);
+
+ if (nsg <= 0)
+ return nsg;
+
+ sg_mark_end(&sg[nsg - 1]);
+
+ return nsg;
+}
+EXPORT_SYMBOL_GPL(skb_to_sgvec);
+
/* As compared with skb_to_sgvec, skb_to_sgvec_nomark only map skb to given
* sglist without mark the sg which contain last skb data as the end.
* So the caller can mannipulate sg list as will when padding new data after
@@ -3573,19 +3602,11 @@
int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
int offset, int len)
{
- return __skb_to_sgvec(skb, sg, offset, len);
+ return __skb_to_sgvec(skb, sg, offset, len, 0);
}
EXPORT_SYMBOL_GPL(skb_to_sgvec_nomark);
-int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
-{
- int nsg = __skb_to_sgvec(skb, sg, offset, len);
- sg_mark_end(&sg[nsg - 1]);
-
- return nsg;
-}
-EXPORT_SYMBOL_GPL(skb_to_sgvec);
/**
* skb_cow_data - Check that a socket buffer's data buffers are writable
@@ -3723,7 +3744,7 @@
skb_queue_tail(&sk->sk_error_queue, skb);
if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_data_ready(sk);
+ sk->sk_error_report(sk);
return 0;
}
EXPORT_SYMBOL(sock_queue_err_skb);
@@ -3868,7 +3889,8 @@
return;
if (tsonly) {
- skb_shinfo(skb)->tx_flags = skb_shinfo(orig_skb)->tx_flags;
+ skb_shinfo(skb)->tx_flags |= skb_shinfo(orig_skb)->tx_flags &
+ SKBTX_ANY_TSTAMP;
skb_shinfo(skb)->tskey = skb_shinfo(orig_skb)->tskey;
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 1d88335..0e82197 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1465,7 +1465,7 @@
static void __sk_free(struct sock *sk)
{
- if (unlikely(sock_diag_has_destroy_listeners(sk) && sk->sk_net_refcnt))
+ if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk)))
sock_diag_broadcast_destroy(sk);
else
sk_destruct(sk);
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 1b46190..546ba76 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -438,8 +438,6 @@
{
struct ctl_table *tbl;
- net->core.sysctl_somaxconn = SOMAXCONN;
-
tbl = netns_core_table;
if (!net_eq(net, &init_net)) {
tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL);
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index 7753681..86a2ed0 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -126,6 +126,16 @@
DCCPF_SEQ_WMAX));
}
+static void dccp_tasklet_schedule(struct sock *sk)
+{
+ struct tasklet_struct *t = &dccp_sk(sk)->dccps_xmitlet;
+
+ if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
+ sock_hold(sk);
+ __tasklet_schedule(t);
+ }
+}
+
static void ccid2_hc_tx_rto_expire(unsigned long data)
{
struct sock *sk = (struct sock *)data;
@@ -166,7 +176,7 @@
/* if we were blocked before, we may now send cwnd=1 packet */
if (sender_was_blocked)
- tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
+ dccp_tasklet_schedule(sk);
/* restart backed-off timer */
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
out:
@@ -706,7 +716,7 @@
done:
/* check if incoming Acks allow pending packets to be sent */
if (sender_was_blocked && !ccid2_cwnd_network_limited(hc))
- tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
+ dccp_tasklet_schedule(sk);
dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks);
}
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 8c7799cd..6697b18 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -620,6 +620,7 @@
ireq = inet_rsk(req);
sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr);
sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr);
+ ireq->ir_mark = inet_request_mark(sk, skb);
ireq->ireq_family = AF_INET;
ireq->ir_iif = sk->sk_bound_dev_if;
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 28e8252..6cbcf39 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -349,6 +349,7 @@
ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
ireq->ireq_family = AF_INET6;
+ ireq->ir_mark = inet_request_mark(sk, skb);
if (ipv6_opt_accepted(sk, skb, IP6CB(skb)) ||
np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 9d43c1f..ff3b058 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -789,6 +789,11 @@
if (skb == NULL)
goto out_release;
+ if (sk->sk_state == DCCP_CLOSED) {
+ rc = -ENOTCONN;
+ goto out_discard;
+ }
+
skb_reserve(skb, sk->sk_prot->max_header);
rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc != 0)
diff --git a/net/dccp/timer.c b/net/dccp/timer.c
index 3a2c340..2a952cb 100644
--- a/net/dccp/timer.c
+++ b/net/dccp/timer.c
@@ -230,12 +230,12 @@
else
dccp_write_xmit(sk);
bh_unlock_sock(sk);
+ sock_put(sk);
}
static void dccp_write_xmit_timer(unsigned long data)
{
dccp_write_xmitlet(data);
- sock_put((struct sock *)data);
}
void dccp_init_xmit_timers(struct sock *sk)
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index e1d4d89..f025276 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -25,6 +25,7 @@
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/ratelimit.h>
#include <linux/kernel.h>
#include <linux/keyctl.h>
#include <linux/err.h>
@@ -91,9 +92,9 @@
next_opt = memchr(opt, '#', end - opt) ?: end;
opt_len = next_opt - opt;
- if (!opt_len) {
- printk(KERN_WARNING
- "Empty option to dns_resolver key\n");
+ if (opt_len <= 0 || opt_len > 128) {
+ pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key\n",
+ opt_len);
return -EINVAL;
}
@@ -127,10 +128,8 @@
}
bad_option_value:
- printk(KERN_WARNING
- "Option '%*.*s' to dns_resolver key:"
- " bad/missing value\n",
- opt_nlen, opt_nlen, opt);
+ pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value\n",
+ opt_nlen, opt_nlen, opt);
return -EINVAL;
} while (opt = next_opt + 1, opt < end);
}
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 4ebe2aa..04b5450 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -324,8 +324,7 @@
unsigned long irqflags;
frame->is_supervision = is_supervision_frame(port->hsr, skb);
- frame->node_src = hsr_get_node(&port->hsr->node_db, skb,
- frame->is_supervision);
+ frame->node_src = hsr_get_node(port, skb, frame->is_supervision);
if (frame->node_src == NULL)
return -1; /* Unknown node and !is_supervision, or no mem */
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 7ea9258..284a9b8 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -158,9 +158,10 @@
/* Get the hsr_node from which 'skb' was sent.
*/
-struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
+struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
bool is_sup)
{
+ struct list_head *node_db = &port->hsr->node_db;
struct hsr_node *node;
struct ethhdr *ethhdr;
u16 seq_out;
@@ -186,7 +187,11 @@
*/
seq_out = hsr_get_skb_sequence_nr(skb) - 1;
} else {
- WARN_ONCE(1, "%s: Non-HSR frame\n", __func__);
+ /* this is called also for frames from master port and
+ * so warn only for non master ports
+ */
+ if (port->type != HSR_PT_MASTER)
+ WARN_ONCE(1, "%s: Non-HSR frame\n", __func__);
seq_out = HSR_SEQNR_START;
}
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index 438b40f..4e04f0e 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -18,7 +18,7 @@
struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[],
u16 seq_out);
-struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
+struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
bool is_sup);
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port);
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index d7efbf0..83af533 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -204,9 +204,13 @@
static int lowpan_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
- struct net_device *wdev = netdev_notifier_info_to_dev(ptr);
+ struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+ struct wpan_dev *wpan_dev;
- if (wdev->type != ARPHRD_IEEE802154)
+ if (ndev->type != ARPHRD_IEEE802154)
+ return NOTIFY_DONE;
+ wpan_dev = ndev->ieee802154_ptr;
+ if (!wpan_dev)
return NOTIFY_DONE;
switch (event) {
@@ -215,8 +219,8 @@
* also delete possible lowpan interfaces which belongs
* to the wpan interface.
*/
- if (wdev->ieee802154_ptr->lowpan_dev)
- lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
+ if (wpan_dev->lowpan_dev)
+ lowpan_dellink(wpan_dev->lowpan_dev, NULL);
break;
default:
return NOTIFY_DONE;
diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c
index e0bd013..bf5d26d 100644
--- a/net/ieee802154/socket.c
+++ b/net/ieee802154/socket.c
@@ -304,12 +304,12 @@
skb->sk = sk;
skb->protocol = htons(ETH_P_IEEE802154);
- dev_put(dev);
-
err = dev_queue_xmit(skb);
if (err > 0)
err = net_xmit_errno(err);
+ dev_put(dev);
+
return err ?: size;
out_skb:
@@ -693,12 +693,12 @@
skb->sk = sk;
skb->protocol = htons(ETH_P_IEEE802154);
- dev_put(dev);
-
err = dev_queue_xmit(skb);
if (err > 0)
err = net_xmit_errno(err);
+ dev_put(dev);
+
return err ?: size;
out_skb:
diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c
index f3c81c2..3403108 100644
--- a/net/ipc_router/ipc_router_core.c
+++ b/net/ipc_router/ipc_router_core.c
@@ -223,6 +223,25 @@
is_wakeup_source_allowed = flag;
}
+/**
+ * is_sensor_port() - Check if the remote port is sensor service or not
+ * @rport: Pointer to the remote port.
+ *
+ * Return: true if the remote port is sensor service else false.
+ */
+static int is_sensor_port(struct msm_ipc_router_remote_port *rport)
+{
+ u32 svcid = 0;
+
+ if (rport && rport->server) {
+ svcid = rport->server->name.service;
+ if (svcid == 400 || (svcid >= 256 && svcid <= 320))
+ return true;
+ }
+
+ return false;
+}
+
static void init_routing_table(void)
{
int i;
@@ -2729,7 +2748,6 @@
struct rr_packet *pkt = NULL;
struct msm_ipc_port *port_ptr;
struct msm_ipc_router_remote_port *rport_ptr;
- int ret;
struct msm_ipc_router_xprt_info *xprt_info =
container_of(work,
@@ -2737,16 +2755,7 @@
read_data);
while ((pkt = rr_read(xprt_info)) != NULL) {
- if (pkt->length < calc_rx_header_size(xprt_info) ||
- pkt->length > MAX_IPC_PKT_SIZE) {
- IPC_RTR_ERR("%s: Invalid pkt length %d\n", __func__,
- pkt->length);
- goto read_next_pkt1;
- }
- ret = extract_header(pkt);
- if (ret < 0)
- goto read_next_pkt1;
hdr = &pkt->hdr;
if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
@@ -4173,6 +4182,7 @@
{
struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
struct msm_ipc_router_xprt_work *xprt_work;
+ struct msm_ipc_router_remote_port *rport_ptr = NULL;
struct rr_packet *pkt;
int ret;
@@ -4223,16 +4233,40 @@
if (!pkt)
return;
+ if (pkt->length < calc_rx_header_size(xprt_info) ||
+ pkt->length > MAX_IPC_PKT_SIZE) {
+ IPC_RTR_ERR("%s: Invalid pkt length %d\n",
+ __func__, pkt->length);
+ release_pkt(pkt);
+ return;
+ }
+
+ ret = extract_header(pkt);
+ if (ret < 0) {
+ release_pkt(pkt);
+ return;
+ }
+
pkt->ws_need = false;
+
+ if (pkt->hdr.type == IPC_ROUTER_CTRL_CMD_DATA)
+ rport_ptr = ipc_router_get_rport_ref(pkt->hdr.src_node_id,
+ pkt->hdr.src_port_id);
+
mutex_lock(&xprt_info->rx_lock_lhb2);
list_add_tail(&pkt->list, &xprt_info->pkt_list);
- if (!xprt_info->dynamic_ws) {
- __pm_stay_awake(&xprt_info->ws);
- pkt->ws_need = true;
- } else {
- if (is_wakeup_source_allowed) {
+ /* check every pkt is from SENSOR services or not and
+ * avoid holding both edge and port specific wake-up sources
+ */
+ if (!is_sensor_port(rport_ptr)) {
+ if (!xprt_info->dynamic_ws) {
__pm_stay_awake(&xprt_info->ws);
pkt->ws_need = true;
+ } else {
+ if (is_wakeup_source_allowed) {
+ __pm_stay_awake(&xprt_info->ws);
+ pkt->ws_need = true;
+ }
}
}
mutex_unlock(&xprt_info->rx_lock_lhb2);
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 22377c8..e8f8623 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -220,7 +220,9 @@
ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
sg_init_table(sg, nfrags + sglists);
- skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ if (unlikely(err < 0))
+ goto out_free;
if (x->props.flags & XFRM_STATE_ESN) {
/* Attach seqhi sg right after packet payload */
@@ -393,7 +395,9 @@
skb_push(skb, ihl);
sg_init_table(sg, nfrags + sglists);
- skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ if (unlikely(err < 0))
+ goto out_free;
if (x->props.flags & XFRM_STATE_ESN) {
/* Attach seqhi sg right after packet payload */
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index e60517e..8cae791 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -437,7 +437,7 @@
/*unsigned long now; */
struct net *net = dev_net(dev);
- rt = ip_route_output(net, sip, tip, 0, 0);
+ rt = ip_route_output(net, sip, tip, 0, l3mdev_master_ifindex_rcu(dev));
if (IS_ERR(rt))
return 1;
if (rt->dst.dev != dev) {
@@ -658,6 +658,7 @@
unsigned char *arp_ptr;
struct rtable *rt;
unsigned char *sha;
+ unsigned char *tha = NULL;
__be32 sip, tip;
u16 dev_type = dev->type;
int addr_type;
@@ -729,6 +730,7 @@
break;
#endif
default:
+ tha = arp_ptr;
arp_ptr += dev->addr_len;
}
memcpy(&tip, arp_ptr, 4);
@@ -847,8 +849,18 @@
It is possible, that this option should be enabled for some
devices (strip is candidate)
*/
- is_garp = arp->ar_op == htons(ARPOP_REQUEST) && tip == sip &&
- addr_type == RTN_UNICAST;
+ is_garp = tip == sip && addr_type == RTN_UNICAST;
+
+ /* Unsolicited ARP _replies_ also require target hwaddr to be
+ * the same as source.
+ */
+ if (is_garp && arp->ar_op == htons(ARPOP_REPLY))
+ is_garp =
+ /* IPv4 over IEEE 1394 doesn't provide target
+ * hardware address field in its ARP payload.
+ */
+ tha &&
+ !memcmp(tha, sha, dev->addr_len);
if (!n &&
((arp->ar_op == htons(ARPOP_REPLY) &&
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 20fb25e..3d8021d 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -268,10 +268,11 @@
esph->spi = x->id.spi;
sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg,
- (unsigned char *)esph - skb->data,
- assoclen + ivlen + clen + alen);
-
+ err = skb_to_sgvec(skb, sg,
+ (unsigned char *)esph - skb->data,
+ assoclen + ivlen + clen + alen);
+ if (unlikely(err < 0))
+ goto error;
aead_request_set_crypt(req, sg, sg, ivlen + clen, iv);
aead_request_set_ad(req, assoclen);
@@ -481,7 +482,9 @@
}
sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ err = skb_to_sgvec(skb, sg, 0, skb->len);
+ if (unlikely(err < 0))
+ goto out;
aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
aead_request_set_ad(req, assoclen);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 38c1c97..e1be244 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -640,6 +640,11 @@
fi->fib_nh, cfg))
return 1;
}
+#ifdef CONFIG_IP_ROUTE_CLASSID
+ if (cfg->fc_flow &&
+ cfg->fc_flow != fi->fib_nh->nh_tclassid)
+ return 1;
+#endif
if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) &&
(!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->nh_gw))
return 0;
@@ -1606,18 +1611,20 @@
bool first = false;
for_nexthops(fi) {
+ if (net->ipv4.sysctl_fib_multipath_use_neigh) {
+ if (!fib_good_nh(nh))
+ continue;
+ if (!first) {
+ res->nh_sel = nhsel;
+ first = true;
+ }
+ }
+
if (hash > atomic_read(&nh->nh_upper_bound))
continue;
- if (!net->ipv4.sysctl_fib_multipath_use_neigh ||
- fib_good_nh(nh)) {
- res->nh_sel = nhsel;
- return;
- }
- if (!first) {
- res->nh_sel = nhsel;
- first = true;
- }
+ res->nh_sel = nhsel;
+ return;
} endfor_nexthops(fi);
}
#endif
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index 631c0d0..8effac0 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -119,6 +119,9 @@
static bool inet_fragq_should_evict(const struct inet_frag_queue *q)
{
+ if (!hlist_unhashed(&q->list_evictor))
+ return false;
+
return q->net->low_thresh == 0 ||
frag_mem_limit(q->net) >= q->net->low_thresh;
}
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index ddcd56c..a6b34ac 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -182,6 +182,7 @@
tw->tw_dport = inet->inet_dport;
tw->tw_family = sk->sk_family;
tw->tw_reuse = sk->sk_reuse;
+ tw->tw_reuseport = sk->sk_reuseport;
tw->tw_hash = sk->sk_hash;
tw->tw_ipv6only = 0;
tw->tw_transparent = inet->transparent;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index e60f9fa..d0bd98f 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1098,7 +1098,8 @@
if (copy > length)
copy = length;
- if (!(rt->dst.dev->features&NETIF_F_SG)) {
+ if (!(rt->dst.dev->features&NETIF_F_SG) &&
+ skb_tailroom(skb) >= copy) {
unsigned int off;
off = skb->len;
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index bf62fa4..5ddd649 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -242,7 +242,8 @@
src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
if (!ipv6_addr_v4mapped(&src_info->ipi6_addr))
return -EINVAL;
- ipc->oif = src_info->ipi6_ifindex;
+ if (src_info->ipi6_ifindex)
+ ipc->oif = src_info->ipi6_ifindex;
ipc->addr = src_info->ipi6_addr.s6_addr32[3];
continue;
}
@@ -272,7 +273,8 @@
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
return -EINVAL;
info = (struct in_pktinfo *)CMSG_DATA(cmsg);
- ipc->oif = info->ipi_ifindex;
+ if (info->ipi_ifindex)
+ ipc->oif = info->ipi_ifindex;
ipc->addr = info->ipi_spec_dst.s_addr;
break;
}
@@ -1552,10 +1554,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
- err = nf_getsockopt(sk, PF_INET, optname, optval,
- &len);
- release_sock(sk);
+ err = nf_getsockopt(sk, PF_INET, optname, optval, &len);
if (err >= 0)
err = put_user(len, optlen);
return err;
@@ -1587,9 +1586,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
err = compat_nf_getsockopt(sk, PF_INET, optname, optval, &len);
- release_sock(sk);
if (err >= 0)
err = put_user(len, optlen);
return err;
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 96536a0..e1271e75 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -253,13 +253,14 @@
struct net_device *dev;
char name[IFNAMSIZ];
- if (parms->name[0])
- strlcpy(name, parms->name, IFNAMSIZ);
- else {
- if (strlen(ops->kind) > (IFNAMSIZ - 3)) {
- err = -E2BIG;
+ err = -E2BIG;
+ if (parms->name[0]) {
+ if (!dev_valid_name(parms->name))
goto failed;
- }
+ strlcpy(name, parms->name, IFNAMSIZ);
+ } else {
+ if (strlen(ops->kind) > (IFNAMSIZ - 3))
+ goto failed;
strlcpy(name, ops->kind, IFNAMSIZ);
strncat(name, "%d", 2);
}
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 27089f5..742a343 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1929,6 +1929,20 @@
struct net *net = dev_net(skb->dev);
int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
struct mr_table *mrt;
+ struct net_device *dev;
+
+ /* skb->dev passed in is the loX master dev for vrfs.
+ * As there are no vifs associated with loopback devices,
+ * get the proper interface that does have a vif associated with it.
+ */
+ dev = skb->dev;
+ if (netif_is_l3_master(skb->dev)) {
+ dev = dev_get_by_index_rcu(net, IPCB(skb)->iif);
+ if (!dev) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+ }
/* Packet is looped back after forward, it should not be
* forwarded second time, but still can be delivered locally.
@@ -1966,7 +1980,7 @@
/* already under rcu_read_lock() */
cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
if (!cache) {
- int vif = ipmr_find_vif(mrt, skb->dev);
+ int vif = ipmr_find_vif(mrt, dev);
if (vif >= 0)
cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr,
@@ -1986,7 +2000,7 @@
}
read_lock(&mrt_lock);
- vif = ipmr_find_vif(mrt, skb->dev);
+ vif = ipmr_find_vif(mrt, dev);
if (vif >= 0) {
int err2 = ipmr_cache_unresolved(mrt, vif, skb);
read_unlock(&mrt_lock);
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 6975384..d35815e 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -261,6 +261,10 @@
}
if (table_base + v
!= arpt_next_entry(e)) {
+ if (unlikely(stackidx >= private->stacksize)) {
+ verdict = NF_DROP;
+ break;
+ }
jumpstack[stackidx++] = e;
}
@@ -415,17 +419,15 @@
}
static inline int
-find_check_entry(struct arpt_entry *e, const char *name, unsigned int size)
+find_check_entry(struct arpt_entry *e, const char *name, unsigned int size,
+ struct xt_percpu_counter_alloc_state *alloc_state)
{
struct xt_entry_target *t;
struct xt_target *target;
- unsigned long pcnt;
int ret;
- pcnt = xt_percpu_counter_alloc();
- if (IS_ERR_VALUE(pcnt))
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
- e->counters.pcnt = pcnt;
t = arpt_get_target(e);
target = xt_request_find_target(NFPROTO_ARP, t->u.user.name,
@@ -443,7 +445,7 @@
err:
module_put(t->u.kernel.target->me);
out:
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
return ret;
}
@@ -523,7 +525,7 @@
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(par.target->me);
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
}
/* Checks and translates the user-supplied table segment (held in
@@ -532,6 +534,7 @@
static int translate_table(struct xt_table_info *newinfo, void *entry0,
const struct arpt_replace *repl)
{
+ struct xt_percpu_counter_alloc_state alloc_state = { 0 };
struct arpt_entry *iter;
unsigned int *offsets;
unsigned int i;
@@ -594,7 +597,8 @@
/* Finally, each sanity check must pass */
i = 0;
xt_entry_foreach(iter, entry0, newinfo->size) {
- ret = find_check_entry(iter, repl->name, repl->size);
+ ret = find_check_entry(iter, repl->name, repl->size,
+ &alloc_state);
if (ret != 0)
break;
++i;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 7c00ce9..e78f652 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -345,8 +345,13 @@
continue;
}
if (table_base + v != ipt_next_entry(e) &&
- !(e->ip.flags & IPT_F_GOTO))
+ !(e->ip.flags & IPT_F_GOTO)) {
+ if (unlikely(stackidx >= private->stacksize)) {
+ verdict = NF_DROP;
+ break;
+ }
jumpstack[stackidx++] = e;
+ }
e = get_entry(table_base, v);
continue;
@@ -535,7 +540,8 @@
static int
find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
- unsigned int size)
+ unsigned int size,
+ struct xt_percpu_counter_alloc_state *alloc_state)
{
struct xt_entry_target *t;
struct xt_target *target;
@@ -543,12 +549,9 @@
unsigned int j;
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
- unsigned long pcnt;
- pcnt = xt_percpu_counter_alloc();
- if (IS_ERR_VALUE(pcnt))
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
- e->counters.pcnt = pcnt;
j = 0;
mtpar.net = net;
@@ -586,7 +589,7 @@
cleanup_match(ematch, net);
}
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
return ret;
}
@@ -674,7 +677,7 @@
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(par.target->me);
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
}
/* Checks and translates the user-supplied table segment (held in
@@ -683,6 +686,7 @@
translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
const struct ipt_replace *repl)
{
+ struct xt_percpu_counter_alloc_state alloc_state = { 0 };
struct ipt_entry *iter;
unsigned int *offsets;
unsigned int i;
@@ -742,7 +746,8 @@
/* Finally, each sanity check must pass */
i = 0;
xt_entry_foreach(iter, entry0, newinfo->size) {
- ret = find_check_entry(iter, net, repl->name, repl->size);
+ ret = find_check_entry(iter, net, repl->name, repl->size,
+ &alloc_state);
if (ret != 0)
break;
++i;
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index 574f7eb..ac8342d 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -252,16 +252,16 @@
if (set_h245_addr(skb, protoff, data, dataoff, taddr,
&ct->tuplehash[!dir].tuple.dst.u3,
htons((port & htons(1)) ? nated_port + 1 :
- nated_port)) == 0) {
- /* Save ports */
- info->rtp_port[i][dir] = rtp_port;
- info->rtp_port[i][!dir] = htons(nated_port);
- } else {
+ nated_port))) {
nf_ct_unexpect_related(rtp_exp);
nf_ct_unexpect_related(rtcp_exp);
return -1;
}
+ /* Save ports */
+ info->rtp_port[i][dir] = rtp_port;
+ info->rtp_port[i][!dir] = htons(nated_port);
+
/* Success */
pr_debug("nf_nat_h323: expect RTP %pI4:%hu->%pI4:%hu\n",
&rtp_exp->tuple.src.u3.ip,
@@ -370,15 +370,15 @@
/* Modify signal */
if (set_h225_addr(skb, protoff, data, dataoff, taddr,
&ct->tuplehash[!dir].tuple.dst.u3,
- htons(nated_port)) == 0) {
- /* Save ports */
- info->sig_port[dir] = port;
- info->sig_port[!dir] = htons(nated_port);
- } else {
+ htons(nated_port))) {
nf_ct_unexpect_related(exp);
return -1;
}
+ /* Save ports */
+ info->sig_port[dir] = port;
+ info->sig_port[!dir] = htons(nated_port);
+
pr_debug("nf_nat_q931: expect H.245 %pI4:%hu->%pI4:%hu\n",
&exp->tuple.src.u3.ip,
ntohs(exp->tuple.src.u.tcp.port),
@@ -462,24 +462,27 @@
/* Modify signal */
if (set_h225_addr(skb, protoff, data, 0, &taddr[idx],
&ct->tuplehash[!dir].tuple.dst.u3,
- htons(nated_port)) == 0) {
- /* Save ports */
- info->sig_port[dir] = port;
- info->sig_port[!dir] = htons(nated_port);
-
- /* Fix for Gnomemeeting */
- if (idx > 0 &&
- get_h225_addr(ct, *data, &taddr[0], &addr, &port) &&
- (ntohl(addr.ip) & 0xff000000) == 0x7f000000) {
- set_h225_addr(skb, protoff, data, 0, &taddr[0],
- &ct->tuplehash[!dir].tuple.dst.u3,
- info->sig_port[!dir]);
- }
- } else {
+ htons(nated_port))) {
nf_ct_unexpect_related(exp);
return -1;
}
+ /* Save ports */
+ info->sig_port[dir] = port;
+ info->sig_port[!dir] = htons(nated_port);
+
+ /* Fix for Gnomemeeting */
+ if (idx > 0 &&
+ get_h225_addr(ct, *data, &taddr[0], &addr, &port) &&
+ (ntohl(addr.ip) & 0xff000000) == 0x7f000000) {
+ if (set_h225_addr(skb, protoff, data, 0, &taddr[0],
+ &ct->tuplehash[!dir].tuple.dst.u3,
+ info->sig_port[!dir])) {
+ nf_ct_unexpect_related(exp);
+ return -1;
+ }
+ }
+
/* Success */
pr_debug("nf_nat_ras: expect Q.931 %pI4:%hu->%pI4:%hu\n",
&exp->tuple.src.u3.ip,
@@ -550,9 +553,9 @@
}
/* Modify signal */
- if (!set_h225_addr(skb, protoff, data, dataoff, taddr,
- &ct->tuplehash[!dir].tuple.dst.u3,
- htons(nated_port)) == 0) {
+ if (set_h225_addr(skb, protoff, data, dataoff, taddr,
+ &ct->tuplehash[!dir].tuple.dst.u3,
+ htons(nated_port))) {
nf_ct_unexpect_related(exp);
return -1;
}
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 93bfadf..8fa153c 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -775,8 +775,10 @@
ipc.addr = faddr = daddr;
if (ipc.opt && ipc.opt->opt.srr) {
- if (!daddr)
- return -EINVAL;
+ if (!daddr) {
+ err = -EINVAL;
+ goto out_free;
+ }
faddr = ipc.opt->opt.faddr;
}
tos = get_rttos(&ipc, inet);
@@ -842,6 +844,7 @@
out:
ip_rt_put(rt);
+out_free:
if (free)
kfree(ipc.opt);
if (!err) {
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 03728c6..e83fdf6 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -126,10 +126,13 @@
static int ip_rt_error_cost __read_mostly = HZ;
static int ip_rt_error_burst __read_mostly = 5 * HZ;
static int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ;
-static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
+static u32 ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
static int ip_rt_min_advmss __read_mostly = 256;
static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT;
+
+static int ip_min_valid_pmtu __read_mostly = IPV4_MIN_MTU;
+
/*
* Interface to generic destination cache.
*/
@@ -2790,7 +2793,8 @@
.data = &ip_rt_min_pmtu,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &ip_min_valid_pmtu,
},
{
.procname = "min_adv_mss",
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index af0b324..fdfaaf0 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1146,7 +1146,8 @@
lock_sock(sk);
flags = msg->msg_flags;
- if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) {
+ if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect) &&
+ !tp->repair) {
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
if (err == -EINPROGRESS && copied_syn > 0)
goto out;
@@ -2558,7 +2559,7 @@
case TCP_REPAIR_QUEUE:
if (!tp->repair)
err = -EPERM;
- else if (val < TCP_QUEUES_NR)
+ else if ((unsigned int)val < TCP_QUEUES_NR)
tp->repair_queue = val;
else
err = -EINVAL;
@@ -2697,8 +2698,10 @@
#ifdef CONFIG_TCP_MD5SIG
case TCP_MD5SIG:
- /* Read the IP->Key mappings from userspace */
- err = tp->af_specific->md5_parse(sk, optval, optlen);
+ if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))
+ err = tp->af_specific->md5_parse(sk, optval, optlen);
+ else
+ err = -EINVAL;
break;
#endif
case TCP_USER_TIMEOUT:
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index 8ec6053..91698595 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -773,7 +773,9 @@
}
}
}
- bbr->idle_restart = 0;
+ /* Restart after idle ends only once we process a new S/ACK for data */
+ if (rs->delivered > 0)
+ bbr->idle_restart = 0;
}
static void bbr_update_model(struct sock *sk, const struct rate_sample *rs)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 8ce03d8..c2ad59d 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -116,6 +116,7 @@
#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */
#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */
#define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */
+#define FLAG_NO_CHALLENGE_ACK 0x8000 /* do not call tcp_send_challenge_ack() */
#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED)
#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
@@ -3619,7 +3620,8 @@
if (before(ack, prior_snd_una)) {
/* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
if (before(ack, prior_snd_una - tp->max_window)) {
- tcp_send_challenge_ack(sk, skb);
+ if (!(flag & FLAG_NO_CHALLENGE_ACK))
+ tcp_send_challenge_ack(sk, skb);
return -1;
}
goto old_ack;
@@ -3942,11 +3944,8 @@
int length = (th->doff << 2) - sizeof(*th);
const u8 *ptr = (const u8 *)(th + 1);
- /* If the TCP option is too short, we can short cut */
- if (length < TCPOLEN_MD5SIG)
- return NULL;
-
- while (length > 0) {
+ /* If not enough data remaining, we can short cut */
+ while (length >= TCPOLEN_MD5SIG) {
int opcode = *ptr++;
int opsize;
@@ -5608,10 +5607,6 @@
else
tp->pred_flags = 0;
- if (!sock_flag(sk, SOCK_DEAD)) {
- sk->sk_state_change(sk);
- sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
- }
}
static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
@@ -5680,6 +5675,7 @@
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_fastopen_cookie foc = { .len = -1 };
int saved_clamp = tp->rx_opt.mss_clamp;
+ bool fastopen_fail;
tcp_parse_options(skb, &tp->rx_opt, 0, &foc);
if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
@@ -5783,10 +5779,15 @@
tcp_finish_connect(sk, skb);
- if ((tp->syn_fastopen || tp->syn_data) &&
- tcp_rcv_fastopen_synack(sk, skb, &foc))
- return -1;
+ fastopen_fail = (tp->syn_fastopen || tp->syn_data) &&
+ tcp_rcv_fastopen_synack(sk, skb, &foc);
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ sk->sk_state_change(sk);
+ sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
+ }
+ if (fastopen_fail)
+ return -1;
if (sk->sk_write_pending ||
icsk->icsk_accept_queue.rskq_defer_accept ||
icsk->icsk_ack.pingpong) {
@@ -5969,13 +5970,17 @@
/* step 5: check the ACK field */
acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH |
- FLAG_UPDATE_TS_RECENT) > 0;
+ FLAG_UPDATE_TS_RECENT |
+ FLAG_NO_CHALLENGE_ACK) > 0;
+ if (!acceptable) {
+ if (sk->sk_state == TCP_SYN_RECV)
+ return 1; /* send one RST */
+ tcp_send_challenge_ack(sk, skb);
+ goto discard;
+ }
switch (sk->sk_state) {
case TCP_SYN_RECV:
- if (!acceptable)
- return 1;
-
if (!tp->srtt_us)
tcp_synack_rtt_meas(sk, req);
@@ -6045,14 +6050,6 @@
* our SYNACK so stop the SYNACK timer.
*/
if (req) {
- /* Return RST if ack_seq is invalid.
- * Note that RFC793 only says to generate a
- * DUPACK for it but for TCP Fast Open it seems
- * better to treat this case like TCP_SYN_RECV
- * above.
- */
- if (!acceptable)
- return 1;
/* We no longer need the request sock. */
reqsk_fastopen_remove(sk, req, false);
tcp_rearm_rto(sk);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 20634ea..70c7212 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1580,7 +1580,7 @@
*/
segs = max_t(u32, bytes / mss_now, min_tso_segs);
- return min_t(u32, segs, sk->sk_gso_max_segs);
+ return segs;
}
EXPORT_SYMBOL(tcp_tso_autosize);
@@ -1592,8 +1592,10 @@
const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
u32 tso_segs = ca_ops->tso_segs_goal ? ca_ops->tso_segs_goal(sk) : 0;
- return tso_segs ? :
- tcp_tso_autosize(sk, mss_now, sysctl_tcp_min_tso_segs);
+ if (!tso_segs)
+ tso_segs = tcp_tso_autosize(sk, mss_now,
+ sysctl_tcp_min_tso_segs);
+ return min_t(u32, tso_segs, sk->sk_gso_max_segs);
}
/* Returns the portion of skb which can be sent right away */
@@ -1907,6 +1909,24 @@
}
}
+static bool tcp_can_coalesce_send_queue_head(struct sock *sk, int len)
+{
+ struct sk_buff *skb, *next;
+
+ skb = tcp_send_head(sk);
+ tcp_for_write_queue_from_safe(skb, next, sk) {
+ if (len <= skb->len)
+ break;
+
+ if (unlikely(TCP_SKB_CB(skb)->eor))
+ return false;
+
+ len -= skb->len;
+ }
+
+ return true;
+}
+
/* Create a new MTU probe if we are ready.
* MTU probe is regularly attempting to increase the path MTU by
* deliberately sending larger packets. This discovers routing
@@ -1979,6 +1999,9 @@
return 0;
}
+ if (!tcp_can_coalesce_send_queue_head(sk, probe_size))
+ return -1;
+
/* We're allowed to probe. Build it now. */
nskb = sk_stream_alloc_skb(sk, probe_size, GFP_ATOMIC, false);
if (!nskb)
@@ -2014,6 +2037,10 @@
/* We've eaten all the data from this skb.
* Throw it away. */
TCP_SKB_CB(nskb)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+ /* If this is the last SKB we copy and eor is set
+ * we need to propagate it to the new skb.
+ */
+ TCP_SKB_CB(nskb)->eor = TCP_SKB_CB(skb)->eor;
tcp_unlink_write_queue(skb, sk);
sk_wmem_free_skb(sk, skb);
} else {
@@ -2664,8 +2691,10 @@
return -EBUSY;
if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
- if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
- BUG();
+ if (unlikely(before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))
return -ENOMEM;
}
@@ -3209,6 +3238,7 @@
sock_reset_flag(sk, SOCK_DONE);
tp->snd_wnd = 0;
tcp_init_wl(tp, 0);
+ tcp_write_queue_purge(sk);
tp->snd_una = tp->write_seq;
tp->snd_sml = tp->write_seq;
tp->snd_up = tp->write_seq;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 1589e6a..885cc39 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -987,8 +987,10 @@
sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);
if (ipc.opt && ipc.opt->opt.srr) {
- if (!daddr)
- return -EINVAL;
+ if (!daddr) {
+ err = -EINVAL;
+ goto out_free;
+ }
faddr = ipc.opt->opt.faddr;
connected = 0;
}
@@ -1096,6 +1098,7 @@
out:
ip_rt_put(rt);
+out_free:
if (free)
kfree(ipc.opt);
if (!err)
@@ -1723,6 +1726,11 @@
err = udplite_checksum_init(skb, uh);
if (err)
return err;
+
+ if (UDP_SKB_CB(skb)->partial_cov) {
+ skb->csum = inet_compute_pseudo(skb, proto);
+ return 0;
+ }
}
/* Note, we are only interested in != 0 or == 0, thus the
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index a5c1ff3..9077060 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -994,7 +994,10 @@
INIT_HLIST_NODE(&ifa->addr_lst);
ifa->scope = scope;
ifa->prefix_len = pfxlen;
- ifa->flags = flags | IFA_F_TENTATIVE;
+ ifa->flags = flags;
+ /* No need to add the TENTATIVE flag for addresses with NODAD */
+ if (!(flags & IFA_F_NODAD))
+ ifa->flags |= IFA_F_TENTATIVE;
ifa->valid_lft = valid_lft;
ifa->prefered_lft = prefered_lft;
ifa->cstamp = ifa->tstamp = jiffies;
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 189eb10..e742c4d 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -423,7 +423,9 @@
ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
sg_init_table(sg, nfrags + sglists);
- skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ if (unlikely(err < 0))
+ goto out_free;
if (x->props.flags & XFRM_STATE_ESN) {
/* Attach seqhi sg right after packet payload */
@@ -603,7 +605,9 @@
ip6h->hop_limit = 0;
sg_init_table(sg, nfrags + sglists);
- skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ err = skb_to_sgvec_nomark(skb, sg, 0, skb->len);
+ if (unlikely(err < 0))
+ goto out_free;
if (x->props.flags & XFRM_STATE_ESN) {
/* Attach seqhi sg right after packet payload */
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index cbcdd5d..44a2010 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -248,9 +248,11 @@
esph->spi = x->id.spi;
sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg,
- (unsigned char *)esph - skb->data,
- assoclen + ivlen + clen + alen);
+ err = skb_to_sgvec(skb, sg,
+ (unsigned char *)esph - skb->data,
+ assoclen + ivlen + clen + alen);
+ if (unlikely(err < 0))
+ goto error;
aead_request_set_crypt(req, sg, sg, ivlen + clen, iv);
aead_request_set_ad(req, assoclen);
@@ -423,7 +425,9 @@
}
sg_init_table(sg, nfrags);
- skb_to_sgvec(skb, sg, 0, skb->len);
+ ret = skb_to_sgvec(skb, sg, 0, skb->len);
+ if (unlikely(ret < 0))
+ goto out;
aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
aead_request_set_ad(req, assoclen);
diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c
index c0cbcb2..1dc023c 100644
--- a/net/ipv6/ip6_checksum.c
+++ b/net/ipv6/ip6_checksum.c
@@ -72,6 +72,11 @@
err = udplite_checksum_init(skb, uh);
if (err)
return err;
+
+ if (UDP_SKB_CB(skb)->partial_cov) {
+ skb->csum = ip6_compute_pseudo(skb, proto);
+ return 0;
+ }
}
/* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels)
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 21bd54f..a88aff0 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -319,11 +319,13 @@
if (t || !create)
return t;
- if (parms->name[0])
+ if (parms->name[0]) {
+ if (!dev_valid_name(parms->name))
+ return NULL;
strlcpy(name, parms->name, IFNAMSIZ);
- else
+ } else {
strcpy(name, "ip6gre%d");
-
+ }
dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,
ip6gre_tunnel_setup);
if (!dev)
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 55ca65c..0b5a75b 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -365,6 +365,11 @@
static inline int ip6_forward_finish(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
+ struct dst_entry *dst = skb_dst(skb);
+
+ __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
+ __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
+
return dst_output(net, sk, skb);
}
@@ -558,8 +563,6 @@
hdr->hop_limit--;
- __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
- __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD,
net, NULL, skb, skb->dev, dst->dev,
ip6_forward_finish);
@@ -1276,7 +1279,7 @@
if (np->frag_size)
mtu = np->frag_size;
}
- if (mtu < IPV6_MIN_MTU)
+ if (!(rt->dst.flags & DST_XFRM_TUNNEL) && mtu < IPV6_MIN_MTU)
return -EINVAL;
cork->base.fragsize = mtu;
if (dst_allfrag(rt->dst.path))
@@ -1299,7 +1302,7 @@
const struct sockcm_cookie *sockc)
{
struct sk_buff *skb, *skb_prev = NULL;
- unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu;
+ unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu, pmtu;
int exthdrlen = 0;
int dst_exthdrlen = 0;
int hh_len;
@@ -1335,6 +1338,12 @@
sizeof(struct frag_hdr) : 0) +
rt->rt6i_nfheader_len;
+ /* as per RFC 7112 section 5, the entire IPv6 Header Chain must fit
+ * the first fragment
+ */
+ if (headersize + transhdrlen > mtu)
+ goto emsgsize;
+
if (cork->length + length > mtu - headersize && ipc6->dontfrag &&
(sk->sk_protocol == IPPROTO_UDP ||
sk->sk_protocol == IPPROTO_RAW)) {
@@ -1350,9 +1359,8 @@
if (cork->length + length > maxnonfragsize - headersize) {
emsgsize:
- ipv6_local_error(sk, EMSGSIZE, fl6,
- mtu - headersize +
- sizeof(struct ipv6hdr));
+ pmtu = max_t(int, mtu - headersize + sizeof(struct ipv6hdr), 0);
+ ipv6_local_error(sk, EMSGSIZE, fl6, pmtu);
return -EMSGSIZE;
}
@@ -1545,7 +1553,8 @@
if (copy > length)
copy = length;
- if (!(rt->dst.dev->features&NETIF_F_SG)) {
+ if (!(rt->dst.dev->features&NETIF_F_SG) &&
+ skb_tailroom(skb) >= copy) {
unsigned int off;
off = skb->len;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 4c52236..f338848 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -298,13 +298,16 @@
struct net_device *dev;
struct ip6_tnl *t;
char name[IFNAMSIZ];
- int err = -ENOMEM;
+ int err = -E2BIG;
- if (p->name[0])
+ if (p->name[0]) {
+ if (!dev_valid_name(p->name))
+ goto failed;
strlcpy(name, p->name, IFNAMSIZ);
- else
+ } else {
sprintf(name, "ip6tnl%%d");
-
+ }
+ err = -ENOMEM;
dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,
ip6_tnl_dev_setup);
if (!dev)
@@ -1097,6 +1100,9 @@
if (!dst) {
route_lookup:
+ /* add dsfield to flowlabel for route lookup */
+ fl6->flowlabel = ip6_make_flowinfo(dsfield, fl6->flowlabel);
+
dst = ip6_route_output(net, NULL, fl6);
if (dst->error)
@@ -1127,8 +1133,13 @@
max_headroom += 8;
mtu -= 8;
}
- if (mtu < IPV6_MIN_MTU)
- mtu = IPV6_MIN_MTU;
+ if (skb->protocol == htons(ETH_P_IPV6)) {
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+ } else if (mtu < 576) {
+ mtu = 576;
+ }
+
if (skb_dst(skb) && !t->parms.collect_md)
skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index afc30a0..5ae1681 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -212,10 +212,13 @@
char name[IFNAMSIZ];
int err;
- if (p->name[0])
+ if (p->name[0]) {
+ if (!dev_valid_name(p->name))
+ goto failed;
strlcpy(name, p->name, IFNAMSIZ);
- else
+ } else {
sprintf(name, "ip6_vti%%d");
+ }
dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, vti6_dev_setup);
if (!dev)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 493a32f..c66b9a8 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1343,10 +1343,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
- err = nf_getsockopt(sk, PF_INET6, optname, optval,
- &len);
- release_sock(sk);
+ err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
if (err >= 0)
err = put_user(len, optlen);
}
@@ -1385,10 +1382,7 @@
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
- err = compat_nf_getsockopt(sk, PF_INET6,
- optname, optval, &len);
- release_sock(sk);
+ err = compat_nf_getsockopt(sk, PF_INET6, optname, optval, &len);
if (err >= 0)
err = put_user(len, optlen);
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 01858ac..52236be 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1518,7 +1518,8 @@
*(opt++) = (rd_len >> 3);
opt += 6;
- memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
+ skb_copy_bits(orig_skb, skb_network_offset(orig_skb), opt,
+ rd_len - 8);
}
void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
@@ -1725,6 +1726,8 @@
case NETDEV_CHANGEADDR:
neigh_changeaddr(&nd_tbl, dev);
fib6_run_gc(0, net, false);
+ /* fallthrough */
+ case NETDEV_UP:
idev = in6_dev_get(dev);
if (!idev)
break;
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 1cee193..aa82858 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -385,6 +385,10 @@
}
if (table_base + v != ip6t_next_entry(e) &&
!(e->ipv6.flags & IP6T_F_GOTO)) {
+ if (unlikely(stackidx >= private->stacksize)) {
+ verdict = NF_DROP;
+ break;
+ }
jumpstack[stackidx++] = e;
}
@@ -575,7 +579,8 @@
static int
find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
- unsigned int size)
+ unsigned int size,
+ struct xt_percpu_counter_alloc_state *alloc_state)
{
struct xt_entry_target *t;
struct xt_target *target;
@@ -583,12 +588,9 @@
unsigned int j;
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
- unsigned long pcnt;
- pcnt = xt_percpu_counter_alloc();
- if (IS_ERR_VALUE(pcnt))
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
- e->counters.pcnt = pcnt;
j = 0;
mtpar.net = net;
@@ -625,7 +627,7 @@
cleanup_match(ematch, net);
}
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
return ret;
}
@@ -712,8 +714,7 @@
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(par.target->me);
-
- xt_percpu_counter_free(e->counters.pcnt);
+ xt_percpu_counter_free(&e->counters);
}
/* Checks and translates the user-supplied table segment (held in
@@ -722,6 +723,7 @@
translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
const struct ip6t_replace *repl)
{
+ struct xt_percpu_counter_alloc_state alloc_state = { 0 };
struct ip6t_entry *iter;
unsigned int *offsets;
unsigned int i;
@@ -781,7 +783,8 @@
/* Finally, each sanity check must pass */
i = 0;
xt_entry_foreach(iter, entry0, newinfo->size) {
- ret = find_check_entry(iter, net, repl->name, repl->size);
+ ret = find_check_entry(iter, net, repl->name, repl->size,
+ &alloc_state);
if (ret != 0)
break;
++i;
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index e0be97e..322c765 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -99,6 +99,10 @@
!l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
target, maniptype))
return false;
+
+ /* must reload, offset might have changed */
+ ipv6h = (void *)skb->data + iphdroff;
+
manip_addr:
if (maniptype == NF_NAT_MANIP_SRC)
ipv6h->saddr = target->src.u3.in6;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 558d566..0fbc5ba 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -847,6 +847,9 @@
struct fib6_node *fn;
struct rt6_info *rt;
+ if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
+ flags &= ~RT6_LOOKUP_F_IFACE;
+
read_lock_bh(&table->tb6_lock);
fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
restart:
@@ -1647,6 +1650,7 @@
}
rt->dst.flags |= DST_HOST;
+ rt->dst.input = ip6_input;
rt->dst.output = ip6_output;
atomic_set(&rt->dst.__refcnt, 1);
rt->rt6i_gateway = fl6->daddr;
@@ -2772,6 +2776,7 @@
static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
+ [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
[RTA_OIF] = { .type = NLA_U32 },
[RTA_IIF] = { .type = NLA_U32 },
[RTA_PRIORITY] = { .type = NLA_U32 },
@@ -2782,6 +2787,7 @@
[RTA_ENCAP] = { .type = NLA_NESTED },
[RTA_EXPIRES] = { .type = NLA_U32 },
[RTA_UID] = { .type = NLA_U32 },
+ [RTA_TABLE] = { .type = NLA_U32 },
};
static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index db6d437..dcb2921 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -176,7 +176,7 @@
#ifdef CONFIG_IPV6_SIT_6RD
struct ip_tunnel *t = netdev_priv(dev);
- if (t->dev == sitn->fb_tunnel_dev) {
+ if (dev == sitn->fb_tunnel_dev) {
ipv6_addr_set(&t->ip6rd.prefix, htonl(0x20020000), 0, 0, 0);
t->ip6rd.relay_prefix = 0;
t->ip6rd.prefixlen = 16;
@@ -244,11 +244,13 @@
if (!create)
goto failed;
- if (parms->name[0])
+ if (parms->name[0]) {
+ if (!dev_valid_name(parms->name))
+ goto failed;
strlcpy(name, parms->name, IFNAMSIZ);
- else
+ } else {
strcpy(name, "sit%d");
-
+ }
dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,
ipip6_tunnel_setup);
if (!dev)
@@ -657,6 +659,7 @@
if (iptunnel_pull_header(skb, 0, htons(ETH_P_IPV6),
!net_eq(tunnel->net, dev_net(tunnel->dev))))
goto out;
+ iph = ip_hdr(skb);
err = IP_ECN_decapsulate(iph, skb);
if (unlikely(err)) {
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 91cbbf1..c2dfc32 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -2418,9 +2418,11 @@
af_iucv_dev->driver = &af_iucv_driver;
err = device_register(af_iucv_dev);
if (err)
- goto out_driver;
+ goto out_iucv_dev;
return 0;
+out_iucv_dev:
+ put_device(af_iucv_dev);
out_driver:
driver_unregister(&af_iucv_driver);
out_iucv:
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index 179cd9b..cc306de 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -1375,24 +1375,32 @@
struct list_head *head;
int index = 0;
struct strp_callbacks cb;
- int err;
+ int err = 0;
csk = csock->sk;
if (!csk)
return -EINVAL;
+ lock_sock(csk);
+
/* Only allow TCP sockets to be attached for now */
if ((csk->sk_family != AF_INET && csk->sk_family != AF_INET6) ||
- csk->sk_protocol != IPPROTO_TCP)
- return -EOPNOTSUPP;
+ csk->sk_protocol != IPPROTO_TCP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
/* Don't allow listeners or closed sockets */
- if (csk->sk_state == TCP_LISTEN || csk->sk_state == TCP_CLOSE)
- return -EOPNOTSUPP;
+ if (csk->sk_state == TCP_LISTEN || csk->sk_state == TCP_CLOSE) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
psock = kmem_cache_zalloc(kcm_psockp, GFP_KERNEL);
- if (!psock)
- return -ENOMEM;
+ if (!psock) {
+ err = -ENOMEM;
+ goto out;
+ }
psock->mux = mux;
psock->sk = csk;
@@ -1406,7 +1414,7 @@
err = strp_init(&psock->strp, csk, &cb);
if (err) {
kmem_cache_free(kcm_psockp, psock);
- return err;
+ goto out;
}
write_lock_bh(&csk->sk_callback_lock);
@@ -1416,9 +1424,11 @@
*/
if (csk->sk_user_data) {
write_unlock_bh(&csk->sk_callback_lock);
+ strp_stop(&psock->strp);
strp_done(&psock->strp);
kmem_cache_free(kcm_psockp, psock);
- return -EALREADY;
+ err = -EALREADY;
+ goto out;
}
psock->save_data_ready = csk->sk_data_ready;
@@ -1454,7 +1464,10 @@
/* Schedule RX work in case there are already bytes queued */
strp_check_rcv(&psock->strp);
- return 0;
+out:
+ release_sock(csk);
+
+ return err;
}
static int kcm_attach_ioctl(struct socket *sock, struct kcm_attach *info)
@@ -1506,6 +1519,7 @@
if (WARN_ON(psock->rx_kcm)) {
write_unlock_bh(&csk->sk_callback_lock);
+ release_sock(csk);
return;
}
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 6482b00..15150b4 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3305,7 +3305,7 @@
p += pol->sadb_x_policy_len*8;
sec_ctx = (struct sadb_x_sec_ctx *)p;
if (len < pol->sadb_x_policy_len*8 +
- sec_ctx->sadb_x_sec_len) {
+ sec_ctx->sadb_x_sec_len*8) {
*dir = -EINVAL;
goto out;
}
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index cfc4dd8..ead98e8 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1612,9 +1612,14 @@
encap = cfg->encap;
/* Quick sanity checks */
+ err = -EPROTONOSUPPORT;
+ if (sk->sk_type != SOCK_DGRAM) {
+ pr_debug("tunl %hu: fd %d wrong socket type\n",
+ tunnel_id, fd);
+ goto err;
+ }
switch (encap) {
case L2TP_ENCAPTYPE_UDP:
- err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_UDP) {
pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP);
@@ -1622,7 +1627,6 @@
}
break;
case L2TP_ENCAPTYPE_IP:
- err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_L2TP) {
pr_err("tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_L2TP);
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 163f1fa..9b214f3 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -590,6 +590,13 @@
lock_sock(sk);
error = -EINVAL;
+
+ if (sockaddr_len != sizeof(struct sockaddr_pppol2tp) &&
+ sockaddr_len != sizeof(struct sockaddr_pppol2tpv3) &&
+ sockaddr_len != sizeof(struct sockaddr_pppol2tpin6) &&
+ sockaddr_len != sizeof(struct sockaddr_pppol2tpv3in6))
+ goto end;
+
if (sp->sa_protocol != PX_PROTO_OL2TP)
goto end;
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index db916cf..85aae8c 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -197,9 +197,19 @@
llc->laddr.lsap, llc->daddr.lsap);
if (!llc_send_disc(sk))
llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
- if (!sock_flag(sk, SOCK_ZAPPED))
+ if (!sock_flag(sk, SOCK_ZAPPED)) {
+ struct llc_sap *sap = llc->sap;
+
+ /* Hold this for release_sock(), so that llc_backlog_rcv()
+ * could still use it.
+ */
+ llc_sap_hold(sap);
llc_sap_remove_socket(llc->sap, sk);
- release_sock(sk);
+ release_sock(sk);
+ llc_sap_put(sap);
+ } else {
+ release_sock(sk);
+ }
if (llc->dev)
dev_put(llc->dev);
sock_put(sk);
@@ -309,6 +319,8 @@
int rc = -EINVAL;
dprintk("%s: binding %02X\n", __func__, addr->sllc_sap);
+
+ lock_sock(sk);
if (unlikely(!sock_flag(sk, SOCK_ZAPPED) || addrlen != sizeof(*addr)))
goto out;
rc = -EAFNOSUPPORT;
@@ -380,6 +392,7 @@
out_put:
llc_sap_put(sap);
out:
+ release_sock(sk);
return rc;
}
@@ -913,6 +926,9 @@
if (size > llc->dev->mtu)
size = llc->dev->mtu;
copied = size - hdrlen;
+ rc = -EINVAL;
+ if (copied < 0)
+ goto release;
release_sock(sk);
skb = sock_alloc_send_skb(sk, size, noblock, &rc);
lock_sock(sk);
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c
index ea225bd..f8d4ab8 100644
--- a/net/llc/llc_c_ac.c
+++ b/net/llc/llc_c_ac.c
@@ -1096,14 +1096,7 @@
int llc_conn_ac_stop_all_timers(struct sock *sk, struct sk_buff *skb)
{
- struct llc_sock *llc = llc_sk(sk);
-
- del_timer(&llc->pf_cycle_timer.timer);
- del_timer(&llc->ack_timer.timer);
- del_timer(&llc->rej_sent_timer.timer);
- del_timer(&llc->busy_state_timer.timer);
- llc->ack_must_be_send = 0;
- llc->ack_pf = 0;
+ llc_sk_stop_all_timers(sk, false);
return 0;
}
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 8bc5a1b..d861b74 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -951,6 +951,26 @@
return sk;
}
+void llc_sk_stop_all_timers(struct sock *sk, bool sync)
+{
+ struct llc_sock *llc = llc_sk(sk);
+
+ if (sync) {
+ del_timer_sync(&llc->pf_cycle_timer.timer);
+ del_timer_sync(&llc->ack_timer.timer);
+ del_timer_sync(&llc->rej_sent_timer.timer);
+ del_timer_sync(&llc->busy_state_timer.timer);
+ } else {
+ del_timer(&llc->pf_cycle_timer.timer);
+ del_timer(&llc->ack_timer.timer);
+ del_timer(&llc->rej_sent_timer.timer);
+ del_timer(&llc->busy_state_timer.timer);
+ }
+
+ llc->ack_must_be_send = 0;
+ llc->ack_pf = 0;
+}
+
/**
* llc_sk_free - Frees a LLC socket
* @sk - socket to free
@@ -963,7 +983,7 @@
llc->state = LLC_CONN_OUT_OF_SVC;
/* Stop all (possibly) running timers */
- llc_conn_ac_stop_all_timers(sk, NULL);
+ llc_sk_stop_all_timers(sk, true);
#ifdef DEBUG_LLC_CONN_ALLOC
printk(KERN_INFO "%s: unackq=%d, txq=%d\n", __func__,
skb_queue_len(&llc->pdu_unack_q),
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 07001b6..d7801f6 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -620,10 +620,11 @@
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
u16 brate;
- sband = sta->local->hw.wiphy->bands[
- ieee80211_get_sdata_band(sta->sdata)];
- brate = sband->bitrates[rate->idx].bitrate;
- rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
+ sband = ieee80211_get_sband(sta->sdata);
+ if (sband) {
+ brate = sband->bitrates[rate->idx].bitrate;
+ rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
+ }
}
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->bw = RATE_INFO_BW_40;
@@ -1218,10 +1219,11 @@
int ret = 0;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
u32 mask, set;
- sband = local->hw.wiphy->bands[band];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
mask = params->sta_flags_mask;
set = params->sta_flags_set;
@@ -1354,7 +1356,7 @@
ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
sband, params->supported_rates,
params->supported_rates_len,
- &sta->sta.supp_rates[band]);
+ &sta->sta.supp_rates[sband->band]);
}
if (params->ht_capa)
@@ -1370,8 +1372,8 @@
/* returned value is only needed for rc update, but the
* rc isn't initialized here yet, so ignore it
*/
- __ieee80211_vht_handle_opmode(sdata, sta,
- params->opmode_notif, band);
+ __ieee80211_vht_handle_opmode(sdata, sta, params->opmode_notif,
+ sband->band);
}
if (params->support_p2p_ps >= 0)
@@ -2017,13 +2019,15 @@
struct bss_parameters *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
u32 changed = 0;
if (!sdata_dereference(sdata->u.ap.beacon, sdata))
return -ENOENT;
- band = ieee80211_get_sdata_band(sdata);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
if (params->use_cts_prot >= 0) {
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
@@ -2036,7 +2040,7 @@
}
if (!sdata->vif.bss_conf.use_short_slot &&
- band == NL80211_BAND_5GHZ) {
+ sband->band == NL80211_BAND_5GHZ) {
sdata->vif.bss_conf.use_short_slot = true;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -2049,7 +2053,7 @@
if (params->basic_rates) {
ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
- wiphy->bands[band],
+ wiphy->bands[sband->band],
params->basic_rates,
params->basic_rates_len,
&sdata->vif.bss_conf.basic_rates);
@@ -2337,10 +2341,17 @@
struct ieee80211_sub_if_data *sdata;
enum nl80211_tx_power_setting txp_type = type;
bool update_txp_type = false;
+ bool has_monitor = false;
if (wdev) {
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (!sdata)
+ return -EOPNOTSUPP;
+ }
+
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
@@ -2379,15 +2390,34 @@
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ has_monitor = true;
+ continue;
+ }
sdata->user_power_level = local->user_power_level;
if (txp_type != sdata->vif.bss_conf.txpower_type)
update_txp_type = true;
sdata->vif.bss_conf.txpower_type = txp_type;
}
- list_for_each_entry(sdata, &local->interfaces, list)
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ continue;
ieee80211_recalc_txpower(sdata, update_txp_type);
+ }
mutex_unlock(&local->iflist_mtx);
+ if (has_monitor) {
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (sdata) {
+ sdata->user_power_level = local->user_power_level;
+ if (txp_type != sdata->vif.bss_conf.txpower_type)
+ update_txp_type = true;
+ sdata->vif.bss_conf.txpower_type = txp_type;
+
+ ieee80211_recalc_txpower(sdata, update_txp_type);
+ }
+ }
+
return 0;
}
@@ -2792,7 +2822,7 @@
}
if (beacon->probe_resp_len) {
new_beacon->probe_resp_len = beacon->probe_resp_len;
- beacon->probe_resp = pos;
+ new_beacon->probe_resp = pos;
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
pos += beacon->probe_resp_len;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 09f77e4..49c8a9c 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -164,7 +164,8 @@
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
sdata->vif.type == NL80211_IFTYPE_NAN ||
(sdata->vif.type == NL80211_IFTYPE_MONITOR &&
- !sdata->vif.mu_mimo_owner)))
+ !sdata->vif.mu_mimo_owner &&
+ !(changed & BSS_CHANGED_TXPOWER))))
return;
if (!check_sdata_in_driver(sdata))
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 62d13ea..a5acaf1 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -427,7 +427,7 @@
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
cfg80211_chandef_create(&chandef, cbss->channel,
- NL80211_CHAN_WIDTH_20_NOHT);
+ NL80211_CHAN_NO_HT);
chandef.width = sdata->u.ibss.chandef.width;
break;
case NL80211_CHAN_WIDTH_80:
@@ -439,7 +439,7 @@
default:
/* fall back to 20 MHz for unsupported modes */
cfg80211_chandef_create(&chandef, cbss->channel,
- NL80211_CHAN_WIDTH_20_NOHT);
+ NL80211_CHAN_NO_HT);
break;
}
@@ -994,7 +994,7 @@
enum nl80211_band band = rx_status->band;
enum nl80211_bss_scan_width scan_width;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_supported_band *sband;
bool rates_updated = false;
u32 supp_rates = 0;
@@ -1004,6 +1004,10 @@
if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid))
return;
+ sband = local->hw.wiphy->bands[band];
+ if (WARN_ON(!sband))
+ return;
+
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 03dbc6b..7fd544d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -991,21 +991,6 @@
lockdep_assert_held(&sdata->wdev.mtx);
}
-static inline enum nl80211_band
-ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
-{
- enum nl80211_band band = NL80211_BAND_2GHZ;
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!WARN_ON(!chanctx_conf))
- band = chanctx_conf->def.chan->band;
- rcu_read_unlock();
-
- return band;
-}
-
static inline int
ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
{
@@ -1410,6 +1395,27 @@
return container_of(wdev, struct ieee80211_sub_if_data, wdev);
}
+static inline struct ieee80211_supported_band *
+ieee80211_get_sband(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum nl80211_band band;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return NULL;
+ }
+
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
+ return local->hw.wiphy->bands[band];
+}
+
/* this struct represents 802.11n's RA/TID combination */
struct ieee80211_ra_tid {
u8 ra[ETH_ALEN];
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index a7aa54f..fa7d757 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1520,7 +1520,7 @@
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
- BUG();
+ WARN_ON(1);
break;
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index b4b3fe0..b2a2726 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -63,6 +63,7 @@
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u32 basic_rates = 0;
struct cfg80211_chan_def sta_chan_def;
+ struct ieee80211_supported_band *sband;
/*
* As support for each feature is added, check for matching
@@ -83,7 +84,11 @@
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
return false;
- ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return false;
+
+ ieee80211_sta_get_rates(sdata, ie, sband->band,
&basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates)
@@ -399,12 +404,13 @@
int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
- struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u8 *pos;
- sband = local->hw.wiphy->bands[band];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
+
if (!sband->ht_cap.ht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -462,12 +468,13 @@
int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
- struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u8 *pos;
- sband = local->hw.wiphy->bands[band];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return -EINVAL;
+
if (!sband->vht_cap.vht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -916,12 +923,16 @@
struct cfg80211_csa_settings params;
struct ieee80211_csa_ie csa_ie;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband;
int err;
u32 sta_flags;
sdata_assert_lock(sdata);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return false;
+
sta_flags = IEEE80211_STA_DISABLE_VHT;
switch (sdata->vif.bss_conf.chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
@@ -935,7 +946,7 @@
memset(¶ms, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index fcba70e5..2d3c469 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -93,19 +93,23 @@
static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
- struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_supported_band *sband;
struct sta_info *sta;
u32 erp_rates = 0, changed = 0;
int i;
bool short_slot = false;
- if (band == NL80211_BAND_5GHZ) {
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return changed;
+
+ if (sband->band == NL80211_BAND_5GHZ) {
/* (IEEE 802.11-2012 19.4.5) */
short_slot = true;
goto out;
- } else if (band != NL80211_BAND_2GHZ)
+ } else if (sband->band != NL80211_BAND_2GHZ) {
goto out;
+ }
for (i = 0; i < sband->n_bitrates; i++)
if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
@@ -121,7 +125,7 @@
continue;
short_slot = false;
- if (erp_rates & sta->sta.supp_rates[band])
+ if (erp_rates & sta->sta.supp_rates[sband->band])
short_slot = true;
else
break;
@@ -247,7 +251,15 @@
mgmt->u.action.u.self_prot.action_code = action;
if (action != WLAN_SP_MESH_PEERING_CLOSE) {
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband;
+ enum nl80211_band band;
+
+ sband = ieee80211_get_sband(sdata);
+ if (!sband) {
+ err = -EINVAL;
+ goto free;
+ }
+ band = sband->band;
/* capability info */
pos = skb_put(skb, 2);
@@ -393,13 +405,16 @@
struct ieee802_11_elems *elems, bool insert)
{
struct ieee80211_local *local = sdata->local;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0, changed = 0;
enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
- sband = local->hw.wiphy->bands[band];
- rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
+ rates = ieee80211_sta_get_rates(sdata, elems, sband->band,
+ &basic_rates);
spin_lock_bh(&sta->mesh->plink_lock);
sta->rx_stats.last_rx = jiffies;
@@ -410,9 +425,9 @@
goto out;
sta->mesh->processed_beacon = true;
- if (sta->sta.supp_rates[band] != rates)
+ if (sta->sta.supp_rates[sband->band] != rates)
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
- sta->sta.supp_rates[band] = rates;
+ sta->sta.supp_rates[sband->band] = rates;
if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem, sta))
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b2c706c..973adf3 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1851,11 +1851,16 @@
u16 capab, bool erp_valid, u8 erp)
{
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+ struct ieee80211_supported_band *sband;
u32 changed = 0;
bool use_protection;
bool use_short_preamble;
bool use_short_slot;
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return changed;
+
if (erp_valid) {
use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
@@ -1865,7 +1870,7 @@
}
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
- if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_5GHZ)
+ if (sband->band == NL80211_BAND_5GHZ)
use_short_slot = true;
if (use_protection != bss_conf->use_cts_prot) {
@@ -2994,7 +2999,12 @@
goto out;
}
- sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+ sband = ieee80211_get_sband(sdata);
+ if (!sband) {
+ mutex_unlock(&sdata->local->sta_mtx);
+ ret = false;
+ goto out;
+ }
/* Set up internal HT/VHT capabilities */
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
@@ -4322,6 +4332,10 @@
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
return -EINVAL;
+ /* If a reconfig is happening, bail out */
+ if (local->in_reconfig)
+ return -EBUSY;
+
if (assoc) {
rcu_read_lock();
have_sta = sta_info_get(sdata, cbss->bssid);
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 206698b..e6096df 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -173,9 +173,11 @@
/* try default if specific alg requested but not found */
ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo);
- /* try built-in one if specific alg requested but not found */
- if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT))
+ /* Note: check for > 0 is intentional to avoid clang warning */
+ if (!ops && (strlen(CONFIG_MAC80211_RC_DEFAULT) > 0))
+ /* try built-in one if specific alg requested but not found */
ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT);
+
kernel_param_unlock(THIS_MODULE);
return ops;
@@ -875,7 +877,9 @@
struct ieee80211_sta_rates *old;
struct ieee80211_supported_band *sband;
- sband = hw->wiphy->bands[ieee80211_get_sdata_band(sta->sdata)];
+ sband = ieee80211_get_sband(sta->sdata);
+ if (!sband)
+ return -EINVAL;
rate_control_apply_mask_ratetbl(sta, sband, rates);
/*
* mac80211 guarantees that this function will not be called
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 439e597..404284a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3611,6 +3611,8 @@
}
return true;
case NL80211_IFTYPE_MESH_POINT:
+ if (ether_addr_equal(sdata->vif.addr, hdr->addr2))
+ return false;
if (multicast)
return true;
return ether_addr_equal(sdata->vif.addr, hdr->addr1);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 348700b4..1ecf3d0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -395,10 +395,15 @@
sta->sta.smps_mode = IEEE80211_SMPS_OFF;
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
- struct ieee80211_supported_band *sband =
- hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
- u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
- IEEE80211_HT_CAP_SM_PS_SHIFT;
+ struct ieee80211_supported_band *sband;
+ u8 smps;
+
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ goto free_txq;
+
+ smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
/*
* Assume that hostapd advertises our caps in the beacon and
* this is the known_smps_mode for a station that just assciated
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index ad37b4e..72fe9bc 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -200,6 +200,7 @@
}
if (ieee80211_is_action(mgmt->frame_control) &&
+ !ieee80211_has_protected(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_HT &&
mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
ieee80211_sdata_running(sdata)) {
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index afca7d1..f20dcf1 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -47,8 +47,7 @@
NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
!ifmgd->tdls_wider_bw_prohibited;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
- struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
+ struct ieee80211_supported_band *sband = ieee80211_get_sband(sdata);
bool vht = sband && sband->vht_cap.vht_supported;
u8 *pos = (void *)skb_put(skb, 10);
@@ -180,11 +179,14 @@
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
u16 status_code)
{
+ struct ieee80211_supported_band *sband;
+
/* The capability will be 0 when sending a failure code */
if (status_code != 0)
return 0;
- if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_2GHZ) {
+ sband = ieee80211_get_sband(sdata);
+ if (sband && sband->band == NL80211_BAND_2GHZ) {
return WLAN_CAPABILITY_SHORT_SLOT_TIME |
WLAN_CAPABILITY_SHORT_PREAMBLE;
}
@@ -358,17 +360,20 @@
u8 action_code, bool initiator,
const u8 *extra_ies, size_t extra_ies_len)
{
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
- struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
struct sta_info *sta = NULL;
size_t offset = 0, noffset;
u8 *pos;
- ieee80211_add_srates_ie(sdata, skb, false, band);
- ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
+ ieee80211_add_srates_ie(sdata, skb, false, sband->band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, sband->band);
ieee80211_tdls_add_supp_channels(sdata, skb);
/* add any custom IEs that go before Extended Capabilities */
@@ -439,7 +444,6 @@
* the same on all bands. The specification limits the setup to a
* single HT-cap, so use the current band for now.
*/
- sband = local->hw.wiphy->bands[band];
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
@@ -545,9 +549,13 @@
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t offset = 0, noffset;
struct sta_info *sta, *ap_sta;
- enum nl80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband;
u8 *pos;
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, peer);
@@ -612,7 +620,8 @@
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
/* only include VHT-operation if not on the 2.4GHz band */
- if (band != NL80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
+ if (sband->band != NL80211_BAND_2GHZ &&
+ sta->sta.vht_cap.vht_supported) {
/*
* if both peers support WIDER_BW, we can expand the chandef to
* a wider compatible one, up to 80MHz
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1ffd1e1..8458299 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4182,7 +4182,10 @@
return bcn;
shift = ieee80211_vif_get_shift(vif);
- sband = hw->wiphy->bands[ieee80211_get_sdata_band(vif_to_sdata(vif))];
+ sband = ieee80211_get_sband(vif_to_sdata(vif));
+ if (!sband)
+ return bcn;
+
ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false);
return bcn;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 031273a..ae91a3c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1590,14 +1590,14 @@
size_t num_rates;
u32 supp_rates, rate_flags;
int i, j, shift;
+
sband = sdata->local->hw.wiphy->bands[band];
+ if (WARN_ON(!sband))
+ return 1;
rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
shift = ieee80211_vif_get_shift(&sdata->vif);
- if (WARN_ON(!sband))
- return 1;
-
num_rates = sband->n_bitrates;
supp_rates = 0;
for (i = 0; i < elems->supp_rates_len +
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index c5a5a69..ffab94d 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -7,6 +7,7 @@
#include <linux/if_arp.h>
#include <linux/ipv6.h>
#include <linux/mpls.h>
+#include <linux/nospec.h>
#include <linux/vmalloc.h>
#include <net/ip.h>
#include <net/dst.h>
@@ -756,6 +757,22 @@
return err;
}
+static bool mpls_label_ok(struct net *net, unsigned int *index)
+{
+ bool is_ok = true;
+
+ /* Reserved labels may not be set */
+ if (*index < MPLS_LABEL_FIRST_UNRESERVED)
+ is_ok = false;
+
+ /* The full 20 bit range may not be supported. */
+ if (is_ok && *index >= net->mpls.platform_labels)
+ is_ok = false;
+
+ *index = array_index_nospec(*index, net->mpls.platform_labels);
+ return is_ok;
+}
+
static int mpls_route_add(struct mpls_route_config *cfg)
{
struct mpls_route __rcu **platform_label;
@@ -774,12 +791,7 @@
index = find_free_label(net);
}
- /* Reserved labels may not be set */
- if (index < MPLS_LABEL_FIRST_UNRESERVED)
- goto errout;
-
- /* The full 20 bit range may not be supported. */
- if (index >= net->mpls.platform_labels)
+ if (!mpls_label_ok(net, &index))
goto errout;
/* Append makes no sense with mpls */
@@ -840,12 +852,7 @@
index = cfg->rc_label;
- /* Reserved labels may not be removed */
- if (index < MPLS_LABEL_FIRST_UNRESERVED)
- goto errout;
-
- /* The full 20 bit range may not be supported */
- if (index >= net->mpls.platform_labels)
+ if (!mpls_label_ok(net, &index))
goto errout;
mpls_route_update(net, index, NULL, &cfg->rc_nlinfo);
@@ -1279,10 +1286,9 @@
&cfg->rc_label))
goto errout;
- /* Reserved labels may not be set */
- if (cfg->rc_label < MPLS_LABEL_FIRST_UNRESERVED)
+ if (!mpls_label_ok(cfg->rc_nlinfo.nl_net,
+ &cfg->rc_label))
goto errout;
-
break;
}
case RTA_VIA:
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 2155c24..c5f2350 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2393,11 +2393,7 @@
strlcpy(cfg.mcast_ifn, dm->mcast_ifn,
sizeof(cfg.mcast_ifn));
cfg.syncid = dm->syncid;
- rtnl_lock();
- mutex_lock(&ipvs->sync_mutex);
ret = start_sync_thread(ipvs, &cfg, dm->state);
- mutex_unlock(&ipvs->sync_mutex);
- rtnl_unlock();
} else {
mutex_lock(&ipvs->sync_mutex);
ret = stop_sync_thread(ipvs, dm->state);
@@ -3092,6 +3088,17 @@
return skb->len;
}
+static bool ip_vs_is_af_valid(int af)
+{
+ if (af == AF_INET)
+ return true;
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6 && ipv6_mod_enabled())
+ return true;
+#endif
+ return false;
+}
+
static int ip_vs_genl_parse_service(struct netns_ipvs *ipvs,
struct ip_vs_service_user_kern *usvc,
struct nlattr *nla, int full_entry,
@@ -3118,11 +3125,7 @@
memset(usvc, 0, sizeof(*usvc));
usvc->af = nla_get_u16(nla_af);
-#ifdef CONFIG_IP_VS_IPV6
- if (usvc->af != AF_INET && usvc->af != AF_INET6)
-#else
- if (usvc->af != AF_INET)
-#endif
+ if (!ip_vs_is_af_valid(usvc->af))
return -EAFNOSUPPORT;
if (nla_fwmark) {
@@ -3488,12 +3491,8 @@
if (ipvs->mixed_address_family_dests > 0)
return -EINVAL;
- rtnl_lock();
- mutex_lock(&ipvs->sync_mutex);
ret = start_sync_thread(ipvs, &c,
nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
- mutex_unlock(&ipvs->sync_mutex);
- rtnl_unlock();
return ret;
}
@@ -3624,6 +3623,11 @@
if (udest.af == 0)
udest.af = svc->af;
+ if (!ip_vs_is_af_valid(udest.af)) {
+ ret = -EAFNOSUPPORT;
+ goto out;
+ }
+
if (udest.af != svc->af && cmd != IPVS_CMD_DEL_DEST) {
/* The synchronization protocol is incompatible
* with mixed family services
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 9350530..5fbf4b2 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -48,6 +48,7 @@
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <asm/unaligned.h> /* Used for ntoh_seq and hton_seq */
@@ -1359,15 +1360,9 @@
/*
* Specifiy default interface for outgoing multicasts
*/
-static int set_mcast_if(struct sock *sk, char *ifname)
+static int set_mcast_if(struct sock *sk, struct net_device *dev)
{
- struct net_device *dev;
struct inet_sock *inet = inet_sk(sk);
- struct net *net = sock_net(sk);
-
- dev = __dev_get_by_name(net, ifname);
- if (!dev)
- return -ENODEV;
if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)
return -EINVAL;
@@ -1395,19 +1390,14 @@
* in the in_addr structure passed in as a parameter.
*/
static int
-join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname)
+join_mcast_group(struct sock *sk, struct in_addr *addr, struct net_device *dev)
{
- struct net *net = sock_net(sk);
struct ip_mreqn mreq;
- struct net_device *dev;
int ret;
memset(&mreq, 0, sizeof(mreq));
memcpy(&mreq.imr_multiaddr, addr, sizeof(struct in_addr));
- dev = __dev_get_by_name(net, ifname);
- if (!dev)
- return -ENODEV;
if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)
return -EINVAL;
@@ -1422,15 +1412,10 @@
#ifdef CONFIG_IP_VS_IPV6
static int join_mcast_group6(struct sock *sk, struct in6_addr *addr,
- char *ifname)
+ struct net_device *dev)
{
- struct net *net = sock_net(sk);
- struct net_device *dev;
int ret;
- dev = __dev_get_by_name(net, ifname);
- if (!dev)
- return -ENODEV;
if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)
return -EINVAL;
@@ -1442,24 +1427,18 @@
}
#endif
-static int bind_mcastif_addr(struct socket *sock, char *ifname)
+static int bind_mcastif_addr(struct socket *sock, struct net_device *dev)
{
- struct net *net = sock_net(sock->sk);
- struct net_device *dev;
__be32 addr;
struct sockaddr_in sin;
- dev = __dev_get_by_name(net, ifname);
- if (!dev)
- return -ENODEV;
-
addr = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
if (!addr)
pr_err("You probably need to specify IP address on "
"multicast interface.\n");
IP_VS_DBG(7, "binding socket with (%s) %pI4\n",
- ifname, &addr);
+ dev->name, &addr);
/* Now bind the socket with the address of multicast interface */
sin.sin_family = AF_INET;
@@ -1492,7 +1471,8 @@
/*
* Set up sending multicast socket over UDP
*/
-static struct socket *make_send_sock(struct netns_ipvs *ipvs, int id)
+static int make_send_sock(struct netns_ipvs *ipvs, int id,
+ struct net_device *dev, struct socket **sock_ret)
{
/* multicast addr */
union ipvs_sockaddr mcast_addr;
@@ -1504,9 +1484,10 @@
IPPROTO_UDP, &sock);
if (result < 0) {
pr_err("Error during creation of socket; terminating\n");
- return ERR_PTR(result);
+ goto error;
}
- result = set_mcast_if(sock->sk, ipvs->mcfg.mcast_ifn);
+ *sock_ret = sock;
+ result = set_mcast_if(sock->sk, dev);
if (result < 0) {
pr_err("Error setting outbound mcast interface\n");
goto error;
@@ -1521,7 +1502,7 @@
set_sock_size(sock->sk, 1, result);
if (AF_INET == ipvs->mcfg.mcast_af)
- result = bind_mcastif_addr(sock, ipvs->mcfg.mcast_ifn);
+ result = bind_mcastif_addr(sock, dev);
else
result = 0;
if (result < 0) {
@@ -1537,19 +1518,18 @@
goto error;
}
- return sock;
+ return 0;
error:
- sock_release(sock);
- return ERR_PTR(result);
+ return result;
}
/*
* Set up receiving multicast socket over UDP
*/
-static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id,
- int ifindex)
+static int make_receive_sock(struct netns_ipvs *ipvs, int id,
+ struct net_device *dev, struct socket **sock_ret)
{
/* multicast addr */
union ipvs_sockaddr mcast_addr;
@@ -1561,8 +1541,9 @@
IPPROTO_UDP, &sock);
if (result < 0) {
pr_err("Error during creation of socket; terminating\n");
- return ERR_PTR(result);
+ goto error;
}
+ *sock_ret = sock;
/* it is equivalent to the REUSEADDR option in user-space */
sock->sk->sk_reuse = SK_CAN_REUSE;
result = sysctl_sync_sock_size(ipvs);
@@ -1570,7 +1551,7 @@
set_sock_size(sock->sk, 0, result);
get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->bcfg, id);
- sock->sk->sk_bound_dev_if = ifindex;
+ sock->sk->sk_bound_dev_if = dev->ifindex;
result = sock->ops->bind(sock, (struct sockaddr *)&mcast_addr, salen);
if (result < 0) {
pr_err("Error binding to the multicast addr\n");
@@ -1581,21 +1562,20 @@
#ifdef CONFIG_IP_VS_IPV6
if (ipvs->bcfg.mcast_af == AF_INET6)
result = join_mcast_group6(sock->sk, &mcast_addr.in6.sin6_addr,
- ipvs->bcfg.mcast_ifn);
+ dev);
else
#endif
result = join_mcast_group(sock->sk, &mcast_addr.in.sin_addr,
- ipvs->bcfg.mcast_ifn);
+ dev);
if (result < 0) {
pr_err("Error joining to the multicast group\n");
goto error;
}
- return sock;
+ return 0;
error:
- sock_release(sock);
- return ERR_PTR(result);
+ return result;
}
@@ -1780,13 +1760,12 @@
int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
int state)
{
- struct ip_vs_sync_thread_data *tinfo;
+ struct ip_vs_sync_thread_data *tinfo = NULL;
struct task_struct **array = NULL, *task;
- struct socket *sock;
struct net_device *dev;
char *name;
int (*threadfn)(void *data);
- int id, count, hlen;
+ int id = 0, count, hlen;
int result = -ENOMEM;
u16 mtu, min_mtu;
@@ -1794,6 +1773,18 @@
IP_VS_DBG(7, "Each ip_vs_sync_conn entry needs %Zd bytes\n",
sizeof(struct ip_vs_sync_conn_v0));
+ /* Do not hold one mutex and then to block on another */
+ for (;;) {
+ rtnl_lock();
+ if (mutex_trylock(&ipvs->sync_mutex))
+ break;
+ rtnl_unlock();
+ mutex_lock(&ipvs->sync_mutex);
+ if (rtnl_trylock())
+ break;
+ mutex_unlock(&ipvs->sync_mutex);
+ }
+
if (!ipvs->sync_state) {
count = clamp(sysctl_sync_ports(ipvs), 1, IPVS_SYNC_PORTS_MAX);
ipvs->threads_mask = count - 1;
@@ -1812,7 +1803,8 @@
dev = __dev_get_by_name(ipvs->net, c->mcast_ifn);
if (!dev) {
pr_err("Unknown mcast interface: %s\n", c->mcast_ifn);
- return -ENODEV;
+ result = -ENODEV;
+ goto out_early;
}
hlen = (AF_INET6 == c->mcast_af) ?
sizeof(struct ipv6hdr) + sizeof(struct udphdr) :
@@ -1829,26 +1821,30 @@
c->sync_maxlen = mtu - hlen;
if (state == IP_VS_STATE_MASTER) {
+ result = -EEXIST;
if (ipvs->ms)
- return -EEXIST;
+ goto out_early;
ipvs->mcfg = *c;
name = "ipvs-m:%d:%d";
threadfn = sync_thread_master;
} else if (state == IP_VS_STATE_BACKUP) {
+ result = -EEXIST;
if (ipvs->backup_threads)
- return -EEXIST;
+ goto out_early;
ipvs->bcfg = *c;
name = "ipvs-b:%d:%d";
threadfn = sync_thread_backup;
} else {
- return -EINVAL;
+ result = -EINVAL;
+ goto out_early;
}
if (state == IP_VS_STATE_MASTER) {
struct ipvs_master_sync_state *ms;
+ result = -ENOMEM;
ipvs->ms = kzalloc(count * sizeof(ipvs->ms[0]), GFP_KERNEL);
if (!ipvs->ms)
goto out;
@@ -1864,39 +1860,38 @@
} else {
array = kzalloc(count * sizeof(struct task_struct *),
GFP_KERNEL);
+ result = -ENOMEM;
if (!array)
goto out;
}
- tinfo = NULL;
for (id = 0; id < count; id++) {
- if (state == IP_VS_STATE_MASTER)
- sock = make_send_sock(ipvs, id);
- else
- sock = make_receive_sock(ipvs, id, dev->ifindex);
- if (IS_ERR(sock)) {
- result = PTR_ERR(sock);
- goto outtinfo;
- }
+ result = -ENOMEM;
tinfo = kmalloc(sizeof(*tinfo), GFP_KERNEL);
if (!tinfo)
- goto outsocket;
+ goto out;
tinfo->ipvs = ipvs;
- tinfo->sock = sock;
+ tinfo->sock = NULL;
if (state == IP_VS_STATE_BACKUP) {
tinfo->buf = kmalloc(ipvs->bcfg.sync_maxlen,
GFP_KERNEL);
if (!tinfo->buf)
- goto outtinfo;
+ goto out;
} else {
tinfo->buf = NULL;
}
tinfo->id = id;
+ if (state == IP_VS_STATE_MASTER)
+ result = make_send_sock(ipvs, id, dev, &tinfo->sock);
+ else
+ result = make_receive_sock(ipvs, id, dev, &tinfo->sock);
+ if (result < 0)
+ goto out;
task = kthread_run(threadfn, tinfo, name, ipvs->gen, id);
if (IS_ERR(task)) {
result = PTR_ERR(task);
- goto outtinfo;
+ goto out;
}
tinfo = NULL;
if (state == IP_VS_STATE_MASTER)
@@ -1913,20 +1908,20 @@
ipvs->sync_state |= state;
spin_unlock_bh(&ipvs->sync_buff_lock);
+ mutex_unlock(&ipvs->sync_mutex);
+ rtnl_unlock();
+
/* increase the module use count */
ip_vs_use_count_inc();
return 0;
-outsocket:
- sock_release(sock);
-
-outtinfo:
- if (tinfo) {
- sock_release(tinfo->sock);
- kfree(tinfo->buf);
- kfree(tinfo);
- }
+out:
+ /* We do not need RTNL lock anymore, release it here so that
+ * sock_release below and in the kthreads can use rtnl_lock
+ * to leave the mcast group.
+ */
+ rtnl_unlock();
count = id;
while (count-- > 0) {
if (state == IP_VS_STATE_MASTER)
@@ -1934,13 +1929,23 @@
else
kthread_stop(array[count]);
}
- kfree(array);
-
-out:
if (!(ipvs->sync_state & IP_VS_STATE_MASTER)) {
kfree(ipvs->ms);
ipvs->ms = NULL;
}
+ mutex_unlock(&ipvs->sync_mutex);
+ if (tinfo) {
+ if (tinfo->sock)
+ sock_release(tinfo->sock);
+ kfree(tinfo->buf);
+ kfree(tinfo);
+ }
+ kfree(array);
+ return result;
+
+out_early:
+ mutex_unlock(&ipvs->sync_mutex);
+ rtnl_unlock();
return result;
}
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index fa4d1ec..18e96a2 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1621,7 +1621,6 @@
struct nf_conntrack_tuple_hash *h;
struct nf_conn *ct;
struct hlist_nulls_node *n;
- int cpu;
spinlock_t *lockp;
for (; *bucket < nf_conntrack_htable_size; (*bucket)++) {
@@ -1643,18 +1642,6 @@
cond_resched();
}
- for_each_possible_cpu(cpu) {
- struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
-
- spin_lock_bh(&pcpu->lock);
- hlist_nulls_for_each_entry(h, n, &pcpu->unconfirmed, hnnode) {
- ct = nf_ct_tuplehash_to_ctrack(h);
- if (iter(ct, data))
- set_bit(IPS_DYING_BIT, &ct->status);
- }
- spin_unlock_bh(&pcpu->lock);
- cond_resched();
- }
return NULL;
found:
atomic_inc(&ct->ct_general.use);
@@ -1663,6 +1650,34 @@
return ct;
}
+static void
+__nf_ct_unconfirmed_destroy(struct net *net)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct nf_conntrack_tuple_hash *h;
+ struct hlist_nulls_node *n;
+ struct ct_pcpu *pcpu;
+
+ pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
+
+ spin_lock_bh(&pcpu->lock);
+ hlist_nulls_for_each_entry(h, n, &pcpu->unconfirmed, hnnode) {
+ struct nf_conn *ct;
+
+ ct = nf_ct_tuplehash_to_ctrack(h);
+
+ /* we cannot call iter() on unconfirmed list, the
+ * owning cpu can reallocate ct->ext at any time.
+ */
+ set_bit(IPS_DYING_BIT, &ct->status);
+ }
+ spin_unlock_bh(&pcpu->lock);
+ cond_resched();
+ }
+}
+
void nf_ct_iterate_cleanup(struct net *net,
int (*iter)(struct nf_conn *i, void *data),
void *data, u32 portid, int report)
@@ -1675,6 +1690,10 @@
if (atomic_read(&net->ct.count) == 0)
return;
+ __nf_ct_unconfirmed_destroy(net);
+
+ synchronize_net();
+
while ((ct = get_next_corpse(net, iter, data, &bucket)) != NULL) {
/* Time to push up daises... */
@@ -1873,7 +1892,7 @@
return 0;
}
-int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
+int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp)
{
unsigned int hashsize;
int rc;
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 6dc44d9..92e69af 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -379,17 +379,33 @@
struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
unsigned int h = helper_hash(&me->tuple);
struct nf_conntrack_helper *cur;
- int ret = 0;
+ int ret = 0, i;
BUG_ON(me->expect_policy == NULL);
BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
mutex_lock(&nf_ct_helper_mutex);
- hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
- if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple, &mask)) {
- ret = -EEXIST;
- goto out;
+ for (i = 0; i < nf_ct_helper_hsize; i++) {
+ hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
+ if (!strcmp(cur->name, me->name) &&
+ (cur->tuple.src.l3num == NFPROTO_UNSPEC ||
+ cur->tuple.src.l3num == me->tuple.src.l3num) &&
+ cur->tuple.dst.protonum == me->tuple.dst.protonum) {
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+ }
+
+ /* avoid unpredictable behaviour for auto_assign_helper */
+ if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
+ hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
+ if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
+ &mask)) {
+ ret = -EEXIST;
+ goto out;
+ }
}
}
hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 3c37253..c9ca529 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -894,8 +894,13 @@
}
out:
local_bh_enable();
- if (last)
+ if (last) {
+ /* nf ct hash resize happened, now clear the leftover. */
+ if ((struct nf_conn *)cb->args[1] == last)
+ cb->args[1] = 0;
+
nf_ct_put(last);
+ }
while (i) {
i--;
@@ -1012,9 +1017,8 @@
static int
ctnetlink_parse_tuple(const struct nlattr * const cda[],
- struct nf_conntrack_tuple *tuple,
- enum ctattr_type type, u_int8_t l3num,
- struct nf_conntrack_zone *zone)
+ struct nf_conntrack_tuple *tuple, u32 type,
+ u_int8_t l3num, struct nf_conntrack_zone *zone)
{
struct nlattr *tb[CTA_TUPLE_MAX+1];
int err;
@@ -2424,7 +2428,7 @@
static int ctnetlink_exp_dump_tuple(struct sk_buff *skb,
const struct nf_conntrack_tuple *tuple,
- enum ctattr_expect type)
+ u32 type)
{
struct nlattr *nest_parms;
diff --git a/net/netfilter/nf_nat_ftp.c b/net/netfilter/nf_nat_ftp.c
index e84a578..d76afaf 100644
--- a/net/netfilter/nf_nat_ftp.c
+++ b/net/netfilter/nf_nat_ftp.c
@@ -134,7 +134,7 @@
}
/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */
-static int warn_set(const char *val, struct kernel_param *kp)
+static int warn_set(const char *val, const struct kernel_param *kp)
{
printk(KERN_INFO KBUILD_MODNAME
": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
diff --git a/net/netfilter/nf_nat_irc.c b/net/netfilter/nf_nat_irc.c
index 1fb2258..8039bcd 100644
--- a/net/netfilter/nf_nat_irc.c
+++ b/net/netfilter/nf_nat_irc.c
@@ -107,7 +107,7 @@
}
/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */
-static int warn_set(const char *val, struct kernel_param *kp)
+static int warn_set(const char *val, const struct kernel_param *kp)
{
printk(KERN_INFO KBUILD_MODNAME
": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
diff --git a/net/netfilter/nf_nat_proto_common.c b/net/netfilter/nf_nat_proto_common.c
index fbce552..7d7466d 100644
--- a/net/netfilter/nf_nat_proto_common.c
+++ b/net/netfilter/nf_nat_proto_common.c
@@ -41,7 +41,7 @@
const struct nf_conn *ct,
u16 *rover)
{
- unsigned int range_size, min, i;
+ unsigned int range_size, min, max, i;
__be16 *portptr;
u_int16_t off;
@@ -71,7 +71,10 @@
}
} else {
min = ntohs(range->min_proto.all);
- range_size = ntohs(range->max_proto.all) - min + 1;
+ max = ntohs(range->max_proto.all);
+ if (unlikely(max < min))
+ swap(max, min);
+ range_size = max - min + 1;
}
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index fa3ef25..762f31f 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2200,41 +2200,46 @@
}
if (nlh->nlmsg_flags & NLM_F_REPLACE) {
- if (nft_is_active_next(net, old_rule)) {
- trans = nft_trans_rule_add(&ctx, NFT_MSG_DELRULE,
- old_rule);
- if (trans == NULL) {
- err = -ENOMEM;
- goto err2;
- }
- nft_deactivate_next(net, old_rule);
- chain->use--;
- list_add_tail_rcu(&rule->list, &old_rule->list);
- } else {
+ if (!nft_is_active_next(net, old_rule)) {
err = -ENOENT;
goto err2;
}
- } else if (nlh->nlmsg_flags & NLM_F_APPEND)
- if (old_rule)
- list_add_rcu(&rule->list, &old_rule->list);
- else
- list_add_tail_rcu(&rule->list, &chain->rules);
- else {
- if (old_rule)
- list_add_tail_rcu(&rule->list, &old_rule->list);
- else
- list_add_rcu(&rule->list, &chain->rules);
- }
+ trans = nft_trans_rule_add(&ctx, NFT_MSG_DELRULE,
+ old_rule);
+ if (trans == NULL) {
+ err = -ENOMEM;
+ goto err2;
+ }
+ nft_deactivate_next(net, old_rule);
+ chain->use--;
- if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
- err = -ENOMEM;
- goto err3;
+ if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
+ err = -ENOMEM;
+ goto err2;
+ }
+
+ list_add_tail_rcu(&rule->list, &old_rule->list);
+ } else {
+ if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
+ err = -ENOMEM;
+ goto err2;
+ }
+
+ if (nlh->nlmsg_flags & NLM_F_APPEND) {
+ if (old_rule)
+ list_add_rcu(&rule->list, &old_rule->list);
+ else
+ list_add_tail_rcu(&rule->list, &chain->rules);
+ } else {
+ if (old_rule)
+ list_add_tail_rcu(&rule->list, &old_rule->list);
+ else
+ list_add_rcu(&rule->list, &chain->rules);
+ }
}
chain->use++;
return 0;
-err3:
- list_del_rcu(&rule->list);
err2:
nf_tables_rule_destroy(&ctx, rule);
err1:
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 31ca947..b9dd4e9 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -82,8 +82,7 @@
nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
timeout = priv->timeout ? : set->timeout;
*nft_set_ext_expiration(ext) = jiffies + timeout;
- } else if (sexpr == NULL)
- goto out;
+ }
if (sexpr != NULL)
sexpr->ops->eval(sexpr, regs, pkt);
@@ -92,7 +91,7 @@
regs->verdict.code = NFT_BREAK;
return;
}
-out:
+
if (!priv->invert)
regs->verdict.code = NFT_BREAK;
}
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index e47ade3..59be898 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -39,6 +39,8 @@
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module");
+#define XT_PCPU_BLOCK_SIZE 4096
+
struct compat_delta {
unsigned int offset; /* offset in kernel */
int delta; /* delta in 32bit user land */
@@ -365,6 +367,36 @@
return buf;
}
+/**
+ * xt_check_proc_name - check that name is suitable for /proc file creation
+ *
+ * @name: file name candidate
+ * @size: length of buffer
+ *
+ * some x_tables modules wish to create a file in /proc.
+ * This function makes sure that the name is suitable for this
+ * purpose, it checks that name is NUL terminated and isn't a 'special'
+ * name, like "..".
+ *
+ * returns negative number on error or 0 if name is useable.
+ */
+int xt_check_proc_name(const char *name, unsigned int size)
+{
+ if (name[0] == '\0')
+ return -EINVAL;
+
+ if (strnlen(name, size) == size)
+ return -ENAMETOOLONG;
+
+ if (strcmp(name, ".") == 0 ||
+ strcmp(name, "..") == 0 ||
+ strchr(name, '/'))
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(xt_check_proc_name);
+
int xt_check_match(struct xt_mtchk_param *par,
unsigned int size, u_int8_t proto, bool inv_proto)
{
@@ -1004,8 +1036,10 @@
list_for_each_entry(t, &init_net.xt.tables[af], list) {
if (strcmp(t->name, name))
continue;
- if (!try_module_get(t->me))
+ if (!try_module_get(t->me)) {
+ mutex_unlock(&xt[af].mutex);
return NULL;
+ }
mutex_unlock(&xt[af].mutex);
if (t->table_init(net) != 0) {
@@ -1619,6 +1653,59 @@
}
EXPORT_SYMBOL_GPL(xt_proto_fini);
+/**
+ * xt_percpu_counter_alloc - allocate x_tables rule counter
+ *
+ * @state: pointer to xt_percpu allocation state
+ * @counter: pointer to counter struct inside the ip(6)/arpt_entry struct
+ *
+ * On SMP, the packet counter [ ip(6)t_entry->counters.pcnt ] will then
+ * contain the address of the real (percpu) counter.
+ *
+ * Rule evaluation needs to use xt_get_this_cpu_counter() helper
+ * to fetch the real percpu counter.
+ *
+ * To speed up allocation and improve data locality, a 4kb block is
+ * allocated.
+ *
+ * xt_percpu_counter_alloc_state contains the base address of the
+ * allocated page and the current sub-offset.
+ *
+ * returns false on error.
+ */
+bool xt_percpu_counter_alloc(struct xt_percpu_counter_alloc_state *state,
+ struct xt_counters *counter)
+{
+ BUILD_BUG_ON(XT_PCPU_BLOCK_SIZE < (sizeof(*counter) * 2));
+
+ if (nr_cpu_ids <= 1)
+ return true;
+
+ if (!state->mem) {
+ state->mem = __alloc_percpu(XT_PCPU_BLOCK_SIZE,
+ XT_PCPU_BLOCK_SIZE);
+ if (!state->mem)
+ return false;
+ }
+ counter->pcnt = (__force unsigned long)(state->mem + state->off);
+ state->off += sizeof(*counter);
+ if (state->off > (XT_PCPU_BLOCK_SIZE - sizeof(*counter))) {
+ state->mem = NULL;
+ state->off = 0;
+ }
+ return true;
+}
+EXPORT_SYMBOL_GPL(xt_percpu_counter_alloc);
+
+void xt_percpu_counter_free(struct xt_counters *counters)
+{
+ unsigned long pcnt = counters->pcnt;
+
+ if (nr_cpu_ids > 1 && (pcnt & (XT_PCPU_BLOCK_SIZE - 1)) == 0)
+ free_percpu((void __percpu *)pcnt);
+}
+EXPORT_SYMBOL_GPL(xt_percpu_counter_free);
+
static int __net_init xt_net_init(struct net *net)
{
int i;
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 6669e68..00ef824 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -168,8 +168,10 @@
goto err_put_timeout;
}
timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
- if (timeout_ext == NULL)
+ if (!timeout_ext) {
ret = -ENOMEM;
+ goto err_put_timeout;
+ }
rcu_read_unlock();
return ret;
@@ -201,6 +203,7 @@
struct xt_ct_target_info_v1 *info)
{
struct nf_conntrack_zone zone;
+ struct nf_conn_help *help;
struct nf_conn *ct;
int ret = -EOPNOTSUPP;
@@ -249,7 +252,7 @@
if (info->timeout[0]) {
ret = xt_ct_set_timeout(ct, par, info->timeout);
if (ret < 0)
- goto err3;
+ goto err4;
}
__set_bit(IPS_CONFIRMED_BIT, &ct->status);
nf_conntrack_get(&ct->ct_general);
@@ -257,6 +260,10 @@
info->ct = ct;
return 0;
+err4:
+ help = nfct_help(ct);
+ if (help)
+ module_put(help->helper->me);
err3:
nf_ct_tmpl_free(ct);
err2:
diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c
index 14e3d85..3d871c4 100644
--- a/net/netfilter/xt_IDLETIMER.c
+++ b/net/netfilter/xt_IDLETIMER.c
@@ -331,11 +331,11 @@
printk(KERN_WARNING "[%s] Failed to register pm notifier %d\n",
__func__, ret);
+ INIT_WORK(&info->timer->work, idletimer_tg_work);
+
mod_timer(&info->timer->timer,
msecs_to_jiffies(info->timeout * 1000) + jiffies);
- INIT_WORK(&info->timer->work, idletimer_tg_work);
-
return 0;
out_free_attr:
@@ -420,7 +420,10 @@
pr_debug("timeout value is zero\n");
return -EINVAL;
}
-
+ if (info->timeout >= INT_MAX / 1000) {
+ pr_debug("timeout value is too big\n");
+ return -EINVAL;
+ }
if (info->label[0] == '\0' ||
strnlen(info->label,
MAX_IDLETIMER_LABEL_SIZE) == MAX_IDLETIMER_LABEL_SIZE) {
diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c
index 3ba31c1..0858fe1 100644
--- a/net/netfilter/xt_LED.c
+++ b/net/netfilter/xt_LED.c
@@ -141,10 +141,11 @@
goto exit_alloc;
}
- /* See if we need to set up a timer */
- if (ledinfo->delay > 0)
- setup_timer(&ledinternal->timer, led_timeout_callback,
- (unsigned long)ledinternal);
+ /* Since the letinternal timer can be shared between multiple targets,
+ * always set it up, even if the current target does not need it
+ */
+ setup_timer(&ledinternal->timer, led_timeout_callback,
+ (unsigned long)ledinternal);
list_add_tail(&ledinternal->list, &xt_led_triggers);
@@ -181,8 +182,7 @@
list_del(&ledinternal->list);
- if (ledinfo->delay > 0)
- del_timer_sync(&ledinternal->timer);
+ del_timer_sync(&ledinternal->timer);
led_trigger_unregister(&ledinternal->netfilter_led_trigger);
diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c
index dffee9d47..a8df0a9 100644
--- a/net/netfilter/xt_bpf.c
+++ b/net/netfilter/xt_bpf.c
@@ -8,8 +8,10 @@
*/
#include <linux/module.h>
+#include <linux/syscalls.h>
#include <linux/skbuff.h>
#include <linux/filter.h>
+#include <linux/bpf.h>
#include <linux/netfilter/xt_bpf.h>
#include <linux/netfilter/x_tables.h>
@@ -20,15 +22,18 @@
MODULE_ALIAS("ipt_bpf");
MODULE_ALIAS("ip6t_bpf");
-static int bpf_mt_check(const struct xt_mtchk_param *par)
+static int __bpf_mt_check_bytecode(struct sock_filter *insns, __u16 len,
+ struct bpf_prog **ret)
{
- struct xt_bpf_info *info = par->matchinfo;
struct sock_fprog_kern program;
- program.len = info->bpf_program_num_elem;
- program.filter = info->bpf_program;
+ if (len > XT_BPF_MAX_NUM_INSTR)
+ return -EINVAL;
- if (bpf_prog_create(&info->filter, &program)) {
+ program.len = len;
+ program.filter = insns;
+
+ if (bpf_prog_create(ret, &program)) {
pr_info("bpf: check failed: parse error\n");
return -EINVAL;
}
@@ -36,6 +41,53 @@
return 0;
}
+static int __bpf_mt_check_fd(int fd, struct bpf_prog **ret)
+{
+ struct bpf_prog *prog;
+
+ prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_SOCKET_FILTER);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ *ret = prog;
+ return 0;
+}
+
+static int __bpf_mt_check_path(const char *path, struct bpf_prog **ret)
+{
+ if (strnlen(path, XT_BPF_PATH_MAX) == XT_BPF_PATH_MAX)
+ return -EINVAL;
+
+ *ret = bpf_prog_get_type_path(path, BPF_PROG_TYPE_SOCKET_FILTER);
+ return PTR_ERR_OR_ZERO(*ret);
+
+}
+
+static int bpf_mt_check(const struct xt_mtchk_param *par)
+{
+ struct xt_bpf_info *info = par->matchinfo;
+
+ return __bpf_mt_check_bytecode(info->bpf_program,
+ info->bpf_program_num_elem,
+ &info->filter);
+}
+
+static int bpf_mt_check_v1(const struct xt_mtchk_param *par)
+{
+ struct xt_bpf_info_v1 *info = par->matchinfo;
+
+ if (info->mode == XT_BPF_MODE_BYTECODE)
+ return __bpf_mt_check_bytecode(info->bpf_program,
+ info->bpf_program_num_elem,
+ &info->filter);
+ else if (info->mode == XT_BPF_MODE_FD_ELF)
+ return __bpf_mt_check_fd(info->fd, &info->filter);
+ else if (info->mode == XT_BPF_MODE_PATH_PINNED)
+ return __bpf_mt_check_path(info->path, &info->filter);
+ else
+ return -EINVAL;
+}
+
static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_bpf_info *info = par->matchinfo;
@@ -43,31 +95,58 @@
return BPF_PROG_RUN(info->filter, skb);
}
+static bool bpf_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_bpf_info_v1 *info = par->matchinfo;
+
+ return !!bpf_prog_run_save_cb(info->filter, (struct sk_buff *) skb);
+}
+
static void bpf_mt_destroy(const struct xt_mtdtor_param *par)
{
const struct xt_bpf_info *info = par->matchinfo;
+
bpf_prog_destroy(info->filter);
}
-static struct xt_match bpf_mt_reg __read_mostly = {
- .name = "bpf",
- .revision = 0,
- .family = NFPROTO_UNSPEC,
- .checkentry = bpf_mt_check,
- .match = bpf_mt,
- .destroy = bpf_mt_destroy,
- .matchsize = sizeof(struct xt_bpf_info),
- .me = THIS_MODULE,
+static void bpf_mt_destroy_v1(const struct xt_mtdtor_param *par)
+{
+ const struct xt_bpf_info_v1 *info = par->matchinfo;
+
+ bpf_prog_destroy(info->filter);
+}
+
+static struct xt_match bpf_mt_reg[] __read_mostly = {
+ {
+ .name = "bpf",
+ .revision = 0,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = bpf_mt_check,
+ .match = bpf_mt,
+ .destroy = bpf_mt_destroy,
+ .matchsize = sizeof(struct xt_bpf_info),
+ .me = THIS_MODULE,
+ },
+ {
+ .name = "bpf",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = bpf_mt_check_v1,
+ .match = bpf_mt_v1,
+ .destroy = bpf_mt_destroy_v1,
+ .matchsize = sizeof(struct xt_bpf_info_v1),
+ .me = THIS_MODULE,
+ },
};
static int __init bpf_mt_init(void)
{
- return xt_register_match(&bpf_mt_reg);
+ return xt_register_matches(bpf_mt_reg, ARRAY_SIZE(bpf_mt_reg));
}
static void __exit bpf_mt_exit(void)
{
- xt_unregister_match(&bpf_mt_reg);
+ xt_unregister_matches(bpf_mt_reg, ARRAY_SIZE(bpf_mt_reg));
}
module_init(bpf_mt_init);
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index b89b688..a1a29cd 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -794,8 +794,9 @@
struct hashlimit_cfg2 cfg = {};
int ret;
- if (info->name[sizeof(info->name) - 1] != '\0')
- return -EINVAL;
+ ret = xt_check_proc_name(info->name, sizeof(info->name));
+ if (ret)
+ return ret;
ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
@@ -809,9 +810,11 @@
static int hashlimit_mt_check(const struct xt_mtchk_param *par)
{
struct xt_hashlimit_mtinfo2 *info = par->matchinfo;
+ int ret;
- if (info->name[sizeof(info->name) - 1] != '\0')
- return -EINVAL;
+ ret = xt_check_proc_name(info->name, sizeof(info->name));
+ if (ret)
+ return ret;
return hashlimit_mt_check_common(par, &info->hinfo, &info->cfg,
info->name, 2);
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index e3b7a09..79d7ad6 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -361,9 +361,9 @@
info->hit_count, XT_RECENT_MAX_NSTAMPS - 1);
return -EINVAL;
}
- if (info->name[0] == '\0' ||
- strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN)
- return -EINVAL;
+ ret = xt_check_proc_name(info->name, sizeof(info->name));
+ if (ret)
+ return ret;
if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot)
nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index e1c123d..15e6e7b 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1054,6 +1054,9 @@
if (addr->sa_family != AF_NETLINK)
return -EINVAL;
+ if (alen < sizeof(struct sockaddr_nl))
+ return -EINVAL;
+
if ((nladdr->nl_groups || nladdr->nl_pid) &&
!netlink_allowed(sock, NL_CFG_F_NONROOT_SEND))
return -EPERM;
@@ -1792,6 +1795,8 @@
if (msg->msg_namelen) {
err = -EINVAL;
+ if (msg->msg_namelen < sizeof(struct sockaddr_nl))
+ goto out;
if (addr->nl_family != AF_NETLINK)
goto out;
dst_portid = addr->nl_pid;
@@ -2258,7 +2263,7 @@
if (cb->start) {
ret = cb->start(cb);
if (ret)
- goto error_unlock;
+ goto error_put;
}
nlk->cb_running = true;
@@ -2278,6 +2283,8 @@
*/
return -EINTR;
+error_put:
+ module_put(control->module);
error_unlock:
sock_put(sk);
mutex_unlock(nlk->cb_mutex);
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index b09d475..a1fae01 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -1103,6 +1103,7 @@
{
struct sk_buff *tmp;
struct net *net, *prev = NULL;
+ bool delivered = false;
int err;
for_each_net_rcu(net) {
@@ -1114,14 +1115,21 @@
}
err = nlmsg_multicast(prev->genl_sock, tmp,
portid, group, flags);
- if (err)
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
goto error;
}
prev = net;
}
- return nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
+ if (!err)
+ delivered = true;
+ else if (err != -ESRCH)
+ return err;
+ return delivered ? 0 : -ESRCH;
error:
kfree_skb(skb);
return err;
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index b28e45b..4663939 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -396,10 +396,38 @@
u16 proto, const struct sk_buff *skb)
{
struct nf_conntrack_tuple tuple;
+ struct nf_conntrack_expect *exp;
if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, net, &tuple))
return NULL;
- return __nf_ct_expect_find(net, zone, &tuple);
+
+ exp = __nf_ct_expect_find(net, zone, &tuple);
+ if (exp) {
+ struct nf_conntrack_tuple_hash *h;
+
+ /* Delete existing conntrack entry, if it clashes with the
+ * expectation. This can happen since conntrack ALGs do not
+ * check for clashes between (new) expectations and existing
+ * conntrack entries. nf_conntrack_in() will check the
+ * expectations only if a conntrack entry can not be found,
+ * which can lead to OVS finding the expectation (here) in the
+ * init direction, but which will not be removed by the
+ * nf_conntrack_in() call, if a matching conntrack entry is
+ * found instead. In this case all init direction packets
+ * would be reported as new related packets, while reply
+ * direction packets would be reported as un-related
+ * established packets.
+ */
+ h = nf_conntrack_find_get(net, zone, &tuple);
+ if (h) {
+ struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
+
+ nf_ct_delete(ct, 0, 0);
+ nf_conntrack_put(&ct->ct_general);
+ }
+ }
+
+ return exp;
}
/* This replicates logic from nf_conntrack_core.c that is not exported. */
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 1668916..326945d 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -1296,13 +1296,10 @@
/* The nlattr stream should already have been validated */
nla_for_each_nested(nla, attr, rem) {
- if (tbl[nla_type(nla)].len == OVS_ATTR_NESTED) {
- if (tbl[nla_type(nla)].next)
- tbl = tbl[nla_type(nla)].next;
- nlattr_set(nla, val, tbl);
- } else {
+ if (tbl[nla_type(nla)].len == OVS_ATTR_NESTED)
+ nlattr_set(nla, val, tbl[nla_type(nla)].next ? : tbl);
+ else
memset(nla_data(nla), val, nla_len(nla));
- }
if (nla_type(nla) == OVS_KEY_ATTR_CT_STATE)
*(u32 *)nla_data(nla) &= CT_SUPPORTED_MASK;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 267db0d..430a7c7 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -333,11 +333,11 @@
skb_set_queue_mapping(skb, queue_index);
}
-/* register_prot_hook must be invoked with the po->bind_lock held,
+/* __register_prot_hook must be invoked through register_prot_hook
* or from a context in which asynchronous accesses to the packet
* socket is not possible (packet_create()).
*/
-static void register_prot_hook(struct sock *sk)
+static void __register_prot_hook(struct sock *sk)
{
struct packet_sock *po = pkt_sk(sk);
@@ -352,8 +352,13 @@
}
}
-/* {,__}unregister_prot_hook() must be invoked with the po->bind_lock
- * held. If the sync parameter is true, we will temporarily drop
+static void register_prot_hook(struct sock *sk)
+{
+ lockdep_assert_held_once(&pkt_sk(sk)->bind_lock);
+ __register_prot_hook(sk);
+}
+
+/* If the sync parameter is true, we will temporarily drop
* the po->bind_lock and do a synchronize_net to make sure no
* asynchronous packet processing paths still refer to the elements
* of po->prot_hook. If the sync parameter is false, it is the
@@ -363,6 +368,8 @@
{
struct packet_sock *po = pkt_sk(sk);
+ lockdep_assert_held_once(&po->bind_lock);
+
po->running = 0;
if (po->fanout)
@@ -1685,7 +1692,7 @@
match->flags = flags;
INIT_LIST_HEAD(&match->list);
spin_lock_init(&match->lock);
- atomic_set(&match->sk_ref, 0);
+ refcount_set(&match->sk_ref, 0);
fanout_init_data(match);
match->prot_hook.type = po->prot_hook.type;
match->prot_hook.dev = po->prot_hook.dev;
@@ -1702,19 +1709,19 @@
match->prot_hook.type == po->prot_hook.type &&
match->prot_hook.dev == po->prot_hook.dev) {
err = -ENOSPC;
- if (atomic_read(&match->sk_ref) < PACKET_FANOUT_MAX) {
+ if (refcount_read(&match->sk_ref) < PACKET_FANOUT_MAX) {
__dev_remove_pack(&po->prot_hook);
po->fanout = match;
po->rollover = rollover;
rollover = NULL;
- atomic_inc(&match->sk_ref);
+ refcount_set(&match->sk_ref, refcount_read(&match->sk_ref) + 1);
__fanout_link(sk, po);
err = 0;
}
}
spin_unlock(&po->bind_lock);
- if (err && !atomic_read(&match->sk_ref)) {
+ if (err && !refcount_read(&match->sk_ref)) {
list_del(&match->list);
kfree(match);
}
@@ -1740,7 +1747,7 @@
if (f) {
po->fanout = NULL;
- if (atomic_dec_and_test(&f->sk_ref))
+ if (refcount_dec_and_test(&f->sk_ref))
list_del(&f->list);
else
f = NULL;
@@ -2903,13 +2910,15 @@
if (skb == NULL)
goto out_unlock;
- skb_set_network_header(skb, reserve);
+ skb_reset_network_header(skb);
err = -EINVAL;
if (sock->type == SOCK_DGRAM) {
offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
if (unlikely(offset < 0))
goto out_free;
+ } else if (reserve) {
+ skb_push(skb, reserve);
}
/* Returns -EFAULT on error */
@@ -3017,6 +3026,7 @@
packet_flush_mclist(sk);
+ lock_sock(sk);
if (po->rx_ring.pg_vec) {
memset(&req_u, 0, sizeof(req_u));
packet_set_ring(sk, &req_u, 1, 0);
@@ -3026,6 +3036,7 @@
memset(&req_u, 0, sizeof(req_u));
packet_set_ring(sk, &req_u, 1, 1);
}
+ release_sock(sk);
f = fanout_release(sk);
@@ -3259,7 +3270,7 @@
if (proto) {
po->prot_hook.type = proto;
- register_prot_hook(sk);
+ __register_prot_hook(sk);
}
mutex_lock(&net->packet.sklist_lock);
@@ -3654,6 +3665,7 @@
union tpacket_req_u req_u;
int len;
+ lock_sock(sk);
switch (po->tp_version) {
case TPACKET_V1:
case TPACKET_V2:
@@ -3664,12 +3676,17 @@
len = sizeof(req_u.req3);
break;
}
- if (optlen < len)
- return -EINVAL;
- if (copy_from_user(&req_u.req, optval, len))
- return -EFAULT;
- return packet_set_ring(sk, &req_u, 0,
- optname == PACKET_TX_RING);
+ if (optlen < len) {
+ ret = -EINVAL;
+ } else {
+ if (copy_from_user(&req_u.req, optval, len))
+ ret = -EFAULT;
+ else
+ ret = packet_set_ring(sk, &req_u, 0,
+ optname == PACKET_TX_RING);
+ }
+ release_sock(sk);
+ return ret;
}
case PACKET_COPY_THRESH:
{
@@ -3735,12 +3752,18 @@
if (optlen != sizeof(val))
return -EINVAL;
- if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
- return -EBUSY;
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
- po->tp_loss = !!val;
- return 0;
+
+ lock_sock(sk);
+ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+ ret = -EBUSY;
+ } else {
+ po->tp_loss = !!val;
+ ret = 0;
+ }
+ release_sock(sk);
+ return ret;
}
case PACKET_AUXDATA:
{
@@ -3751,7 +3774,9 @@
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
+ lock_sock(sk);
po->auxdata = !!val;
+ release_sock(sk);
return 0;
}
case PACKET_ORIGDEV:
@@ -3763,7 +3788,9 @@
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
+ lock_sock(sk);
po->origdev = !!val;
+ release_sock(sk);
return 0;
}
case PACKET_VNET_HDR:
@@ -3772,15 +3799,20 @@
if (sock->type != SOCK_RAW)
return -EINVAL;
- if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
- return -EBUSY;
if (optlen < sizeof(val))
return -EINVAL;
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
- po->has_vnet_hdr = !!val;
- return 0;
+ lock_sock(sk);
+ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+ ret = -EBUSY;
+ } else {
+ po->has_vnet_hdr = !!val;
+ ret = 0;
+ }
+ release_sock(sk);
+ return ret;
}
case PACKET_TIMESTAMP:
{
@@ -3818,11 +3850,17 @@
if (optlen != sizeof(val))
return -EINVAL;
- if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
- return -EBUSY;
if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
- po->tp_tx_has_off = !!val;
+
+ lock_sock(sk);
+ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+ ret = -EBUSY;
+ } else {
+ po->tp_tx_has_off = !!val;
+ ret = 0;
+ }
+ release_sock(sk);
return 0;
}
case PACKET_QDISC_BYPASS:
@@ -4219,7 +4257,6 @@
/* Added to avoid minimal code churn */
struct tpacket_req *req = &req_u->req;
- lock_sock(sk);
/* Opening a Tx-ring is NOT supported in TPACKET_V3 */
if (!closing && tx_ring && (po->tp_version > TPACKET_V2)) {
net_warn_ratelimited("Tx-ring is not supported.\n");
@@ -4355,7 +4392,6 @@
if (pg_vec)
free_pg_vec(pg_vec, order, req->tp_block_nr);
out:
- release_sock(sk);
return err;
}
diff --git a/net/packet/internal.h b/net/packet/internal.h
index d55bfc3..b8d5618 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -1,6 +1,8 @@
#ifndef __PACKET_INTERNAL_H__
#define __PACKET_INTERNAL_H__
+#include <linux/refcount.h>
+
struct packet_mclist {
struct packet_mclist *next;
int ifindex;
@@ -86,7 +88,7 @@
struct list_head list;
struct sock *arr[PACKET_FANOUT_MAX];
spinlock_t lock;
- atomic_t sk_ref;
+ refcount_t sk_ref;
struct packet_type prot_hook ____cacheline_aligned_in_smp;
};
@@ -109,10 +111,12 @@
int copy_thresh;
spinlock_t bind_lock;
struct mutex pg_vec_lock;
- unsigned int running:1, /* prot_hook is attached*/
- auxdata:1,
+ unsigned int running; /* bind_lock must be held */
+ unsigned int auxdata:1, /* writer must hold sock lock */
origdev:1,
- has_vnet_hdr:1;
+ has_vnet_hdr:1,
+ tp_loss:1,
+ tp_tx_has_off:1;
int pressure;
int ifindex; /* bound device */
__be16 num;
@@ -122,8 +126,6 @@
enum tpacket_versions tp_version;
unsigned int tp_hdrlen;
unsigned int tp_reserve;
- unsigned int tp_loss:1;
- unsigned int tp_tx_has_off:1;
unsigned int tp_tstamp;
struct net_device __rcu *cached_dev;
int (*xmit)(struct sk_buff *skb);
diff --git a/net/rds/bind.c b/net/rds/bind.c
index 095f6ce..adb53ae 100644
--- a/net/rds/bind.c
+++ b/net/rds/bind.c
@@ -114,6 +114,7 @@
rs, &addr, (int)ntohs(*port));
break;
} else {
+ rs->rs_bound_addr = 0;
rds_sock_put(rs);
ret = -ENOMEM;
break;
diff --git a/net/rds/send.c b/net/rds/send.c
index ef53d164..50241d3 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 Oracle. All rights reserved.
+ * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -983,10 +983,15 @@
if (conn->c_npaths == 0 && hash != 0) {
rds_send_ping(conn);
- if (conn->c_npaths == 0) {
- wait_event_interruptible(conn->c_hs_waitq,
- (conn->c_npaths != 0));
- }
+ /* The underlying connection is not up yet. Need to wait
+ * until it is up to be sure that the non-zero c_path can be
+ * used. But if we are interrupted, we have to use the zero
+ * c_path in case the connection ends up being non-MP capable.
+ */
+ if (conn->c_npaths == 0)
+ if (wait_event_interruptible(conn->c_hs_waitq,
+ conn->c_npaths != 0))
+ hash = 0;
if (conn->c_npaths == 1)
hash = 0;
}
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 76c01cb..d6d8b34 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -138,13 +138,18 @@
ret = rfkill_register(rfkill->rfkill_dev);
if (ret < 0)
- return ret;
+ goto err_destroy;
platform_set_drvdata(pdev, rfkill);
dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
return 0;
+
+err_destroy:
+ rfkill_destroy(rfkill->rfkill_dev);
+
+ return ret;
}
static int rfkill_gpio_remove(struct platform_device *pdev)
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index c4ef460..3603c5e 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -578,7 +578,7 @@
LOGE("Failed to to register netdev [%s]", dev->name);
free_netdev(dev);
*new_device = 0;
- rc = RMNET_CONFIG_UNKNOWN_ERROR;
+ return RMNET_CONFIG_UNKNOWN_ERROR;
} else {
rmnet_devices[id] = dev;
*new_device = dev;
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 5dab1ff..59d3286 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -391,7 +391,7 @@
(char *)&opt, sizeof(opt));
if (ret == 0) {
ret = kernel_sendmsg(conn->params.local->socket, &msg,
- iov, 1, iov[0].iov_len);
+ iov, 2, len);
opt = IPV6_PMTUDISC_DO;
kernel_setsockopt(conn->params.local->socket,
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index 4374e7b..90df95e 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -229,7 +229,9 @@
len &= ~(call->conn->size_align - 1);
sg_init_table(sg, nsg);
- skb_to_sgvec(skb, sg, 0, len);
+ err = skb_to_sgvec(skb, sg, 0, len);
+ if (unlikely(err < 0))
+ goto out;
skcipher_request_set_crypt(req, sg, sg, len, iv.x);
crypto_skcipher_encrypt(req);
@@ -325,7 +327,7 @@
struct sk_buff *trailer;
u32 data_size, buf;
u16 check;
- int nsg;
+ int nsg, ret;
_enter("");
@@ -342,7 +344,9 @@
goto nomem;
sg_init_table(sg, nsg);
- skb_to_sgvec(skb, sg, offset, 8);
+ ret = skb_to_sgvec(skb, sg, offset, 8);
+ if (unlikely(ret < 0))
+ return ret;
/* start the decryption afresh */
memset(&iv, 0, sizeof(iv));
@@ -405,7 +409,7 @@
struct sk_buff *trailer;
u32 data_size, buf;
u16 check;
- int nsg;
+ int nsg, ret;
_enter(",{%d}", skb->len);
@@ -429,7 +433,12 @@
}
sg_init_table(sg, nsg);
- skb_to_sgvec(skb, sg, offset, len);
+ ret = skb_to_sgvec(skb, sg, offset, len);
+ if (unlikely(ret < 0)) {
+ if (sg != _sg)
+ kfree(sg);
+ return ret;
+ }
/* decrypt from the session key */
token = call->conn->params.key->payload.data[0];
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index f311732..67adb4e 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -95,8 +95,10 @@
continue;
nest = nla_nest_start(skb, n_i);
- if (nest == NULL)
+ if (nest == NULL) {
+ index--;
goto nla_put_failure;
+ }
err = tcf_action_dump_1(skb, p, 0, 0);
if (err < 0) {
index--;
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
index 1d39600..40496f3 100644
--- a/net/sched/act_bpf.c
+++ b/net/sched/act_bpf.c
@@ -245,10 +245,14 @@
static void tcf_bpf_cfg_cleanup(const struct tcf_bpf_cfg *cfg)
{
- if (cfg->is_ebpf)
- bpf_prog_put(cfg->filter);
- else
- bpf_prog_destroy(cfg->filter);
+ struct bpf_prog *filter = cfg->filter;
+
+ if (filter) {
+ if (cfg->is_ebpf)
+ bpf_prog_put(filter);
+ else
+ bpf_prog_destroy(filter);
+ }
kfree(cfg->bpf_ops);
kfree(cfg->bpf_name);
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index e0defce..24a0c66 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -180,6 +180,9 @@
struct tcphdr *tcph;
const struct iphdr *iph;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
+ return 1;
+
tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
if (tcph == NULL)
return 0;
@@ -201,6 +204,9 @@
struct tcphdr *tcph;
const struct ipv6hdr *ip6h;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
+ return 1;
+
tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
if (tcph == NULL)
return 0;
@@ -224,6 +230,9 @@
const struct iphdr *iph;
u16 ul;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+ return 1;
+
/*
* Support both UDP and UDPLITE checksum algorithms, Don't use
* udph->len to get the real length without any protocol check,
@@ -277,6 +286,9 @@
const struct ipv6hdr *ip6h;
u16 ul;
+ if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+ return 1;
+
/*
* Support both UDP and UDPLITE checksum algorithms, Don't use
* udph->len to get the real length without any protocol check,
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 95c463c..235db2c 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -634,7 +634,7 @@
}
}
- return 0;
+ return -ENOENT;
}
struct ifeheadr {
diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c
index f85313d..bd8e86c 100644
--- a/net/sched/act_skbmod.c
+++ b/net/sched/act_skbmod.c
@@ -192,7 +192,8 @@
struct tcf_skbmod_params *p;
p = rcu_dereference_protected(d->skbmod_p, 1);
- kfree_rcu(p, rcu);
+ if (p)
+ kfree_rcu(p, rcu);
}
static int tcf_skbmod_dump(struct sk_buff *skb, struct tc_action *a,
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index af47bdf..901fb8b 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -141,6 +141,7 @@
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX;
break;
default:
+ ret = -EINVAL;
goto err_out;
}
@@ -195,11 +196,12 @@
struct tcf_tunnel_key_params *params;
params = rcu_dereference_protected(t->params, 1);
+ if (params) {
+ if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
+ dst_release(¶ms->tcft_enc_metadata->dst);
- if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
- dst_release(¶ms->tcft_enc_metadata->dst);
-
- kfree_rcu(params, rcu);
+ kfree_rcu(params, rcu);
+ }
}
static int tunnel_key_dump_addresses(struct sk_buff *skb,
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index 18e7524..b57b4de 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -128,6 +128,28 @@
return f->next == &detached;
}
+static bool fq_flow_is_throttled(const struct fq_flow *f)
+{
+ return f->next == &throttled;
+}
+
+static void fq_flow_add_tail(struct fq_flow_head *head, struct fq_flow *flow)
+{
+ if (head->first)
+ head->last->next = flow;
+ else
+ head->first = flow;
+ head->last = flow;
+ flow->next = NULL;
+}
+
+static void fq_flow_unset_throttled(struct fq_sched_data *q, struct fq_flow *f)
+{
+ rb_erase(&f->rate_node, &q->delayed);
+ q->throttled_flows--;
+ fq_flow_add_tail(&q->old_flows, f);
+}
+
static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f)
{
struct rb_node **p = &q->delayed.rb_node, *parent = NULL;
@@ -155,15 +177,6 @@
static struct kmem_cache *fq_flow_cachep __read_mostly;
-static void fq_flow_add_tail(struct fq_flow_head *head, struct fq_flow *flow)
-{
- if (head->first)
- head->last->next = flow;
- else
- head->first = flow;
- head->last = flow;
- flow->next = NULL;
-}
/* limit number of collected flows per round */
#define FQ_GC_MAX 8
@@ -267,6 +280,8 @@
f->socket_hash != sk->sk_hash)) {
f->credit = q->initial_quantum;
f->socket_hash = sk->sk_hash;
+ if (fq_flow_is_throttled(f))
+ fq_flow_unset_throttled(q, f);
f->time_next_packet = 0ULL;
}
return f;
@@ -430,9 +445,7 @@
q->time_next_delayed_flow = f->time_next_packet;
break;
}
- rb_erase(p, &q->delayed);
- q->throttled_flows--;
- fq_flow_add_tail(&q->old_flows, f);
+ fq_flow_unset_throttled(q, f);
}
}
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 9f7b380..e899d9e 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -462,7 +462,7 @@
/* If a delay is expected, orphan the skb. (orphaning usually takes
* place at TX completion time, so _before_ the link transit delay)
*/
- if (q->latency || q->jitter)
+ if (q->latency || q->jitter || q->rate)
skb_orphan_partial(skb);
/*
@@ -513,7 +513,7 @@
}
if (unlikely(sch->q.qlen >= sch->limit))
- return qdisc_drop(skb, sch, to_free);
+ return qdisc_drop_all(skb, sch, to_free);
qdisc_qstats_backlog_inc(sch, skb);
@@ -530,21 +530,31 @@
now = psched_get_time();
if (q->rate) {
- struct sk_buff *last;
+ struct netem_skb_cb *last = NULL;
- if (sch->q.qlen)
- last = sch->q.tail;
- else
- last = netem_rb_to_skb(rb_last(&q->t_root));
+ if (sch->q.tail)
+ last = netem_skb_cb(sch->q.tail);
+ if (q->t_root.rb_node) {
+ struct sk_buff *t_skb;
+ struct netem_skb_cb *t_last;
+
+ t_skb = netem_rb_to_skb(rb_last(&q->t_root));
+ t_last = netem_skb_cb(t_skb);
+ if (!last ||
+ t_last->time_to_send > last->time_to_send) {
+ last = t_last;
+ }
+ }
+
if (last) {
/*
* Last packet in queue is reference point (now),
* calculate this time bonus and subtract
* from delay.
*/
- delay -= netem_skb_cb(last)->time_to_send - now;
+ delay -= last->time_to_send - now;
delay = max_t(psched_tdiff_t, 0, delay);
- now = netem_skb_cb(last)->time_to_send;
+ now = last->time_to_send;
}
delay += packet_len_2_sched_time(qdisc_pkt_len(skb), q);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index f10d339..738c55e 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1006,9 +1006,10 @@
struct sctp_endpoint *ep;
struct sctp_chunk *chunk;
struct sctp_inq *inqueue;
- int state;
sctp_subtype_t subtype;
+ int first_time = 1; /* is this the first time through the loop */
int error = 0;
+ int state;
/* The association should be held so we should be safe. */
ep = asoc->ep;
@@ -1019,6 +1020,30 @@
state = asoc->state;
subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
+ /* If the first chunk in the packet is AUTH, do special
+ * processing specified in Section 6.3 of SCTP-AUTH spec
+ */
+ if (first_time && subtype.chunk == SCTP_CID_AUTH) {
+ struct sctp_chunkhdr *next_hdr;
+
+ next_hdr = sctp_inq_peek(inqueue);
+ if (!next_hdr)
+ goto normal;
+
+ /* If the next chunk is COOKIE-ECHO, skip the AUTH
+ * chunk while saving a pointer to it so we can do
+ * Authentication later (during cookie-echo
+ * processing).
+ */
+ if (next_hdr->type == SCTP_CID_COOKIE_ECHO) {
+ chunk->auth_chunk = skb_clone(chunk->skb,
+ GFP_ATOMIC);
+ chunk->auth = 1;
+ continue;
+ }
+ }
+
+normal:
/* SCTP-AUTH, Section 6.3:
* The receiver has a list of chunk types which it expects
* to be received only after an AUTH-chunk. This list has
@@ -1057,6 +1082,9 @@
/* If there is an error on chunk, discard this packet. */
if (error && chunk)
chunk->pdiscard = 1;
+
+ if (first_time)
+ first_time = 0;
}
sctp_association_put(asoc);
}
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index f731de3..e06083c 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -217,7 +217,7 @@
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
- if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
+ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <=
skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */
chunk->singleton = 0;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 5d01527..f4d5efb 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -324,8 +324,10 @@
final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
bdst = ip6_dst_lookup_flow(sk, fl6, final_p);
- if (!IS_ERR(bdst) &&
- ipv6_chk_addr(dev_net(bdst->dev),
+ if (IS_ERR(bdst))
+ continue;
+
+ if (ipv6_chk_addr(dev_net(bdst->dev),
&laddr->a.v6.sin6_addr, bdst->dev, 1)) {
if (!IS_ERR_OR_NULL(dst))
dst_release(dst);
@@ -334,8 +336,10 @@
}
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
- if (matchlen > bmatchlen)
+ if (matchlen > bmatchlen) {
+ dst_release(bdst);
continue;
+ }
if (!IS_ERR_OR_NULL(dst))
dst_release(dst);
@@ -517,44 +521,47 @@
addr->v6.sin6_scope_id = 0;
}
+static int __sctp_v6_cmp_addr(const union sctp_addr *addr1,
+ const union sctp_addr *addr2)
+{
+ if (addr1->sa.sa_family != addr2->sa.sa_family) {
+ if (addr1->sa.sa_family == AF_INET &&
+ addr2->sa.sa_family == AF_INET6 &&
+ ipv6_addr_v4mapped(&addr2->v6.sin6_addr) &&
+ addr2->v6.sin6_addr.s6_addr32[3] ==
+ addr1->v4.sin_addr.s_addr)
+ return 1;
+
+ if (addr2->sa.sa_family == AF_INET &&
+ addr1->sa.sa_family == AF_INET6 &&
+ ipv6_addr_v4mapped(&addr1->v6.sin6_addr) &&
+ addr1->v6.sin6_addr.s6_addr32[3] ==
+ addr2->v4.sin_addr.s_addr)
+ return 1;
+
+ return 0;
+ }
+
+ if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
+ return 0;
+
+ /* If this is a linklocal address, compare the scope_id. */
+ if ((ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
+ addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id &&
+ addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)
+ return 0;
+
+ return 1;
+}
+
/* Compare addresses exactly.
* v4-mapped-v6 is also in consideration.
*/
static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2)
{
- if (addr1->sa.sa_family != addr2->sa.sa_family) {
- if (addr1->sa.sa_family == AF_INET &&
- addr2->sa.sa_family == AF_INET6 &&
- ipv6_addr_v4mapped(&addr2->v6.sin6_addr)) {
- if (addr2->v6.sin6_port == addr1->v4.sin_port &&
- addr2->v6.sin6_addr.s6_addr32[3] ==
- addr1->v4.sin_addr.s_addr)
- return 1;
- }
- if (addr2->sa.sa_family == AF_INET &&
- addr1->sa.sa_family == AF_INET6 &&
- ipv6_addr_v4mapped(&addr1->v6.sin6_addr)) {
- if (addr1->v6.sin6_port == addr2->v4.sin_port &&
- addr1->v6.sin6_addr.s6_addr32[3] ==
- addr2->v4.sin_addr.s_addr)
- return 1;
- }
- return 0;
- }
- if (addr1->v6.sin6_port != addr2->v6.sin6_port)
- return 0;
- if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
- return 0;
- /* If this is a linklocal address, compare the scope_id. */
- if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
- if (addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id &&
- (addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)) {
- return 0;
- }
- }
-
- return 1;
+ return __sctp_v6_cmp_addr(addr1, addr2) &&
+ addr1->v6.sin6_port == addr2->v6.sin6_port;
}
/* Initialize addr struct to INADDR_ANY. */
@@ -723,8 +730,10 @@
sctp_v6_map_v4(addr);
}
- if (addr->sa.sa_family == AF_INET)
+ if (addr->sa.sa_family == AF_INET) {
+ memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero));
return sizeof(struct sockaddr_in);
+ }
return sizeof(struct sockaddr_in6);
}
@@ -838,8 +847,8 @@
const union sctp_addr *addr2,
struct sctp_sock *opt)
{
- struct sctp_af *af1, *af2;
struct sock *sk = sctp_opt2sk(opt);
+ struct sctp_af *af1, *af2;
af1 = sctp_get_af_specific(addr1->sa.sa_family);
af2 = sctp_get_af_specific(addr2->sa.sa_family);
@@ -855,10 +864,10 @@
if (sctp_is_any(sk, addr1) || sctp_is_any(sk, addr2))
return 1;
- if (addr1->sa.sa_family != addr2->sa.sa_family)
- return 0;
+ if (addr1->sa.sa_family == AF_INET && addr2->sa.sa_family == AF_INET)
+ return addr1->v4.sin_addr.s_addr == addr2->v4.sin_addr.s_addr;
- return af1->cmp_addr(addr1, addr2);
+ return __sctp_v6_cmp_addr(addr1, addr2);
}
/* Verify that the provided sockaddr looks bindable. Common verification,
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 7b523e3..fb7b763 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -510,22 +510,20 @@
if (IS_ERR(rt))
continue;
- if (!dst)
- dst = &rt->dst;
-
/* Ensure the src address belongs to the output
* interface.
*/
odev = __ip_dev_find(sock_net(sk), laddr->a.v4.sin_addr.s_addr,
false);
if (!odev || odev->ifindex != fl4->flowi4_oif) {
- if (&rt->dst != dst)
+ if (!dst)
+ dst = &rt->dst;
+ else
dst_release(&rt->dst);
continue;
}
- if (dst != &rt->dst)
- dst_release(dst);
+ dst_release(dst);
dst = &rt->dst;
break;
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 9e9690b..fc67d35 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1373,9 +1373,14 @@
sctp_chunkhdr_t *chunk_hdr;
struct sk_buff *skb;
struct sock *sk;
+ int chunklen;
+
+ chunklen = SCTP_PAD4(sizeof(*chunk_hdr) + paylen);
+ if (chunklen > SCTP_MAX_CHUNK_LEN)
+ goto nodata;
/* No need to allocate LL here, as this is only a chunk. */
- skb = alloc_skb(SCTP_PAD4(sizeof(sctp_chunkhdr_t) + paylen), gfp);
+ skb = alloc_skb(chunklen, gfp);
if (!skb)
goto nodata;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 8ec20a6..bfd0686 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -144,10 +144,8 @@
void *arg,
sctp_cmd_seq_t *commands);
-static sctp_ierror_t sctp_sf_authenticate(struct net *net,
- const struct sctp_endpoint *ep,
+static sctp_ierror_t sctp_sf_authenticate(
const struct sctp_association *asoc,
- const sctp_subtype_t type,
struct sctp_chunk *chunk);
static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net,
@@ -615,6 +613,38 @@
return SCTP_DISPOSITION_CONSUME;
}
+static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk,
+ const struct sctp_association *asoc)
+{
+ struct sctp_chunk auth;
+
+ if (!chunk->auth_chunk)
+ return true;
+
+ /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
+ * is supposed to be authenticated and we have to do delayed
+ * authentication. We've just recreated the association using
+ * the information in the cookie and now it's much easier to
+ * do the authentication.
+ */
+
+ /* Make sure that we and the peer are AUTH capable */
+ if (!net->sctp.auth_enable || !asoc->peer.auth_capable)
+ return false;
+
+ /* set-up our fake chunk so that we can process it */
+ auth.skb = chunk->auth_chunk;
+ auth.asoc = chunk->asoc;
+ auth.sctp_hdr = chunk->sctp_hdr;
+ auth.chunk_hdr = (struct sctp_chunkhdr *)
+ skb_push(chunk->auth_chunk,
+ sizeof(struct sctp_chunkhdr));
+ skb_pull(chunk->auth_chunk, sizeof(struct sctp_chunkhdr));
+ auth.transport = chunk->transport;
+
+ return sctp_sf_authenticate(asoc, &auth) == SCTP_IERROR_NO_ERROR;
+}
+
/*
* Respond to a normal COOKIE ECHO chunk.
* We are the side that is being asked for an association.
@@ -751,36 +781,9 @@
if (error)
goto nomem_init;
- /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
- * is supposed to be authenticated and we have to do delayed
- * authentication. We've just recreated the association using
- * the information in the cookie and now it's much easier to
- * do the authentication.
- */
- if (chunk->auth_chunk) {
- struct sctp_chunk auth;
- sctp_ierror_t ret;
-
- /* Make sure that we and the peer are AUTH capable */
- if (!net->sctp.auth_enable || !new_asoc->peer.auth_capable) {
- sctp_association_free(new_asoc);
- return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
- }
-
- /* set-up our fake chunk so that we can process it */
- auth.skb = chunk->auth_chunk;
- auth.asoc = chunk->asoc;
- auth.sctp_hdr = chunk->sctp_hdr;
- auth.chunk_hdr = (sctp_chunkhdr_t *)skb_push(chunk->auth_chunk,
- sizeof(sctp_chunkhdr_t));
- skb_pull(chunk->auth_chunk, sizeof(sctp_chunkhdr_t));
- auth.transport = chunk->transport;
-
- ret = sctp_sf_authenticate(net, ep, new_asoc, type, &auth);
- if (ret != SCTP_IERROR_NO_ERROR) {
- sctp_association_free(new_asoc);
- return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
- }
+ if (!sctp_auth_chunk_verify(net, chunk, new_asoc)) {
+ sctp_association_free(new_asoc);
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
}
repl = sctp_make_cookie_ack(new_asoc, chunk);
@@ -1717,13 +1720,15 @@
GFP_ATOMIC))
goto nomem;
+ if (!sctp_auth_chunk_verify(net, chunk, new_asoc))
+ return SCTP_DISPOSITION_DISCARD;
+
/* Make sure no new addresses are being added during the
* restart. Though this is a pretty complicated attack
* since you'd have to get inside the cookie.
*/
- if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
+ if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands))
return SCTP_DISPOSITION_CONSUME;
- }
/* If the endpoint is in the SHUTDOWN-ACK-SENT state and recognizes
* the peer has restarted (Action A), it MUST NOT setup a new
@@ -1828,6 +1833,9 @@
GFP_ATOMIC))
goto nomem;
+ if (!sctp_auth_chunk_verify(net, chunk, new_asoc))
+ return SCTP_DISPOSITION_DISCARD;
+
/* Update the content of current association. */
sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
@@ -1920,6 +1928,9 @@
* a COOKIE ACK.
*/
+ if (!sctp_auth_chunk_verify(net, chunk, asoc))
+ return SCTP_DISPOSITION_DISCARD;
+
/* Don't accidentally move back into established state. */
if (asoc->state < SCTP_STATE_ESTABLISHED) {
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
@@ -1959,7 +1970,7 @@
}
}
- repl = sctp_make_cookie_ack(new_asoc, chunk);
+ repl = sctp_make_cookie_ack(asoc, chunk);
if (!repl)
goto nomem;
@@ -3981,10 +3992,8 @@
*
* The return value is the disposition of the chunk.
*/
-static sctp_ierror_t sctp_sf_authenticate(struct net *net,
- const struct sctp_endpoint *ep,
+static sctp_ierror_t sctp_sf_authenticate(
const struct sctp_association *asoc,
- const sctp_subtype_t type,
struct sctp_chunk *chunk)
{
struct sctp_authhdr *auth_hdr;
@@ -4083,7 +4092,7 @@
commands);
auth_hdr = (struct sctp_authhdr *)chunk->skb->data;
- error = sctp_sf_authenticate(net, ep, asoc, type, chunk);
+ error = sctp_sf_authenticate(asoc, chunk);
switch (error) {
case SCTP_IERROR_AUTH_BAD_HMAC:
/* Generate the ERROR chunk and discard the rest
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index fd5b9d5..78f3805 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -335,11 +335,14 @@
if (!opt->pf->af_supported(addr->sa.sa_family, opt))
return NULL;
- /* V4 mapped address are really of AF_INET family */
- if (addr->sa.sa_family == AF_INET6 &&
- ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
- !opt->pf->af_supported(AF_INET, opt))
- return NULL;
+ if (addr->sa.sa_family == AF_INET6) {
+ if (len < SIN6_LEN_RFC2133)
+ return NULL;
+ /* V4 mapped address are really of AF_INET family */
+ if (ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
+ !opt->pf->af_supported(AF_INET, opt))
+ return NULL;
+ }
/* If we get this far, af is valid. */
af = sctp_get_af_specific(addr->sa.sa_family);
@@ -1519,7 +1522,7 @@
pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout);
- lock_sock(sk);
+ lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
sk->sk_shutdown = SHUTDOWN_MASK;
sk->sk_state = SCTP_SS_CLOSING;
@@ -1569,7 +1572,7 @@
* held and that should be grabbed before socket lock.
*/
spin_lock_bh(&net->sctp.addr_wq_lock);
- bh_lock_sock(sk);
+ bh_lock_sock_nested(sk);
/* Hold the sock, since sk_common_release() will put sock_put()
* and we have just a little more cleanup.
@@ -4765,7 +4768,7 @@
len = sizeof(int);
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
+ if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
return -EFAULT;
return 0;
}
@@ -5342,6 +5345,9 @@
err = -EFAULT;
goto out;
}
+ /* XXX: We should have accounted for sizeof(struct sctp_getaddrs) too,
+ * but we can't change it anymore.
+ */
if (put_user(bytes_copied, optlen))
err = -EFAULT;
out:
@@ -5778,7 +5784,7 @@
params.assoc_id = 0;
} else if (len >= sizeof(struct sctp_assoc_value)) {
len = sizeof(struct sctp_assoc_value);
- if (copy_from_user(¶ms, optval, sizeof(params)))
+ if (copy_from_user(¶ms, optval, len))
return -EFAULT;
} else
return -EINVAL;
@@ -5947,7 +5953,9 @@
if (len < sizeof(struct sctp_authkeyid))
return -EINVAL;
- if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
+
+ len = sizeof(struct sctp_authkeyid);
+ if (copy_from_user(&val, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, val.scact_assoc_id);
@@ -5959,7 +5967,6 @@
else
val.scact_keynumber = ep->active_key_id;
- len = sizeof(struct sctp_authkeyid);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
@@ -5985,7 +5992,7 @@
if (len < sizeof(struct sctp_authchunks))
return -EINVAL;
- if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+ if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
to = p->gauth_chunks;
@@ -6030,7 +6037,7 @@
if (len < sizeof(struct sctp_authchunks))
return -EINVAL;
- if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+ if (copy_from_user(&val, optval, sizeof(val)))
return -EFAULT;
to = p->gauth_chunks;
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index bea0005..6825e05 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -723,7 +723,6 @@
return event;
fail_mark:
- sctp_chunk_put(chunk);
kfree_skb(skb);
fail:
return NULL;
diff --git a/net/strparser/strparser.c b/net/strparser/strparser.c
index b5c279b..bbee334 100644
--- a/net/strparser/strparser.c
+++ b/net/strparser/strparser.c
@@ -59,7 +59,7 @@
strp->rx_stopped = 1;
/* Report an error on the lower socket */
- csk->sk_err = err;
+ csk->sk_err = -err;
csk->sk_error_report(csk);
}
@@ -285,9 +285,9 @@
strp_start_rx_timer(strp);
}
+ rxm->accum_len += cand_len;
strp->rx_need_bytes = rxm->strp.full_len -
rxm->accum_len;
- rxm->accum_len += cand_len;
rxm->early_eaten = cand_len;
STRP_STATS_ADD(strp->stats.rx_bytes, cand_len);
desc->count = 0; /* Stop reading socket */
@@ -310,6 +310,7 @@
/* Hurray, we have a new message! */
del_timer(&strp->rx_msg_timer);
strp->rx_skb_head = NULL;
+ strp->rx_need_bytes = 0;
STRP_STATS_INCR(strp->stats.rx_msgs);
/* Give skb to upper layer */
@@ -374,9 +375,7 @@
return;
if (strp->rx_need_bytes) {
- if (strp_peek_len(strp) >= strp->rx_need_bytes)
- strp->rx_need_bytes = 0;
- else
+ if (strp_peek_len(strp) < strp->rx_need_bytes)
return;
}
@@ -422,7 +421,7 @@
/* Message assembly timed out */
STRP_STATS_INCR(strp->stats.rx_msg_timeouts);
lock_sock(strp->sk);
- strp->cb.abort_parser(strp, ETIMEDOUT);
+ strp->cb.abort_parser(strp, -ETIMEDOUT);
release_sock(strp->sk);
}
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index 79aec90..4afd414 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -237,9 +237,6 @@
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
- err = crypto_ahash_init(req);
- if (err)
- goto out;
err = crypto_ahash_setkey(hmac_md5, cksumkey, kctx->gk5e->keylength);
if (err)
goto out;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 61a504f..34f9405 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -1375,6 +1375,7 @@
struct dentry *clnt_dir = pipe_dentry->d_parent;
struct dentry *gssd_dir = clnt_dir->d_parent;
+ dget(pipe_dentry);
__rpc_rmpipe(d_inode(clnt_dir), pipe_dentry);
__rpc_depopulate(clnt_dir, gssd_dummy_info_file, 0, 1);
__rpc_depopulate(gssd_dir, gssd_dummy_clnt_dir, 0, 1);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 272c345..b43818c 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -50,7 +50,7 @@
static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */
static int
-param_set_pool_mode(const char *val, struct kernel_param *kp)
+param_set_pool_mode(const char *val, const struct kernel_param *kp)
{
int *ip = (int *)kp->arg;
struct svc_pool_map *m = &svc_pool_map;
@@ -80,7 +80,7 @@
}
static int
-param_get_pool_mode(char *buf, struct kernel_param *kp)
+param_get_pool_mode(char *buf, const struct kernel_param *kp)
{
int *ip = (int *)kp->arg;
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index 69502fa..1a2b1f6 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -1054,6 +1054,7 @@
rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
{
cancel_delayed_work_sync(&buf->rb_recovery_worker);
+ cancel_delayed_work_sync(&buf->rb_refresh_worker);
while (!list_empty(&buf->rb_recv_bufs)) {
struct rpcrdma_rep *rep;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index d24d14e..1bf9153 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -2384,7 +2384,12 @@
case -EHOSTUNREACH:
case -EADDRINUSE:
case -ENOBUFS:
- /* retry with existing socket, after a delay */
+ /*
+ * xs_tcp_force_close() wakes tasks with -EIO.
+ * We need to wake them first to ensure the
+ * correct error code.
+ */
+ xprt_wake_pending_tasks(xprt, status);
xs_tcp_force_close(xprt);
goto out;
}
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 52d7476..ca68db2 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -322,6 +322,7 @@
if (res) {
pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
name, -res);
+ kfree(b);
return -EINVAL;
}
@@ -345,8 +346,10 @@
if (skb)
tipc_bearer_xmit_skb(net, bearer_id, skb, &b->bcast_addr);
- if (tipc_mon_create(net, bearer_id))
+ if (tipc_mon_create(net, bearer_id)) {
+ bearer_disable(net, b);
return -ENOMEM;
+ }
pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
name,
diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c
index 9e109bb..0fcfb39 100644
--- a/net/tipc/monitor.c
+++ b/net/tipc/monitor.c
@@ -633,9 +633,13 @@
{
struct tipc_net *tn = tipc_net(net);
struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
- struct tipc_peer *self = get_self(net, bearer_id);
+ struct tipc_peer *self;
struct tipc_peer *peer, *tmp;
+ if (!mon)
+ return;
+
+ self = get_self(net, bearer_id);
write_lock_bh(&mon->lock);
tn->monitors[bearer_id] = NULL;
list_for_each_entry_safe(peer, tmp, &self->list, list) {
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 3200059..9ba3c46 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -79,7 +79,8 @@
const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = {
[TIPC_NLA_NET_UNSPEC] = { .type = NLA_UNSPEC },
- [TIPC_NLA_NET_ID] = { .type = NLA_U32 }
+ [TIPC_NLA_NET_ID] = { .type = NLA_U32 },
+ [TIPC_NLA_NET_ADDR] = { .type = NLA_U32 },
};
const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 5b3e1ea..db8fbc0 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -2094,6 +2094,8 @@
int err;
msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg.skb)
+ return -ENOMEM;
msg.portid = info->snd_portid;
msg.seq = info->snd_seq;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 3181b07..c88874f 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -95,6 +95,9 @@
ASSERT_RTNL();
+ if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN)
+ return -EINVAL;
+
/* prohibit calling the thing phy%d when %d is not its number */
sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index eb4cc00..f900f5c 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -37,9 +37,10 @@
country AL: DFS-ETSI
(2402 - 2482 @ 40), (20)
- (5150 - 5250 @ 80), (23), AUTO-BW
- (5250 - 5350 @ 80), (23), DFS, AUTO-BW
- (5470 - 5710 @ 160), (30), DFS
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country AM: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -50,7 +51,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country AR:
(2402 - 2482 @ 40), (36)
@@ -73,16 +75,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -111,7 +105,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country BB: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -127,16 +122,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -151,16 +138,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -249,16 +228,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -311,16 +282,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -330,16 +293,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -353,16 +308,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -370,16 +317,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -414,16 +353,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -436,16 +367,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -459,16 +382,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -483,16 +398,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -500,16 +407,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -529,7 +428,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country GH: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -560,16 +460,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -613,16 +505,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -637,16 +521,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -659,16 +535,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -694,16 +562,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -711,16 +571,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -810,16 +662,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -840,16 +684,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -857,16 +693,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -874,16 +702,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -898,19 +718,22 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country MD: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country ME: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country MF: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -929,7 +752,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country MN: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -956,7 +780,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country MR: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -968,25 +793,17 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
-country MU: DFS-FCC
+country MU: DFS-ETSI
(2402 - 2482 @ 40), (20)
- (5170 - 5250 @ 80), (24), AUTO-BW
- (5250 - 5330 @ 80), (24), DFS, AUTO-BW
- (5490 - 5730 @ 160), (24), DFS
- (5735 - 5835 @ 80), (30)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country MV: DFS-ETSI
(2402 - 2482 @ 40), (20)
@@ -1043,16 +860,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1060,16 +869,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1110,7 +911,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country PG: DFS-FCC
(2402 - 2482 @ 40), (20)
@@ -1136,16 +938,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1153,7 +947,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country PR: DFS-FCC
(2402 - 2472 @ 40), (30)
@@ -1173,16 +968,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1219,16 +1006,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1238,7 +1017,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1267,16 +1047,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1293,16 +1065,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1310,16 +1074,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
- # 5.9ghz band
- # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
# 60 gHz band channels 1-4, ref: Etsi En 302 567
(57000 - 66000 @ 2160), (40)
@@ -1378,7 +1134,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country TT:
(2402 - 2482 @ 40), (20)
@@ -1430,18 +1187,6 @@
(5250 - 5330 @ 80), (24), DFS, AUTO-BW
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
- # 5.9ghz band
- # reference: https://apps.fcc.gov/edocs_public/attachmatch/FCC-03-324A1.pdf
- (5842 - 5863 @ 5), (30)
- (5850 - 5870 @ 10), (30)
- (5860 - 5880 @ 10), (30)
- (5865 - 5885 @ 20), (30)
- (5870 - 5890 @ 10), (30)
- (5880 - 5900 @ 10), (30)
- (5890 - 5910 @ 10), (30)
- (5895 - 5915 @ 20), (30)
- (5900 - 5920 @ 10), (30)
- (5910 - 5930 @ 10), (30)
# 60g band
# reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
# channels 1,2,3,4,5,6 EIRP=40dBm(43dBm peak)
@@ -1464,7 +1209,8 @@
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5490 - 5710 @ 160), (30), DFS
+ (5490 - 5730 @ 160), (30), DFS
+ (5735 - 5875 @ 80), (14)
country VE: DFS-FCC
(2402 - 2482 @ 40), (20)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index febd5ab..f00e7d3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4133,7 +4133,7 @@
struct nlattr *rate;
u32 bitrate;
u16 bitrate_compat;
- enum nl80211_attrs rate_flg;
+ enum nl80211_rate_info rate_flg;
rate = nla_nest_start(msg, attr);
if (!rate)
@@ -11058,7 +11058,8 @@
break;
case NL80211_NAN_FUNC_FOLLOW_UP:
if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] ||
- !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]) {
+ !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] ||
+ !tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]) {
err = -EINVAL;
goto out;
}
@@ -14867,7 +14868,8 @@
if (!ft_event->target_ap)
return;
- msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
+ msg = nlmsg_new(100 + ft_event->ies_len + ft_event->ric_ies_len,
+ GFP_KERNEL);
if (!msg)
return;
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index bc0ebd4..9195f23 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2142,6 +2142,21 @@
reg_free_request(reg_request);
}
+static void notify_self_managed_wiphys(struct regulatory_request *request)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy;
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ wiphy = &rdev->wiphy;
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ request->initiator == NL80211_REGDOM_SET_BY_USER &&
+ request->user_reg_hint_type ==
+ NL80211_USER_REG_HINT_CELL_BASE)
+ reg_call_notifier(wiphy, request);
+ }
+}
+
static bool reg_only_self_managed_wiphys(void)
{
struct cfg80211_registered_device *rdev;
@@ -2193,6 +2208,7 @@
spin_unlock(®_requests_lock);
+ notify_self_managed_wiphys(reg_request);
if (reg_only_self_managed_wiphys()) {
reg_free_request(reg_request);
return;
@@ -3073,17 +3089,26 @@
void wiphy_regulatory_register(struct wiphy *wiphy)
{
- struct regulatory_request *lr;
+ struct regulatory_request *lr = get_last_request();
- /* self-managed devices ignore external hints */
- if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ /* self-managed devices ignore beacon hints and country IE */
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
REGULATORY_COUNTRY_IE_IGNORE;
+ /*
+ * The last request may have been received before this
+ * registration call. Call the driver notifier if
+ * initiator is USER and user type is CELL_BASE.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE)
+ reg_call_notifier(wiphy, lr);
+ }
+
if (!reg_dev_ignore_cell_hint(wiphy))
reg_num_devs_support_basehint++;
- lr = get_last_request();
wiphy_update_regulatory(wiphy, lr->initiator);
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 2d64e0f..0dc41fd 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1012,6 +1012,8 @@
wdev->current_bss = NULL;
wdev->ssid_len = 0;
wdev->conn_owner_nlportid = 0;
+ kzfree(wdev->connect_keys);
+ wdev->connect_keys = NULL;
nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 29c5661..13ff407 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -549,7 +549,7 @@
int offset, int len)
{
struct skb_shared_info *sh = skb_shinfo(skb);
- const skb_frag_t *frag = &sh->frags[-1];
+ const skb_frag_t *frag = &sh->frags[0];
struct page *frag_page;
void *frag_ptr;
int frag_len, frag_size;
@@ -562,10 +562,10 @@
while (offset >= frag_size) {
offset -= frag_size;
- frag++;
frag_page = skb_frag_page(frag);
frag_ptr = skb_frag_address(frag);
frag_size = skb_frag_size(frag);
+ frag++;
}
frag_ptr += offset;
@@ -577,12 +577,12 @@
len -= cur_len;
while (len > 0) {
- frag++;
frag_len = skb_frag_size(frag);
cur_len = min(len, frag_len);
__frame_add_frag(frame, skb_frag_page(frag),
skb_frag_address(frag), cur_len, frag_len);
len -= cur_len;
+ frag++;
}
}
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index f83b74d..0077216 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1790,32 +1790,40 @@
static int __init x25_init(void)
{
- int rc = proto_register(&x25_proto, 0);
+ int rc;
- if (rc != 0)
+ rc = proto_register(&x25_proto, 0);
+ if (rc)
goto out;
rc = sock_register(&x25_family_ops);
- if (rc != 0)
+ if (rc)
goto out_proto;
dev_add_pack(&x25_packet_type);
rc = register_netdevice_notifier(&x25_dev_notifier);
- if (rc != 0)
+ if (rc)
goto out_sock;
+ rc = x25_register_sysctl();
+ if (rc)
+ goto out_dev;
+
+ rc = x25_proc_init();
+ if (rc)
+ goto out_sysctl;
+
pr_info("Linux Version 0.2\n");
- x25_register_sysctl();
- rc = x25_proc_init();
- if (rc != 0)
- goto out_dev;
out:
return rc;
+out_sysctl:
+ x25_unregister_sysctl();
out_dev:
unregister_netdevice_notifier(&x25_dev_notifier);
out_sock:
+ dev_remove_pack(&x25_packet_type);
sock_unregister(AF_X25);
out_proto:
proto_unregister(&x25_proto);
diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c
index 4323952..703d46a 100644
--- a/net/x25/sysctl_net_x25.c
+++ b/net/x25/sysctl_net_x25.c
@@ -73,9 +73,12 @@
{ 0, },
};
-void __init x25_register_sysctl(void)
+int __init x25_register_sysctl(void)
{
x25_table_header = register_net_sysctl(&init_net, "net/x25", x25_table);
+ if (!x25_table_header)
+ return -ENOMEM;
+ return 0;
}
void x25_unregister_sysctl(void)
diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
index ccfdc71..a00ec71 100644
--- a/net/xfrm/xfrm_ipcomp.c
+++ b/net/xfrm/xfrm_ipcomp.c
@@ -283,7 +283,7 @@
struct crypto_comp *tfm;
/* This can be any valid CPU ID so we don't need locking. */
- tfm = __this_cpu_read(*pos->tfms);
+ tfm = this_cpu_read(*pos->tfms);
if (!strcmp(crypto_comp_name(tfm), alg_name)) {
pos->users++;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 74f2e8f..3fbe584 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1197,6 +1197,7 @@
if (orig->aead) {
x->aead = xfrm_algo_aead_clone(orig->aead);
+ x->geniv = orig->geniv;
if (!x->aead)
goto error;
}
@@ -1246,6 +1247,8 @@
x->curlft.add_time = orig->curlft.add_time;
x->km.state = orig->km.state;
x->km.seq = orig->km.seq;
+ x->replay = orig->replay;
+ x->preplay = orig->preplay;
return x;
@@ -1883,6 +1886,11 @@
struct xfrm_mgr *km;
struct xfrm_policy *pol = NULL;
+#ifdef CONFIG_COMPAT
+ if (in_compat_syscall())
+ return -EOPNOTSUPP;
+#endif
+
if (!optval && !optlen) {
xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 3b3455a..35e60d3 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -121,22 +121,17 @@
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
struct xfrm_replay_state_esn *rs;
- if (p->flags & XFRM_STATE_ESN) {
- if (!rt)
- return -EINVAL;
-
- rs = nla_data(rt);
-
- if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
- return -EINVAL;
-
- if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
- nla_len(rt) != sizeof(*rs))
- return -EINVAL;
- }
-
if (!rt)
- return 0;
+ return (p->flags & XFRM_STATE_ESN) ? -EINVAL : 0;
+
+ rs = nla_data(rt);
+
+ if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
+ return -EINVAL;
+
+ if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
+ nla_len(rt) != sizeof(*rs))
+ return -EINVAL;
/* As only ESP and AH support ESN feature. */
if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH))
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 140a0fa2..0f101f7 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -147,6 +147,37 @@
# Expands to either gcc or clang
cc-name = $(shell $(CC) -v 2>&1 | grep -q "clang version" && echo clang || echo gcc)
+# __cc-version
+# Returns compiler version
+__cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/$(cc-name)-version.sh $(CC))
+
+# __cc-fullversion
+# Returns full compiler version
+__cc-fullversion = $(shell $(CONFIG_SHELL) \
+ $(srctree)/scripts/$(cc-name)-version.sh -p $(CC))
+
+# __cc-ifversion
+# Matches compiler name and version
+# Usage: EXTRA_CFLAGS += $(call cc-if-name-version, gcc, -lt, 0402, -O1)
+__cc-ifversion = $(shell [ $(cc-name) = $(1) ] && [ $(__cc-version) $(2) $(3) ] && echo $(4) || echo $(5))
+
+# __cc-if-fullversion
+# Matches compiler name and full version
+# Usage: EXTRA_CFLAGS += $(call cc-if-name-fullversion, gcc, -lt, 040502, -O1)
+__cc-if-fullversion = $(shell [ $(cc-name) = $(1) ] && [ $(__cc-fullversion) $(2) $(3) ] && echo $(4) || echo $(5))
+
+# gcc-ifversion
+gcc-ifversion = $(call __cc-ifversion, gcc, $(1), $(2), $(3), $(4))
+
+# gcc-if-fullversion
+gcc-if-fullversion = (call __cc-if-fullversion, gcc, $(1), $(2), $(3), $(4))
+
+# clang-ifversion
+clang-ifversion = $(call __cc-ifversion, clang, $(1), $(2), $(3), $(4))
+
+# clang-if-fullversion
+clang-if-fullversion = (call __cc-if-fullversion, clang, $(1), $(2), $(3), $(4))
+
# cc-version
cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC))
@@ -154,9 +185,9 @@
cc-fullversion = $(shell $(CONFIG_SHELL) \
$(srctree)/scripts/gcc-version.sh -p $(CC))
-# cc-ifversion
-# Usage: EXTRA_CFLAGS += $(call cc-ifversion, -lt, 0402, -O1)
-cc-ifversion = $(shell [ $(cc-version) $(1) $(2) ] && echo $(3) || echo $(4))
+# backward compatibility
+cc-ifversion = $(gcc-ifversion)
+cc-if-fullversion = $(gcc-if-fullversion)
# cc-ldoption
# Usage: ldflags += $(call cc-ldoption, -Wl$(comma)--hash-style=both)
@@ -173,6 +204,10 @@
# Important: no spaces around options
ar-option = $(call try-run, $(AR) rc$(1) "$$TMP",$(1),$(2))
+# ld-name
+# Expands to either bfd or gold
+ld-name = $(shell $(LD) -v 2>&1 | grep -q "GNU gold" && echo gold || echo bfd)
+
# ld-version
# Note this is mainly for HJ Lu's 3 number binutil versions
ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh)
@@ -181,6 +216,18 @@
# Usage: $(call ld-ifversion, -ge, 22252, y)
ld-ifversion = $(shell [ $(ld-version) $(1) $(2) ] && echo $(3) || echo $(4))
+# __ld-ifversion
+# Usage: $(call __ld-ifversion, gold, -ge, 112000000, y)
+__ld-ifversion = $(shell [ $(ld-name) = $(1) ] && [ $(ld-version) $(2) $(3) ] && echo $(4) || echo $(5))
+
+# bfd-ifversion
+# Usage: $(call bfd-ifversion, -ge, 227000000, y)
+bfd-ifversion = $(call __ld-ifversion, bfd, $(1), $(2), $(3), $(4))
+
+# gold-ifversion
+# Usage: $(call gold-ifversion, -ge, 112000000, y)
+gold-ifversion = $(call __ld-ifversion, gold, $(1), $(2), $(3), $(4))
+
######
###
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 3ecec72..a9c1da5 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -213,6 +213,23 @@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
+ifdef CONFIG_LTO_CLANG
+# Generate .o.symversions files for each .o with exported symbols, and link these
+# to the kernel and/or modules at the end.
+cmd_modversions_c = \
+ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) >/dev/null 2>/dev/null; then \
+ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
+ $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
+ > $(@D)/$(@F).symversions; \
+ fi; \
+ else \
+ if $(LLVM_DIS) -o=- $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
+ $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
+ > $(@D)/$(@F).symversions; \
+ fi; \
+ fi; \
+ mv -f $(@D)/.tmp_$(@F) $@;
+else
cmd_modversions_c = \
if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
$(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
@@ -225,12 +242,19 @@
mv -f $(@D)/.tmp_$(@F) $@; \
fi;
endif
+endif
ifdef CONFIG_FTRACE_MCOUNT_RECORD
ifdef BUILD_C_RECORDMCOUNT
ifeq ("$(origin RECORDMCOUNT_WARN)", "command line")
RECORDMCOUNT_FLAGS = -w
endif
+
+ifdef CONFIG_LTO_CLANG
+# With LTO, we postpone running recordmcount until after the LTO link step, so
+# let's export the parameters for the link script.
+export RECORDMCOUNT_FLAGS
+else
# Due to recursion, we must skip empty.o.
# The empty.o file is created in the make process in order to determine
# the target endianness and word size. It is made before all other C
@@ -239,23 +263,29 @@
if [ $(@) != "scripts/mod/empty.o" ]; then \
$(objtree)/scripts/recordmcount $(RECORDMCOUNT_FLAGS) "$(@)"; \
fi;
+endif
+
recordmcount_source := $(srctree)/scripts/recordmcount.c \
$(srctree)/scripts/recordmcount.h
-else
+else # !BUILD_C_RECORDMCOUNT
sub_cmd_record_mcount = set -e ; perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \
"$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
"$(if $(CONFIG_64BIT),64,32)" \
"$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CFLAGS)" \
"$(LD)" "$(NM)" "$(RM)" "$(MV)" \
"$(if $(part-of-module),1,0)" "$(@)";
+
recordmcount_source := $(srctree)/scripts/recordmcount.pl
-endif
+endif # BUILD_C_RECORDMCOUNT
+
+ifndef CONFIG_LTO_CLANG
cmd_record_mcount = \
if [ "$(findstring $(CC_FLAGS_FTRACE),$(_c_flags))" = \
"$(CC_FLAGS_FTRACE)" ]; then \
$(sub_cmd_record_mcount) \
fi;
endif
+endif # CONFIG_FTRACE_MCOUNT_RECORD
ifdef CONFIG_STACK_VALIDATION
ifneq ($(SKIP_STACK_VALIDATION),1)
@@ -439,9 +469,30 @@
#
ifdef builtin-target
+ifdef CONFIG_LTO_CLANG
+ ifdef CONFIG_MODVERSIONS
+ # combine symversions for later processing
+ update_lto_symversions = \
+ rm -f $@.symversions; \
+ for i in $(filter-out FORCE,$^); do \
+ if [ -f $$i.symversions ]; then \
+ cat $$i.symversions \
+ >> $@.symversions; \
+ fi; \
+ done;
+ endif
+ # rebuild the symbol table with llvm-ar to include IR files
+ update_lto_symtable = ; \
+ mv -f $@ $@.tmp; \
+ $(LLVM_AR) rcsT$(KBUILD_ARFLAGS) $@ \
+ $$($(AR) t $@.tmp); \
+ rm -f $@.tmp
+endif
+
ifdef CONFIG_THIN_ARCHIVES
- cmd_make_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
- cmd_make_empty_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
+ cmd_make_builtin = $(update_lto_symversions) \
+ rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS)
+ cmd_make_empty_builtin = rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS)
quiet_cmd_link_o_target = AR $@
else
cmd_make_builtin = $(LD) $(ld_flags) -r -o
@@ -481,7 +532,11 @@
quiet_cmd_link_l_target = AR $@
ifdef CONFIG_THIN_ARCHIVES
- cmd_link_l_target = rm -f $@; $(AR) rcsT$(KBUILD_ARFLAGS) $@ $(lib-y)
+ cmd_link_l_target = \
+ $(update_lto_symversions) \
+ rm -f $@; \
+ $(AR) rcsTP$(KBUILD_ARFLAGS) $@ $(lib-y) \
+ $(update_lto_symtable)
else
cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
endif
@@ -499,14 +554,36 @@
ref_prefix = EXTERN(
endif
-quiet_cmd_export_list = EXPORTS $@
-cmd_export_list = $(OBJDUMP) -h $< | \
- sed -ne '/___ksymtab/{s/.*+/$(ref_prefix)/;s/ .*/)/;p}' >$(ksyms-lds);\
- rm -f $(dummy-object);\
- $(AR) rcs$(KBUILD_ARFLAGS) $(dummy-object);\
+filter_export_list = sed -ne '/___ksymtab/s/.*+\([^ "]*\).*/$(ref_prefix)\1)/p'
+link_export_list = rm -f $(dummy-object);\
+ echo | $(CC) $(a_flags) -c -o $(dummy-object) -x assembler -;\
$(LD) $(ld_flags) -r -o $@ -T $(ksyms-lds) $(dummy-object);\
rm $(dummy-object) $(ksyms-lds)
+quiet_cmd_export_list = EXPORTS $@
+
+ifdef CONFIG_LTO_CLANG
+# objdump doesn't understand IR files and llvm-dis doesn't support archives,
+# so we'll walk through each file in the archive separately
+cmd_export_list = \
+ rm -f $(ksyms-lds); \
+ for o in $$($(AR) t $<); do \
+ if $(OBJDUMP) -h $$o >/dev/null 2>/dev/null; then \
+ $(OBJDUMP) -h $$o | \
+ $(filter_export_list) \
+ >>$(ksyms-lds); \
+ else \
+ $(LLVM_DIS) -o=- $$o | \
+ $(filter_export_list) \
+ >>$(ksyms-lds); \
+ fi; \
+ done; \
+ $(link_export_list)
+else
+cmd_export_list = $(OBJDUMP) -h $< | $(filter_export_list) >$(ksyms-lds); \
+ $(link_export_list)
+endif
+
$(obj)/lib-ksyms.o: $(lib-target) FORCE
$(call if_changed,export_list)
@@ -530,20 +607,36 @@
$($(subst $(obj)/,,$(@:.o=-y))) \
$($(subst $(obj)/,,$(@:.o=-m)))), $^)
-quiet_cmd_link_multi-y = LD $@
-cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
+cmd_link_multi-link = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
+
+ifdef CONFIG_THIN_ARCHIVES
+ quiet_cmd_link_multi-y = AR $@
+ cmd_link_multi-y = $(update_lto_symversions) \
+ rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(link_multi_deps) \
+ $(update_lto_symtable)
+else
+ quiet_cmd_link_multi-y = LD $@
+ cmd_link_multi-y = $(cmd_link_multi-link)
+endif
quiet_cmd_link_multi-m = LD [M] $@
-cmd_link_multi-m = $(cmd_link_multi-y)
+
+ifdef CONFIG_LTO_CLANG
+ # don't compile IR until needed
+ cmd_link_multi-m = $(cmd_link_multi-y)
+else
+ cmd_link_multi-m = $(cmd_link_multi-link)
+endif
$(multi-used-y): FORCE
$(call if_changed,link_multi-y)
-$(call multi_depend, $(multi-used-y), .o, -objs -y)
$(multi-used-m): FORCE
$(call if_changed,link_multi-m)
@{ echo $(@:.o=.ko); echo $(link_multi_deps); \
$(cmd_undef_syms); } > $(MODVERDIR)/$(@F:.o=.mod)
+
+$(call multi_depend, $(multi-used-y), .o, -objs -y)
$(call multi_depend, $(multi-used-m), .o, -objs -y -m)
targets += $(multi-used-y) $(multi-used-m)
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 37323b0..8c69cd1 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -9,10 +9,7 @@
CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address
-CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \
- -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET) \
- --param asan-stack=1 --param asan-globals=1 \
- --param asan-instrumentation-with-call-threshold=$(call_threshold))
+cc-param = $(call cc-option, -mllvm -$(1), $(call cc-option, --param $(1)))
ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),)
ifneq ($(CONFIG_COMPILE_TEST),y)
@@ -20,12 +17,23 @@
-fsanitize=kernel-address is not supported by compiler)
endif
else
- ifeq ($(CFLAGS_KASAN),)
- ifneq ($(CONFIG_COMPILE_TEST),y)
- $(warning CONFIG_KASAN: compiler does not support all options.\
- Trying minimal configuration)
- endif
- CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
- endif
+ # -fasan-shadow-offset fails without -fsanitize
+ CFLAGS_KASAN_SHADOW := $(call cc-option, -fsanitize=kernel-address \
+ -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET), \
+ $(call cc-option, -fsanitize=kernel-address \
+ -mllvm -asan-mapping-offset=$(KASAN_SHADOW_OFFSET)))
+
+ ifeq ($(strip $(CFLAGS_KASAN_SHADOW)),)
+ CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
+ else
+ # Now add all the compiler specific options that are valid standalone
+ CFLAGS_KASAN := $(CFLAGS_KASAN_SHADOW) \
+ $(call cc-param,asan-globals=1) \
+ $(call cc-param,asan-instrumentation-with-call-threshold=$(call_threshold)) \
+ $(call cc-param,asan-stack=1) \
+ $(call cc-param,asan-use-after-scope=1) \
+ $(call cc-param,asan-instrument-allocas=1)
+ endif
+
endif
endif
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 6bc33ee..ea98308 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -290,11 +290,11 @@
echo '\#include <asm-generic/vmlinux.lds.h>'; \
echo '.section .dtb.init.rodata,"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
- echo '.global __dtb_$(*F)_begin'; \
- echo '__dtb_$(*F)_begin:'; \
+ echo '.global __dtb_$(subst -,_,$(*F))_begin'; \
+ echo '__dtb_$(subst -,_,$(*F))_begin:'; \
echo '.incbin "$<" '; \
- echo '__dtb_$(*F)_end:'; \
- echo '.global __dtb_$(*F)_end'; \
+ echo '__dtb_$(subst -,_,$(*F))_end:'; \
+ echo '.global __dtb_$(subst -,_,$(*F))_end'; \
echo '.balign STRUCT_ALIGNMENT'; \
) > $@
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 16923ba..b7c50cb 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -82,12 +82,28 @@
MODPOST_OPT=$(subst -i,-n,$(filter -i,$(MAKEFLAGS)))
+# If CONFIG_LTO_CLANG is enabled, .o files are either LLVM IR, or empty, so we
+# need to link them into actual objects before passing them to modpost
+modpost-ext = $(if $(CONFIG_LTO_CLANG),.lto,)
+
+ifdef CONFIG_LTO_CLANG
+quiet_cmd_cc_lto_link_modules = LD [M] $@
+cmd_cc_lto_link_modules = \
+ $(LD) $(ld_flags) -r -o $(@) \
+ $(shell [ -s $(@:$(modpost-ext).o=.o.symversions) ] && \
+ echo -T $(@:$(modpost-ext).o=.o.symversions)) \
+ --whole-archive $(filter-out FORCE,$^)
+
+$(modules:.ko=$(modpost-ext).o): %$(modpost-ext).o: %.o FORCE
+ $(call if_changed,cc_lto_link_modules)
+endif
+
# We can go over command line length here, so be careful.
quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules
- cmd_modpost = $(MODLISTCMD) | sed 's/\.ko$$/.o/' | $(modpost) $(MODPOST_OPT) -s -T -
+ cmd_modpost = $(MODLISTCMD) | sed 's/\.ko$$/$(modpost-ext)\.o/' | $(modpost) $(MODPOST_OPT) -s -T -
PHONY += __modpost
-__modpost: $(modules:.ko=.o) FORCE
+__modpost: $(modules:.ko=$(modpost-ext).o) FORCE
$(call cmd,modpost) $(wildcard vmlinux)
quiet_cmd_kernel-mod = MODPOST $@
@@ -98,8 +114,7 @@
# Declare generated files as targets for modpost
$(symverfile): __modpost ;
-$(modules:.ko=.mod.c): __modpost ;
-
+$(modules:.ko=$(modpost-ext).mod.c): __modpost ;
# Step 5), compile all *.mod.c files
@@ -110,22 +125,37 @@
cmd_cc_o_c = $(CC) $(c_flags) $(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE) \
-c -o $@ $<
-$(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
+$(modules:.ko=.mod.o): %.mod.o: %$(modpost-ext).mod.c FORCE
$(call if_changed_dep,cc_o_c)
-targets += $(modules:.ko=.mod.o)
+targets += $(modules:.ko=$(modpost-ext).mod.o)
ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
# Step 6), final link of the modules with optional arch pass after final link
quiet_cmd_ld_ko_o = LD [M] $@
+
+ifdef CONFIG_LTO_CLANG
+ cmd_ld_ko_o = \
+ $(LD) -r $(LDFLAGS) \
+ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
+ $(shell [ -s $(@:.ko=.o.symversions) ] && \
+ echo -T $(@:.ko=.o.symversions)) \
+ -o $@ --whole-archive \
+ $(filter-out FORCE,$(^:$(modpost-ext).o=.o))
+
+ ifdef CONFIG_FTRACE_MCOUNT_RECORD
+ cmd_ld_ko_o += ; $(objtree)/scripts/recordmcount $(RECORDMCOUNT_FLAGS) $@
+ endif
+else
cmd_ld_ko_o = \
$(LD) -r $(LDFLAGS) \
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-o $@ $(filter-out FORCE,$^) ; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
+endif
-$(modules): %.ko :%.o %.mod.o FORCE
+$(modules): %.ko: %$(modpost-ext).o %.mod.o FORCE
+$(call if_changed,ld_ko_o)
targets += $(modules)
diff --git a/scripts/build-all.py b/scripts/build-all.py
index bd468cd..4a60ebc 100755
--- a/scripts/build-all.py
+++ b/scripts/build-all.py
@@ -59,12 +59,8 @@
def check_kernel():
"""Ensure that PWD is a kernel directory"""
- have_defconfig = any([
- os.path.isfile('arch/arm64/configs/msm_defconfig'),
- os.path.isfile('arch/arm64/configs/sdm845_defconfig')])
-
- if not all([os.path.isfile('MAINTAINERS'), have_defconfig]):
- fail("This doesn't seem to be an MSM kernel dir")
+ if not os.path.isfile('MAINTAINERS'):
+ fail("This doesn't seem to be a kernel dir")
def check_build():
"""Ensure that the build directory is present."""
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index cdfa754..0534378 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3012,6 +3012,10 @@
} elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/) {
$msg_type = "";
+ # Long copyright statements are another special case
+ } elsif ($rawline =~ /^\+.\*.*copyright.*\(c\).*$/i) {
+ $msg_type = "";
+
# Otherwise set the alternate message types
# a comment starts before $max_line_length
diff --git a/scripts/clang-version.sh b/scripts/clang-version.sh
new file mode 100755
index 0000000..9780efa
--- /dev/null
+++ b/scripts/clang-version.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# clang-version [-p] clang-command
+#
+# Prints the compiler version of `clang-command' in a canonical 4-digit form
+# such as `0500' for clang-5.0 etc.
+#
+# With the -p option, prints the patchlevel as well, for example `050001' for
+# clang-5.0.1 etc.
+#
+
+if [ "$1" = "-p" ] ; then
+ with_patchlevel=1;
+ shift;
+fi
+
+compiler="$*"
+
+if [ ${#compiler} -eq 0 ]; then
+ echo "Error: No compiler specified."
+ printf "Usage:\n\t$0 <clang-command>\n"
+ exit 1
+fi
+
+MAJOR=$(echo __clang_major__ | $compiler -E -x c - | tail -n 1)
+MINOR=$(echo __clang_minor__ | $compiler -E -x c - | tail -n 1)
+if [ "x$with_patchlevel" != "x" ] ; then
+ PATCHLEVEL=$(echo __clang_patchlevel__ | $compiler -E -x c - | tail -n 1)
+ printf "%02d%02d%02d\\n" $MAJOR $MINOR $PATCHLEVEL
+else
+ printf "%02d%02d\\n" $MAJOR $MINOR
+fi
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 1b9e67b..7858b30 100644
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -53,9 +53,40 @@
${AR} rcsT${KBUILD_ARFLAGS} built-in.o \
${KBUILD_VMLINUX_INIT} \
${KBUILD_VMLINUX_MAIN}
+
+ if [ -n "${CONFIG_LTO_CLANG}" ]; then
+ mv -f built-in.o built-in.o.tmp
+ ${LLVM_AR} rcsT${KBUILD_ARFLAGS} built-in.o $(${AR} t built-in.o.tmp)
+ rm -f built-in.o.tmp
+ fi
fi
}
+# If CONFIG_LTO_CLANG is selected, collect generated symbol versions into
+# .tmp_symversions
+modversions()
+{
+ if [ -z "${CONFIG_LTO_CLANG}" ]; then
+ return
+ fi
+
+ if [ -z "${CONFIG_MODVERSIONS}" ]; then
+ return
+ fi
+
+ rm -f .tmp_symversions
+
+ for a in built-in.o ${KBUILD_VMLINUX_LIBS}; do
+ for o in $(${AR} t $a); do
+ if [ -f ${o}.symversions ]; then
+ cat ${o}.symversions >> .tmp_symversions
+ fi
+ done
+ done
+
+ echo "-T .tmp_symversions"
+}
+
# Link of vmlinux.o used for section mismatch analysis
# ${1} output file
modpost_link()
@@ -70,7 +101,29 @@
${KBUILD_VMLINUX_MAIN} \
--end-group"
fi
- ${LD} ${LDFLAGS} -r -o ${1} ${objects}
+
+ if [ -n "${CONFIG_LTO_CLANG}" ]; then
+ # This might take a while, so indicate that we're doing
+ # an LTO link
+ info LTO vmlinux.o
+ else
+ info LD vmlinux.o
+ fi
+
+ ${LD} ${LDFLAGS} -r -o ${1} $(modversions) ${objects}
+}
+
+# If CONFIG_LTO_CLANG is selected, we postpone running recordmcount until
+# we have compiled LLVM IR to an object file.
+recordmcount()
+{
+ if [ -z "${CONFIG_LTO_CLANG}" ]; then
+ return
+ fi
+
+ if [ -n "${CONFIG_FTRACE_MCOUNT_RECORD}" ]; then
+ scripts/recordmcount ${RECORDMCOUNT_FLAGS} $*
+ fi
}
# Link of vmlinux
@@ -82,7 +135,15 @@
local objects
if [ "${SRCARCH}" != "um" ]; then
- if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+ local ld=${LD}
+ local ldflags="${LDFLAGS} ${LDFLAGS_vmlinux}"
+
+ if [ -n "${LDFINAL_vmlinux}" ]; then
+ ld=${LDFINAL_vmlinux}
+ ldflags="${LDFLAGS_FINAL_vmlinux} ${LDFLAGS_vmlinux}"
+ fi
+
+ if [[ -n "${CONFIG_THIN_ARCHIVES}" && -z "${CONFIG_LTO_CLANG}" ]]; then
objects="--whole-archive built-in.o ${1}"
else
objects="${KBUILD_VMLINUX_INIT} \
@@ -92,8 +153,7 @@
${1}"
fi
- ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \
- -T ${lds} ${objects}
+ ${ld} ${ldflags} -o ${2} -T ${lds} ${objects}
else
if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
objects="-Wl,--whole-archive built-in.o ${1}"
@@ -113,7 +173,6 @@
fi
}
-
# Create ${2} .o file with all symbols from the ${1} object file
kallsyms()
{
@@ -183,6 +242,7 @@
rm -f .tmp_System.map
rm -f .tmp_kallsyms*
rm -f .tmp_version
+ rm -f .tmp_symversions
rm -f .tmp_vmlinux*
rm -f built-in.o
rm -f System.map
@@ -230,15 +290,6 @@
. "./${KCONFIG_CONFIG}"
esac
-archive_builtin
-
-#link vmlinux.o
-info LD vmlinux.o
-modpost_link vmlinux.o
-
-# modpost vmlinux.o to check for section mismatches
-${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o
-
# Update version
info GEN .version
if [ ! -r .version ]; then
@@ -249,9 +300,27 @@
expr 0$(cat .old_version) + 1 >.version;
fi;
+archive_builtin
+
+#link vmlinux.o
+modpost_link vmlinux.o
+
+# modpost vmlinux.o to check for section mismatches
+${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o
+
# final build of init/
${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init GCC_PLUGINS_CFLAGS="${GCC_PLUGINS_CFLAGS}"
+if [ -n "${CONFIG_LTO_CLANG}" ]; then
+ # Re-use vmlinux.o, so we can avoid the slow LTO link step in
+ # vmlinux_link
+ KBUILD_VMLINUX_INIT=
+ KBUILD_VMLINUX_MAIN=vmlinux.o
+
+ # Call recordmcount if needed
+ recordmcount vmlinux.o
+fi
+
# Generate RTIC MP placeholder compile unit of the correct size
# and add it to the list of link objects
# this needs to be done before generating kallsyms
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index b497d97..247cf1f 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -1,4 +1,5 @@
OBJECT_FILES_NON_STANDARD := y
+CFLAGS_empty.o += $(DISABLE_LTO)
hostprogs-y := modpost mk_elfconfig
always := $(hostprogs-y) empty.o
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
index 5423a58..e0c5082 100644
--- a/scripts/recordmcount.c
+++ b/scripts/recordmcount.c
@@ -366,7 +366,8 @@
strcmp(".softirqentry.text", txtname) == 0 ||
strcmp(".kprobes.text", txtname) == 0 ||
strcmp(".cpuidle.text", txtname) == 0 ||
- strcmp(".text.unlikely", txtname) == 0;
+ (strncmp(".text.", txtname, 6) == 0 &&
+ strcmp(".text..ftrace", txtname) != 0);
}
/* 32 bit and 64 bit are very similar */
diff --git a/scripts/tags.sh b/scripts/tags.sh
index a2ff338..2a61db3 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -106,6 +106,7 @@
case "$i" in
*.[cS])
j=${i/\.[cS]/\.o}
+ j="${j#$tree}"
if [ -e $j ]; then
echo $i
fi
diff --git a/security/Kconfig b/security/Kconfig
index 2e68fa4..638afc8 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -6,6 +6,11 @@
source security/keys/Kconfig
+if ARCH_QCOM
+source security/pfe/Kconfig
+endif
+
+
config SECURITY_DMESG_RESTRICT
bool "Restrict unprivileged access to the kernel syslog"
default n
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..79166ba 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
+subdir-$(CONFIG_ARCH_QCOM) += pfe
# always enable default capabilities
obj-y += commoncap.o
@@ -24,6 +25,7 @@
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
+obj-$(CONFIG_ARCH_QCOM) += pfe/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 935752c..777e132 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -656,11 +656,11 @@
.get = param_get_aalockpolicy
};
-static int param_set_audit(const char *val, struct kernel_param *kp);
-static int param_get_audit(char *buffer, struct kernel_param *kp);
+static int param_set_audit(const char *val, const struct kernel_param *kp);
+static int param_get_audit(char *buffer, const struct kernel_param *kp);
-static int param_set_mode(const char *val, struct kernel_param *kp);
-static int param_get_mode(char *buffer, struct kernel_param *kp);
+static int param_set_mode(const char *val, const struct kernel_param *kp);
+static int param_get_mode(char *buffer, const struct kernel_param *kp);
/* Flag values, also controllable via /sys/module/apparmor/parameters
* We define special types as we want to do additional mediation.
@@ -707,7 +707,7 @@
/* Maximum pathname length before accesses will start getting rejected */
unsigned int aa_g_path_max = 2 * PATH_MAX;
-module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
+module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR);
/* Determines how paranoid loading of policy is and how much verification
* on the loaded policy is done.
@@ -774,7 +774,7 @@
return param_get_uint(buffer, kp);
}
-static int param_get_audit(char *buffer, struct kernel_param *kp)
+static int param_get_audit(char *buffer, const struct kernel_param *kp)
{
if (!policy_view_capable())
return -EPERM;
@@ -785,7 +785,7 @@
return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]);
}
-static int param_set_audit(const char *val, struct kernel_param *kp)
+static int param_set_audit(const char *val, const struct kernel_param *kp)
{
int i;
if (!policy_admin_capable())
@@ -807,7 +807,7 @@
return -EINVAL;
}
-static int param_get_mode(char *buffer, struct kernel_param *kp)
+static int param_get_mode(char *buffer, const struct kernel_param *kp)
{
if (!policy_admin_capable())
return -EPERM;
@@ -818,7 +818,7 @@
return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]);
}
-static int param_set_mode(const char *val, struct kernel_param *kp)
+static int param_set_mode(const char *val, const struct kernel_param *kp)
{
int i;
if (!policy_admin_capable())
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 6830d24..7bf8b00 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -207,7 +207,8 @@
if (opened & FILE_CREATED)
iint->flags |= IMA_NEW_FILE;
if ((iint->flags & IMA_NEW_FILE) &&
- !(iint->flags & IMA_DIGSIG_REQUIRED))
+ (!(iint->flags & IMA_DIGSIG_REQUIRED) ||
+ (inode->i_size == 0)))
status = INTEGRITY_PASS;
goto out;
}
diff --git a/security/pfe/Kconfig b/security/pfe/Kconfig
new file mode 100644
index 0000000..0cd9e81
--- /dev/null
+++ b/security/pfe/Kconfig
@@ -0,0 +1,28 @@
+menu "Qualcomm Technologies, Inc Per File Encryption security device drivers"
+ depends on ARCH_QCOM
+
+config PFT
+ bool "Per-File-Tagger driver"
+ depends on SECURITY
+ default n
+ help
+ This driver is used for tagging enterprise files.
+ It is part of the Per-File-Encryption (PFE) feature.
+ The driver is tagging files when created by
+ registered application.
+ Tagged files are encrypted using the dm-req-crypt driver.
+
+config PFK
+ bool "Per-File-Key driver"
+ depends on SECURITY
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This driver is used for storing eCryptfs information
+ in file node.
+ This is part of eCryptfs hardware enhanced solution
+ provided by Qualcomm Technologies, Inc.
+ Information is used when file is encrypted later using
+ ICE or dm crypto engine
+
+endmenu
diff --git a/security/pfe/Makefile b/security/pfe/Makefile
new file mode 100644
index 0000000..4096aad
--- /dev/null
+++ b/security/pfe/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the MSM specific security device drivers.
+#
+
+ccflags-y += -Isecurity/selinux -Isecurity/selinux/include
+#ccflags-y += -Ifs/ext4
+ccflags-y += -Ifs/crypto
+
+obj-$(CONFIG_PFT) += pft.o
+obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o pfk_ext4.o pfk_f2fs.o
diff --git a/security/pfe/pfk.c b/security/pfe/pfk.c
new file mode 100644
index 0000000..740da32
--- /dev/null
+++ b/security/pfe/pfk.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Per-File-Key (PFK).
+ *
+ * This driver is responsible for overall management of various
+ * Per File Encryption variants that work on top of or as part of different
+ * file systems.
+ *
+ * The driver has the following purpose :
+ * 1) Define priorities between PFE's if more than one is enabled
+ * 2) Extract key information from inode
+ * 3) Load and manage various keys in ICE HW engine
+ * 4) It should be invoked from various layers in FS/BLOCK/STORAGE DRIVER
+ * that need to take decision on HW encryption management of the data
+ * Some examples:
+ * BLOCK LAYER: when it takes decision on whether 2 chunks can be united
+ * to one encryption / decryption request sent to the HW
+ *
+ * UFS DRIVER: when it need to configure ICE HW with a particular key slot
+ * to be used for encryption / decryption
+ *
+ * PFE variants can differ on particular way of storing the cryptographic info
+ * inside inode, actions to be taken upon file operations, etc., but the common
+ * properties are described above
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+#define pr_fmt(fmt) "pfk [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+#include <linux/bio.h>
+#include <linux/security.h>
+#include <crypto/algapi.h>
+#include <crypto/ice.h>
+
+#include <linux/pfk.h>
+
+#include "pfk_kc.h"
+#include "objsec.h"
+#include "pfk_ice.h"
+#include "pfk_ext4.h"
+#include "pfk_f2fs.h"
+#include "pfk_internal.h"
+//#include "ext4.h"
+
+static bool pfk_ready;
+
+
+/* might be replaced by a table when more than one cipher is supported */
+#define PFK_SUPPORTED_KEY_SIZE 32
+#define PFK_SUPPORTED_SALT_SIZE 32
+
+/* Various PFE types and function tables to support each one of them */
+enum pfe_type {EXT4_CRYPT_PFE, F2FS_CRYPT_PFE, INVALID_PFE};
+
+typedef int (*pfk_parse_inode_type)(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe);
+
+typedef bool (*pfk_allow_merge_bio_type)(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2);
+
+static const pfk_parse_inode_type pfk_parse_inode_ftable[] = {
+ /* EXT4_CRYPT_PFE */ &pfk_ext4_parse_inode,
+ /* F2FS_CRYPT_PFE */ &pfk_f2fs_parse_inode,
+};
+
+static const pfk_allow_merge_bio_type pfk_allow_merge_bio_ftable[] = {
+ /* EXT4_CRYPT_PFE */ &pfk_ext4_allow_merge_bio,
+ /* F2FS_CRYPT_PFE */ &pfk_f2fs_allow_merge_bio,
+};
+
+static void __exit pfk_exit(void)
+{
+ pfk_ready = false;
+ pfk_ext4_deinit();
+ pfk_f2fs_deinit();
+ pfk_kc_deinit();
+}
+
+static int __init pfk_init(void)
+{
+
+ int ret = 0;
+
+ ret = pfk_ext4_init();
+ if (ret != 0)
+ goto fail;
+
+ ret = pfk_f2fs_init();
+ if (ret != 0)
+ goto fail;
+
+ ret = pfk_kc_init();
+ if (ret != 0) {
+ pr_err("could init pfk key cache, error %d\n", ret);
+ pfk_ext4_deinit();
+ pfk_f2fs_deinit();
+ goto fail;
+ }
+
+ pfk_ready = true;
+ pr_info("Driver initialized successfully\n");
+
+ return 0;
+
+fail:
+ pr_err("Failed to init driver\n");
+ return -ENODEV;
+}
+
+/*
+ * If more than one type is supported simultaneously, this function will also
+ * set the priority between them
+ */
+static enum pfe_type pfk_get_pfe_type(const struct inode *inode)
+{
+ if (!inode)
+ return INVALID_PFE;
+
+ if (pfk_is_ext4_type(inode))
+ return EXT4_CRYPT_PFE;
+
+ if (pfk_is_f2fs_type(inode))
+ return F2FS_CRYPT_PFE;
+
+ return INVALID_PFE;
+}
+
+/**
+ * inode_to_filename() - get the filename from inode pointer.
+ * @inode: inode pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+char *inode_to_filename(const struct inode *inode)
+{
+ struct dentry *dentry = NULL;
+ char *filename = NULL;
+
+ if (!inode)
+ return "NULL";
+
+ if (hlist_empty(&inode->i_dentry))
+ return "unknown";
+
+ dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
+ filename = dentry->d_iname;
+
+ return filename;
+}
+
+/**
+ * pfk_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_is_ready(void)
+{
+ return pfk_ready;
+}
+
+/**
+ * pfk_bio_get_inode() - get the inode from a bio.
+ * @bio: Pointer to BIO structure.
+ *
+ * Walk the bio struct links to get the inode.
+ * Please note, that in general bio may consist of several pages from
+ * several files, but in our case we always assume that all pages come
+ * from the same file, since our logic ensures it. That is why we only
+ * walk through the first page to look for inode.
+ *
+ * Return: pointer to the inode struct if successful, or NULL otherwise.
+ *
+ */
+static struct inode *pfk_bio_get_inode(const struct bio *bio)
+{
+ if (!bio)
+ return NULL;
+ if (!bio_has_data((struct bio *)bio))
+ return NULL;
+ if (!bio->bi_io_vec)
+ return NULL;
+ if (!bio->bi_io_vec->bv_page)
+ return NULL;
+
+ if (PageAnon(bio->bi_io_vec->bv_page)) {
+ struct inode *inode;
+
+ /* Using direct-io (O_DIRECT) without page cache */
+ inode = dio_bio_get_inode((struct bio *)bio);
+ pr_debug("inode on direct-io, inode = 0x%pK.\n", inode);
+
+ return inode;
+ }
+
+ if (!page_mapping(bio->bi_io_vec->bv_page))
+ return NULL;
+
+ if (!bio->bi_io_vec->bv_page->mapping->host)
+
+ return NULL;
+
+ return bio->bi_io_vec->bv_page->mapping->host;
+}
+
+/**
+ * pfk_key_size_to_key_type() - translate key size to key size enum
+ * @key_size: key size in bytes
+ * @key_size_type: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported key size)
+ */
+int pfk_key_size_to_key_type(size_t key_size,
+ enum ice_crpto_key_size *key_size_type)
+{
+ /*
+ * currently only 32 bit key size is supported
+ * in the future, table with supported key sizes might
+ * be introduced
+ */
+
+ if (key_size != PFK_SUPPORTED_KEY_SIZE) {
+ pr_err("not supported key size %zu\n", key_size);
+ return -EINVAL;
+ }
+
+ if (key_size_type)
+ *key_size_type = ICE_CRYPTO_KEY_SIZE_256;
+
+ return 0;
+}
+
+/*
+ * Retrieves filesystem type from inode's superblock
+ */
+bool pfe_is_inode_filesystem_type(const struct inode *inode,
+ const char *fs_type)
+{
+ if (!inode || !fs_type)
+ return false;
+
+ if (!inode->i_sb)
+ return false;
+
+ if (!inode->i_sb->s_type)
+ return false;
+
+ return (strcmp(inode->i_sb->s_type->name, fs_type) == 0);
+}
+
+/**
+ * pfk_get_key_for_bio() - get the encryption key to be used for a bio
+ *
+ * @bio: pointer to the BIO
+ * @key_info: pointer to the key information which will be filled in
+ * @algo_mode: optional pointer to the algorithm identifier which will be set
+ * @is_pfe: will be set to false if the BIO should be left unencrypted
+ *
+ * Return: 0 if a key is being used, otherwise a -errno value
+ */
+static int pfk_get_key_for_bio(const struct bio *bio,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo_mode,
+ bool *is_pfe)
+{
+ const struct inode *inode;
+ enum pfe_type which_pfe;
+ const struct blk_encryption_key *key;
+
+ inode = pfk_bio_get_inode(bio);
+ which_pfe = pfk_get_pfe_type(inode);
+
+ if (which_pfe != INVALID_PFE) {
+ /* Encrypted file; override ->bi_crypt_key */
+ pr_debug("parsing inode %lu with PFE type %d\n",
+ inode->i_ino, which_pfe);
+ return (*(pfk_parse_inode_ftable[which_pfe]))
+ (bio, inode, key_info, algo_mode, is_pfe);
+ }
+
+ /*
+ * bio is not for an encrypted file. Use ->bi_crypt_key if it was set.
+ * Otherwise, don't encrypt/decrypt the bio.
+ */
+ key = bio->bi_crypt_key;
+ if (!key) {
+ *is_pfe = false;
+ return -EINVAL;
+ }
+
+ /* Note: the "salt" is really just the second half of the XTS key. */
+ BUILD_BUG_ON(sizeof(key->raw) !=
+ PFK_SUPPORTED_KEY_SIZE + PFK_SUPPORTED_SALT_SIZE);
+ key_info->key = &key->raw[0];
+ key_info->key_size = PFK_SUPPORTED_KEY_SIZE;
+ key_info->salt = &key->raw[PFK_SUPPORTED_KEY_SIZE];
+ key_info->salt_size = PFK_SUPPORTED_SALT_SIZE;
+ if (algo_mode)
+ *algo_mode = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+ return 0;
+}
+
+
+/**
+ * pfk_load_key_start() - loads PFE encryption key to the ICE
+ * Can also be invoked from non
+ * PFE context, in this case it
+ * is not relevant and is_pfe
+ * flag is set to false
+ *
+ * @bio: Pointer to the BIO structure
+ * @ice_setting: Pointer to ice setting structure that will be filled with
+ * ice configuration values, including the index to which the key was loaded
+ * @is_pfe: will be false if inode is not relevant to PFE, in such a case
+ * it should be treated as non PFE by the block layer
+ *
+ * Returns the index where the key is stored in encryption hw and additional
+ * information that will be used later for configuration of the encryption hw.
+ *
+ * Must be followed by pfk_load_key_end when key is no longer used by ice
+ *
+ */
+int pfk_load_key_start(const struct bio *bio,
+ struct ice_crypto_setting *ice_setting, bool *is_pfe,
+ bool async)
+{
+ int ret = 0;
+ struct pfk_key_info key_info = {NULL, NULL, 0, 0};
+ enum ice_cryto_algo_mode algo_mode = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+ enum ice_crpto_key_size key_size_type = 0;
+ u32 key_index = 0;
+
+ if (!is_pfe) {
+ pr_err("is_pfe is NULL\n");
+ return -EINVAL;
+ }
+
+ /*
+ * only a few errors below can indicate that
+ * this function was not invoked within PFE context,
+ * otherwise we will consider it PFE
+ */
+ *is_pfe = true;
+
+ if (!pfk_is_ready())
+ return -ENODEV;
+
+ if (!ice_setting) {
+ pr_err("ice setting is NULL\n");
+ return -EINVAL;
+ }
+
+ ret = pfk_get_key_for_bio(bio, &key_info, &algo_mode, is_pfe);
+
+ if (ret != 0)
+ return ret;
+
+ ret = pfk_key_size_to_key_type(key_info.key_size, &key_size_type);
+ if (ret != 0)
+ return ret;
+
+ ret = pfk_kc_load_key_start(key_info.key, key_info.key_size,
+ key_info.salt, key_info.salt_size, &key_index, async);
+ if (ret) {
+ if (ret != -EBUSY && ret != -EAGAIN)
+ pr_err("start: could not load key into pfk key cache, error %d\n",
+ ret);
+
+ return ret;
+ }
+
+ ice_setting->key_size = key_size_type;
+ ice_setting->algo_mode = algo_mode;
+ /* hardcoded for now */
+ ice_setting->key_mode = ICE_CRYPTO_USE_LUT_SW_KEY;
+ ice_setting->key_index = key_index;
+
+ pr_debug("loaded key for file %s key_index %d\n",
+ inode_to_filename(pfk_bio_get_inode(bio)), key_index);
+
+ return 0;
+}
+
+/**
+ * pfk_load_key_end() - marks the PFE key as no longer used by ICE
+ * Can also be invoked from non
+ * PFE context, in this case it is not
+ * relevant and is_pfe flag is
+ * set to false
+ *
+ * @bio: Pointer to the BIO structure
+ * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked
+ * from PFE context
+ */
+int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
+{
+ int ret = 0;
+ struct pfk_key_info key_info = {NULL, NULL, 0, 0};
+
+ if (!is_pfe) {
+ pr_err("is_pfe is NULL\n");
+ return -EINVAL;
+ }
+
+ /* only a few errors below can indicate that
+ * this function was not invoked within PFE context,
+ * otherwise we will consider it PFE
+ */
+ *is_pfe = true;
+
+ if (!pfk_is_ready())
+ return -ENODEV;
+
+ ret = pfk_get_key_for_bio(bio, &key_info, NULL, is_pfe);
+ if (ret != 0)
+ return ret;
+
+ pfk_kc_load_key_end(key_info.key, key_info.key_size,
+ key_info.salt, key_info.salt_size);
+
+ pr_debug("finished using key for file %s\n",
+ inode_to_filename(pfk_bio_get_inode(bio)));
+
+ return 0;
+}
+
+/**
+ * pfk_allow_merge_bio() - Check if 2 BIOs can be merged.
+ * @bio1: Pointer to first BIO structure.
+ * @bio2: Pointer to second BIO structure.
+ *
+ * Prevent merging of BIOs from encrypted and non-encrypted
+ * files, or files encrypted with different key.
+ * Also prevent non encrypted and encrypted data from the same file
+ * to be merged (ecryptfs header if stored inside file should be non
+ * encrypted)
+ * This API is called by the file system block layer.
+ *
+ * Return: true if the BIOs allowed to be merged, false
+ * otherwise.
+ */
+bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2)
+{
+ const struct blk_encryption_key *key1;
+ const struct blk_encryption_key *key2;
+ const struct inode *inode1;
+ const struct inode *inode2;
+ enum pfe_type which_pfe1;
+ enum pfe_type which_pfe2;
+
+ if (!pfk_is_ready())
+ return false;
+
+ if (!bio1 || !bio2)
+ return false;
+
+ if (bio1 == bio2)
+ return true;
+
+ key1 = bio1->bi_crypt_key;
+ key2 = bio2->bi_crypt_key;
+
+ inode1 = pfk_bio_get_inode(bio1);
+ inode2 = pfk_bio_get_inode(bio2);
+
+ which_pfe1 = pfk_get_pfe_type(inode1);
+ which_pfe2 = pfk_get_pfe_type(inode2);
+
+ /*
+ * If one bio is for an encrypted file and the other is for a different
+ * type of encrypted file or for blocks that are not part of an
+ * encrypted file, do not merge.
+ */
+ if (which_pfe1 != which_pfe2)
+ return false;
+
+ if (which_pfe1 != INVALID_PFE) {
+ /* Both bios are for the same type of encrypted file. */
+ return (*(pfk_allow_merge_bio_ftable[which_pfe1]))(bio1, bio2,
+ inode1, inode2);
+ }
+
+ /*
+ * Neither bio is for an encrypted file. Merge only if the default keys
+ * are the same (or both are NULL).
+ */
+ return key1 == key2 ||
+ (key1 && key2 &&
+ !crypto_memneq(key1->raw, key2->raw, sizeof(key1->raw)));
+}
+
+/**
+ * Flush key table on storage core reset. During core reset key configuration
+ * is lost in ICE. We need to flash the cache, so that the keys will be
+ * reconfigured again for every subsequent transaction
+ */
+void pfk_clear_on_reset(void)
+{
+ if (!pfk_is_ready())
+ return;
+
+ pfk_kc_clear_on_reset();
+}
+
+module_init(pfk_init);
+module_exit(pfk_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Key driver");
diff --git a/security/pfe/pfk_ext4.c b/security/pfe/pfk_ext4.c
new file mode 100644
index 0000000..0eb1225
--- /dev/null
+++ b/security/pfe/pfk_ext4.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Per-File-Key (PFK) - EXT4
+ *
+ * This driver is used for working with EXT4 crypt extension
+ *
+ * The key information is stored in node by EXT4 when file is first opened
+ * and will be later accessed by Block Device Driver to actually load the key
+ * to encryption hw.
+ *
+ * PFK exposes API's for loading and removing keys from encryption hw
+ * and also API to determine whether 2 adjacent blocks can be agregated by
+ * Block Layer in one request to encryption hw.
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+#define pr_fmt(fmt) "pfk_ext4 [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+
+#include "fscrypt_ice.h"
+#include "pfk_ext4.h"
+//#include "ext4_ice.h"
+
+static bool pfk_ext4_ready;
+
+/*
+ * pfk_ext4_deinit() - Deinit function, should be invoked by upper PFK layer
+ */
+void pfk_ext4_deinit(void)
+{
+ pfk_ext4_ready = false;
+}
+
+/*
+ * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer
+ */
+int __init pfk_ext4_init(void)
+{
+ pfk_ext4_ready = true;
+ pr_info("PFK EXT4 inited successfully\n");
+
+ return 0;
+}
+
+/**
+ * pfk_ecryptfs_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_ext4_is_ready(void)
+{
+ return pfk_ext4_ready;
+}
+
+/**
+ * pfk_ext4_dump_inode() - dumps all interesting info about inode to the screen
+ *
+ *
+ */
+/*
+ * static void pfk_ext4_dump_inode(const struct inode* inode)
+ * {
+ * struct ext4_crypt_info *ci = ext4_encryption_info((struct inode*)inode);
+ *
+ * pr_debug("dumping inode with address 0x%p\n", inode);
+ * pr_debug("S_ISREG is %d\n", S_ISREG(inode->i_mode));
+ * pr_debug("EXT4_INODE_ENCRYPT flag is %d\n",
+ * ext4_test_inode_flag((struct inode*)inode, EXT4_INODE_ENCRYPT));
+ * if (ci) {
+ * pr_debug("crypt_info address 0x%p\n", ci);
+ * pr_debug("ci->ci_data_mode %d\n", ci->ci_data_mode);
+ * } else {
+ * pr_debug("crypt_info is NULL\n");
+ * }
+ * }
+ */
+
+/**
+ * pfk_is_ext4_type() - return true if inode belongs to ICE EXT4 PFE
+ * @inode: inode pointer
+ */
+bool pfk_is_ext4_type(const struct inode *inode)
+{
+ if (!pfe_is_inode_filesystem_type(inode, "ext4"))
+ return false;
+
+ return fscrypt_should_be_processed_by_ice(inode);
+}
+
+/**
+ * pfk_ext4_parse_cipher() - parse cipher from inode to enum
+ * @inode: inode
+ * @algo: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported cipher)
+ */
+static int pfk_ext4_parse_cipher(const struct inode *inode,
+ enum ice_cryto_algo_mode *algo)
+{
+ /*
+ * currently only AES XTS algo is supported
+ * in the future, table with supported ciphers might
+ * be introduced
+ */
+
+ if (!inode)
+ return -EINVAL;
+
+ if (!fscrypt_is_aes_xts_cipher(inode)) {
+ pr_err("ext4 alghoritm is not supported by pfk\n");
+ return -EINVAL;
+ }
+
+ if (algo)
+ *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+
+ return 0;
+}
+
+
+int pfk_ext4_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe)
+{
+ int ret = 0;
+
+ if (!is_pfe)
+ return -EINVAL;
+
+ /*
+ * only a few errors below can indicate that
+ * this function was not invoked within PFE context,
+ * otherwise we will consider it PFE
+ */
+ *is_pfe = true;
+
+ if (!pfk_ext4_is_ready())
+ return -ENODEV;
+
+ if (!inode)
+ return -EINVAL;
+
+ if (!key_info)
+ return -EINVAL;
+
+ key_info->key = fscrypt_get_ice_encryption_key(inode);
+ if (!key_info->key) {
+ pr_err("could not parse key from ext4\n");
+ return -EINVAL;
+ }
+
+ key_info->key_size = fscrypt_get_ice_encryption_key_size(inode);
+ if (!key_info->key_size) {
+ pr_err("could not parse key size from ext4\n");
+ return -EINVAL;
+ }
+
+ key_info->salt = fscrypt_get_ice_encryption_salt(inode);
+ if (!key_info->salt) {
+ pr_err("could not parse salt from ext4\n");
+ return -EINVAL;
+ }
+
+ key_info->salt_size = fscrypt_get_ice_encryption_salt_size(inode);
+ if (!key_info->salt_size) {
+ pr_err("could not parse salt size from ext4\n");
+ return -EINVAL;
+ }
+
+ ret = pfk_ext4_parse_cipher(inode, algo);
+ if (ret != 0) {
+ pr_err("not supported cipher\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+bool pfk_ext4_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2)
+{
+ /* if there is no ext4 pfk, don't disallow merging blocks */
+ if (!pfk_ext4_is_ready())
+ return true;
+
+ if (!inode1 || !inode2)
+ return false;
+
+ return fscrypt_is_ice_encryption_info_equal(inode1, inode2);
+}
diff --git a/security/pfe/pfk_ext4.h b/security/pfe/pfk_ext4.h
new file mode 100644
index 0000000..c33232f
--- /dev/null
+++ b/security/pfe/pfk_ext4.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PFK_EXT4_H_
+#define _PFK_EXT4_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <crypto/ice.h>
+#include "pfk_internal.h"
+
+bool pfk_is_ext4_type(const struct inode *inode);
+
+int pfk_ext4_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe);
+
+bool pfk_ext4_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2);
+
+int __init pfk_ext4_init(void);
+
+void pfk_ext4_deinit(void);
+
+#endif /* _PFK_EXT4_H_ */
diff --git a/security/pfe/pfk_f2fs.c b/security/pfe/pfk_f2fs.c
new file mode 100644
index 0000000..8b9d515
--- /dev/null
+++ b/security/pfe/pfk_f2fs.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Per-File-Key (PFK) - f2fs
+ *
+ * This driver is used for working with EXT4/F2FS crypt extension
+ *
+ * The key information is stored in node by EXT4/F2FS when file is first opened
+ * and will be later accessed by Block Device Driver to actually load the key
+ * to encryption hw.
+ *
+ * PFK exposes API's for loading and removing keys from encryption hw
+ * and also API to determine whether 2 adjacent blocks can be agregated by
+ * Block Layer in one request to encryption hw.
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+#define DEBUG 1
+#define pr_fmt(fmt) "pfk_f2fs [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+
+#include "fscrypt_ice.h"
+#include "pfk_f2fs.h"
+
+static bool pfk_f2fs_ready;
+
+/*
+ * pfk_f2fs_deinit() - Deinit function, should be invoked by upper PFK layer
+ */
+void pfk_f2fs_deinit(void)
+{
+ pfk_f2fs_ready = false;
+}
+
+/*
+ * pfk_f2fs_init() - Init function, should be invoked by upper PFK layer
+ */
+int __init pfk_f2fs_init(void)
+{
+ pfk_f2fs_ready = true;
+ pr_info("PFK F2FS inited successfully\n");
+
+ return 0;
+}
+
+/**
+ * pfk_f2fs_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_f2fs_is_ready(void)
+{
+ return pfk_f2fs_ready;
+}
+
+/**
+ * pfk_is_f2fs_type() - return true if inode belongs to ICE F2FS PFE
+ * @inode: inode pointer
+ */
+bool pfk_is_f2fs_type(const struct inode *inode)
+{
+ if (!pfe_is_inode_filesystem_type(inode, "f2fs"))
+ return false;
+
+ return fscrypt_should_be_processed_by_ice(inode);
+}
+
+/**
+ * pfk_f2fs_parse_cipher() - parse cipher from inode to enum
+ * @inode: inode
+ * @algo: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported cipher)
+ */
+static int pfk_f2fs_parse_cipher(const struct inode *inode,
+ enum ice_cryto_algo_mode *algo)
+{
+ /*
+ * currently only AES XTS algo is supported
+ * in the future, table with supported ciphers might
+ * be introduced
+ */
+ if (!inode)
+ return -EINVAL;
+
+ if (!fscrypt_is_aes_xts_cipher(inode)) {
+ pr_err("f2fs alghoritm is not supported by pfk\n");
+ return -EINVAL;
+ }
+
+ if (algo)
+ *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+
+ return 0;
+}
+
+
+int pfk_f2fs_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe)
+{
+ int ret = 0;
+
+ if (!is_pfe)
+ return -EINVAL;
+
+ /*
+ * only a few errors below can indicate that
+ * this function was not invoked within PFE context,
+ * otherwise we will consider it PFE
+ */
+ *is_pfe = true;
+
+ if (!pfk_f2fs_is_ready())
+ return -ENODEV;
+
+ if (!inode)
+ return -EINVAL;
+
+ if (!key_info)
+ return -EINVAL;
+
+ key_info->key = fscrypt_get_ice_encryption_key(inode);
+ if (!key_info->key) {
+ pr_err("could not parse key from f2fs\n");
+ return -EINVAL;
+ }
+
+ key_info->key_size = fscrypt_get_ice_encryption_key_size(inode);
+ if (!key_info->key_size) {
+ pr_err("could not parse key size from f2fs\n");
+ return -EINVAL;
+ }
+
+ key_info->salt = fscrypt_get_ice_encryption_salt(inode);
+ if (!key_info->salt) {
+ pr_err("could not parse salt from f2fs\n");
+ return -EINVAL;
+ }
+
+ key_info->salt_size = fscrypt_get_ice_encryption_salt_size(inode);
+ if (!key_info->salt_size) {
+ pr_err("could not parse salt size from f2fs\n");
+ return -EINVAL;
+ }
+
+ ret = pfk_f2fs_parse_cipher(inode, algo);
+ if (ret != 0) {
+ pr_err("not supported cipher\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+bool pfk_f2fs_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2)
+{
+ bool mergeable;
+
+ /* if there is no f2fs pfk, don't disallow merging blocks */
+ if (!pfk_f2fs_is_ready())
+ return true;
+
+ if (!inode1 || !inode2)
+ return false;
+
+ mergeable = fscrypt_is_ice_encryption_info_equal(inode1, inode2);
+ if (!mergeable)
+ return false;
+
+
+ /* ICE allows only consecutive iv_key stream. */
+ if (!bio_dun(bio1) && !bio_dun(bio2))
+ return true;
+ else if (!bio_dun(bio1) || !bio_dun(bio2))
+ return false;
+
+ return bio_end_dun(bio1) == bio_dun(bio2);
+}
diff --git a/security/pfe/pfk_f2fs.h b/security/pfe/pfk_f2fs.h
new file mode 100644
index 0000000..551d529
--- /dev/null
+++ b/security/pfe/pfk_f2fs.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PFK_F2FS_H_
+#define _PFK_F2FS_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <crypto/ice.h>
+#include "pfk_internal.h"
+
+bool pfk_is_f2fs_type(const struct inode *inode);
+
+int pfk_f2fs_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe);
+
+bool pfk_f2fs_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2);
+
+int __init pfk_f2fs_init(void);
+
+void pfk_f2fs_deinit(void);
+
+#endif /* _PFK_F2FS_H_ */
diff --git a/security/pfe/pfk_ice.c b/security/pfe/pfk_ice.c
new file mode 100644
index 0000000..16ed516
--- /dev/null
+++ b/security/pfe/pfk_ice.c
@@ -0,0 +1,189 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/async.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <soc/qcom/scm.h>
+#include <linux/device-mapper.h>
+#include <soc/qcom/qseecomi.h>
+#include <crypto/ice.h>
+#include "pfk_ice.h"
+
+
+/**********************************/
+/** global definitions **/
+/**********************************/
+
+#define TZ_ES_SET_ICE_KEY 0x2
+#define TZ_ES_INVALIDATE_ICE_KEY 0x3
+
+/* index 0 and 1 is reserved for FDE */
+#define MIN_ICE_KEY_INDEX 2
+
+#define MAX_ICE_KEY_INDEX 31
+
+
+#define TZ_ES_SET_ICE_KEY_ID \
+ TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_ES, TZ_ES_SET_ICE_KEY)
+
+
+#define TZ_ES_INVALIDATE_ICE_KEY_ID \
+ TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, \
+ TZ_SVC_ES, TZ_ES_INVALIDATE_ICE_KEY)
+
+
+#define TZ_ES_SET_ICE_KEY_PARAM_ID \
+ TZ_SYSCALL_CREATE_PARAM_ID_5( \
+ TZ_SYSCALL_PARAM_TYPE_VAL, \
+ TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL, \
+ TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL)
+
+#define TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID \
+ TZ_SYSCALL_CREATE_PARAM_ID_1( \
+ TZ_SYSCALL_PARAM_TYPE_VAL)
+
+#define ICE_KEY_SIZE 32
+#define ICE_SALT_SIZE 32
+
+static uint8_t ice_key[ICE_KEY_SIZE];
+static uint8_t ice_salt[ICE_KEY_SIZE];
+
+int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt,
+ char *storage_type)
+{
+ struct scm_desc desc = {0};
+ int ret, ret1;
+ char *tzbuf_key = (char *)ice_key;
+ char *tzbuf_salt = (char *)ice_salt;
+ char *s_type = storage_type;
+
+ uint32_t smc_id = 0;
+ u32 tzbuflen_key = sizeof(ice_key);
+ u32 tzbuflen_salt = sizeof(ice_salt);
+
+ if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX) {
+ pr_err("%s Invalid index %d\n", __func__, index);
+ return -EINVAL;
+ }
+ if (!key || !salt) {
+ pr_err("%s Invalid key/salt\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!tzbuf_key || !tzbuf_salt) {
+ pr_err("%s No Memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (s_type == NULL) {
+ pr_err("%s Invalid Storage type\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(tzbuf_key, 0, tzbuflen_key);
+ memset(tzbuf_salt, 0, tzbuflen_salt);
+
+ memcpy(ice_key, key, tzbuflen_key);
+ memcpy(ice_salt, salt, tzbuflen_salt);
+
+ dmac_flush_range(tzbuf_key, tzbuf_key + tzbuflen_key);
+ dmac_flush_range(tzbuf_salt, tzbuf_salt + tzbuflen_salt);
+
+ smc_id = TZ_ES_SET_ICE_KEY_ID;
+
+ desc.arginfo = TZ_ES_SET_ICE_KEY_PARAM_ID;
+ desc.args[0] = index;
+ desc.args[1] = virt_to_phys(tzbuf_key);
+ desc.args[2] = tzbuflen_key;
+ desc.args[3] = virt_to_phys(tzbuf_salt);
+ desc.args[4] = tzbuflen_salt;
+
+ ret = qcom_ice_setup_ice_hw((const char *)s_type, true);
+
+ if (ret) {
+ pr_err("%s: could not enable clocks: %d\n", __func__, ret);
+ goto out;
+ }
+
+ ret = scm_call2(smc_id, &desc);
+
+ if (ret) {
+ pr_err("%s: Set Key Error: %d\n", __func__, ret);
+ if (ret == -EBUSY) {
+ if (qcom_ice_setup_ice_hw((const char *)s_type, false))
+ pr_err("%s: clock disable failed\n", __func__);
+ goto out;
+ }
+ /*Try to invalidate the key to keep ICE in proper state*/
+ smc_id = TZ_ES_INVALIDATE_ICE_KEY_ID;
+ desc.arginfo = TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID;
+ desc.args[0] = index;
+ ret1 = scm_call2(smc_id, &desc);
+ if (ret1)
+ pr_err("%s: Invalidate Key Error: %d\n", __func__,
+ ret1);
+ goto out;
+ }
+ ret = qcom_ice_setup_ice_hw((const char *)s_type, false);
+
+out:
+ return ret;
+}
+
+int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type)
+{
+ struct scm_desc desc = {0};
+ int ret;
+
+ uint32_t smc_id = 0;
+
+ if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX) {
+ pr_err("%s Invalid index %d\n", __func__, index);
+ return -EINVAL;
+ }
+
+ if (storage_type == NULL) {
+ pr_err("%s Invalid Storage type\n", __func__);
+ return -EINVAL;
+ }
+
+ smc_id = TZ_ES_INVALIDATE_ICE_KEY_ID;
+
+ desc.arginfo = TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID;
+ desc.args[0] = index;
+
+ ret = qcom_ice_setup_ice_hw((const char *)storage_type, true);
+
+ if (ret) {
+ pr_err("%s: could not enable clocks: 0x%x\n", __func__, ret);
+ return ret;
+ }
+
+ ret = scm_call2(smc_id, &desc);
+
+ if (ret) {
+ pr_err("%s: Error: 0x%x\n", __func__, ret);
+ if (qcom_ice_setup_ice_hw((const char *)storage_type, false))
+ pr_err("%s: could not disable clocks\n", __func__);
+ } else {
+ ret = qcom_ice_setup_ice_hw((const char *)storage_type, false);
+ }
+
+ return ret;
+}
diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h
new file mode 100644
index 0000000..31772e7
--- /dev/null
+++ b/security/pfe/pfk_ice.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PFK_ICE_H_
+#define PFK_ICE_H_
+
+/*
+ * PFK ICE
+ *
+ * ICE keys configuration through scm calls.
+ *
+ */
+
+#include <linux/types.h>
+
+int pfk_ice_init(void);
+int pfk_ice_deinit(void);
+
+int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt,
+ char *storage_type);
+int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type);
+
+
+#endif /* PFK_ICE_H_ */
diff --git a/security/pfe/pfk_internal.h b/security/pfe/pfk_internal.h
new file mode 100644
index 0000000..3214327
--- /dev/null
+++ b/security/pfe/pfk_internal.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PFK_INTERNAL_H_
+#define _PFK_INTERNAL_H_
+
+#include <linux/types.h>
+#include <crypto/ice.h>
+
+struct pfk_key_info {
+ const unsigned char *key;
+ const unsigned char *salt;
+ size_t key_size;
+ size_t salt_size;
+};
+
+int pfk_key_size_to_key_type(size_t key_size,
+ enum ice_crpto_key_size *key_size_type);
+
+bool pfe_is_inode_filesystem_type(const struct inode *inode,
+ const char *fs_type);
+
+char *inode_to_filename(const struct inode *inode);
+
+#endif /* _PFK_INTERNAL_H_ */
diff --git a/security/pfe/pfk_kc.c b/security/pfe/pfk_kc.c
new file mode 100644
index 0000000..eecc026
--- /dev/null
+++ b/security/pfe/pfk_kc.c
@@ -0,0 +1,906 @@
+/*
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * PFK Key Cache
+ *
+ * Key Cache used internally in PFK.
+ * The purpose of the cache is to save access time to QSEE when loading keys.
+ * Currently the cache is the same size as the total number of keys that can
+ * be loaded to ICE. Since this number is relatively small, the algorithms for
+ * cache eviction are simple, linear and based on last usage timestamp, i.e
+ * the node that will be evicted is the one with the oldest timestamp.
+ * Empty entries always have the oldest timestamp.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <crypto/ice.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+
+#include "pfk_kc.h"
+#include "pfk_ice.h"
+
+
+/** the first available index in ice engine */
+#define PFK_KC_STARTING_INDEX 2
+
+/** currently the only supported key and salt sizes */
+#define PFK_KC_KEY_SIZE 32
+#define PFK_KC_SALT_SIZE 32
+
+/** Table size */
+/* TODO replace by some constant from ice.h */
+#define PFK_KC_TABLE_SIZE ((32) - (PFK_KC_STARTING_INDEX))
+
+/** The maximum key and salt size */
+#define PFK_MAX_KEY_SIZE PFK_KC_KEY_SIZE
+#define PFK_MAX_SALT_SIZE PFK_KC_SALT_SIZE
+#define PFK_UFS "ufs"
+
+static DEFINE_SPINLOCK(kc_lock);
+static unsigned long flags;
+static bool kc_ready;
+static char *s_type = "sdcc";
+
+/**
+ * enum pfk_kc_entry_state - state of the entry inside kc table
+ *
+ * @FREE: entry is free
+ * @ACTIVE_ICE_PRELOAD: entry is actively used by ICE engine
+ and cannot be used by others. SCM call
+ to load key to ICE is pending to be performed
+ * @ACTIVE_ICE_LOADED: entry is actively used by ICE engine and
+ cannot be used by others. SCM call to load the
+ key to ICE was successfully executed and key is
+ now loaded
+ * @INACTIVE_INVALIDATING: entry is being invalidated during file close
+ and cannot be used by others until invalidation
+ is complete
+ * @INACTIVE: entry's key is already loaded, but is not
+ currently being used. It can be re-used for
+ optimization and to avoid SCM call cost or
+ it can be taken by another key if there are
+ no FREE entries
+ * @SCM_ERROR: error occurred while scm call was performed to
+ load the key to ICE
+ */
+enum pfk_kc_entry_state {
+ FREE,
+ ACTIVE_ICE_PRELOAD,
+ ACTIVE_ICE_LOADED,
+ INACTIVE_INVALIDATING,
+ INACTIVE,
+ SCM_ERROR
+};
+
+struct kc_entry {
+ unsigned char key[PFK_MAX_KEY_SIZE];
+ size_t key_size;
+
+ unsigned char salt[PFK_MAX_SALT_SIZE];
+ size_t salt_size;
+
+ u64 time_stamp;
+ u32 key_index;
+
+ struct task_struct *thread_pending;
+
+ enum pfk_kc_entry_state state;
+
+ /* ref count for the number of requests in the HW queue for this key */
+ int loaded_ref_cnt;
+ int scm_error;
+};
+
+static struct kc_entry kc_table[PFK_KC_TABLE_SIZE];
+
+/**
+ * kc_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the key cache is ready.
+ */
+static inline bool kc_is_ready(void)
+{
+ return kc_ready;
+}
+
+static inline void kc_spin_lock(void)
+{
+ spin_lock_irqsave(&kc_lock, flags);
+}
+
+static inline void kc_spin_unlock(void)
+{
+ spin_unlock_irqrestore(&kc_lock, flags);
+}
+
+/**
+ * kc_entry_is_available() - checks whether the entry is available
+ *
+ * Return true if it is , false otherwise or if invalid
+ * Should be invoked under spinlock
+ */
+static bool kc_entry_is_available(const struct kc_entry *entry)
+{
+ if (!entry)
+ return false;
+
+ return (entry->state == FREE || entry->state == INACTIVE);
+}
+
+/**
+ * kc_entry_wait_till_available() - waits till entry is available
+ *
+ * Returns 0 in case of success or -ERESTARTSYS if the wait was interrupted
+ * by signal
+ *
+ * Should be invoked under spinlock
+ */
+static int kc_entry_wait_till_available(struct kc_entry *entry)
+{
+ int res = 0;
+
+ while (!kc_entry_is_available(entry)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ res = -ERESTARTSYS;
+ break;
+ }
+ /* assuming only one thread can try to invalidate
+ * the same entry
+ */
+ entry->thread_pending = current;
+ kc_spin_unlock();
+ schedule();
+ kc_spin_lock();
+ }
+ set_current_state(TASK_RUNNING);
+
+ return res;
+}
+
+/**
+ * kc_entry_start_invalidating() - moves entry to state
+ * INACTIVE_INVALIDATING
+ * If entry is in use, waits till
+ * it gets available
+ * @entry: pointer to entry
+ *
+ * Return 0 in case of success, otherwise error
+ * Should be invoked under spinlock
+ */
+static int kc_entry_start_invalidating(struct kc_entry *entry)
+{
+ int res;
+
+ res = kc_entry_wait_till_available(entry);
+ if (res)
+ return res;
+
+ entry->state = INACTIVE_INVALIDATING;
+
+ return 0;
+}
+
+/**
+ * kc_entry_finish_invalidating() - moves entry to state FREE
+ * wakes up all the tasks waiting
+ * on it
+ *
+ * @entry: pointer to entry
+ *
+ * Return 0 in case of success, otherwise error
+ * Should be invoked under spinlock
+ */
+static void kc_entry_finish_invalidating(struct kc_entry *entry)
+{
+ if (!entry)
+ return;
+
+ if (entry->state != INACTIVE_INVALIDATING)
+ return;
+
+ entry->state = FREE;
+}
+
+/**
+ * kc_min_entry() - compare two entries to find one with minimal time
+ * @a: ptr to the first entry. If NULL the other entry will be returned
+ * @b: pointer to the second entry
+ *
+ * Return the entry which timestamp is the minimal, or b if a is NULL
+ */
+static inline struct kc_entry *kc_min_entry(struct kc_entry *a,
+ struct kc_entry *b)
+{
+ if (!a)
+ return b;
+
+ if (time_before64(b->time_stamp, a->time_stamp))
+ return b;
+
+ return a;
+}
+
+/**
+ * kc_entry_at_index() - return entry at specific index
+ * @index: index of entry to be accessed
+ *
+ * Return entry
+ * Should be invoked under spinlock
+ */
+static struct kc_entry *kc_entry_at_index(int index)
+{
+ return &(kc_table[index]);
+}
+
+/**
+ * kc_find_key_at_index() - find kc entry starting at specific index
+ * @key: key to look for
+ * @key_size: the key size
+ * @salt: salt to look for
+ * @salt_size: the salt size
+ * @sarting_index: index to start search with, if entry found, updated with
+ * index of that entry
+ *
+ * Return entry or NULL in case of error
+ * Should be invoked under spinlock
+ */
+static struct kc_entry *kc_find_key_at_index(const unsigned char *key,
+ size_t key_size, const unsigned char *salt, size_t salt_size,
+ int *starting_index)
+{
+ struct kc_entry *entry = NULL;
+ int i = 0;
+
+ for (i = *starting_index; i < PFK_KC_TABLE_SIZE; i++) {
+ entry = kc_entry_at_index(i);
+
+ if (salt != NULL) {
+ if (entry->salt_size != salt_size)
+ continue;
+
+ if (memcmp(entry->salt, salt, salt_size) != 0)
+ continue;
+ }
+
+ if (entry->key_size != key_size)
+ continue;
+
+ if (memcmp(entry->key, key, key_size) == 0) {
+ *starting_index = i;
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * kc_find_key() - find kc entry
+ * @key: key to look for
+ * @key_size: the key size
+ * @salt: salt to look for
+ * @salt_size: the salt size
+ *
+ * Return entry or NULL in case of error
+ * Should be invoked under spinlock
+ */
+static struct kc_entry *kc_find_key(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size)
+{
+ int index = 0;
+
+ return kc_find_key_at_index(key, key_size, salt, salt_size, &index);
+}
+
+/**
+ * kc_find_oldest_entry_non_locked() - finds the entry with minimal timestamp
+ * that is not locked
+ *
+ * Returns entry with minimal timestamp. Empty entries have timestamp
+ * of 0, therefore they are returned first.
+ * If all the entries are locked, will return NULL
+ * Should be invoked under spin lock
+ */
+static struct kc_entry *kc_find_oldest_entry_non_locked(void)
+{
+ struct kc_entry *curr_min_entry = NULL;
+ struct kc_entry *entry = NULL;
+ int i = 0;
+
+ for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+ entry = kc_entry_at_index(i);
+
+ if (entry->state == FREE)
+ return entry;
+
+ if (entry->state == INACTIVE)
+ curr_min_entry = kc_min_entry(curr_min_entry, entry);
+ }
+
+ return curr_min_entry;
+}
+
+/**
+ * kc_update_timestamp() - updates timestamp of entry to current
+ *
+ * @entry: entry to update
+ *
+ */
+static void kc_update_timestamp(struct kc_entry *entry)
+{
+ if (!entry)
+ return;
+
+ entry->time_stamp = get_jiffies_64();
+}
+
+/**
+ * kc_clear_entry() - clear the key from entry and mark entry not in use
+ *
+ * @entry: pointer to entry
+ *
+ * Should be invoked under spinlock
+ */
+static void kc_clear_entry(struct kc_entry *entry)
+{
+ if (!entry)
+ return;
+
+ memset(entry->key, 0, entry->key_size);
+ memset(entry->salt, 0, entry->salt_size);
+
+ entry->key_size = 0;
+ entry->salt_size = 0;
+
+ entry->time_stamp = 0;
+ entry->scm_error = 0;
+
+ entry->state = FREE;
+
+ entry->loaded_ref_cnt = 0;
+ entry->thread_pending = NULL;
+}
+
+/**
+ * kc_update_entry() - replaces the key in given entry and
+ * loads the new key to ICE
+ *
+ * @entry: entry to replace key in
+ * @key: key
+ * @key_size: key_size
+ * @salt: salt
+ * @salt_size: salt_size
+ *
+ * The previous key is securely released and wiped, the new one is loaded
+ * to ICE.
+ * Should be invoked under spinlock
+ */
+static int kc_update_entry(struct kc_entry *entry, const unsigned char *key,
+ size_t key_size, const unsigned char *salt, size_t salt_size)
+{
+ int ret;
+
+ kc_clear_entry(entry);
+
+ memcpy(entry->key, key, key_size);
+ entry->key_size = key_size;
+
+ memcpy(entry->salt, salt, salt_size);
+ entry->salt_size = salt_size;
+
+ /* Mark entry as no longer free before releasing the lock */
+ entry->state = ACTIVE_ICE_PRELOAD;
+ kc_spin_unlock();
+
+ ret = qti_pfk_ice_set_key(entry->key_index, entry->key,
+ entry->salt, s_type);
+
+ kc_spin_lock();
+ return ret;
+}
+
+/**
+ * pfk_kc_init() - init function
+ *
+ * Return 0 in case of success, error otherwise
+ */
+int pfk_kc_init(void)
+{
+ int i = 0;
+ struct kc_entry *entry = NULL;
+
+ kc_spin_lock();
+ for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+ entry = kc_entry_at_index(i);
+ entry->key_index = PFK_KC_STARTING_INDEX + i;
+ }
+ kc_ready = true;
+ kc_spin_unlock();
+
+ return 0;
+}
+
+/**
+ * pfk_kc_denit() - deinit function
+ *
+ * Return 0 in case of success, error otherwise
+ */
+int pfk_kc_deinit(void)
+{
+ int res = pfk_kc_clear();
+ kc_ready = false;
+
+ return res;
+}
+
+/**
+ * pfk_kc_load_key_start() - retrieve the key from cache or add it if
+ * it's not there and return the ICE hw key index in @key_index.
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ * @salt: pointer to the salt
+ * @salt_size: the size of the salt
+ * @key_index: the pointer to key_index where the output will be stored
+ * @async: whether scm calls are allowed in the caller context
+ *
+ * If key is present in cache, than the key_index will be retrieved from cache.
+ * If it is not present, the oldest entry from kc table will be evicted,
+ * the key will be loaded to ICE via QSEE to the index that is the evicted
+ * entry number and stored in cache.
+ * Entry that is going to be used is marked as being used, it will mark
+ * as not being used when ICE finishes using it and pfk_kc_load_key_end
+ * will be invoked.
+ * As QSEE calls can only be done from a non-atomic context, when @async flag
+ * is set to 'false', it specifies that it is ok to make the calls in the
+ * current context. Otherwise, when @async is set, the caller should retry the
+ * call again from a different context, and -EAGAIN error will be returned.
+ *
+ * Return 0 in case of success, error otherwise
+ */
+int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size, u32 *key_index,
+ bool async)
+{
+ int ret = 0;
+ struct kc_entry *entry = NULL;
+ bool entry_exists = false;
+
+ if (!kc_is_ready())
+ return -ENODEV;
+
+ if (!key || !salt || !key_index) {
+ pr_err("%s key/salt/key_index NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (key_size != PFK_KC_KEY_SIZE) {
+ pr_err("unsupported key size %zu\n", key_size);
+ return -EINVAL;
+ }
+
+ if (salt_size != PFK_KC_SALT_SIZE) {
+ pr_err("unsupported salt size %zu\n", salt_size);
+ return -EINVAL;
+ }
+
+ kc_spin_lock();
+
+ entry = kc_find_key(key, key_size, salt, salt_size);
+ if (!entry) {
+ if (async) {
+ pr_debug("%s task will populate entry\n", __func__);
+ kc_spin_unlock();
+ return -EAGAIN;
+ }
+
+ entry = kc_find_oldest_entry_non_locked();
+ if (!entry) {
+ /* could not find a single non locked entry,
+ * return EBUSY to upper layers so that the
+ * request will be rescheduled
+ */
+ kc_spin_unlock();
+ return -EBUSY;
+ }
+ } else {
+ entry_exists = true;
+ }
+
+ pr_debug("entry with index %d is in state %d\n",
+ entry->key_index, entry->state);
+
+ switch (entry->state) {
+ case (INACTIVE):
+ if (entry_exists) {
+ kc_update_timestamp(entry);
+ entry->state = ACTIVE_ICE_LOADED;
+
+ if (!strcmp(s_type, (char *)PFK_UFS)) {
+ if (async)
+ entry->loaded_ref_cnt++;
+ } else {
+ entry->loaded_ref_cnt++;
+ }
+ break;
+ }
+ case (FREE):
+ ret = kc_update_entry(entry, key, key_size, salt, salt_size);
+ if (ret) {
+ entry->state = SCM_ERROR;
+ entry->scm_error = ret;
+ pr_err("%s: key load error (%d)\n", __func__, ret);
+ } else {
+ kc_update_timestamp(entry);
+ entry->state = ACTIVE_ICE_LOADED;
+
+ /*
+ * In case of UFS only increase ref cnt for async calls,
+ * sync calls from within work thread do not pass
+ * requests further to HW
+ */
+ if (!strcmp(s_type, (char *)PFK_UFS)) {
+ if (async)
+ entry->loaded_ref_cnt++;
+ } else {
+ entry->loaded_ref_cnt++;
+ }
+ }
+ break;
+ case (ACTIVE_ICE_PRELOAD):
+ case (INACTIVE_INVALIDATING):
+ ret = -EAGAIN;
+ break;
+ case (ACTIVE_ICE_LOADED):
+ kc_update_timestamp(entry);
+
+ if (!strcmp(s_type, (char *)PFK_UFS)) {
+ if (async)
+ entry->loaded_ref_cnt++;
+ } else {
+ entry->loaded_ref_cnt++;
+ }
+ break;
+ case(SCM_ERROR):
+ ret = entry->scm_error;
+ kc_clear_entry(entry);
+ entry->state = FREE;
+ break;
+ default:
+ pr_err("invalid state %d for entry with key index %d\n",
+ entry->state, entry->key_index);
+ ret = -EINVAL;
+ }
+
+ *key_index = entry->key_index;
+ kc_spin_unlock();
+
+ return ret;
+}
+
+/**
+ * pfk_kc_load_key_end() - finish the process of key loading that was started
+ * by pfk_kc_load_key_start
+ * by marking the entry as not
+ * being in use
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ * @salt: pointer to the salt
+ * @salt_size: the size of the salt
+ *
+ */
+void pfk_kc_load_key_end(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size)
+{
+ struct kc_entry *entry = NULL;
+ struct task_struct *tmp_pending = NULL;
+ int ref_cnt = 0;
+
+ if (!kc_is_ready())
+ return;
+
+ if (!key || !salt)
+ return;
+
+ if (key_size != PFK_KC_KEY_SIZE)
+ return;
+
+ if (salt_size != PFK_KC_SALT_SIZE)
+ return;
+
+ kc_spin_lock();
+
+ entry = kc_find_key(key, key_size, salt, salt_size);
+ if (!entry) {
+ kc_spin_unlock();
+ pr_err("internal error, there should an entry to unlock\n");
+
+ return;
+ }
+ ref_cnt = --entry->loaded_ref_cnt;
+
+ if (ref_cnt < 0)
+ pr_err("internal error, ref count should never be negative\n");
+
+ if (!ref_cnt) {
+ entry->state = INACTIVE;
+ /*
+ * wake-up invalidation if it's waiting
+ * for the entry to be released
+ */
+ if (entry->thread_pending) {
+ tmp_pending = entry->thread_pending;
+ entry->thread_pending = NULL;
+
+ kc_spin_unlock();
+ wake_up_process(tmp_pending);
+ return;
+ }
+ }
+
+ kc_spin_unlock();
+}
+
+/**
+ * pfk_kc_remove_key() - remove the key from cache and from ICE engine
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ * @salt: pointer to the key
+ * @salt_size: the size of the key
+ *
+ * Return 0 in case of success, error otherwise (also in case of non
+ * (existing key)
+ */
+int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size)
+{
+ struct kc_entry *entry = NULL;
+ int res = 0;
+
+ if (!kc_is_ready())
+ return -ENODEV;
+
+ if (!key)
+ return -EINVAL;
+
+ if (!salt)
+ return -EINVAL;
+
+ if (key_size != PFK_KC_KEY_SIZE)
+ return -EINVAL;
+
+ if (salt_size != PFK_KC_SALT_SIZE)
+ return -EINVAL;
+
+ kc_spin_lock();
+
+ entry = kc_find_key(key, key_size, salt, salt_size);
+ if (!entry) {
+ pr_debug("%s: key does not exist\n", __func__);
+ kc_spin_unlock();
+ return -EINVAL;
+ }
+
+ res = kc_entry_start_invalidating(entry);
+ if (res != 0) {
+ kc_spin_unlock();
+ return res;
+ }
+ kc_clear_entry(entry);
+
+ kc_spin_unlock();
+
+ qti_pfk_ice_invalidate_key(entry->key_index, s_type);
+
+ kc_spin_lock();
+ kc_entry_finish_invalidating(entry);
+ kc_spin_unlock();
+
+ return 0;
+}
+
+/**
+ * pfk_kc_remove_key() - remove the key from cache and from ICE engine
+ * when no salt is available. Will only search key part, if there are several,
+ * all will be removed
+ *
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ *
+ * Return 0 in case of success, error otherwise (also for non-existing key)
+ */
+int pfk_kc_remove_key(const unsigned char *key, size_t key_size)
+{
+ struct kc_entry *entry = NULL;
+ int index = 0;
+ int temp_indexes[PFK_KC_TABLE_SIZE] = {0};
+ int temp_indexes_size = 0;
+ int i = 0;
+ int res = 0;
+
+ if (!kc_is_ready())
+ return -ENODEV;
+
+ if (!key)
+ return -EINVAL;
+
+ if (key_size != PFK_KC_KEY_SIZE)
+ return -EINVAL;
+
+ memset(temp_indexes, -1, sizeof(temp_indexes));
+
+ kc_spin_lock();
+
+ entry = kc_find_key_at_index(key, key_size, NULL, 0, &index);
+ if (!entry) {
+ pr_err("%s: key does not exist\n", __func__);
+ kc_spin_unlock();
+ return -EINVAL;
+ }
+
+ res = kc_entry_start_invalidating(entry);
+ if (res != 0) {
+ kc_spin_unlock();
+ return res;
+ }
+
+ temp_indexes[temp_indexes_size++] = index;
+ kc_clear_entry(entry);
+
+ /* let's clean additional entries with the same key if there are any */
+ do {
+ index++;
+ entry = kc_find_key_at_index(key, key_size, NULL, 0, &index);
+ if (!entry)
+ break;
+
+ res = kc_entry_start_invalidating(entry);
+ if (res != 0) {
+ kc_spin_unlock();
+ goto out;
+ }
+
+ temp_indexes[temp_indexes_size++] = index;
+
+ kc_clear_entry(entry);
+
+
+ } while (true);
+
+ kc_spin_unlock();
+
+ temp_indexes_size--;
+ for (i = temp_indexes_size; i >= 0 ; i--)
+ qti_pfk_ice_invalidate_key(
+ kc_entry_at_index(temp_indexes[i])->key_index,
+ s_type);
+
+ /* fall through */
+ res = 0;
+
+out:
+ kc_spin_lock();
+ for (i = temp_indexes_size; i >= 0 ; i--)
+ kc_entry_finish_invalidating(
+ kc_entry_at_index(temp_indexes[i]));
+ kc_spin_unlock();
+
+ return res;
+}
+
+/**
+ * pfk_kc_clear() - clear the table and remove all keys from ICE
+ *
+ * Return 0 on success, error otherwise
+ *
+ */
+int pfk_kc_clear(void)
+{
+ struct kc_entry *entry = NULL;
+ int i = 0;
+ int res = 0;
+
+ if (!kc_is_ready())
+ return -ENODEV;
+
+ kc_spin_lock();
+ for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+ entry = kc_entry_at_index(i);
+ res = kc_entry_start_invalidating(entry);
+ if (res != 0) {
+ kc_spin_unlock();
+ goto out;
+ }
+ kc_clear_entry(entry);
+ }
+ kc_spin_unlock();
+
+ for (i = 0; i < PFK_KC_TABLE_SIZE; i++)
+ qti_pfk_ice_invalidate_key(kc_entry_at_index(i)->key_index,
+ s_type);
+
+ /* fall through */
+ res = 0;
+out:
+ kc_spin_lock();
+ for (i = 0; i < PFK_KC_TABLE_SIZE; i++)
+ kc_entry_finish_invalidating(kc_entry_at_index(i));
+ kc_spin_unlock();
+
+ return res;
+}
+
+/**
+ * pfk_kc_clear_on_reset() - clear the table and remove all keys from ICE
+ * The assumption is that at this point we don't have any pending transactions
+ * Also, there is no need to clear keys from ICE
+ *
+ * Return 0 on success, error otherwise
+ *
+ */
+void pfk_kc_clear_on_reset(void)
+{
+ struct kc_entry *entry = NULL;
+ int i = 0;
+
+ if (!kc_is_ready())
+ return;
+
+ kc_spin_lock();
+ for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+ entry = kc_entry_at_index(i);
+ kc_clear_entry(entry);
+ }
+ kc_spin_unlock();
+}
+
+static int pfk_kc_find_storage_type(char **device)
+{
+ char boot[20] = {'\0'};
+ char *match = (char *)strnstr(saved_command_line,
+ "androidboot.bootdevice=",
+ strlen(saved_command_line));
+ if (match) {
+ memcpy(boot, (match + strlen("androidboot.bootdevice=")),
+ sizeof(boot) - 1);
+ if (strnstr(boot, PFK_UFS, strlen(boot)))
+ *device = PFK_UFS;
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int __init pfk_kc_pre_init(void)
+{
+ return pfk_kc_find_storage_type(&s_type);
+}
+
+static void __exit pfk_kc_exit(void)
+{
+ s_type = NULL;
+}
+
+module_init(pfk_kc_pre_init);
+module_exit(pfk_kc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Key-KC driver");
diff --git a/security/pfe/pfk_kc.h b/security/pfe/pfk_kc.h
new file mode 100644
index 0000000..6adeee2
--- /dev/null
+++ b/security/pfe/pfk_kc.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PFK_KC_H_
+#define PFK_KC_H_
+
+#include <linux/types.h>
+
+int pfk_kc_init(void);
+int pfk_kc_deinit(void);
+int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size, u32 *key_index,
+ bool async);
+void pfk_kc_load_key_end(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size);
+int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size,
+ const unsigned char *salt, size_t salt_size);
+int pfk_kc_remove_key(const unsigned char *key, size_t key_size);
+int pfk_kc_clear(void);
+void pfk_kc_clear_on_reset(void);
+extern char *saved_command_line;
+
+
+#endif /* PFK_KC_H_ */
diff --git a/security/security.c b/security/security.c
index e43c50c..35c8dce 100644
--- a/security/security.c
+++ b/security/security.c
@@ -525,6 +525,14 @@
}
EXPORT_SYMBOL_GPL(security_inode_create);
+int security_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+ return call_int_hook(inode_post_create, 0, dir, dentry, mode);
+}
+
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
{
@@ -1700,6 +1708,8 @@
.inode_init_security =
LIST_HEAD_INIT(security_hook_heads.inode_init_security),
.inode_create = LIST_HEAD_INIT(security_hook_heads.inode_create),
+ .inode_post_create =
+ LIST_HEAD_INIT(security_hook_heads.inode_post_create),
.inode_link = LIST_HEAD_INIT(security_hook_heads.inode_link),
.inode_unlink = LIST_HEAD_INIT(security_hook_heads.inode_unlink),
.inode_symlink =
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 52f3c55..84d9a2e 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -348,26 +348,27 @@
struct avc_xperms_decision_node *xpd_node;
struct extended_perms_decision *xpd;
- xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, GFP_NOWAIT);
+ xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd_node)
return NULL;
xpd = &xpd_node->xpd;
if (which & XPERMS_ALLOWED) {
xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_NOWAIT);
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd->allowed)
goto error;
}
if (which & XPERMS_AUDITALLOW) {
xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_NOWAIT);
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd->auditallow)
goto error;
}
if (which & XPERMS_DONTAUDIT) {
xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_NOWAIT);
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd->dontaudit)
goto error;
}
@@ -395,7 +396,8 @@
{
struct avc_xperms_node *xp_node;
- xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT);
+ xp_node = kmem_cache_zalloc(avc_xperms_cachep,
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xp_node)
return xp_node;
INIT_LIST_HEAD(&xp_node->xpd_head);
@@ -548,7 +550,7 @@
{
struct avc_node *node;
- node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT);
+ node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT | __GFP_NOWARN);
if (!node)
goto out;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3b02b82..5f3fa60 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -407,18 +407,6 @@
kfree(sbsec);
}
-/* The file system's label must be initialized prior to use. */
-
-static const char *labeling_behaviors[7] = {
- "uses xattr",
- "uses transition SIDs",
- "uses task SIDs",
- "uses genfs_contexts",
- "not configured for labeling",
- "uses mountpoint labeling",
- "uses native labeling",
-};
-
static inline int inode_doinit(struct inode *inode)
{
return inode_doinit_with_dentry(inode, NULL);
@@ -530,10 +518,6 @@
}
}
- if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
- printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
- sb->s_id, sb->s_type->name);
-
sbsec->flags |= SE_SBINITIALIZED;
if (selinux_is_sblabel_mnt(sb))
sbsec->flags |= SBLABEL_MNT;
@@ -1108,10 +1092,9 @@
goto out_err;
opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC);
- if (!opts->mnt_opts_flags) {
- kfree(opts->mnt_opts);
+ if (!opts->mnt_opts_flags)
goto out_err;
- }
+
if (fscontext) {
opts->mnt_opts[num_mnt_opts] = fscontext;
@@ -1134,6 +1117,7 @@
return 0;
out_err:
+ security_free_mnt_opts(opts);
kfree(context);
kfree(defcontext);
kfree(fscontext);
@@ -2062,8 +2046,9 @@
static inline u32 open_file_to_av(struct file *file)
{
u32 av = file_to_av(file);
+ struct inode *inode = file_inode(file);
- if (selinux_policycap_openperm)
+ if (selinux_policycap_openperm && inode->i_sb->s_magic != SOCKFS_MAGIC)
av |= FILE__OPEN;
return av;
@@ -3066,6 +3051,7 @@
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
const struct cred *cred = current_cred();
+ struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = iattr->ia_valid;
__u32 av = FILE__WRITE;
@@ -3081,8 +3067,10 @@
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, dentry, FILE__SETATTR);
- if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE)
- && !(ia_valid & ATTR_FILE))
+ if (selinux_policycap_openperm &&
+ inode->i_sb->s_magic != SOCKFS_MAGIC &&
+ (ia_valid & ATTR_SIZE) &&
+ !(ia_valid & ATTR_FILE))
av |= FILE__OPEN;
return dentry_has_perm(cred, dentry, av);
@@ -4347,10 +4335,18 @@
u32 sid, node_perm;
if (family == PF_INET) {
+ if (addrlen < sizeof(struct sockaddr_in)) {
+ err = -EINVAL;
+ goto out;
+ }
addr4 = (struct sockaddr_in *)address;
snum = ntohs(addr4->sin_port);
addrp = (char *)&addr4->sin_addr.s_addr;
} else {
+ if (addrlen < SIN6_LEN_RFC2133) {
+ err = -EINVAL;
+ goto out;
+ }
addr6 = (struct sockaddr_in6 *)address;
snum = ntohs(addr6->sin6_port);
addrp = (char *)&addr6->sin6_addr.s6_addr;
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 43535cd..60cdcf4 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -25,8 +25,9 @@
#include <linux/in.h>
#include <linux/spinlock.h>
#include <net/net_namespace.h>
-#include "flask.h"
-#include "avc.h"
+//#include "flask.h"
+//#include "avc.h"
+#include "security.h"
struct task_security_struct {
u32 osid; /* SID prior to last execve */
@@ -52,6 +53,8 @@
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
+ u32 tag; /* Per-File-Encryption tag */
+ void *pfk_data; /* Per-File-Key data from ecryptfs */
struct mutex lock;
};
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 308a286..b8e98c1 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -12,7 +12,6 @@
#include <linux/dcache.h>
#include <linux/magic.h>
#include <linux/types.h>
-#include "flask.h"
#define SECSID_NULL 0x00000000 /* unspecified SID */
#define SECSID_WILD 0xffffffff /* wildcard SID */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 66ea81c..a6b0970 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -154,7 +154,7 @@
}
k = 0;
- while (p_in->perms && p_in->perms[k]) {
+ while (p_in->perms[k]) {
/* An empty permission string skips ahead */
if (!*p_in->perms[k]) {
k++;
@@ -1434,7 +1434,7 @@
scontext_len, &context, def_sid);
if (rc == -EINVAL && force) {
context.str = str;
- context.len = scontext_len;
+ context.len = strlen(str) + 1;
str = NULL;
} else if (rc)
goto out_unlock;
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 1fa7076..84ee29c 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -400,8 +400,7 @@
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
goto error;
- if (get_user(data->owner, &data32->owner) ||
- get_user(data->type, &data32->type))
+ if (get_user(data->owner, &data32->owner))
goto error;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 3321348..cfb8f58 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -834,8 +834,25 @@
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
}
-static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
- bool trylock)
+/* parameter locking: returns immediately if tried during streaming */
+static int lock_params(struct snd_pcm_runtime *runtime)
+{
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+ if (atomic_read(&runtime->oss.rw_ref)) {
+ mutex_unlock(&runtime->oss.params_lock);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void unlock_params(struct snd_pcm_runtime *runtime)
+{
+ mutex_unlock(&runtime->oss.params_lock);
+}
+
+/* call with params_lock held */
+static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_params *params, *sparams;
@@ -849,11 +866,8 @@
struct snd_mask sformat_mask;
struct snd_mask mask;
- if (trylock) {
- if (!(mutex_trylock(&runtime->oss.params_lock)))
- return -EAGAIN;
- } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
- return -EINTR;
+ if (!runtime->oss.params)
+ return 0;
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
params = kmalloc(sizeof(*params), GFP_KERNEL);
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
@@ -1079,6 +1093,23 @@
kfree(sw_params);
kfree(params);
kfree(sparams);
+ return err;
+}
+
+/* this one takes the lock by itself */
+static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
+ bool trylock)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (trylock) {
+ if (!(mutex_trylock(&runtime->oss.params_lock)))
+ return -EAGAIN;
+ } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+
+ err = snd_pcm_oss_change_params_locked(substream);
mutex_unlock(&runtime->oss.params_lock);
return err;
}
@@ -1107,6 +1138,10 @@
return 0;
}
+/* call with params_lock held */
+/* NOTE: this always call PREPARE unconditionally no matter whether
+ * runtime->oss.prepare is set or not
+ */
static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
{
int err;
@@ -1131,8 +1166,6 @@
struct snd_pcm_runtime *runtime;
int err;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (runtime->oss.params) {
err = snd_pcm_oss_change_params(substream, false);
@@ -1140,6 +1173,29 @@
return err;
}
if (runtime->oss.prepare) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+ err = snd_pcm_oss_prepare(substream);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* call with params_lock held */
+static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ runtime = substream->runtime;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params_locked(substream);
+ if (err < 0)
+ return err;
+ }
+ if (runtime->oss.prepare) {
err = snd_pcm_oss_prepare(substream);
if (err < 0)
return err;
@@ -1361,19 +1417,21 @@
static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const char __user *buf, size_t bytes)
{
size_t xfer = 0;
- ssize_t tmp;
+ ssize_t tmp = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
return -ENXIO;
- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
- return tmp;
+ atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) {
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
tmp = -ERESTARTSYS;
break;
}
+ tmp = snd_pcm_oss_make_ready_locked(substream);
+ if (tmp < 0)
+ goto err;
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
@@ -1429,6 +1487,7 @@
}
tmp = 0;
}
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1468,19 +1527,21 @@
static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __user *buf, size_t bytes)
{
size_t xfer = 0;
- ssize_t tmp;
+ ssize_t tmp = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
return -ENXIO;
- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
- return tmp;
+ atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) {
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
tmp = -ERESTARTSYS;
break;
}
+ tmp = snd_pcm_oss_make_ready_locked(substream);
+ if (tmp < 0)
+ goto err;
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
@@ -1521,6 +1582,7 @@
}
tmp = 0;
}
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1536,10 +1598,12 @@
continue;
runtime = substream->runtime;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
runtime->oss.prev_hw_ptr_period = 0;
runtime->oss.period_ptr = 0;
+ mutex_unlock(&runtime->oss.params_lock);
}
return 0;
}
@@ -1625,9 +1689,13 @@
goto __direct;
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
return err;
+ atomic_inc(&runtime->oss.rw_ref);
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ atomic_dec(&runtime->oss.rw_ref);
+ return -ERESTARTSYS;
+ }
format = snd_pcm_oss_format_from(runtime->oss.format);
width = snd_pcm_format_physical_width(format);
- mutex_lock(&runtime->oss.params_lock);
if (runtime->oss.buffer_used > 0) {
#ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "sync: buffer_used\n");
@@ -1637,10 +1705,8 @@
runtime->oss.buffer + runtime->oss.buffer_used,
size);
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
- if (err < 0) {
- mutex_unlock(&runtime->oss.params_lock);
- return err;
- }
+ if (err < 0)
+ goto unlock;
} else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "sync: period_ptr\n");
@@ -1650,10 +1716,8 @@
runtime->oss.buffer,
size * 8 / width);
err = snd_pcm_oss_sync1(substream, size);
- if (err < 0) {
- mutex_unlock(&runtime->oss.params_lock);
- return err;
- }
+ if (err < 0)
+ goto unlock;
}
/*
* The ALSA's period might be a bit large than OSS one.
@@ -1684,7 +1748,11 @@
snd_pcm_lib_writev(substream, buffers, size);
}
}
+unlock:
mutex_unlock(&runtime->oss.params_lock);
+ atomic_dec(&runtime->oss.rw_ref);
+ if (err < 0)
+ return err;
/*
* finish sync: drain the buffer
*/
@@ -1695,7 +1763,9 @@
substream->f_flags = saved_f_flags;
if (err < 0)
return err;
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.prepare = 1;
+ mutex_unlock(&runtime->oss.params_lock);
}
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
@@ -1706,8 +1776,10 @@
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if (err < 0)
return err;
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.buffer_used = 0;
runtime->oss.prepare = 1;
+ mutex_unlock(&runtime->oss.params_lock);
}
return 0;
}
@@ -1719,6 +1791,8 @@
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime;
+ int err;
+
if (substream == NULL)
continue;
runtime = substream->runtime;
@@ -1726,10 +1800,14 @@
rate = 1000;
else if (rate > 192000)
rate = 192000;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.rate != rate) {
runtime->oss.params = 1;
runtime->oss.rate = rate;
}
+ unlock_params(runtime);
}
return snd_pcm_oss_get_rate(pcm_oss_file);
}
@@ -1754,13 +1832,19 @@
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime;
+ int err;
+
if (substream == NULL)
continue;
runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.channels != channels) {
runtime->oss.params = 1;
runtime->oss.channels = channels;
}
+ unlock_params(runtime);
}
return snd_pcm_oss_get_channels(pcm_oss_file);
}
@@ -1814,10 +1898,9 @@
return -ENOMEM;
_snd_pcm_hw_params_any(params);
err = snd_pcm_hw_refine(substream, params);
- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- kfree(params);
if (err < 0)
- return err;
+ goto error;
+ format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
for (fmt = 0; fmt < 32; ++fmt) {
if (snd_mask_test(&format_mask, fmt)) {
int f = snd_pcm_oss_format_to(fmt);
@@ -1825,12 +1908,16 @@
formats |= f;
}
}
- return formats;
+
+ error:
+ kfree(params);
+ return err < 0 ? err : formats;
}
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
{
int formats, idx;
+ int err;
if (format != AFMT_QUERY) {
formats = snd_pcm_oss_get_formats(pcm_oss_file);
@@ -1844,10 +1931,14 @@
if (substream == NULL)
continue;
runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.format != format) {
runtime->oss.params = 1;
runtime->oss.format = format;
}
+ unlock_params(runtime);
}
}
return snd_pcm_oss_get_format(pcm_oss_file);
@@ -1867,8 +1958,6 @@
{
struct snd_pcm_runtime *runtime;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (subdivide == 0) {
subdivide = runtime->oss.subdivision;
@@ -1892,9 +1981,17 @@
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
+ struct snd_pcm_runtime *runtime;
+
if (substream == NULL)
continue;
- if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
+ runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
+ err = snd_pcm_oss_set_subdivide1(substream, subdivide);
+ unlock_params(runtime);
+ if (err < 0)
return err;
}
return err;
@@ -1904,8 +2001,6 @@
{
struct snd_pcm_runtime *runtime;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (runtime->oss.subdivision || runtime->oss.fragshift)
return -EINVAL;
@@ -1925,9 +2020,17 @@
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
+ struct snd_pcm_runtime *runtime;
+
if (substream == NULL)
continue;
- if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
+ runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
+ err = snd_pcm_oss_set_fragment1(substream, val);
+ unlock_params(runtime);
+ if (err < 0)
return err;
}
return err;
@@ -2011,6 +2114,9 @@
}
if (psubstream) {
runtime = psubstream->runtime;
+ cmd = 0;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
if (trigger & PCM_ENABLE_OUTPUT) {
if (runtime->oss.trigger)
goto _skip1;
@@ -2028,13 +2134,19 @@
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
- err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
- if (err < 0)
- return err;
- }
_skip1:
+ mutex_unlock(&runtime->oss.params_lock);
+ if (cmd) {
+ err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
if (csubstream) {
runtime = csubstream->runtime;
+ cmd = 0;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
if (trigger & PCM_ENABLE_INPUT) {
if (runtime->oss.trigger)
goto _skip2;
@@ -2049,11 +2161,14 @@
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
- err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
- if (err < 0)
- return err;
- }
_skip2:
+ mutex_unlock(&runtime->oss.params_lock);
+ if (cmd) {
+ err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
@@ -2305,6 +2420,7 @@
runtime->oss.maxfrags = 0;
runtime->oss.subdivision = 0;
substream->pcm_release = snd_pcm_oss_release_substream;
+ atomic_set(&runtime->oss.rw_ref, 0);
}
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 48f6aee..d79a04e 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -28,6 +28,7 @@
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
+#include <sound/timer.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -1036,8 +1037,13 @@
snd_free_pages((void*)runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
- kfree(runtime);
+ /* Avoid concurrent access to runtime via PCM timer interface */
+ if (substream->timer)
+ spin_lock_irq(&substream->timer->lock);
substream->runtime = NULL;
+ if (substream->timer)
+ spin_unlock_irq(&substream->timer->lock);
+ kfree(runtime);
put_pid(substream->pid);
substream->pid = NULL;
substream->pstr->substream_opened--;
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index e1512ae..0c81e26 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -426,6 +426,8 @@
return -ENOTTY;
if (substream->stream != dir)
return -EINVAL;
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
if ((ch = substream->runtime->channels) > 128)
return -EINVAL;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 8e5649a..2df7e6b 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2765,6 +2765,7 @@
sync_ptr.s.status.hw_ptr = status->hw_ptr;
sync_ptr.s.status.tstamp = status->tstamp;
sync_ptr.s.status.suspended_state = status->suspended_state;
+ sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
return -EFAULT;
@@ -3460,7 +3461,7 @@
area,
substream->runtime->dma_area,
substream->runtime->dma_addr,
- area->vm_end - area->vm_start);
+ substream->runtime->dma_bytes);
#endif /* CONFIG_X86 */
/* mmap with fault handler */
area->vm_ops = &snd_pcm_vm_ops_data_fault;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 5143801..180261d 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -976,9 +976,9 @@
struct snd_rawmidi_runtime *runtime = substream->runtime;
unsigned long appl_ptr;
- spin_lock_irqsave(&runtime->lock, flags);
if (userbuf)
mutex_lock(&runtime->realloc_mutex);
+ spin_lock_irqsave(&runtime->lock, flags);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index f69764d..e30e30b 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -36,8 +36,6 @@
struct snd_rawmidi_params params;
unsigned int val;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(params.stream, &src->stream) ||
get_user(params.buffer_size, &src->buffer_size) ||
get_user(params.avail_min, &src->avail_min) ||
@@ -46,8 +44,12 @@
params.no_active_sensing = val;
switch (params.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
return snd_rawmidi_output_params(rfile->output, ¶ms);
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
return snd_rawmidi_input_params(rfile->input, ¶ms);
}
return -EINVAL;
@@ -67,16 +69,18 @@
int err;
struct snd_rawmidi_status status;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(status.stream, &src->stream))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
@@ -112,16 +116,18 @@
int err;
struct snd_rawmidi_status status;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(status.stream, &src->stream))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
index c390886..86ca584 100644
--- a/sound/core/seq/oss/seq_oss_event.c
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -26,6 +26,7 @@
#include <sound/seq_oss_legacy.h>
#include "seq_oss_readq.h"
#include "seq_oss_writeq.h"
+#include <linux/nospec.h>
/*
@@ -287,10 +288,10 @@
{
struct seq_oss_synthinfo *info;
- if (!snd_seq_oss_synth_is_valid(dp, dev))
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
return -ENXIO;
- info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -298,6 +299,7 @@
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
+ ch = array_index_nospec(ch, info->nr_voices);
if (note == 255 && info->ch[ch].note >= 0) {
/* volume control */
int type;
@@ -347,10 +349,10 @@
{
struct seq_oss_synthinfo *info;
- if (!snd_seq_oss_synth_is_valid(dp, dev))
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
return -ENXIO;
- info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -358,6 +360,7 @@
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
+ ch = array_index_nospec(ch, info->nr_voices);
if (info->ch[ch].note >= 0) {
note = info->ch[ch].note;
info->ch[ch].vel = 0;
@@ -381,7 +384,7 @@
static int
set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ if (!snd_seq_oss_synth_info(dp, dev))
return -ENXIO;
ev->type = type;
@@ -399,7 +402,7 @@
static int
set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ if (!snd_seq_oss_synth_info(dp, dev))
return -ENXIO;
ev->type = type;
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index b30b213..9debd1b 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -29,6 +29,7 @@
#include "../seq_lock.h"
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
@@ -315,6 +316,7 @@
{
if (dev < 0 || dev >= dp->max_mididev)
return NULL;
+ dev = array_index_nospec(dev, dp->max_mididev);
return get_mdev(dev);
}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index cd0e0eb..278ebb9 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -26,6 +26,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
* constants
@@ -339,17 +340,13 @@
dp->max_synthdev = 0;
}
-/*
- * check if the specified device is MIDI mapped device
- */
-static int
-is_midi_dev(struct seq_oss_devinfo *dp, int dev)
+static struct seq_oss_synthinfo *
+get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_synthdev)
- return 0;
- if (dp->synths[dev].is_midi)
- return 1;
- return 0;
+ return NULL;
+ dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS);
+ return &dp->synths[dev];
}
/*
@@ -359,14 +356,20 @@
get_synthdev(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
- if (dev < 0 || dev >= dp->max_synthdev)
+ struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);
+
+ if (!info)
return NULL;
- if (! dp->synths[dev].opened)
+ if (!info->opened)
return NULL;
- if (dp->synths[dev].is_midi)
- return &midi_synth_dev;
- if ((rec = get_sdev(dev)) == NULL)
- return NULL;
+ if (info->is_midi) {
+ rec = &midi_synth_dev;
+ snd_use_lock_use(&rec->use_lock);
+ } else {
+ rec = get_sdev(dev);
+ if (!rec)
+ return NULL;
+ }
if (! rec->opened) {
snd_use_lock_free(&rec->use_lock);
return NULL;
@@ -402,10 +405,8 @@
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
- return;
- info = &dp->synths[dev];
- if (! info->opened)
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info || !info->opened)
return;
if (info->sysex)
info->sysex->len = 0; /* reset sysex */
@@ -454,12 +455,14 @@
const char __user *buf, int p, int c)
{
struct seq_oss_synth *rec;
+ struct seq_oss_synthinfo *info;
int rc;
- if (dev < 0 || dev >= dp->max_synthdev)
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info)
return -ENXIO;
- if (is_midi_dev(dp, dev))
+ if (info->is_midi)
return 0;
if ((rec = get_synthdev(dp, dev)) == NULL)
return -ENXIO;
@@ -467,24 +470,25 @@
if (rec->oper.load_patch == NULL)
rc = -ENXIO;
else
- rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
+ rc = rec->oper.load_patch(&info->arg, fmt, buf, p, c);
snd_use_lock_free(&rec->use_lock);
return rc;
}
/*
- * check if the device is valid synth device
+ * check if the device is valid synth device and return the synth info
*/
-int
-snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev)
+struct seq_oss_synthinfo *
+snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
+
rec = get_synthdev(dp, dev);
if (rec) {
snd_use_lock_free(&rec->use_lock);
- return 1;
+ return get_synthinfo_nospec(dp, dev);
}
- return 0;
+ return NULL;
}
@@ -499,16 +503,18 @@
int i, send;
unsigned char *dest;
struct seq_oss_synth_sysex *sysex;
+ struct seq_oss_synthinfo *info;
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
return -ENXIO;
- sysex = dp->synths[dev].sysex;
+ sysex = info->sysex;
if (sysex == NULL) {
sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);
if (sysex == NULL)
return -ENOMEM;
- dp->synths[dev].sysex = sysex;
+ info->sysex = sysex;
}
send = 0;
@@ -553,10 +559,12 @@
int
snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev);
+
+ if (!info)
return -EINVAL;
- snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
- dp->synths[dev].arg.addr.port);
+ snd_seq_oss_fill_addr(dp, ev, info->arg.addr.client,
+ info->arg.addr.port);
return 0;
}
@@ -568,16 +576,18 @@
snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)
{
struct seq_oss_synth *rec;
+ struct seq_oss_synthinfo *info;
int rc;
- if (is_midi_dev(dp, dev))
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info || info->is_midi)
return -ENXIO;
if ((rec = get_synthdev(dp, dev)) == NULL)
return -ENXIO;
if (rec->oper.ioctl == NULL)
rc = -ENXIO;
else
- rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
+ rc = rec->oper.ioctl(&info->arg, cmd, addr);
snd_use_lock_free(&rec->use_lock);
return rc;
}
@@ -589,7 +599,10 @@
int
snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info || info->is_midi)
return -ENXIO;
ev->type = SNDRV_SEQ_EVENT_OSS;
memcpy(ev->data.raw8.d, data, 8);
diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h
index 74ac55f..a63f9e2 100644
--- a/sound/core/seq/oss/seq_oss_synth.h
+++ b/sound/core/seq/oss/seq_oss_synth.h
@@ -37,7 +37,8 @@
void snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c);
-int snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev);
+struct seq_oss_synthinfo *snd_seq_oss_synth_info(struct seq_oss_devinfo *dp,
+ int dev);
int snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
struct snd_seq_event *ev);
int snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 0b40861..ecd1c5f 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -255,12 +255,12 @@
if (!client)
return 0;
- snd_seq_delete_all_ports(client);
- snd_seq_queue_client_leave(client->number);
spin_lock_irqsave(&clients_lock, flags);
clienttablock[client->number] = 1;
clienttab[client->number] = NULL;
spin_unlock_irqrestore(&clients_lock, flags);
+ snd_seq_delete_all_ports(client);
+ snd_seq_queue_client_leave(client->number);
snd_use_lock_sync(&client->use_lock);
snd_seq_queue_client_termination(client->number);
if (client->pool)
@@ -906,7 +906,8 @@
static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
struct snd_seq_event *event,
struct file *file, int blocking,
- int atomic, int hop)
+ int atomic, int hop,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
int err;
@@ -944,7 +945,8 @@
return -ENXIO; /* queue is not allocated */
/* allocate an event cell */
- err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+ err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
+ file, mutexp);
if (err < 0)
return err;
@@ -1013,12 +1015,11 @@
return -ENXIO;
/* allocate the pool now if the pool is not allocated yet */
+ mutex_lock(&client->ioctl_mutex);
if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
- mutex_lock(&client->ioctl_mutex);
err = snd_seq_pool_init(client->pool);
- mutex_unlock(&client->ioctl_mutex);
if (err < 0)
- return -ENOMEM;
+ goto out;
}
/* only process whole events */
@@ -1069,7 +1070,7 @@
/* ok, enqueue it */
err = snd_seq_client_enqueue_event(client, &event, file,
!(file->f_flags & O_NONBLOCK),
- 0, 0);
+ 0, 0, &client->ioctl_mutex);
if (err < 0)
break;
@@ -1080,6 +1081,8 @@
written += len;
}
+ out:
+ mutex_unlock(&client->ioctl_mutex);
return written ? written : err;
}
@@ -1835,6 +1838,9 @@
(! snd_seq_write_pool_allocated(client) ||
info->output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
+ /* is the pool in use? */
+ if (atomic_read(&client->pool->counter))
+ return -EBUSY;
/* remove all existing cells */
snd_seq_pool_mark_closing(client->pool);
snd_seq_queue_client_leave_cells(client->number);
@@ -2259,7 +2265,8 @@
if (! cptr->accept_output)
result = -EPERM;
else /* send it */
- result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+ result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+ atomic, hop, NULL);
snd_seq_client_unlock(cptr);
return result;
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 3490d21..9acbed1 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -123,7 +123,7 @@
return -EINVAL;
snd_use_lock_use(&f->use_lock);
- err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+ err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
if (err < 0) {
if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 5847c44..4c8cbcd 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -221,7 +221,8 @@
*/
static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
struct snd_seq_event_cell **cellp,
- int nonblock, struct file *file)
+ int nonblock, struct file *file,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
@@ -245,7 +246,11 @@
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pool->output_sleep, &wait);
spin_unlock_irq(&pool->lock);
+ if (mutexp)
+ mutex_unlock(mutexp);
schedule();
+ if (mutexp)
+ mutex_lock(mutexp);
spin_lock_irq(&pool->lock);
remove_wait_queue(&pool->output_sleep, &wait);
/* interrupted? */
@@ -288,7 +293,7 @@
*/
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
struct snd_seq_event_cell **cellp, int nonblock,
- struct file *file)
+ struct file *file, struct mutex *mutexp)
{
int ncells, err;
unsigned int extlen;
@@ -305,7 +310,7 @@
if (ncells >= pool->total_elements)
return -ENOMEM;
- err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &cell, nonblock, file, mutexp);
if (err < 0)
return err;
@@ -331,7 +336,8 @@
int size = sizeof(struct snd_seq_event);
if (len < size)
size = len;
- err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
+ mutexp);
if (err < 0)
goto __error;
if (cell->event.data.ext.ptr == NULL)
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 32f959c..3abe306 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -66,7 +66,8 @@
void snd_seq_cell_free(struct snd_seq_event_cell *cell);
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
- struct snd_seq_event_cell **cellp, int nonblock, struct file *file);
+ struct snd_seq_event_cell **cellp, int nonblock,
+ struct file *file, struct mutex *mutexp);
/* return number of unused (free) cells */
static inline int snd_seq_unused_cells(struct snd_seq_pool *pool)
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index bc1c848..2bc6759 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -87,7 +87,7 @@
if (f->cells > 0) {
/* drain prioQ */
while (f->cells > 0)
- snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+ snd_seq_cell_free(snd_seq_prioq_cell_out(f, NULL));
}
kfree(f);
@@ -214,8 +214,18 @@
return 0;
}
+/* return 1 if the current time >= event timestamp */
+static int event_is_ready(struct snd_seq_event *ev, void *current_time)
+{
+ if ((ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK)
+ return snd_seq_compare_tick_time(current_time, &ev->time.tick);
+ else
+ return snd_seq_compare_real_time(current_time, &ev->time.time);
+}
+
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
@@ -227,6 +237,8 @@
spin_lock_irqsave(&f->lock, flags);
cell = f->head;
+ if (cell && current_time && !event_is_ready(&cell->event, current_time))
+ cell = NULL;
if (cell) {
f->head = cell->next;
@@ -252,18 +264,6 @@
return f->cells;
}
-
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f)
-{
- if (f == NULL) {
- pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
- return NULL;
- }
- return f->head;
-}
-
-
static inline int prioq_match(struct snd_seq_event_cell *cell,
int client, int timestamp)
{
diff --git a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h
index d38bb78..2c315ca 100644
--- a/sound/core/seq/seq_prioq.h
+++ b/sound/core/seq/seq_prioq.h
@@ -44,14 +44,12 @@
int snd_seq_prioq_cell_in(struct snd_seq_prioq *f, struct snd_seq_event_cell *cell);
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f);
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time);
/* return number of events available in prioq */
int snd_seq_prioq_avail(struct snd_seq_prioq *f);
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq *f);
-
/* client left queue */
void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 79e0c56..1a6dc4f 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -277,30 +277,20 @@
__again:
/* Process tick queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
- if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick,
- &cell->event.time.tick)) {
- cell = snd_seq_prioq_cell_out(q->tickq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->tickq,
+ &q->timer->tick.cur_tick);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
}
-
/* Process time queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
- if (snd_seq_compare_real_time(&q->timer->cur_time,
- &cell->event.time.time)) {
- cell = snd_seq_prioq_cell_out(q->timeq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
}
/* free lock */
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 2007649..8bdc4c9 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -174,12 +174,12 @@
}
return;
}
+ spin_lock_irqsave(&substream->runtime->lock, flags);
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
+ goto out;
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
}
- spin_lock_irqsave(&substream->runtime->lock, flags);
while (1) {
count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
if (count <= 0)
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index cbd20cb..847f703 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -192,6 +192,11 @@
dpcm->timer.expires = 0;
}
+static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm)
+{
+ del_timer_sync(&dpcm->timer);
+}
+
#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
@@ -291,6 +296,8 @@
cable->pause |= stream;
loopback_timer_stop(dpcm);
spin_unlock(&cable->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -299,6 +306,8 @@
cable->pause &= ~stream;
loopback_timer_start(dpcm);
spin_unlock(&cable->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
default:
return -EINVAL;
@@ -326,6 +335,8 @@
struct loopback_cable *cable = dpcm->cable;
int bps, salign;
+ loopback_timer_stop_sync(dpcm);
+
salign = (snd_pcm_format_width(runtime->format) *
runtime->channels) / 8;
bps = salign * runtime->rate;
@@ -659,7 +670,9 @@
return;
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
+ spin_lock_irq(&cable->lock);
cable->streams[substream->stream] = NULL;
+ spin_unlock_irq(&cable->lock);
} else {
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
@@ -699,7 +712,6 @@
loopback->cables[substream->number][dev] = cable;
}
dpcm->cable = cable;
- cable->streams[substream->stream] = dpcm;
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
@@ -731,6 +743,11 @@
runtime->hw = loopback_pcm_hardware;
else
runtime->hw = cable->hw;
+
+ spin_lock_irq(&cable->lock);
+ cable->streams[substream->stream] = dpcm;
+ spin_unlock_irq(&cable->lock);
+
unlock:
if (err < 0) {
free_cable(substream);
@@ -745,7 +762,7 @@
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
- loopback_timer_stop(dpcm);
+ loopback_timer_stop_sync(dpcm);
mutex_lock(&loopback->cable_lock);
free_cable(substream);
mutex_unlock(&loopback->cable_lock);
@@ -815,9 +832,11 @@
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
@@ -849,9 +868,11 @@
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
@@ -863,12 +884,14 @@
int change = 0;
val = ucontrol->value.integer.value[0] ? 1 : 0;
+ mutex_lock(&loopback->cable_lock);
if (val != loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify) {
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify = val;
change = 1;
}
+ mutex_unlock(&loopback->cable_lock);
return change;
}
@@ -876,13 +899,18 @@
struct snd_ctl_elem_value *ucontrol)
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
- struct loopback_cable *cable = loopback->cables
- [kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ struct loopback_cable *cable;
+
unsigned int val = 0;
- if (cable != NULL)
- val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
- 1 : 0;
+ mutex_lock(&loopback->cable_lock);
+ cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ if (cable != NULL) {
+ unsigned int running = cable->running ^ cable->pause;
+
+ val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0;
+ }
+ mutex_unlock(&loopback->cable_lock);
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -925,9 +953,11 @@
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
@@ -947,9 +977,11 @@
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].channels;
+ mutex_unlock(&loopback->cable_lock);
return 0;
}
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index ddcc1a3..42920a2 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/nospec.h>
#include <sound/opl3.h>
#include <sound/asound_fm.h>
@@ -448,7 +449,7 @@
{
unsigned short reg_side;
unsigned char op_offset;
- unsigned char voice_offset;
+ unsigned char voice_offset, voice_op;
unsigned short opl3_reg;
unsigned char reg_val;
@@ -473,7 +474,9 @@
voice_offset = voice->voice - MAX_OPL2_VOICES;
}
/* Get register offset of operator */
- op_offset = snd_opl3_regmap[voice_offset][voice->op];
+ voice_offset = array_index_nospec(voice_offset, MAX_OPL2_VOICES);
+ voice_op = array_index_nospec(voice->op, 4);
+ op_offset = snd_opl3_regmap[voice_offset][voice_op];
reg_val = 0x00;
/* Set amplitude modulation (tremolo) effect */
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 9741757..d0ad61f 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -471,8 +471,9 @@
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
*/
- if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+ if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+ (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index f7c054b..8136bd2 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -29,6 +29,8 @@
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
* packet is larger than IEC 61883-6 defines. Current implementation
* allows 5 times as large as IEC 61883-6 defines.
+ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
+ * valid EOH.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -39,6 +41,7 @@
CIP_SKIP_DBC_ZERO_CHECK = 0x10,
CIP_EMPTY_HAS_WRONG_DBC = 0x20,
CIP_JUMBO_PAYLOAD = 0x40,
+ CIP_HEADER_WITHOUT_EOH = 0x80,
};
/**
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index ec4db3a..257cfbf 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -425,7 +425,7 @@
err = init_stream(dice, AMDTP_IN_STREAM, i);
if (err < 0) {
for (; i >= 0; i--)
- destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
goto end;
}
}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 25e9f77..0d3d36f 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -14,7 +14,7 @@
#define OUI_WEISS 0x001c6a
#define OUI_LOUD 0x000ff2
#define OUI_FOCUSRITE 0x00130e
-#define OUI_TCELECTRONIC 0x001486
+#define OUI_TCELECTRONIC 0x000166
#define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index b3cffd0..a468854 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -28,6 +28,9 @@
*/
#define MAX_MIDI_RX_BLOCKS 8
+/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
+#define MAX_MIDI_PORTS 3
+
/*
* The double-oh-three algorithm was discovered by Robin Gareus and Damien
* Zammit in 2012, with reverse-engineering for Digi 003 Rack.
@@ -42,10 +45,8 @@
unsigned int pcm_channels;
struct dot_state state;
- unsigned int midi_ports;
- /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
- struct snd_rawmidi_substream *midi[2];
- int midi_fifo_used[2];
+ struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
+ int midi_fifo_used[MAX_MIDI_PORTS];
int midi_fifo_limit;
void (*transfer_samples)(struct amdtp_stream *s,
@@ -124,8 +125,8 @@
return -EBUSY;
/*
- * A first data channel is for MIDI conformant data channel, the rest is
- * Multi Bit Linear Audio data channel.
+ * A first data channel is for MIDI messages, the rest is Multi Bit
+ * Linear Audio data channel.
*/
err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
if (err < 0)
@@ -135,11 +136,6 @@
p->pcm_channels = pcm_channels;
- if (s->direction == AMDTP_IN_STREAM)
- p->midi_ports = DOT_MIDI_IN_PORTS;
- else
- p->midi_ports = DOT_MIDI_OUT_PORTS;
-
/*
* We do not know the actual MIDI FIFO size of most devices. Just
* assume two bytes, i.e., one byte can be received over the bus while
@@ -281,13 +277,25 @@
b = (u8 *)&buffer[0];
len = 0;
- if (port < p->midi_ports &&
+ if (port < MAX_MIDI_PORTS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL)
len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
if (len > 0) {
- b[3] = (0x10 << port) | len;
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1.
+ * - 0010b: physical MIDI port 2.
+ * - 1110b: console MIDI port.
+ */
+ if (port == 2)
+ b[3] = 0xe0;
+ else if (port == 1)
+ b[3] = 0x20;
+ else
+ b[3] = 0x00;
+ b[3] |= len;
midi_use_bytes(s, port, len);
} else {
b[1] = 0;
@@ -309,11 +317,22 @@
for (f = 0; f < data_blocks; f++) {
b = (u8 *)&buffer[0];
- port = b[3] >> 4;
- len = b[3] & 0x0f;
- if (port < p->midi_ports && p->midi[port] && len > 0)
- snd_rawmidi_receive(p->midi[port], b + 1, len);
+ len = b[3] & 0x0f;
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1. Use port 0.
+ * - 1110b: console MIDI port. Use port 2.
+ */
+ if (b[3] >> 4 > 0)
+ port = 2;
+ else
+ port = 0;
+
+ if (port < MAX_MIDI_PORTS && p->midi[port])
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+ }
buffer += s->data_block_quadlets;
}
@@ -364,7 +383,7 @@
{
struct amdtp_dot *p = s->protocol;
- if (port < p->midi_ports)
+ if (port < MAX_MIDI_PORTS)
ACCESS_ONCE(p->midi[port]) = midi;
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index cc4776c..1f5e1d2 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -13,7 +13,8 @@
MODULE_LICENSE("GPL v2");
#define VENDOR_DIGIDESIGN 0x00a07e
-#define MODEL_DIGI00X 0x000002
+#define MODEL_CONSOLE 0x000001
+#define MODEL_RACK 0x000002
static int name_card(struct snd_dg00x *dg00x)
{
@@ -129,6 +130,8 @@
spin_lock_init(&dg00x->lock);
init_waitqueue_head(&dg00x->hwdep_wait);
+ dg00x->is_console = entry->model_id == MODEL_CONSOLE;
+
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
snd_fw_schedule_registration(unit, &dg00x->dwork);
@@ -183,7 +186,13 @@
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = VENDOR_DIGIDESIGN,
- .model_id = MODEL_DIGI00X,
+ .model_id = MODEL_CONSOLE,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .model_id = MODEL_RACK,
},
{}
};
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 2cd465c..43bcb0c 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -60,6 +60,7 @@
/* For asynchronous MIDI controls. */
struct snd_rawmidi_substream *in_control;
struct snd_fw_async_midi_port out_control;
+ bool is_console;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull
diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c
index 7eb6171..a31a70d 100644
--- a/sound/pci/asihpi/hpimsginit.c
+++ b/sound/pci/asihpi/hpimsginit.c
@@ -23,6 +23,7 @@
#include "hpi_internal.h"
#include "hpimsginit.h"
+#include <linux/nospec.h>
/* The actual message size for each object type */
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
@@ -39,10 +40,12 @@
{
u16 size;
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
size = msg_size[object];
- else
+ } else {
size = sizeof(*phm);
+ }
memset(phm, 0, size);
phm->size = size;
@@ -66,10 +69,12 @@
{
u16 size;
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
size = res_size[object];
- else
+ } else {
size = sizeof(*phr);
+ }
memset(phr, 0, sizeof(*phr));
phr->size = size;
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 7e3aa50..3ef9af5 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -33,6 +33,7 @@
#include <linux/stringify.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
+#include <linux/nospec.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
@@ -182,7 +183,8 @@
struct hpi_adapter *pa = NULL;
if (hm->h.adapter_index < ARRAY_SIZE(adapters))
- pa = &adapters[hm->h.adapter_index];
+ pa = &adapters[array_index_nospec(hm->h.adapter_index,
+ ARRAY_SIZE(adapters))];
if (!pa || !pa->adapter || !pa->adapter->type) {
hpi_init_response(&hr->r0, hm->h.object,
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 57df06e..cc009a4 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/compat.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
@@ -51,7 +52,16 @@
if (get_user(verb, &arg->verb))
return -EFAULT;
- res = get_wcaps(codec, verb >> 24);
+ /* open-code get_wcaps(verb>>24) with nospec */
+ verb >>= 24;
+ if (verb < codec->core.start_nid ||
+ verb >= codec->core.start_nid + codec->core.num_nodes) {
+ res = 0;
+ } else {
+ verb -= codec->core.start_nid;
+ verb = array_index_nospec(verb, codec->core.num_nodes);
+ res = codec->wcaps[verb];
+ }
if (put_user(res, &arg->res))
return -EFAULT;
return 0;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 293f3f2..4e91120 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -185,6 +185,10 @@
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
+static bool pm_blacklist = true;
+module_param(pm_blacklist, bool, 0644);
+MODULE_PARM_DESC(pm_blacklist, "Enable power-management blacklist");
+
/* reset the HD-audio controller in power save mode.
* this may give more power-saving, but will take longer time to
* wake up.
@@ -369,8 +373,10 @@
#define IS_KBL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d71)
#define IS_KBL_H(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa2f0)
#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
+#define IS_GLK(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x3198)
#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) || \
- IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci)
+ IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci) || \
+ IS_GLK(pci)
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
@@ -1508,7 +1514,8 @@
*/
u8 val;
pci_read_config_byte(chip->pci, 0x42, &val);
- if (!(val & 0x80) && chip->pci->revision == 0x30)
+ if (!(val & 0x80) && (chip->pci->revision == 0x30 ||
+ chip->pci->revision == 0x20))
snoop = false;
}
@@ -2042,6 +2049,26 @@
return err;
}
+#ifdef CONFIG_PM
+/* On some boards setting power_save to a non 0 value leads to clicking /
+ * popping sounds when ever we enter/leave powersaving mode. Ideally we would
+ * figure out how to avoid these sounds, but that is not always feasible.
+ * So we keep a list of devices where we disable powersaving as its known
+ * to causes problems on these devices.
+ */
+static struct snd_pci_quirk power_save_blacklist[] = {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
+ SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
+ SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0),
+ {}
+};
+#endif /* CONFIG_PM */
+
/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
[AZX_DRIVER_NVIDIA] = 8,
@@ -2054,6 +2081,7 @@
struct hdac_bus *bus = azx_bus(chip);
struct pci_dev *pci = chip->pci;
int dev = chip->dev_index;
+ int val;
int err;
hda->probe_continued = 1;
@@ -2129,7 +2157,21 @@
chip->running = 1;
azx_add_card_list(chip);
- snd_hda_set_power_save(&chip->bus, power_save * 1000);
+
+ val = power_save;
+#ifdef CONFIG_PM
+ if (pm_blacklist) {
+ const struct snd_pci_quirk *q;
+
+ q = snd_pci_quirk_lookup(chip->pci, power_save_blacklist);
+ if (q && val) {
+ dev_info(chip->card->dev, "device %04x:%04x is on the power_save blacklist, forcing power_save to 0\n",
+ q->subvendor, q->subdevice);
+ val = 0;
+ }
+ }
+#endif /* CONFIG_PM */
+ snd_hda_set_power_save(&chip->bus, val * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
pm_runtime_put_autosuspend(&pci->dev);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2c3065c..b3851b9 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -849,6 +849,8 @@
SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 89c166b..7ece1ab 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -329,6 +329,7 @@
break;
case 0x10ec0225:
case 0x10ec0233:
+ case 0x10ec0235:
case 0x10ec0236:
case 0x10ec0255:
case 0x10ec0256:
@@ -3261,8 +3262,12 @@
pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid);
pinval &= ~AC_PINCTL_VREFEN;
pinval |= enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80;
- if (spec->mute_led_nid)
+ if (spec->mute_led_nid) {
+ /* temporarily power up/down for setting VREF */
+ snd_hda_power_up_pm(codec);
snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
+ snd_hda_power_down_pm(codec);
+ }
}
/* Make sure the led works even in runtime suspend */
@@ -4480,13 +4485,14 @@
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ } else if (action == HDA_FIXUP_ACT_INIT) {
/* Enable DOCK device */
snd_hda_codec_write(codec, 0x17, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
/* Enable DOCK device */
snd_hda_codec_write(codec, 0x19, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
- snd_hda_apply_pincfgs(codec, pincfgs);
}
}
@@ -4759,6 +4765,16 @@
}
}
+/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */
+static void alc295_fixup_disable_dac3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ hda_nid_t conn[2] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x17, 2, conn);
+ }
+}
+
/* Hook to update amp GPIO4 for automute */
static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
struct hda_jack_callback *jack)
@@ -4908,6 +4924,7 @@
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
ALC255_FIXUP_DELL_SPK_NOISE,
ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_FIXUP_DISABLE_DAC3,
ALC280_FIXUP_HP_HEADSET_MIC,
ALC221_FIXUP_HP_FRONT_MIC,
ALC292_FIXUP_TPT460,
@@ -5600,6 +5617,10 @@
.chained = true,
.chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
},
+ [ALC295_FIXUP_DISABLE_DAC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_disable_dac3,
+ },
[ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -5663,6 +5684,7 @@
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3),
SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -5784,9 +5806,11 @@
SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
@@ -6336,6 +6360,7 @@
case 0x10ec0298:
spec->codec_variant = ALC269_TYPE_ALC298;
break;
+ case 0x10ec0235:
case 0x10ec0255:
spec->codec_variant = ALC269_TYPE_ALC255;
break;
@@ -6761,6 +6786,7 @@
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
ALC668_FIXUP_ASUS_Nx51,
ALC891_FIXUP_HEADSET_MODE,
ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
@@ -7012,14 +7038,21 @@
.chained = true,
.chain_id = ALC662_FIXUP_BASS_1A
},
+ [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP
+ },
[ALC668_FIXUP_ASUS_Nx51] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
- {0x1a, 0x90170151}, /* bass speaker */
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x90170151 }, /* bass speaker */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
{}
},
.chained = true,
- .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
},
[ALC891_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 14bbf55..9899ef4 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -137,6 +137,7 @@
#include <linux/pci.h>
#include <linux/math64.h>
#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -5692,40 +5693,43 @@
struct snd_pcm_channel_info *info)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
+ unsigned int channel = info->channel;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) {
+ if (snd_BUG_ON(channel >= hdspm->max_channels_out)) {
dev_info(hdspm->card->dev,
"snd_hdspm_channel_info: output channel out of range (%d)\n",
- info->channel);
+ channel);
return -EINVAL;
}
- if (hdspm->channel_map_out[info->channel] < 0) {
+ channel = array_index_nospec(channel, hdspm->max_channels_out);
+ if (hdspm->channel_map_out[channel] < 0) {
dev_info(hdspm->card->dev,
"snd_hdspm_channel_info: output channel %d mapped out\n",
- info->channel);
+ channel);
return -EINVAL;
}
- info->offset = hdspm->channel_map_out[info->channel] *
+ info->offset = hdspm->channel_map_out[channel] *
HDSPM_CHANNEL_BUFFER_BYTES;
} else {
- if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) {
+ if (snd_BUG_ON(channel >= hdspm->max_channels_in)) {
dev_info(hdspm->card->dev,
"snd_hdspm_channel_info: input channel out of range (%d)\n",
- info->channel);
+ channel);
return -EINVAL;
}
- if (hdspm->channel_map_in[info->channel] < 0) {
+ channel = array_index_nospec(channel, hdspm->max_channels_in);
+ if (hdspm->channel_map_in[channel] < 0) {
dev_info(hdspm->card->dev,
"snd_hdspm_channel_info: input channel %d mapped out\n",
- info->channel);
+ channel);
return -EINVAL;
}
- info->offset = hdspm->channel_map_in[info->channel] *
+ info->offset = hdspm->channel_map_in[channel] *
HDSPM_CHANNEL_BUFFER_BYTES;
}
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 55172c6..a76b1f1 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -26,6 +26,7 @@
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/io.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -2036,9 +2037,10 @@
if (snd_BUG_ON(info->channel >= RME9652_NCHANNELS))
return -EINVAL;
- if ((chn = rme9652->channel_map[info->channel]) < 0) {
+ chn = rme9652->channel_map[array_index_nospec(info->channel,
+ RME9652_NCHANNELS)];
+ if (chn < 0)
return -EINVAL;
- }
info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES;
info->first = 0;
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index 29a97d5..66d6c52 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -91,8 +91,8 @@
do {
mutex_lock(&ctx->lock);
- tmo = 5;
- while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+ tmo = 6;
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
udelay(21); /* wait an ac97 frame time */
if (!tmo) {
pr_debug("ac97rd timeout #1\n");
@@ -105,7 +105,7 @@
* poll, Forrest, poll...
*/
tmo = 0x10000;
- while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+ while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo)
asm volatile ("nop");
data = RD(ctx, AC97_CMDRESP);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index f9f2737..a4871c4 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -882,6 +882,7 @@
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ msleep(125);
regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
break;
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index f5d3415..f0c9e25 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1736,6 +1736,7 @@
.num_reg_defaults = ARRAY_SIZE(rt5651_reg),
.ranges = rt5651_ranges,
.num_ranges = ARRAY_SIZE(rt5651_ranges),
+ .use_single_rw = true,
};
#if defined(CONFIG_OF)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index abc802a..65ac4518 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5035,6 +5035,12 @@
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+static const struct of_device_id rt5677_of_match[] = {
+ { .compatible = "realtek,rt5677", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5677_of_match);
+
static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false };
static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false };
static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false };
@@ -5294,6 +5300,7 @@
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
.name = "rt5677",
+ .of_match_table = rt5677_of_match,
},
.probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 1589325..3dba555 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -774,15 +774,26 @@
static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct sgtl5000_priv *sgtl = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
+ regcache_cache_only(sgtl->regmap, false);
+ ret = regcache_sync(sgtl->regmap);
+ if (ret) {
+ regcache_cache_only(sgtl->regmap, true);
+ return ret;
+ }
+
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP,
SGTL5000_REFTOP_POWERUP);
break;
case SND_SOC_BIAS_OFF:
+ regcache_cache_only(sgtl->regmap, true);
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_REFTOP_POWERUP, 0);
break;
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 993bde2..7693e63 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -54,10 +54,17 @@
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
-static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = {
+ { .reg = 0x00, .def = 0x0097 },
+ { .reg = 0x01, .def = 0x0097 },
+ { .reg = 0x02, .def = 0x0079 },
+ { .reg = 0x03, .def = 0x0079 },
+ { .reg = 0x04, .def = 0x000a },
+ { .reg = 0x05, .def = 0x0008 },
+ { .reg = 0x06, .def = 0x009f },
+ { .reg = 0x07, .def = 0x000a },
+ { .reg = 0x08, .def = 0x0000 },
+ { .reg = 0x09, .def = 0x0000 }
};
@@ -620,8 +627,8 @@
.volatile_reg = ssm2602_register_volatile,
.cache_type = REGCACHE_RBTREE,
- .reg_defaults_raw = ssm2602_reg,
- .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+ .reg_defaults = ssm2602_reg,
+ .num_reg_defaults = ARRAY_SIZE(ssm2602_reg),
};
EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 38bfd46..3ef1745 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -145,6 +145,13 @@
psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;
+ /* Do not loop-search if PM (1 ~ 256) alone can serve the ratio */
+ if (ratio <= 256) {
+ pm = ratio;
+ fp = 1;
+ goto out;
+ }
+
/* Set the max fluctuation -- 0.1% of the max devisor */
savesub = (psr ? 1 : 8) * 256 * maxfp / 1000;
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index dd88c2c..48804e4 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -201,7 +201,7 @@
if (ret < 0)
return ret;
- ret = asoc_simple_card_init_mic(rtd->card, &priv->hp_jack, PREFIX);
+ ret = asoc_simple_card_init_mic(rtd->card, &priv->mic_jack, PREFIX);
if (ret < 0)
return ret;
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 0bfa688..cd16b43 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -420,7 +420,21 @@
.callback = byt_thinkpad10_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ },
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ },
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
},
},
{ }
diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c
index 4ccc80e..c798f8d 100644
--- a/sound/soc/intel/atom/sst/sst_stream.c
+++ b/sound/soc/intel/atom/sst/sst_stream.c
@@ -221,7 +221,7 @@
sst_free_block(sst_drv_ctx, block);
out:
test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
- return 0;
+ return ret;
}
/*
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 9052561..b1eb696 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -111,6 +111,7 @@
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Analog Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@@ -121,6 +122,8 @@
{"IN1N", NULL, "Headset Mic"},
{"DMIC L1", NULL, "Int Mic"},
{"DMIC R1", NULL, "Int Mic"},
+ {"IN2P", NULL, "Int Analog Mic"},
+ {"IN2N", NULL, "Int Analog Mic"},
{"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"},
{"Ext Spk", NULL, "SPOL"},
@@ -134,6 +137,9 @@
{"Headphone", NULL, "Platform Clock"},
{"Headset Mic", NULL, "Platform Clock"},
{"Int Mic", NULL, "Platform Clock"},
+ {"Int Analog Mic", NULL, "Platform Clock"},
+ {"Int Analog Mic", NULL, "micbias1"},
+ {"Int Analog Mic", NULL, "micbias2"},
{"Ext Spk", NULL, "Platform Clock"},
};
@@ -162,6 +168,7 @@
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Analog Mic"),
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 805b7f2..78472c9 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -331,7 +331,11 @@
if (skl->skl_sst->is_first_boot == true)
return 0;
+ /* disable dynamic clock gating during fw and lib download */
+ ctx->enable_miscbdcge(ctx->dev, false);
+
ret = skl_dsp_wake(ctx->dsp);
+ ctx->enable_miscbdcge(ctx->dev, true);
if (ret < 0)
return ret;
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 58c7286..2fd213c 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -1191,7 +1191,11 @@
return -EIO;
}
+ /* disable dynamic clock gating during fw and lib download */
+ skl->skl_sst->enable_miscbdcge(platform->dev, false);
+
ret = ops->init_fw(platform->dev, skl->skl_sst);
+ skl->skl_sst->enable_miscbdcge(platform->dev, true);
if (ret < 0) {
dev_err(platform->dev, "Failed to boot first fw: %d\n", ret);
return ret;
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 06fa5e8..3e526fb 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -457,7 +457,7 @@
struct hdac_bus *bus = ebus_to_hbus(ebus);
unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
+ unsigned int res = -1;
mutex_lock(&bus->cmd_mutex);
snd_hdac_bus_send_cmd(bus, cmd);
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index b6615af..fde974d 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -67,7 +67,7 @@
/* polling the AC_R_FINISH */
while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH)
- && timeout--)
+ && --timeout)
mdelay(1);
if (!timeout) {
@@ -121,7 +121,7 @@
/* polling the AC_W_FINISH */
while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH)
- && timeout--)
+ && --timeout)
mdelay(1);
if (!timeout)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 85324e6..2d14e37 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -642,8 +642,12 @@
tmp |= mod_slave;
break;
case SND_SOC_DAIFMT_CBS_CFS:
- /* Set default source clock in Master mode */
- if (i2s->rclk_srcrate == 0)
+ /*
+ * Set default source clock in Master mode, only when the
+ * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
+ * clock configuration assigned in DT is not overwritten.
+ */
+ if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
break;
@@ -858,6 +862,11 @@
return 0;
if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
+ struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
+
+ if (i2s->rclk_srcrate == 0 && rclksrc && !IS_ERR(rclksrc))
+ i2s->rclk_srcrate = clk_get_rate(rclksrc);
+
psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
dev_dbg(&i2s->pdev->dev,
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
index 7d92a24..98835e9 100644
--- a/sound/soc/sh/rcar/cmd.c
+++ b/sound/soc/sh/rcar/cmd.c
@@ -82,6 +82,9 @@
[9] = 0x2,
};
+ if (unlikely(!src))
+ return -EIO;
+
data = path[rsnd_mod_id(src)] |
cmd_case[rsnd_mod_id(src)] << 16;
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index a9a43ac..17e305b 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -233,6 +233,15 @@
for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
/*
+ * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+ * with it is not allowed. (SSIWSR.WS_MODE with
+ * SSICR.CKDV = 000 is not allowed either).
+ * Skip it. See SSICR.CKDV
+ */
+ if (j == 0)
+ continue;
+
+ /*
* this driver is assuming that
* system word is 32bit x chan
* see rsnd_ssi_init()
@@ -543,6 +552,13 @@
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 *buf = (u32 *)(runtime->dma_area +
rsnd_dai_pointer_offset(io, 0));
+ int shift = 0;
+
+ switch (runtime->sample_bits) {
+ case 32:
+ shift = 8;
+ break;
+ }
/*
* 8/16/32 data can be assesse to TDR/RDR register
@@ -550,9 +566,9 @@
* see rsnd_ssi_init()
*/
if (rsnd_io_is_play(io))
- rsnd_mod_write(mod, SSITDR, *buf);
+ rsnd_mod_write(mod, SSITDR, (*buf) << shift);
else
- *buf = rsnd_mod_read(mod, SSIRDR);
+ *buf = (rsnd_mod_read(mod, SSIRDR) >> shift);
elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 4f6c777..b761761 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -34,6 +34,7 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -2665,6 +2666,8 @@
if (ret != 0)
return ret;
+ arch_setup_dma_ops(card->dev, 0, 0, NULL, 0);
+
/* deactivate pins to sleep state */
list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 8a758c9..d6b48c7 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1180,6 +1180,9 @@
kfree(sm);
continue;
}
+
+ /* create any TLV data */
+ soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
}
return kc;
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index d0fb2f2..4f4ebe9 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -125,7 +125,7 @@
}
usb_fill_int_urb(urb, line6->usbdev,
- usb_sndbulkpipe(line6->usbdev,
+ usb_sndintpipe(line6->usbdev,
line6->properties->ep_ctrl_w),
transfer_buffer, length, midi_sent, line6,
line6->interval);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index ce11cc9..1c5d099 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -975,6 +975,14 @@
}
break;
+ case USB_ID(0x0d8c, 0x0103):
+ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
+ usb_audio_info(chip,
+ "set volume quirk for CM102-A+/102S+\n");
+ cval->min = -256;
+ }
+ break;
+
case USB_ID(0x0471, 0x0101):
case USB_ID(0x0471, 0x0104):
case USB_ID(0x0471, 0x0105):
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 9038b2e..eaa03ac 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -353,8 +353,11 @@
/*
* Dell usb dock with ALC4020 codec had a firmware problem where it got
* screwed up when zero volume is passed; just skip it as a workaround
+ *
+ * Also the extension unit gives an access error, so skip it as well.
*/
static const struct usbmix_name_map dell_alc4020_map[] = {
+ { 4, NULL }, /* extension unit */
{ 16, NULL },
{ 19, NULL },
{ 0 }
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 8a59d47..69bf5cf 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3277,4 +3277,51 @@
}
},
+{
+ /*
+ * Bower's & Wilkins PX headphones only support the 48 kHz sample rate
+ * even though it advertises more. The capture interface doesn't work
+ * even on windows.
+ */
+ USB_DEVICE(0x19b5, 0x0021),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ /* Capture */
+ {
+ .ifnum = 1,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ /* Playback */
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 2,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_FILL_MAX |
+ UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x03,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) {
+ 48000
+ }
+ }
+ },
+ }
+ }
+},
+
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 1cd7f8b..da9fc08 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1153,28 +1153,32 @@
return false;
}
-/* Marantz/Denon USB DACs need a vendor cmd to switch
+/* ITF-USB DSD based DACs need a vendor cmd to switch
* between PCM and native DSD mode
+ * (2 altsets version)
*/
-static bool is_marantz_denon_dac(unsigned int id)
+static bool is_itf_usb_dsd_2alts_dac(unsigned int id)
{
switch (id) {
case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */
case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */
case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */
+ case USB_ID(0x1852, 0x5065): /* Luxman DA-06 */
return true;
}
return false;
}
-/* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch
- * between PCM/DOP and native DSD mode
+/* ITF-USB DSD based DACs need a vendor cmd to switch
+ * between PCM and native DSD mode
+ * (3 altsets version)
*/
-static bool is_teac_dsd_dac(unsigned int id)
+static bool is_itf_usb_dsd_3alts_dac(unsigned int id)
{
switch (id) {
case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */
case USB_ID(0x0644, 0x8044): /* Esoteric D-05X */
+ case USB_ID(0x0644, 0x804a): /* TEAC UD-301 */
return true;
}
return false;
@@ -1186,7 +1190,7 @@
struct usb_device *dev = subs->dev;
int err;
- if (is_marantz_denon_dac(subs->stream->chip->usb_id)) {
+ if (is_itf_usb_dsd_2alts_dac(subs->stream->chip->usb_id)) {
/* First switch to alt set 0, otherwise the mode switch cmd
* will not be accepted by the DAC
*/
@@ -1207,7 +1211,7 @@
break;
}
mdelay(20);
- } else if (is_teac_dsd_dac(subs->stream->chip->usb_id)) {
+ } else if (is_itf_usb_dsd_3alts_dac(subs->stream->chip->usb_id)) {
/* Vendor mode switch cmd is required. */
switch (fmt->altsetting) {
case 3: /* DSD mode (DSD_U32) requested */
@@ -1303,10 +1307,10 @@
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
- /* Marantz/Denon devices with USB DAC functionality need a delay
+ /* ITF-USB DSD based DACs functionality need a delay
* after each class compliant request
*/
- if (is_marantz_denon_dac(chip->usb_id)
+ if (is_itf_usb_dsd_2alts_dac(chip->usb_id)
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
@@ -1370,14 +1374,14 @@
break;
}
- /* Denon/Marantz devices with USB DAC functionality */
- if (is_marantz_denon_dac(chip->usb_id)) {
+ /* ITF-USB DSD based DACs (2 altsets version) */
+ if (is_itf_usb_dsd_2alts_dac(chip->usb_id)) {
if (fp->altsetting == 2)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
}
- /* TEAC devices with USB DAC functionality */
- if (is_teac_dsd_dac(chip->usb_id)) {
+ /* ITF-USB DSD based DACs (3 altsets version) */
+ if (is_itf_usb_dsd_3alts_dac(chip->usb_id)) {
if (fp->altsetting == 3)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
}
diff --git a/tools/lib/str_error_r.c b/tools/lib/str_error_r.c
index 503ae07..9ab2d0a 100644
--- a/tools/lib/str_error_r.c
+++ b/tools/lib/str_error_r.c
@@ -21,6 +21,6 @@
{
int err = strerror_r(errnum, buf, buflen);
if (err)
- snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, %p, %zd)=%d", errnum, buf, buflen, err);
+ snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, [buf], %zd)=%d", errnum, buflen, err);
return buf;
}
diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c
index 6518bea..68af60f 100644
--- a/tools/lib/subcmd/pager.c
+++ b/tools/lib/subcmd/pager.c
@@ -29,10 +29,13 @@
* have real input
*/
fd_set in;
+ fd_set exception;
FD_ZERO(&in);
+ FD_ZERO(&exception);
FD_SET(0, &in);
- select(1, &in, NULL, &in, NULL);
+ FD_SET(0, &exception);
+ select(1, &in, NULL, &exception, NULL);
setenv("LESS", "FRSX", 0);
}
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index f87996b..9a250c7 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -442,9 +442,9 @@
}
if (ret == -ENOENT && ret2 == -ENOENT)
- pr_debug("\"%s\" does not hit any event.\n", str);
- /* Note that this is silently ignored */
- ret = 0;
+ pr_warning("\"%s\" does not hit any event.\n", str);
+ else
+ ret = 0;
error:
if (kfd >= 0)
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 688dea7..68861e8 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -146,6 +146,7 @@
static bool append_file;
static const char *output_name;
static int output_fd;
+static int print_free_counters_hint;
struct perf_stat {
bool record;
@@ -310,8 +311,12 @@
struct perf_counts_values *count;
count = perf_counts(counter->counts, cpu, thread);
- if (perf_evsel__read(counter, cpu, thread, count))
+ if (perf_evsel__read(counter, cpu, thread, count)) {
+ counter->counts->scaled = -1;
+ perf_counts(counter->counts, cpu, thread)->ena = 0;
+ perf_counts(counter->counts, cpu, thread)->run = 0;
return -1;
+ }
if (STAT_RECORD) {
if (perf_evsel__write_stat_event(counter, cpu, thread, count)) {
@@ -336,12 +341,14 @@
static void read_counters(void)
{
struct perf_evsel *counter;
+ int ret;
evlist__for_each_entry(evsel_list, counter) {
- if (read_counter(counter))
+ ret = read_counter(counter);
+ if (ret)
pr_debug("failed to read counter %s\n", counter->name);
- if (perf_stat_process_counter(&stat_config, counter))
+ if (ret == 0 && perf_stat_process_counter(&stat_config, counter))
pr_warning("failed to process counter %s\n", counter->name);
}
}
@@ -869,7 +876,7 @@
char buf[64], *vals, *ends;
if (unit == NULL || fmt == NULL) {
- fprintf(out, "%s%s%s%s", csv_sep, csv_sep, csv_sep, csv_sep);
+ fprintf(out, "%s%s", csv_sep, csv_sep);
return;
}
snprintf(buf, sizeof(buf), fmt, val);
@@ -1109,6 +1116,9 @@
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep);
+ if (counter->supported)
+ print_free_counters_hint = 1;
+
fprintf(stat_config.output, "%-*s%s",
csv_output ? 0 : unit_width,
counter->unit, csv_sep);
@@ -1477,6 +1487,13 @@
avg_stats(&walltime_nsecs_stats));
}
fprintf(output, "\n\n");
+
+ if (print_free_counters_hint)
+ fprintf(output,
+"Some events weren't counted. Try disabling the NMI watchdog:\n"
+" echo 0 > /proc/sys/kernel/nmi_watchdog\n"
+" perf stat ...\n"
+" echo 1 > /proc/sys/kernel/nmi_watchdog\n");
}
static void print_counters(struct timespec *ts, int argc, const char **argv)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 21f8a81..0fd8bfb 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -679,6 +679,10 @@
{ .name = "mlockall", .errmsg = true,
.arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },
{ .name = "mmap", .hexret = true,
+/* The standard mmap maps to old_mmap on s390x */
+#if defined(__s390x__)
+ .alias = "old_mmap",
+#endif
.arg_scnprintf = { [0] = SCA_HEX, /* addr */
[2] = SCA_MMAP_PROT, /* prot */
[3] = SCA_MMAP_FLAGS, /* flags */ }, },
@@ -822,12 +826,21 @@
void **arg_parm;
};
-static size_t fprintf_duration(unsigned long t, FILE *fp)
+/*
+ * We need to have this 'calculated' boolean because in some cases we really
+ * don't know what is the duration of a syscall, for instance, when we start
+ * a session and some threads are waiting for a syscall to finish, say 'poll',
+ * in which case all we can do is to print "( ? ) for duration and for the
+ * start timestamp.
+ */
+static size_t fprintf_duration(unsigned long t, bool calculated, FILE *fp)
{
double duration = (double)t / NSEC_PER_MSEC;
size_t printed = fprintf(fp, "(");
- if (duration >= 1.0)
+ if (!calculated)
+ printed += fprintf(fp, " ? ");
+ else if (duration >= 1.0)
printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration);
else if (duration >= 0.01)
printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration);
@@ -1030,13 +1043,27 @@
return t < (trace->duration_filter * NSEC_PER_MSEC);
}
-static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
+static size_t __trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
{
double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC;
return fprintf(fp, "%10.3f ", ts);
}
+/*
+ * We're handling tstamp=0 as an undefined tstamp, i.e. like when we are
+ * using ttrace->entry_time for a thread that receives a sys_exit without
+ * first having received a sys_enter ("poll" issued before tracing session
+ * starts, lost sys_enter exit due to ring buffer overflow).
+ */
+static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
+{
+ if (tstamp > 0)
+ return __trace__fprintf_tstamp(trace, tstamp, fp);
+
+ return fprintf(fp, " ? ");
+}
+
static bool done = false;
static bool interrupted = false;
@@ -1047,10 +1074,10 @@
}
static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread,
- u64 duration, u64 tstamp, FILE *fp)
+ u64 duration, bool duration_calculated, u64 tstamp, FILE *fp)
{
size_t printed = trace__fprintf_tstamp(trace, tstamp, fp);
- printed += fprintf_duration(duration, fp);
+ printed += fprintf_duration(duration, duration_calculated, fp);
if (trace->multiple_threads) {
if (trace->show_comm)
@@ -1452,7 +1479,7 @@
duration = sample->time - ttrace->entry_time;
- printed = trace__fprintf_entry_head(trace, trace->current, duration, ttrace->entry_time, trace->output);
+ printed = trace__fprintf_entry_head(trace, trace->current, duration, true, ttrace->entry_time, trace->output);
printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str);
ttrace->entry_pending = false;
@@ -1499,7 +1526,7 @@
if (sc->is_exit) {
if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) {
- trace__fprintf_entry_head(trace, thread, 1, ttrace->entry_time, trace->output);
+ trace__fprintf_entry_head(trace, thread, 0, false, ttrace->entry_time, trace->output);
fprintf(trace->output, "%-70s)\n", ttrace->entry_str);
}
} else {
@@ -1547,6 +1574,7 @@
{
long ret;
u64 duration = 0;
+ bool duration_calculated = false;
struct thread *thread;
int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0;
struct syscall *sc = trace__syscall_info(trace, evsel, id);
@@ -1577,6 +1605,7 @@
duration = sample->time - ttrace->entry_time;
if (trace__filter_duration(trace, duration))
goto out;
+ duration_calculated = true;
} else if (trace->duration_filter)
goto out;
@@ -1592,7 +1621,7 @@
if (trace->summary_only)
goto out;
- trace__fprintf_entry_head(trace, thread, duration, ttrace->entry_time, trace->output);
+ trace__fprintf_entry_head(trace, thread, duration, duration_calculated, ttrace->entry_time, trace->output);
if (ttrace->entry_pending) {
fprintf(trace->output, "%-70s", ttrace->entry_str);
@@ -1855,7 +1884,7 @@
thread__find_addr_location(thread, sample->cpumode, MAP__FUNCTION,
sample->ip, &al);
- trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output);
+ trace__fprintf_entry_head(trace, thread, 0, true, sample->time, trace->output);
fprintf(trace->output, "%sfault [",
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
diff --git a/tools/perf/tests/kmod-path.c b/tools/perf/tests/kmod-path.c
index 76f41f2..6cd9e51 100644
--- a/tools/perf/tests/kmod-path.c
+++ b/tools/perf/tests/kmod-path.c
@@ -61,6 +61,7 @@
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_KERNEL, true);
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_USER, false);
+#ifdef HAVE_ZLIB_SUPPORT
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x.ko.gz", true , true , true, true, "[x]", "gz");
T("/xxxx/xxxx/x.ko.gz", false , true , true, true, NULL , "gz");
@@ -96,6 +97,7 @@
M("x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("x.ko.gz", PERF_RECORD_MISC_KERNEL, true);
M("x.ko.gz", PERF_RECORD_MISC_USER, false);
+#endif
/* path alloc_name alloc_ext kmod comp name ext */
T("[test_module]", true , true , true, false, "[test_module]", NULL);
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 430d039..a38227e 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1250,6 +1250,7 @@
{
char linkname[PATH_MAX];
char *build_id_filename;
+ char *build_id_path = NULL;
if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
!dso__is_kcore(dso))
@@ -1265,8 +1266,14 @@
goto fallback;
}
+ build_id_path = strdup(filename);
+ if (!build_id_path)
+ return -1;
+
+ dirname(build_id_path);
+
if (dso__is_kcore(dso) ||
- readlink(filename, linkname, sizeof(linkname)) < 0 ||
+ readlink(build_id_path, linkname, sizeof(linkname)) < 0 ||
strstr(linkname, DSO__NAME_KALLSYMS) ||
access(filename, R_OK)) {
fallback:
@@ -1278,6 +1285,7 @@
__symbol__join_symfs(filename, filename_size, dso->long_name);
}
+ free(build_id_path);
return 0;
}
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index e528c40..993ef27 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -182,13 +182,17 @@
char buf[PATH_MAX];
char *ret = NULL, *p;
size_t offs = 5; /* == strlen("../..") */
+ ssize_t len;
linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
if (!linkname)
return NULL;
- if (readlink(linkname, buf, PATH_MAX) < 0)
+ len = readlink(linkname, buf, sizeof(buf) - 1);
+ if (len <= 0)
goto out;
+ buf[len] = '\0';
+
/* The link should be "../..<origpath>/<sbuild_id>" */
p = strrchr(buf, '/'); /* Cut off the "/<sbuild_id>" */
if (p && (p > buf + offs)) {
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 8ab0d7d..6631923 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -255,8 +255,8 @@
if (machine__is_default_guest(machine))
return 0;
- snprintf(filename, sizeof(filename), "%s/proc/%d/maps",
- machine->root_dir, pid);
+ snprintf(filename, sizeof(filename), "%s/proc/%d/task/%d/maps",
+ machine->root_dir, pid, pid);
fp = fopen(filename, "r");
if (fp == NULL) {
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 8bc2711..bce80f8 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1221,7 +1221,7 @@
if (FD(evsel, cpu, thread) < 0)
return -EINVAL;
- if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) < 0)
+ if (readn(FD(evsel, cpu, thread), count, sizeof(*count)) <= 0)
return -errno;
return 0;
@@ -1239,7 +1239,7 @@
if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0)
return -ENOMEM;
- if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
+ if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) <= 0)
return -errno;
perf_evsel__compute_deltas(evsel, cpu, thread, &count);
@@ -2400,11 +2400,17 @@
int err, char *msg, size_t size)
{
char sbuf[STRERR_BUFSIZE];
+ int printed = 0;
switch (err) {
case EPERM:
case EACCES:
- return scnprintf(msg, size,
+ if (err == EPERM)
+ printed = scnprintf(msg, size,
+ "No permission to enable %s event.\n\n",
+ perf_evsel__name(evsel));
+
+ return scnprintf(msg + printed, size - printed,
"You may not have permission to collect %sstats.\n\n"
"Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n"
"which controls use of the performance events system by\n"
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 28bdb48..ab36aa5 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1454,8 +1454,16 @@
dso__set_build_id(dso, &bev->build_id);
- if (!is_kernel_module(filename, cpumode))
- dso->kernel = dso_type;
+ if (dso_type != DSO_TYPE_USER) {
+ struct kmod_path m = { .name = NULL, };
+
+ if (!kmod_path__parse_name(&m, filename) && m.kmod)
+ dso__set_short_name(dso, strdup(m.name), true);
+ else
+ dso->kernel = dso_type;
+
+ free(m.name);
+ }
build_id__sprintf(dso->build_id, sizeof(dso->build_id),
sbuild_id);
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index 7e27207..cac3953 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -1300,6 +1300,7 @@
intel_pt_clear_tx_flags(decoder);
decoder->have_tma = false;
decoder->cbr = 0;
+ decoder->timestamp_insn_cnt = 0;
decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
decoder->overflow = true;
return -EOVERFLOW;
@@ -1522,6 +1523,7 @@
case INTEL_PT_PSBEND:
intel_pt_log("ERROR: Missing TIP after FUP\n");
decoder->pkt_state = INTEL_PT_STATE_ERR3;
+ decoder->pkt_step = 0;
return -ENOENT;
case INTEL_PT_OVF:
@@ -2182,14 +2184,6 @@
return &decoder->state;
}
-static bool intel_pt_at_psb(unsigned char *buf, size_t len)
-{
- if (len < INTEL_PT_PSB_LEN)
- return false;
- return memmem(buf, INTEL_PT_PSB_LEN, INTEL_PT_PSB_STR,
- INTEL_PT_PSB_LEN);
-}
-
/**
* intel_pt_next_psb - move buffer pointer to the start of the next PSB packet.
* @buf: pointer to buffer pointer
@@ -2278,6 +2272,7 @@
* @buf: buffer
* @len: size of buffer
* @tsc: TSC value returned
+ * @rem: returns remaining size when TSC is found
*
* Find a TSC packet in @buf and return the TSC value. This function assumes
* that @buf starts at a PSB and that PSB+ will contain TSC and so stops if a
@@ -2285,7 +2280,8 @@
*
* Return: %true if TSC is found, false otherwise.
*/
-static bool intel_pt_next_tsc(unsigned char *buf, size_t len, uint64_t *tsc)
+static bool intel_pt_next_tsc(unsigned char *buf, size_t len, uint64_t *tsc,
+ size_t *rem)
{
struct intel_pt_pkt packet;
int ret;
@@ -2296,6 +2292,7 @@
return false;
if (packet.type == INTEL_PT_TSC) {
*tsc = packet.payload;
+ *rem = len;
return true;
}
if (packet.type == INTEL_PT_PSBEND)
@@ -2346,6 +2343,8 @@
* @len_a: size of first buffer
* @buf_b: second buffer
* @len_b: size of second buffer
+ * @consecutive: returns true if there is data in buf_b that is consecutive
+ * to buf_a
*
* If the trace contains TSC we can look at the last TSC of @buf_a and the
* first TSC of @buf_b in order to determine if the buffers overlap, and then
@@ -2358,33 +2357,41 @@
static unsigned char *intel_pt_find_overlap_tsc(unsigned char *buf_a,
size_t len_a,
unsigned char *buf_b,
- size_t len_b)
+ size_t len_b, bool *consecutive)
{
uint64_t tsc_a, tsc_b;
unsigned char *p;
- size_t len;
+ size_t len, rem_a, rem_b;
p = intel_pt_last_psb(buf_a, len_a);
if (!p)
return buf_b; /* No PSB in buf_a => no overlap */
len = len_a - (p - buf_a);
- if (!intel_pt_next_tsc(p, len, &tsc_a)) {
+ if (!intel_pt_next_tsc(p, len, &tsc_a, &rem_a)) {
/* The last PSB+ in buf_a is incomplete, so go back one more */
len_a -= len;
p = intel_pt_last_psb(buf_a, len_a);
if (!p)
return buf_b; /* No full PSB+ => assume no overlap */
len = len_a - (p - buf_a);
- if (!intel_pt_next_tsc(p, len, &tsc_a))
+ if (!intel_pt_next_tsc(p, len, &tsc_a, &rem_a))
return buf_b; /* No TSC in buf_a => assume no overlap */
}
while (1) {
/* Ignore PSB+ with no TSC */
- if (intel_pt_next_tsc(buf_b, len_b, &tsc_b) &&
- intel_pt_tsc_cmp(tsc_a, tsc_b) < 0)
- return buf_b; /* tsc_a < tsc_b => no overlap */
+ if (intel_pt_next_tsc(buf_b, len_b, &tsc_b, &rem_b)) {
+ int cmp = intel_pt_tsc_cmp(tsc_a, tsc_b);
+
+ /* Same TSC, so buffers are consecutive */
+ if (!cmp && rem_b >= rem_a) {
+ *consecutive = true;
+ return buf_b + len_b - (rem_b - rem_a);
+ }
+ if (cmp < 0)
+ return buf_b; /* tsc_a < tsc_b => no overlap */
+ }
if (!intel_pt_step_psb(&buf_b, &len_b))
return buf_b + len_b; /* No PSB in buf_b => no data */
@@ -2398,6 +2405,8 @@
* @buf_b: second buffer
* @len_b: size of second buffer
* @have_tsc: can use TSC packets to detect overlap
+ * @consecutive: returns true if there is data in buf_b that is consecutive
+ * to buf_a
*
* When trace samples or snapshots are recorded there is the possibility that
* the data overlaps. Note that, for the purposes of decoding, data is only
@@ -2408,7 +2417,7 @@
*/
unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a,
unsigned char *buf_b, size_t len_b,
- bool have_tsc)
+ bool have_tsc, bool *consecutive)
{
unsigned char *found;
@@ -2420,7 +2429,8 @@
return buf_b; /* No overlap */
if (have_tsc) {
- found = intel_pt_find_overlap_tsc(buf_a, len_a, buf_b, len_b);
+ found = intel_pt_find_overlap_tsc(buf_a, len_a, buf_b, len_b,
+ consecutive);
if (found)
return found;
}
@@ -2435,28 +2445,16 @@
}
/* Now len_b >= len_a */
- if (len_b > len_a) {
- /* The leftover buffer 'b' must start at a PSB */
- while (!intel_pt_at_psb(buf_b + len_a, len_b - len_a)) {
- if (!intel_pt_step_psb(&buf_a, &len_a))
- return buf_b; /* No overlap */
- }
- }
-
while (1) {
/* Potential overlap so check the bytes */
found = memmem(buf_a, len_a, buf_b, len_a);
- if (found)
+ if (found) {
+ *consecutive = true;
return buf_b + len_a;
+ }
/* Try again at next PSB in buffer 'a' */
if (!intel_pt_step_psb(&buf_a, &len_a))
return buf_b; /* No overlap */
-
- /* The leftover buffer 'b' must start at a PSB */
- while (!intel_pt_at_psb(buf_b + len_a, len_b - len_a)) {
- if (!intel_pt_step_psb(&buf_a, &len_a))
- return buf_b; /* No overlap */
- }
}
}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
index 8939998..9ae4df1 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
@@ -103,7 +103,7 @@
unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a,
unsigned char *buf_b, size_t len_b,
- bool have_tsc);
+ bool have_tsc, bool *consecutive);
int intel_pt__strerror(int code, char *buf, size_t buflen);
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index dc041d4..b1161d7 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -131,6 +131,7 @@
bool stop;
bool step_through_buffers;
bool use_buffer_pid_tid;
+ bool sync_switch;
pid_t pid, tid;
int cpu;
int switch_state;
@@ -194,14 +195,17 @@
static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *a,
struct auxtrace_buffer *b)
{
+ bool consecutive = false;
void *start;
start = intel_pt_find_overlap(a->data, a->size, b->data, b->size,
- pt->have_tsc);
+ pt->have_tsc, &consecutive);
if (!start)
return -EINVAL;
b->use_size = b->data + b->size - start;
b->use_data = start;
+ if (b->use_size && consecutive)
+ b->consecutive = true;
return 0;
}
@@ -928,10 +932,12 @@
if (pt->timeless_decoding || !pt->have_sched_switch)
ptq->use_buffer_pid_tid = true;
}
+
+ ptq->sync_switch = pt->sync_switch;
}
if (!ptq->on_heap &&
- (!pt->sync_switch ||
+ (!ptq->sync_switch ||
ptq->switch_state != INTEL_PT_SS_EXPECTING_SWITCH_EVENT)) {
const struct intel_pt_state *state;
int ret;
@@ -1333,7 +1339,7 @@
if (pt->synth_opts.last_branch)
intel_pt_update_last_branch_rb(ptq);
- if (!pt->sync_switch)
+ if (!ptq->sync_switch)
return 0;
if (intel_pt_is_switch_ip(ptq, state->to_ip)) {
@@ -1414,6 +1420,21 @@
return switch_ip;
}
+static void intel_pt_enable_sync_switch(struct intel_pt *pt)
+{
+ unsigned int i;
+
+ pt->sync_switch = true;
+
+ for (i = 0; i < pt->queues.nr_queues; i++) {
+ struct auxtrace_queue *queue = &pt->queues.queue_array[i];
+ struct intel_pt_queue *ptq = queue->priv;
+
+ if (ptq)
+ ptq->sync_switch = true;
+ }
+}
+
static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp)
{
const struct intel_pt_state *state = ptq->state;
@@ -1430,7 +1451,7 @@
if (pt->switch_ip) {
intel_pt_log("switch_ip: %"PRIx64" ptss_ip: %"PRIx64"\n",
pt->switch_ip, pt->ptss_ip);
- pt->sync_switch = true;
+ intel_pt_enable_sync_switch(pt);
}
}
}
@@ -1446,9 +1467,9 @@
if (state->err) {
if (state->err == INTEL_PT_ERR_NODATA)
return 1;
- if (pt->sync_switch &&
+ if (ptq->sync_switch &&
state->from_ip >= pt->kernel_start) {
- pt->sync_switch = false;
+ ptq->sync_switch = false;
intel_pt_next_tid(pt, ptq);
}
if (pt->synth_opts.errors) {
@@ -1474,7 +1495,7 @@
state->timestamp, state->est_timestamp);
ptq->timestamp = state->est_timestamp;
/* Use estimated TSC in unknown switch state */
- } else if (pt->sync_switch &&
+ } else if (ptq->sync_switch &&
ptq->switch_state == INTEL_PT_SS_UNKNOWN &&
intel_pt_is_switch_ip(ptq, state->to_ip) &&
ptq->next_tid == -1) {
@@ -1621,7 +1642,7 @@
return 1;
ptq = intel_pt_cpu_to_ptq(pt, cpu);
- if (!ptq)
+ if (!ptq || !ptq->sync_switch)
return 1;
switch (ptq->switch_state) {
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c
index fe84df1..e70e935 100644
--- a/tools/perf/util/ordered-events.c
+++ b/tools/perf/util/ordered-events.c
@@ -79,7 +79,7 @@
static void free_dup_event(struct ordered_events *oe, union perf_event *event)
{
- if (oe->copy_on_queue) {
+ if (event && oe->copy_on_queue) {
oe->cur_alloc_size -= event->header.size;
free(event);
}
@@ -150,6 +150,7 @@
list_move(&event->list, &oe->cache);
oe->nr_events--;
free_dup_event(oe, event->event);
+ event->event = NULL;
}
int ordered_events__queue(struct ordered_events *oe, union perf_event *event,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6a6f44d..c93dacc 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2609,6 +2609,14 @@
out:
free(nbase);
+
+ /* Final validation */
+ if (ret >= 0 && !is_c_func_name(buf)) {
+ pr_warning("Internal error: \"%s\" is an invalid event name.\n",
+ buf);
+ ret = -EINVAL;
+ }
+
return ret;
}
@@ -3060,7 +3068,7 @@
struct probe_trace_event *new_tevs;
int ret = 0;
- if (ntevs == 0) {
+ if (*ntevs == 0) {
*tevs = *tevs2;
*ntevs = ntevs2;
*tevs2 = NULL;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 5d61242..7e0573e 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -139,8 +139,14 @@
if (perf_session__open(session) < 0)
goto out_close;
- perf_session__set_id_hdr_size(session);
- perf_session__set_comm_exec(session);
+ /*
+ * set session attributes that are present in perf.data
+ * but not in pipe-mode.
+ */
+ if (!file->is_pipe) {
+ perf_session__set_id_hdr_size(session);
+ perf_session__set_comm_exec(session);
+ }
}
} else {
session->machines.host.env = &perf_env;
@@ -155,7 +161,11 @@
pr_warning("Cannot read kernel map\n");
}
- if (tool && tool->ordering_requires_timestamps &&
+ /*
+ * In pipe-mode, evlist is empty until PERF_RECORD_HEADER_ATTR is
+ * processed, so perf_evlist__sample_id_all is not meaningful here.
+ */
+ if ((!file || !file->is_pipe) && tool && tool->ordering_requires_timestamps &&
tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) {
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
tool->ordered_events = false;
@@ -1628,6 +1638,7 @@
buf = malloc(cur_size);
if (!buf)
return -errno;
+ ordered_events__set_copy_on_queue(oe, true);
more:
event = buf;
err = readn(fd, event, sizeof(struct perf_event_header));
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 452e15a..031e64c 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -846,6 +846,9 @@
static int64_t
sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
{
+ if (!left->branch_info || !right->branch_info)
+ return cmp_null(left->branch_info, right->branch_info);
+
return left->branch_info->flags.cycles -
right->branch_info->flags.cycles;
}
@@ -853,6 +856,8 @@
static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
+ if (!he->branch_info)
+ return scnprintf(bf, size, "%-.*s", width, "N/A");
if (he->branch_info->flags.cycles == 0)
return repsep_snprintf(bf, size, "%-*s", width, "-");
return repsep_snprintf(bf, size, "%-*hd", width,
diff --git a/tools/perf/util/trigger.h b/tools/perf/util/trigger.h
index e97d701..ce7e6e5 100644
--- a/tools/perf/util/trigger.h
+++ b/tools/perf/util/trigger.h
@@ -11,7 +11,7 @@
* States and transits:
*
*
- * OFF--(on)--> READY --(hit)--> HIT
+ * OFF--> ON --> READY --(hit)--> HIT
* ^ |
* | (ready)
* | |
@@ -26,8 +26,9 @@
volatile enum {
TRIGGER_ERROR = -2,
TRIGGER_OFF = -1,
- TRIGGER_READY = 0,
- TRIGGER_HIT = 1,
+ TRIGGER_ON = 0,
+ TRIGGER_READY = 1,
+ TRIGGER_HIT = 2,
} state;
const char *name;
};
@@ -49,7 +50,7 @@
static inline void trigger_on(struct trigger *t)
{
TRIGGER_WARN_ONCE(t, TRIGGER_OFF);
- t->state = TRIGGER_READY;
+ t->state = TRIGGER_ON;
}
static inline void trigger_ready(struct trigger *t)
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 783a53f..b46e1cf 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -38,6 +38,14 @@
return 0;
mod = dwfl_addrmodule(ui->dwfl, ip);
+ if (mod) {
+ Dwarf_Addr s;
+
+ dwfl_module_info(mod, NULL, &s, NULL, NULL, NULL, NULL, NULL);
+ if (s != al->map->start)
+ mod = 0;
+ }
+
if (!mod)
mod = dwfl_report_elf(ui->dwfl, dso->short_name,
dso->long_name, -1, al->map->start,
@@ -167,12 +175,16 @@
{
struct unwind_info *ui = arg;
Dwarf_Addr pc;
+ bool isactivation;
- if (!dwfl_frame_pc(state, &pc, NULL)) {
+ if (!dwfl_frame_pc(state, &pc, &isactivation)) {
pr_err("%s", dwfl_errmsg(-1));
return DWARF_CB_ABORT;
}
+ if (!isactivation)
+ --pc;
+
return entry(pc, ui) || !(--ui->max_stack) ?
DWARF_CB_ABORT : DWARF_CB_OK;
}
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 20c2e57..1203834 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -646,6 +646,17 @@
while (!ret && (unw_step(&c) > 0) && i < max_stack) {
unw_get_reg(&c, UNW_REG_IP, &ips[i]);
+
+ /*
+ * Decrement the IP for any non-activation frames.
+ * this is required to properly find the srcline
+ * for caller frames.
+ * See also the documentation for dwfl_frame_pc(),
+ * which this code tries to replicate.
+ */
+ if (unw_is_signal_frame(&c) <= 0)
+ --ips[i];
+
++i;
}
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 85c5680..dfb010b 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -207,7 +207,7 @@
size -= ret;
off_in += ret;
- off_out -= ret;
+ off_out += ret;
}
munmap(ptr, off_in + size);
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index 71620fa..d025eaf 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -1908,6 +1908,7 @@
put_device(&pdev->dev);
goto err_register;
}
+ get_device(&pdev->dev);
rc = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc)
@@ -1926,6 +1927,10 @@
if (instances[i])
platform_device_unregister(&instances[i]->pdev);
nfit_test_teardown();
+ for (i = 0; i < NUM_NFITS; i++)
+ if (instances[i])
+ put_device(&instances[i]->pdev.dev);
+
return rc;
}
@@ -1933,10 +1938,13 @@
{
int i;
- platform_driver_unregister(&nfit_test_driver);
for (i = 0; i < NUM_NFITS; i++)
platform_device_unregister(&instances[i]->pdev);
+ platform_driver_unregister(&nfit_test_driver);
nfit_test_teardown();
+
+ for (i = 0; i < NUM_NFITS; i++)
+ put_device(&instances[i]->pdev.dev);
class_destroy(nfit_test_dimm);
}
diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh
index d8ac9ba..99d7f13 100755
--- a/tools/testing/selftests/firmware/fw_filesystem.sh
+++ b/tools/testing/selftests/firmware/fw_filesystem.sh
@@ -28,7 +28,12 @@
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
fi
- echo -n "$OLD_PATH" >/sys/module/firmware_class/parameters/path
+ if [ "$OLD_FWPATH" = "" ]; then
+ # A zero-length write won't work; write a null byte
+ printf '\000' >/sys/module/firmware_class/parameters/path
+ else
+ echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path
+ fi
rm -f "$FW"
rmdir "$FWPATH"
}
diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
index d9c49f4..e79ccd6 100644
--- a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
+++ b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
@@ -42,12 +42,12 @@
printf("Check DSCR TM context switch: ");
fflush(stdout);
for (;;) {
- rv = 1;
asm __volatile__ (
/* set a known value into the DSCR */
"ld 3, %[dscr1];"
"mtspr %[sprn_dscr], 3;"
+ "li %[rv], 1;"
/* start and suspend a transaction */
"tbegin.;"
"beq 1f;"
diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh
index 3f81a10..50a6371 100755
--- a/tools/testing/selftests/rcutorture/bin/configinit.sh
+++ b/tools/testing/selftests/rcutorture/bin/configinit.sh
@@ -51,7 +51,7 @@
mkdir $builddir
fi
else
- echo Bad build directory: \"$builddir\"
+ echo Bad build directory: \"$buildloc\"
exit 2
fi
fi
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index cbb0564..d5be7b5 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -1318,7 +1318,7 @@
iov.iov_len = sizeof(regs);
ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov);
#endif
- EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, ret) {}
#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc__) || \
defined(__s390__) || defined(__hppa__)
@@ -1692,7 +1692,11 @@
#endif
#ifndef SECCOMP_FILTER_FLAG_TSYNC
-#define SECCOMP_FILTER_FLAG_TSYNC 1
+#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0)
+#endif
+
+#ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW
+#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
#endif
#ifndef seccomp
@@ -1791,6 +1795,78 @@
}
}
+/*
+ * Test detection of known and unknown filter flags. Userspace needs to be able
+ * to check if a filter flag is supported by the current kernel and a good way
+ * of doing that is by attempting to enter filter mode, with the flag bit in
+ * question set, and a NULL pointer for the _args_ parameter. EFAULT indicates
+ * that the flag is valid and EINVAL indicates that the flag is invalid.
+ */
+TEST(detect_seccomp_filter_flags)
+{
+ unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC,
+ SECCOMP_FILTER_FLAG_SPEC_ALLOW };
+ unsigned int flag, all_flags;
+ int i;
+ long ret;
+
+ /* Test detection of known-good filter flags */
+ for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) {
+ int bits = 0;
+
+ flag = flags[i];
+ /* Make sure the flag is a single bit! */
+ while (flag) {
+ if (flag & 0x1)
+ bits ++;
+ flag >>= 1;
+ }
+ ASSERT_EQ(1, bits);
+ flag = flags[i];
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
+ ASSERT_NE(ENOSYS, errno) {
+ TH_LOG("Kernel does not support seccomp syscall!");
+ }
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EFAULT, errno) {
+ TH_LOG("Failed to detect that a known-good filter flag (0x%X) is supported!",
+ flag);
+ }
+
+ all_flags |= flag;
+ }
+
+ /* Test detection of all known-good filter flags */
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, all_flags, NULL);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EFAULT, errno) {
+ TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!",
+ all_flags);
+ }
+
+ /* Test detection of an unknown filter flag */
+ flag = -1;
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported!",
+ flag);
+ }
+
+ /*
+ * Test detection of an unknown filter flag that may simply need to be
+ * added to this test
+ */
+ flag = flags[ARRAY_SIZE(flags) - 1] << 1;
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported! Does a new flag need to be added to this test?",
+ flag);
+ }
+}
+
TEST(TSYNC_first)
{
struct sock_filter filter[] = {
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 6eb5015..e5b459a 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -17,7 +17,7 @@
BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64)
-CFLAGS := -O2 -g -std=gnu99 -pthread -Wall
+CFLAGS := -O2 -g -std=gnu99 -pthread -Wall -no-pie
UNAME_M := $(shell uname -m)
CAN_BUILD_I386 := $(shell ./check_cc.sh $(CC) trivial_32bit_program.c -m32)
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
index d075ea0..ade443a 100644
--- a/tools/testing/selftests/x86/entry_from_vm86.c
+++ b/tools/testing/selftests/x86/entry_from_vm86.c
@@ -95,6 +95,31 @@
"int3\n\t"
"vmcode_int80:\n\t"
"int $0x80\n\t"
+ "vmcode_popf_hlt:\n\t"
+ "push %ax\n\t"
+ "popf\n\t"
+ "hlt\n\t"
+ "vmcode_umip:\n\t"
+ /* addressing via displacements */
+ "smsw (2052)\n\t"
+ "sidt (2054)\n\t"
+ "sgdt (2060)\n\t"
+ /* addressing via registers */
+ "mov $2066, %bx\n\t"
+ "smsw (%bx)\n\t"
+ "mov $2068, %bx\n\t"
+ "sidt (%bx)\n\t"
+ "mov $2074, %bx\n\t"
+ "sgdt (%bx)\n\t"
+ /* register operands, only for smsw */
+ "smsw %ax\n\t"
+ "mov %ax, (2080)\n\t"
+ "int3\n\t"
+ "vmcode_umip_str:\n\t"
+ "str %eax\n\t"
+ "vmcode_umip_sldt:\n\t"
+ "sldt %eax\n\t"
+ "int3\n\t"
".size vmcode, . - vmcode\n\t"
"end_vmcode:\n\t"
".code32\n\t"
@@ -103,7 +128,8 @@
extern unsigned char vmcode[], end_vmcode[];
extern unsigned char vmcode_bound[], vmcode_sysenter[], vmcode_syscall[],
- vmcode_sti[], vmcode_int3[], vmcode_int80[];
+ vmcode_sti[], vmcode_int3[], vmcode_int80[], vmcode_popf_hlt[],
+ vmcode_umip[], vmcode_umip_str[], vmcode_umip_sldt[];
/* Returns false if the test was skipped. */
static bool do_test(struct vm86plus_struct *v86, unsigned long eip,
@@ -153,13 +179,75 @@
(VM86_TYPE(ret) == rettype && VM86_ARG(ret) == retarg)) {
printf("[OK]\tReturned correctly\n");
} else {
- printf("[FAIL]\tIncorrect return reason\n");
+ printf("[FAIL]\tIncorrect return reason (started at eip = 0x%lx, ended at eip = 0x%lx)\n", eip, v86->regs.eip);
nerrs++;
}
return true;
}
+void do_umip_tests(struct vm86plus_struct *vm86, unsigned char *test_mem)
+{
+ struct table_desc {
+ unsigned short limit;
+ unsigned long base;
+ } __attribute__((packed));
+
+ /* Initialize variables with arbitrary values */
+ struct table_desc gdt1 = { .base = 0x3c3c3c3c, .limit = 0x9999 };
+ struct table_desc gdt2 = { .base = 0x1a1a1a1a, .limit = 0xaeae };
+ struct table_desc idt1 = { .base = 0x7b7b7b7b, .limit = 0xf1f1 };
+ struct table_desc idt2 = { .base = 0x89898989, .limit = 0x1313 };
+ unsigned short msw1 = 0x1414, msw2 = 0x2525, msw3 = 3737;
+
+ /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */
+ do_test(vm86, vmcode_umip - vmcode, VM86_TRAP, 3, "UMIP tests");
+
+ /* Results from displacement-only addressing */
+ msw1 = *(unsigned short *)(test_mem + 2052);
+ memcpy(&idt1, test_mem + 2054, sizeof(idt1));
+ memcpy(&gdt1, test_mem + 2060, sizeof(gdt1));
+
+ /* Results from register-indirect addressing */
+ msw2 = *(unsigned short *)(test_mem + 2066);
+ memcpy(&idt2, test_mem + 2068, sizeof(idt2));
+ memcpy(&gdt2, test_mem + 2074, sizeof(gdt2));
+
+ /* Results when using register operands */
+ msw3 = *(unsigned short *)(test_mem + 2080);
+
+ printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1);
+ printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n",
+ idt1.limit, idt1.base);
+ printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n",
+ gdt1.limit, gdt1.base);
+
+ if (msw1 != msw2 || msw1 != msw3)
+ printf("[FAIL]\tAll the results of SMSW should be the same.\n");
+ else
+ printf("[PASS]\tAll the results from SMSW are identical.\n");
+
+ if (memcmp(&gdt1, &gdt2, sizeof(gdt1)))
+ printf("[FAIL]\tAll the results of SGDT should be the same.\n");
+ else
+ printf("[PASS]\tAll the results from SGDT are identical.\n");
+
+ if (memcmp(&idt1, &idt2, sizeof(idt1)))
+ printf("[FAIL]\tAll the results of SIDT should be the same.\n");
+ else
+ printf("[PASS]\tAll the results from SIDT are identical.\n");
+
+ sethandler(SIGILL, sighandler, 0);
+ do_test(vm86, vmcode_umip_str - vmcode, VM86_SIGNAL, 0,
+ "STR instruction");
+ clearhandler(SIGILL);
+
+ sethandler(SIGILL, sighandler, 0);
+ do_test(vm86, vmcode_umip_sldt - vmcode, VM86_SIGNAL, 0,
+ "SLDT instruction");
+ clearhandler(SIGILL);
+}
+
int main(void)
{
struct vm86plus_struct v86;
@@ -180,6 +268,9 @@
v86.regs.ds = load_addr / 16;
v86.regs.es = load_addr / 16;
+ /* Use the end of the page as our stack. */
+ v86.regs.esp = 4096;
+
assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */
/* #BR -- should deliver SIG??? */
@@ -211,6 +302,23 @@
v86.regs.eflags &= ~X86_EFLAGS_IF;
do_test(&v86, vmcode_sti - vmcode, VM86_STI, 0, "STI with VIP set");
+ /* POPF with VIP set but IF clear: should not trap */
+ v86.regs.eflags = X86_EFLAGS_VIP;
+ v86.regs.eax = 0;
+ do_test(&v86, vmcode_popf_hlt - vmcode, VM86_UNKNOWN, 0, "POPF with VIP set and IF clear");
+
+ /* POPF with VIP set and IF set: should trap */
+ v86.regs.eflags = X86_EFLAGS_VIP;
+ v86.regs.eax = X86_EFLAGS_IF;
+ do_test(&v86, vmcode_popf_hlt - vmcode, VM86_STI, 0, "POPF with VIP and IF set");
+
+ /* POPF with VIP clear and IF set: should not trap */
+ v86.regs.eflags = 0;
+ v86.regs.eax = X86_EFLAGS_IF;
+ do_test(&v86, vmcode_popf_hlt - vmcode, VM86_UNKNOWN, 0, "POPF with VIP clear and IF set");
+
+ v86.regs.eflags = 0;
+
/* INT3 -- should cause #BP */
do_test(&v86, vmcode_int3 - vmcode, VM86_TRAP, 3, "INT3");
@@ -218,6 +326,9 @@
v86.regs.eax = (unsigned int)-1;
do_test(&v86, vmcode_int80 - vmcode, VM86_INTx, 0x80, "int80");
+ /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */
+ do_umip_tests(&v86, addr);
+
/* Execute a null pointer */
v86.regs.cs = 0;
v86.regs.ss = 0;
@@ -231,7 +342,7 @@
clearhandler(SIGSEGV);
/* Make sure nothing explodes if we fork. */
- if (fork() > 0)
+ if (fork() == 0)
return 0;
return (nerrs == 0 ? 0 : 1);
diff --git a/tools/testing/selftests/x86/mpx-mini-test.c b/tools/testing/selftests/x86/mpx-mini-test.c
index 79e1d13..5838418 100644
--- a/tools/testing/selftests/x86/mpx-mini-test.c
+++ b/tools/testing/selftests/x86/mpx-mini-test.c
@@ -419,8 +419,7 @@
br_count++;
dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count);
-#define __SI_FAULT (3 << 16)
-#define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */
+#define SEGV_BNDERR 3 /* failed address bound checks */
dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n",
status, ip, br_reason);
diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c
index 2842a5f..85a78eb 100644
--- a/tools/testing/selftests/x86/protection_keys.c
+++ b/tools/testing/selftests/x86/protection_keys.c
@@ -188,17 +188,29 @@
#define u64 uint64_t
#ifdef __i386__
-#define SYS_mprotect_key 380
-#define SYS_pkey_alloc 381
-#define SYS_pkey_free 382
+
+#ifndef SYS_mprotect_key
+# define SYS_mprotect_key 380
+#endif
+#ifndef SYS_pkey_alloc
+# define SYS_pkey_alloc 381
+# define SYS_pkey_free 382
+#endif
#define REG_IP_IDX REG_EIP
-#define si_pkey_offset 0x18
+#define si_pkey_offset 0x14
+
#else
-#define SYS_mprotect_key 329
-#define SYS_pkey_alloc 330
-#define SYS_pkey_free 331
+
+#ifndef SYS_mprotect_key
+# define SYS_mprotect_key 329
+#endif
+#ifndef SYS_pkey_alloc
+# define SYS_pkey_alloc 330
+# define SYS_pkey_free 331
+#endif
#define REG_IP_IDX REG_RIP
#define si_pkey_offset 0x20
+
#endif
void dump_mem(void *dumpme, int len_bytes)
@@ -212,19 +224,18 @@
}
}
-#define __SI_FAULT (3 << 16)
-#define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */
-#define SEGV_PKUERR (__SI_FAULT|4)
+#define SEGV_BNDERR 3 /* failed address bound checks */
+#define SEGV_PKUERR 4
static char *si_code_str(int si_code)
{
- if (si_code & SEGV_MAPERR)
+ if (si_code == SEGV_MAPERR)
return "SEGV_MAPERR";
- if (si_code & SEGV_ACCERR)
+ if (si_code == SEGV_ACCERR)
return "SEGV_ACCERR";
- if (si_code & SEGV_BNDERR)
+ if (si_code == SEGV_BNDERR)
return "SEGV_BNDERR";
- if (si_code & SEGV_PKUERR)
+ if (si_code == SEGV_PKUERR)
return "SEGV_PKUERR";
return "UNKNOWN";
}
@@ -238,7 +249,7 @@
unsigned long ip;
char *fpregs;
u32 *pkru_ptr;
- u64 si_pkey;
+ u64 siginfo_pkey;
u32 *si_pkey_ptr;
int pkru_offset;
fpregset_t fpregset;
@@ -280,9 +291,9 @@
si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset);
dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
dump_mem(si_pkey_ptr - 8, 24);
- si_pkey = *si_pkey_ptr;
- pkey_assert(si_pkey < NR_PKEYS);
- last_si_pkey = si_pkey;
+ siginfo_pkey = *si_pkey_ptr;
+ pkey_assert(siginfo_pkey < NR_PKEYS);
+ last_si_pkey = siginfo_pkey;
if ((si->si_code == SEGV_MAPERR) ||
(si->si_code == SEGV_ACCERR) ||
@@ -294,7 +305,7 @@
dprintf1("signal pkru from xsave: %08x\n", *pkru_ptr);
/* need __rdpkru() version so we do not do shadow_pkru checking */
dprintf1("signal pkru from pkru: %08x\n", __rdpkru());
- dprintf1("si_pkey from siginfo: %jx\n", si_pkey);
+ dprintf1("pkey from siginfo: %jx\n", siginfo_pkey);
*(u64 *)pkru_ptr = 0x00000000;
dprintf1("WARNING: set PRKU=0 to allow faulting instruction to continue\n");
pkru_faults++;
diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c
index eaea924..1e3da13 100644
--- a/tools/testing/selftests/x86/ptrace_syscall.c
+++ b/tools/testing/selftests/x86/ptrace_syscall.c
@@ -182,8 +182,10 @@
if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
err(1, "PTRACE_TRACEME");
+ pid_t pid = getpid(), tid = syscall(SYS_gettid);
+
printf("\tChild will make one syscall\n");
- raise(SIGSTOP);
+ syscall(SYS_tgkill, pid, tid, SIGSTOP);
syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
_exit(0);
@@ -300,9 +302,11 @@
if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
err(1, "PTRACE_TRACEME");
+ pid_t pid = getpid(), tid = syscall(SYS_gettid);
+
printf("\tChild will take a nap until signaled\n");
setsigign(SIGUSR1, SA_RESTART);
- raise(SIGSTOP);
+ syscall(SYS_tgkill, pid, tid, SIGSTOP);
syscall(SYS_pause, 0, 0, 0, 0, 0, 0);
_exit(0);
diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c
index a0972de..1935565 100644
--- a/tools/usb/usbip/src/usbipd.c
+++ b/tools/usb/usbip/src/usbipd.c
@@ -463,7 +463,7 @@
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
act.sa_handler = SIG_IGN;
- sigaction(SIGCLD, &act, NULL);
+ sigaction(SIGCHLD, &act, NULL);
}
static const char *pid_file;
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 31f5625..1ebbf23 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -208,8 +208,8 @@
u8 prop;
int ret;
- ret = kvm_read_guest(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
- &prop, 1);
+ ret = kvm_read_guest_lock(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
+ &prop, 1);
if (ret)
return ret;
@@ -339,8 +339,9 @@
* this very same byte in the last iteration. Reuse that.
*/
if (byte_offset != last_byte_offset) {
- ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
- &pendmask, 1);
+ ret = kvm_read_guest_lock(vcpu->kvm,
+ pendbase + byte_offset,
+ &pendmask, 1);
if (ret) {
kfree(intids);
return ret;
@@ -628,7 +629,7 @@
return false;
/* Each 1st level entry is represented by a 64-bit value. */
- if (kvm_read_guest(its->dev->kvm,
+ if (kvm_read_guest_lock(its->dev->kvm,
BASER_ADDRESS(baser) + index * sizeof(indirect_ptr),
&indirect_ptr, sizeof(indirect_ptr)))
return false;
@@ -1152,8 +1153,8 @@
cbaser = CBASER_ADDRESS(its->cbaser);
while (its->cwriter != its->creadr) {
- int ret = kvm_read_guest(kvm, cbaser + its->creadr,
- cmd_buf, ITS_CMD_SIZE);
+ int ret = kvm_read_guest_lock(kvm, cbaser + its->creadr,
+ cmd_buf, ITS_CMD_SIZE);
/*
* If kvm_read_guest() fails, this could be due to the guest
* programming a bogus value in CBASER or something else going
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 1b20768..eaae725 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -976,8 +976,7 @@
/* Check for overlaps */
r = -EEXIST;
kvm_for_each_memslot(slot, __kvm_memslots(kvm, as_id)) {
- if ((slot->id >= KVM_USER_MEM_SLOTS) ||
- (slot->id == id))
+ if (slot->id == id)
continue;
if (!((base_gfn + npages <= slot->base_gfn) ||
(base_gfn >= slot->base_gfn + slot->npages)))